--- name: fluent2-design-system description: > Build interfaces using Microsoft's Fluent 2 design system via @fluentui/react-components (v9). Use when the user requests UI built with Fluent UI, Fluent 2, Microsoft design language, Teams-style UI, or Office-style interfaces. Covers: React component usage, theming with FluentProvider, styling with makeStyles/tokens/Griffel, design token application, layout patterns, typography, color system, accessibility, dark/light/high-contrast themes, and custom branding. Also triggers for: "make it look like Teams/Outlook/Office", "use Fluent", "Microsoft design system", "@fluentui", or any request to build UI that follows Microsoft's design language. Do NOT use for Fluent UI v8 (@fluentui/react) unless migrating to v9. --- # Fluent 2 Design System Build production-grade interfaces using Microsoft's Fluent 2 design system with `@fluentui/react-components` (v9). ## Quick Start Every Fluent 2 React app requires a `FluentProvider` wrapping the component tree with a theme: ```jsx import { FluentProvider, webLightTheme, webDarkTheme, Button, tokens, makeStyles, mergeClasses, } from "@fluentui/react-components"; export default function App() { return ( ); } ``` Install: `npm install @fluentui/react-components` ## Core Architecture ### Theming - **Built-in themes**: `webLightTheme`, `webDarkTheme`, `teamsLightTheme`, `teamsDarkTheme`, `teamsHighContrastTheme` - **Custom branding**: Use `createLightTheme(brandRamp)` / `createDarkTheme(brandRamp)` with a `BrandVariants` object (keys `10`–`160`) - **Nesting**: `FluentProvider` can nest for sub-trees with different themes - Theme values are emitted as CSS custom properties on the provider element ### Styling with Griffel Use `makeStyles` (from `@fluentui/react-components`) — never inline styles or external CSS for token-aware styling. ```jsx const useStyles = makeStyles({ root: { backgroundColor: tokens.colorNeutralBackground1, color: tokens.colorNeutralForeground1, display: "flex", gap: tokens.spacingHorizontalM, padding: tokens.spacingVerticalM, }, active: { backgroundColor: tokens.colorBrandBackground, color: tokens.colorNeutralForegroundOnBrand, }, }); function MyComponent({ isActive }) { const styles = useStyles(); return (
Content
); } ``` **Critical rules**: - Define `makeStyles` outside components (module scope) - Use `mergeClasses()` to compose classes — never concatenate strings - Use `tokens.*` for all colors, spacing, typography, radii, shadows — never hardcode hex/px values - CSS shorthands (`border`, `borderRadius`, `padding`, etc.) are **not supported** — use `shorthands.*` helper or longhand properties - Pseudo-selectors use nested objects: `":hover": { color: tokens.colorBrandForeground1 }` - Media queries use nested objects: `"@media (min-width: 768px)": { flexDirection: "row" }` ### Component Model All v9 components follow a consistent pattern: - **Slots**: Named sub-parts (e.g., `root`, `icon`, `content`) that accept props or JSX - **Appearance variants**: `"primary"`, `"secondary"`, `"subtle"`, `"transparent"`, `"outline"` - **Size variants**: `"small"`, `"medium"`, `"large"` - **Shape variants**: `"rounded"`, `"circular"`, `"square"` Override component styles via `className` with `makeStyles`/`mergeClasses`. ## Design Tokens Tokens are the bridge between design intent and code. Always use `tokens.*` — never raw values. **For complete token reference tables (color, typography, spacing, elevation, stroke, corner radius), see** `references/tokens.md`. ### Token Categories at a Glance | Category | Prefix | Example | |---|---|---| | Neutral colors | `colorNeutral*` | `tokens.colorNeutralBackground1` | | Brand colors | `colorBrand*` | `tokens.colorBrandBackground` | | Status colors | `colorPalette{Color}*` | `tokens.colorPaletteRedForeground1` | | Typography | `fontFamily*`, `fontSize*`, `fontWeight*`, `lineHeight*` | `tokens.fontSizeBase300` | | Spacing | `spacingHorizontal*`, `spacingVertical*` | `tokens.spacingHorizontalM` | | Border radius | `borderRadius*` | `tokens.borderRadiusMedium` | | Stroke width | `strokeWidth*` | `tokens.strokeWidthThin` | | Shadow | `shadow*` | `tokens.shadow4` | | Duration | `duration*` | `tokens.durationNormal` | | Easing | `curve*` | `tokens.curveEasyEase` | ### Two-Layer Token System 1. **Global tokens** — context-agnostic raw values (e.g., `colorBlue60`, `fontSize300`) 2. **Alias tokens** — semantic meaning applied to globals (e.g., `colorBrandBackground` → blue, `colorNeutralForeground1` → dark gray) In code, consume alias tokens via the `tokens` object. ## Layout - **Grid**: Use CSS Grid/Flexbox — Fluent 2 v9 has no `Stack` component - **Spacing scale**: 4px base unit. Values: `XXS`(2), `XS`(4), `S`(8), `M`(12), `L`(16), `XL`(20), `XXL`(24), `XXXL`(32) - **Column grid**: 12-column framework recommended for web; use CSS grid - **Alignment**: Use `tokens.spacingHorizontal*` and `tokens.spacingVertical*` for consistent spacing ## Typography - **Primary typeface**: Segoe UI (web), Segoe UI Variable (Windows), SF Pro (macOS/iOS), Roboto (Android) - **Font stack**: `tokens.fontFamilyBase` = Segoe UI → system fallbacks → sans-serif - **Monospace**: `tokens.fontFamilyMonospace` - **Text presets**: Use `` component with preset variants — `Caption1`, `Body1`, `Body1Strong`, `Subtitle1`, `Subtitle2`, `Title1`, `Title2`, `Title3`, `LargeTitle`, `Display` - **Alignment**: Left-align for LTR; Fluent handles RTL automatically via Griffel ## Color System Three palettes: 1. **Neutral** — blacks, whites, grays for surfaces, text, layout 2. **Brand** — accent colors reinforcing identity (default Microsoft brand = blue) 3. **Shared/Status** — cross-product colors: red (danger), green (success), yellow (warning), blue (informational) **Interaction states**: rest → hover (darker) → pressed (darkest). Focus adds a thicker stroke, not a color change. ## Accessibility - All components include ARIA roles, keyboard navigation (via Tabster), and focus management - High contrast: use `teamsHighContrastTheme` or handle `@media (forced-colors: active)` with system colors (`ButtonText`, `Highlight`, etc.) - Focus indicators are visible by default — never remove them - Ensure WCAG contrast via semantic token pairing (e.g., `colorNeutralForeground1` on `colorNeutralBackground1`) ## Component Inventory **For the full categorized component list with usage notes, see** `references/components.md`. ### Most-Used Components **Actions**: Button, CompoundButton, SplitButton, ToggleButton, MenuButton, Link **Inputs**: Input, Textarea, Select, Combobox, Dropdown, Checkbox, RadioGroup, Switch, Slider, SpinButton, DatePicker, TimePicker **Layout**: Card, Divider, Drawer, Dialog, Popover, Tooltip **Data Display**: Table, DataGrid, Tree, Accordion, Badge, Avatar, AvatarGroup, Tag, Persona **Navigation**: TabList, Breadcrumb, Nav (preview) **Feedback**: Toast, MessageBar, ProgressBar, Spinner, Skeleton **Surfaces**: Menu, Toolbar, Overflow ## Common Patterns ### App Shell Layout ```jsx const useStyles = makeStyles({ app: { backgroundColor: tokens.colorNeutralBackground1, display: "grid", gridTemplateColumns: "1fr", gridTemplateRows: "auto 1fr auto", height: "100vh", width: "100%", }, header: { borderBottom: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`, paddingTop: tokens.spacingVerticalS, paddingBottom: tokens.spacingVerticalS, paddingLeft: tokens.spacingHorizontalM, paddingRight: tokens.spacingHorizontalM, }, content: { display: "grid", gridTemplateColumns: "280px 1fr", overflow: "hidden", }, nav: { borderRight: `${tokens.strokeWidthThin} solid ${tokens.colorNeutralStroke1}`, overflowY: "auto", }, main: { overflowY: "auto", paddingLeft: tokens.spacingHorizontalL, paddingRight: tokens.spacingHorizontalL, paddingTop: tokens.spacingVerticalL, paddingBottom: tokens.spacingVerticalL, }, }); ``` ### Custom Brand Theme ```jsx import { createLightTheme, createDarkTheme } from "@fluentui/react-components"; const myBrand = { 10: "#020305", 20: "#111723", 30: "#16263D", 40: "#193253", 50: "#1B3F6A", 60: "#1B4C82", 70: "#18599B", 80: "#1267B4", 90: "#3174C2", 100: "#4F82C8", 110: "#6790CF", 120: "#7F9ED5", 130: "#96ADDC", 140: "#ADBCE3", 150: "#C4CBE9", 160: "#DBDBF0", }; const lightTheme = createLightTheme(myBrand); const darkTheme = createDarkTheme(myBrand); ``` ### Dark Mode Toggle ```jsx function App() { const [isDark, setIsDark] = useState(false); return ( setIsDark(data.checked)} /> ); } ``` ## Anti-Patterns - ❌ Hardcoded colors (`color: "#333"`) — use `tokens.colorNeutralForeground1` - ❌ CSS shorthand properties in `makeStyles` (`border: "1px solid red"`) — use longhand or `shorthands.*` - ❌ String concatenation of classNames — use `mergeClasses()` - ❌ Inline `style` props for token-based values — use `makeStyles` with `tokens` - ❌ Importing from `@fluentui/react` (v8) in v9 projects — use `@fluentui/react-components` - ❌ Using v8 `Stack` — use CSS Grid or Flexbox - ❌ Overriding CSS custom properties from the color system directly in CSS — the adaptive color system is JS-driven - ❌ Removing focus indicators — accessibility requirement