Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions apps/public-docsite-v9-headless/.storybook/manager-head.html
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,12 @@
[id*='accessibility-stories'] {
display: none !important;
}

/* Target the brand image container */
.sidebar-header a::after {
content: ' Headless Components';
display: block;
font-size: 12px;
margin-left: 34px;
}
</style>
2 changes: 1 addition & 1 deletion apps/public-docsite-v9-headless/.storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const parameters = {
options: {
storySort: {
method: 'alphabetical',
order: ['Introduction', 'Headless Components'],
order: ['Overview', ['Introduction', 'Getting Started', 'Accessibility'], 'Guides', 'Components', 'Concepts'],
},
},
reactStorybookAddon: {
Expand Down
136 changes: 136 additions & 0 deletions apps/public-docsite-v9-headless/src/Accessibility.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Overview/Accessibility" />

# Accessibility

Fluent UI Headless provides accessible, interactive React components without opinionated styling. Headless components implement WAI-ARIA authoring practices, keyboard navigation patterns, and focus management across a broad range of browsers and assistive technologies.

**Important:** Headless components provide accessibility infrastructure: ARIA attributes, roles, keyboard event handling, and focus management. They **do not provide visual styling**, so your design system must supply visible focus indicators, sufficient color contrast, and clear interactive state feedback.

## Built In, And What You Own

When building custom components with Fluent UI Headless, you own the visual accessibility layer:

- **Visible focus indicators** — Use CSS to style `:focus-visible` with sufficient contrast and size
- **Color contrast** — Ensure text and interactive elements meet WCAG AA (or AAA) color contrast ratios
- **Visual feedback** — Provide clear indication of hover, active, disabled, and error states
- **Touch targets** — Make interactive elements at least 44×44px for mobile accessibility
- **Motion considerations** — Respect `prefers-reduced-motion` media query for users with vestibular disorders

## WAI-ARIA Patterns

Headless components implement standard WAI-ARIA patterns and apply appropriate roles, states, and properties:

| Component | ARIA Pattern | Key Attributes |
| -------------- | ------------- | ---------------------------------------------------------------- |
| Button | Button / Link | `role="button"`, `aria-pressed` (toggle), `aria-disabled` |
| Checkbox | Checkbox | `role="checkbox"`, `aria-checked` |
| Radio Group | Radio Group | `role="radiogroup"`, `role="radio"`, `aria-checked` |
| Switch | Switch | `role="switch"`, `aria-checked` |
| Accordion | Accordion | `role="button"`, `aria-expanded`, `aria-controls` |
| Dialog | Dialog | `role="dialog"`, `aria-modal`, `aria-labelledby` |
| Tooltip | Tooltip | `role="tooltip"` |
| Tabs | Tab List | `role="tablist"`, `role="tab"`, `aria-selected`, `aria-controls` |
| Menu / Popover | Menu | `role="menu"`, `role="menuitem"` |

**Note:** When a component is disabled but should remain focusable (for context), headless components set `aria-disabled="true"` while keeping the element keyboard-accessible. Style both `:disabled` pseudo-classes and `[aria-disabled="true"]` states.

## Keyboard Navigation

Fluent UI Headless components support keyboard interaction patterns consistent with WAI-ARIA authoring practices:

- **Tab/Shift+Tab** — Move focus between components; focus management respects `tabindex`
- **Enter/Space** — Activate buttons, checkboxes, switches, and toggle buttons
- **Arrow Keys** — Navigate within components like radio groups, tab lists, menus, and sliders
- **Home/End** — Jump to first/last item in lists, tabs, and menus (where applicable)
- **Escape** — Close overlays (dialogs, popovers, menus, tooltips)

Each component's keyboard behavior is identical to its styled counterpart in `@fluentui/react-components`. Check individual component docs for pattern-specific details, like arrow key direction for horizontal versus vertical components.

## Accessible Labels

Form controls and custom UI patterns require accessible names so screen readers can identify them:

### Form Controls

Use the native HTML `<label>` element to associate text with form inputs:

```tsx
<label htmlFor="email">Email address</label>
<input id="email" type="email" />
```

For headless `<Input>`, `<Checkbox>`, `<RadioGroup>`, and `<Select>` components, wrap them with a `<Field>` component or manually associate a label:

```tsx
import { Field } from '@fluentui/react-headless-components-preview/field';
import { Input } from '@fluentui/react-headless-components-preview/input';

<Field label="Email">
<Input />
</Field>;
```

### Buttons and Icon-Only Controls

Buttons must have visible or accessible text:

```tsx
// ✅ Visible text
<button>Save</button>

// ✅ aria-label for icon-only buttons
<button aria-label="Close dialog">×</button>

// ❌ Avoid: unlabeled buttons
<button></button>
```

### Custom Controls

For complex components without standard labels, provide an accessible name using:

- `aria-label` — Direct label text on the element
- `aria-labelledby` — Reference to another element's id
- `aria-describedby` — Additional description for context

```tsx
<div
role="combobox"
aria-label="Select a team member"
aria-describedby="team-help"
>
{/* component content */}
</div>
<p id="team-help">Start typing to filter the list</p>
```

## Focus Management

Headless hooks automatically manage focus based on user interaction:

- **Logical tab order** — Focus flows naturally through the page; modify with `tabindex` if needed
- **Focus on interaction** — When a user opens a menu or dialog, focus may move to the first menu item or a default action button
- **Disabled elements** — Disabled elements are removed from tab order (unless `disabledFocusable` prop is used)
- **Trapped focus** — Dialogs and overlays keep focus within their boundaries during keyboard navigation

**Your responsibility:** Ensure focused elements are **visually obvious** with a `:focus-visible` style. Don't remove the default focus outline without providing an alternative visual indicator.

## Testing Your Accessibility

- **Keyboard navigation** — Tab through your interface; verify arrow keys work in appropriate components
- **Screen reader** — Test with NVDA (Windows), JAWS, or VoiceOver (macOS/iOS)
- **Color contrast** — Use tools like WebAIM or Axe to verify WCAG AA or AAA ratios
- **Focus visibility** — Ensure all interactive elements show a clear focus indicator
- **Responsive behavior** — Test touch interactions on mobile; verify 44×44px minimum target size
- **Motion sensitivity** — Test with `prefers-reduced-motion: reduce` enabled in your OS

Fluent UI Headless components are tested across a wide spectrum of browsers, devices, and assistive technologies. The ARIA patterns are battle-tested; focus on ensuring your **custom styling layer** maintains accessibility.

## Resources

- [WAI-ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/)
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)
- [WebAIM Color Contrast Checker](https://webaim.org/resources/contrastchecker/)
- [APCA (Advanced Perceptual Contrast Algorithm)](https://contrast-ratio.com/#%23ffffff-on-%23000000)
112 changes: 112 additions & 0 deletions apps/public-docsite-v9-headless/src/GettingStarted.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Overview/Getting Started" />

# Get started

## Install the package

```bash
npm install @fluentui/react-headless-components-preview
```

Fluent UI Headless ships as a single package and is tree-shakable, so your bundle includes only the components you import.

You can also install with your preferred package manager:

```bash
yarn add @fluentui/react-headless-components-preview
pnpm add @fluentui/react-headless-components-preview
```

## Build your first component

Import a headless component and compose its subcomponents. Headless components provide ARIA attributes, keyboard handling, and interaction logic out of the box.

You are responsible for the visual layer, including focus indicators, color contrast, and state styling.

```jsx
import {
Accordion,
AccordionItem,
AccordionHeader,
AccordionPanel,
} from '@fluentui/react-headless-components-preview/accordion';

export function MyAccordion() {
return (
<Accordion>
<AccordionItem value="item-1">
<AccordionHeader>Section 1</AccordionHeader>
<AccordionPanel>Content goes here</AccordionPanel>
</AccordionItem>
</Accordion>
);
}
```

## Style with your framework

Fluent UI Headless components are unstyled by default, so you can style them with any approach that fits your stack.

```jsx
// With CSS modules
import styles from './MyAccordion.module.css';

<AccordionHeader className={styles.header}>Section 1</AccordionHeader>;
```

```css
/* MyAccordion.module.css */
.header {
padding: 0.75rem 1rem;
border-radius: 0.5rem;
}

.header:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
```

```jsx
// With Tailwind
<AccordionHeader className="rounded-md px-4 py-3 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2">
Section 1
</AccordionHeader>
```

```jsx
// With styled-components
const StyledHeader = styled(AccordionHeader)`
padding: 0.75rem 1rem;
border-radius: 0.5rem;

&:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
}
`;
```

## First-component accessibility check

Before shipping, verify:

- Keyboard navigation works with Tab, Shift+Tab, Enter, Space, and arrow keys where expected
- Focus indicators are clearly visible on every interactive element
- Text and UI states meet your target contrast standard (WCAG AA or AAA)
- Interactive targets are large enough for touch usage
- Screen readers announce labels and state changes correctly

For detailed guidance, review the [Accessibility overview](/?path=/docs/overview-accessibility--docs).

## Working with LLMs

Each docs page has a "View as Markdown" link at the top. Share that URL with your AI assistant to provide clean, context-rich documentation for Fluent UI concepts and component APIs.

Additionally, the "llms.txt" link in the "Handbook" sidebar helps assistants discover and navigate the docs more effectively.

## Explore more

Visit the [components documentation](/components) to see all available headless components and their APIs.
94 changes: 73 additions & 21 deletions apps/public-docsite-v9-headless/src/Introduction.mdx
Original file line number Diff line number Diff line change
@@ -1,34 +1,86 @@
import { Meta } from '@storybook/addon-docs/blocks';

<Meta title="Introduction" />
import pkg from '@fluentui/react-headless-components-preview/package.json';

## Overview
import { Hero, Features, Feature, WhyGrid, WhyCard } from './Introduction';
import hero from './assets/images/hero.svg';
import composition from './assets/images/features-composition.svg';
import primitives from './assets/images/features-primitives.svg';
import accessibility from './assets/images/features-accessibility.svg';
import whyControl from './assets/images/why-control.svg';
import whyBrand from './assets/images/why-brand.svg';
import whyAccessibility from './assets/images/why-accessibility.svg';
import whyBundle from './assets/images/why-bundle.svg';
import whyFlexibility from './assets/images/why-flexibility.svg';
import whyBattleTested from './assets/images/why-battle-tested.svg';

Fluent UI React Headless Components is a set of unstyled, accessible, and composable React components that implement the Fluent UI design patterns without prescribing any specific styling solution.
<Meta title="Overview/Introduction" />

These components provide the behavior, accessibility, and state management while giving you full control over the visual presentation using your preferred styling approach (e.g., Tailwind CSS, CSS Modules, or any other solution).
<h1 class="fluent">
Fluent UI Headless Components <span class="fluent-version">v{pkg.version}</span>
</h1>

## Getting Started
<Hero src={hero} alt="Fluent headless hero" />

To get started with the headless components package in your project:
# Overview

1. Install the package using `npm` or `yarn`:
**Fluent UI Headless** provides accessible, interactive React components without opinionated styling. Perfect for teams building custom design systems, maintaining brand consistency, or integrating with existing design frameworks.

```
npm install @fluentui/react-headless-components-preview
```
## What you get

2. Import and use the components:
- **Behavior without styling** — Accessible primitives with full keyboard navigation and ARIA support
- **Interaction logic** — Pre-built state management, event handling, and user interactions
- **Zero visual opinions** — No CSS framework lock-in, style components however you want
- **Enterprise-ready** — WCAG 2.1 AA compliant out of the box
- **Framework agnostic** — Pure JavaScript logic you control completely

```tsx
import {
Accordion,
AccordionItem,
AccordionHeader,
AccordionPanel,
} from '@fluentui/react-headless-components-preview';
```
## Features

## Contributing
<Features>
<Feature src={composition} alt="Composition" title="Flexible composition">
Build component hierarchies that match your design system. Slot-based architecture gives you complete control over
structure and content nesting.
</Feature>
<Feature src={primitives} alt="Primitives" title="Unstyled building blocks">
Start from zero styling constraints. Use CSS-in-JS, CSS modules, Tailwind, or any styling approach without conflicts
or overrides.
</Feature>
<Feature src={accessibility} alt="Accessibility" title="Accessible by default">
WCAG 2.1 AA compliant with full keyboard support, screen reader optimization, and semantic HTML. Accessibility is
built in, not bolted on.
</Feature>
</Features>

Refer to the main FluentUI [wiki](https://github.com/microsoft/fluentui/wiki) for detailed instructions on setup and contributing to the package.
## Why choose Fluent UI Headless

Designed for flexibility, maintainability, and performance without sacrificing accessibility or developer experience.

<WhyGrid>
<WhyCard src={whyControl} alt="Complete control" title="Complete control">
No predefined colors, spacing, or typography. You own every pixel of your design system implementation.
</WhyCard>
<WhyCard src={whyBrand} alt="Brand consistency" title="Brand consistency">
Integrate seamlessly with existing design systems. Apply your brand identity without fighting CSS specificity.
</WhyCard>
<WhyCard src={whyAccessibility} alt="Enterprise accessibility" title="Enterprise accessibility">
WCAG 2.1 AA compliant with keyboard navigation and screen reader support out of the box.
</WhyCard>
<WhyCard src={whyBundle} alt="Reduced bundle size" title="Reduced bundle size">
Only ship the styles you use. No unused CSS framework bloat in your production bundles.
</WhyCard>
<WhyCard src={whyFlexibility} alt="Framework flexibility" title="Framework flexibility">
React hooks-based architecture. Easy to adapt patterns to other frameworks if needed.
</WhyCard>
<WhyCard src={whyBattleTested} alt="Battle-tested logic" title="Battle-tested logic">
Leverages years of Fluent UI development. Proven interaction patterns for common components.
</WhyCard>
</WhyGrid>

## Perfect for

- **Custom design systems** — Maintain complete design system control without style conflicts
- **Multi-brand applications** — Easy theming and brand switching without component rewrites
- **Enterprise products** — WCAG 2.1 AA compliance and mature accessibility patterns
- **Minimalist bundles** — Ship only the code you need, no unused styling framework
- **Design-first teams** — Designers control the visual expression, developers ensure accessibility
- **Micro-frontends** — Self-contained components that don't leak styles to other parts of your app
Loading
Loading