diff --git a/docs/pages/Theming/Tokens/Basics.md b/docs/pages/Theming/Tokens/Basics.md index dd7e2d9372..9da2133097 100644 --- a/docs/pages/Theming/Tokens/Basics.md +++ b/docs/pages/Theming/Tokens/Basics.md @@ -74,7 +74,7 @@ export const defaultNotificationTokens: TokenSettings Notes about alias color tokens: -- Different platforms can have different sets of alias tokens; however, the entire set of alias tokens are all defined in the same interface. See (Color.types.ts)[https://github.com/microsoft/fluentui-react-native/blob/main/packages/theming/theme-types/src/Color.types.ts#L453] for this interface and which platforms define which alias tokens. +- Different platforms can have different sets of alias tokens; however, the entire set of alias tokens are all defined in the same interface. See [Color.types.ts](https://github.com/microsoft/fluentui-react-native/blob/main/packages/agentic-design/src/theming/Color.types.ts#L861) for this interface and which platforms define which alias tokens. - As a result, if an alias token is referenced that does not exist for that platform, there won't be any compile-time or run-time errors. Instead, the color shown will default to black. Special case: if accessing a specific color, you can find it in the `globalTokens.color` property. diff --git a/packages/agentic-components/specs/accordion/SPEC.md b/packages/agentic-components/specs/accordion/SPEC.md new file mode 100644 index 0000000000..6d916e274a --- /dev/null +++ b/packages/agentic-components/specs/accordion/SPEC.md @@ -0,0 +1,65 @@ +--- +name: accordion +platform: react-native (Windows, macOS) +description: Atomic collapsible panel with an interactive header and a free body content slot. Covers chevron layout (start/end), expand/collapse state, optional leading icon, focus ring behavior, and body content slot conventions. +argument-hint: "[variant axis or behavior question, e.g. 'chevron end layout' or 'focus ring tokens']" +tokens: tokens.yaml +accessibility: accessibility.md +interaction: interaction.md +usage: usage.md +--- + +## Metadata + +| Field | Value | +| --------- | --------- | +| Type | atomic | +| Component | Accordion | + +This spec covers the Accordion component for React Native (Windows & macOS). React Native tokens are in `tokens.yaml`, React Native interaction guidance (keyboard, focus, animation) is in `interaction.md`, React Native accessibility guidance (ARIA, WCAG, screen reader) is in `accessibility.md`, and shared usage guidance is in `usage.md` — read the relevant companion file before answering. + +Answer design questions directly — lead with rationale, then tokens. The most important principle: the body slot is a free content area — never put information required for the current task inside an accordion. The Expanded axis is a variant property (not a State), and Focused is a boolean component prop — do not conflate these with the State axis. + +--- + +# Accordion + +## Spec + +### Anatomy + +1. **Container** — root auto-layout wrapper; owns the full component width. +2. **Header** — interactive row; owns padding, gap, border radius, and the focus ring. The only interactive surface on the component. +3. **Chevron** — 16px Fluent Iconography instance. ChevronRight at rest (Expanded=False), rotates to ChevronDown when Expanded=True. Position determined by Layout variant: at the leading edge in Chevron start, pushed to the far trailing edge in Chevron end. +4. **Leading icon** — optional 16px Fluent Iconography instance before the title. Shown by default. +5. **Title** — section label text; always Semibold (`body-small-strong`). Fills remaining header width in Chevron start layout; natural width in Chevron end layout. +6. **Body** — content panel visible only when Expanded=True. Accepts any child component via the `Body content` slot. +7. **Content placeholder** — default body content shown when no child is provided. Swap with your component. + +| Slot | Required | Default | +| ------------ | -------- | ------------------- | +| Title | Yes | "Section title" | +| Leading icon | No | Shown | +| Body content | No | Content placeholder | + +--- + +### Variants + +Variant properties are ordered in the design tool: **Layout → Size → State → Expanded**. + +#### Layout + +| Value | When to Use | +| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Chevron start** | Default. Anchors the expand affordance to the leading edge; title fills remaining width. | +| **Chevron end** | When the leading icon is the primary anchor; chevron is pushed to the far end of the header (opposite edge from the title). Title at natural width. | + +#### Expanded + +| Value | Visual | +| --------- | ------------------------------------------------ | +| **False** | Body hidden; chevron points right (ChevronRight) | +| **True** | Body visible; chevron points down (ChevronDown) | + +**Expanded is a variant axis, not a State.** It runs in parallel with State — a Rest+Expanded=True accordion is valid. diff --git a/packages/agentic-components/specs/accordion/accessibility.md b/packages/agentic-components/specs/accordion/accessibility.md new file mode 100644 index 0000000000..d53ea8c275 --- /dev/null +++ b/packages/agentic-components/specs/accordion/accessibility.md @@ -0,0 +1,28 @@ +--- +component: Accordion +platform: react-native (Windows, macOS) +--- + +# Accordion Accessibility (React Native — Windows & macOS) + +## Spec + +- **ARIA role:** The Header should render as a ``) and mark the Badge `aria-hidden="true"`. + +--- + +## Usage + +- **Folding Badge into the host's accessible name is the preferred pattern.** It produces the most natural screen reader output and avoids the double-announce problem. Apply `aria-hidden="true"` to the Badge in this case. +- **Use `role="img"` only when the Badge is the sole signal.** A colored dot next to a channel name indicating unread activity, or a Danger-colored Badge on a list row indicating an error — these need an explicit `aria-label` because nothing else in the DOM communicates the meaning. +- **Icon-only Badges require an `aria-label` describing the meaning, not the icon.** "Warning" or "Verified", not "triangle icon" or "checkmark icon". Because Badge is not focusable, a hover/focus tooltip does not apply as the visible alternative — give sighted users the meaning through adjacent text or the host control's visible label, never the icon glyph alone. +- **Decorative Badge on a Disabled host:** the host control's `aria-disabled` is sufficient. Do not add Badge-specific disabled attributes — Badge has no disabled state. +- **Zoom and reflow:** Badge uses token-driven typography and padding. Avoid fixed widths — let the container size to its content so reflow at 200% zoom works without clipping. +- **Color and color vision deficiency:** Never use Color alone to differentiate Badges in a list (e.g., "the red ones are errors"). Pair with an Icon or include the status word in the host's accessible name. +- **High contrast / forced colors mode:** Outline style and stroke-based color signals may render in system colors. Verify both styles in Windows High Contrast and `forced-colors: active` to ensure the Badge remains visible and distinguishable from adjacent UI. diff --git a/packages/agentic-components/specs/badge/interaction.md b/packages/agentic-components/specs/badge/interaction.md new file mode 100644 index 0000000000..335afeb366 --- /dev/null +++ b/packages/agentic-components/specs/badge/interaction.md @@ -0,0 +1,28 @@ +--- +component: Badge +platform: react-native (Windows, macOS) +--- + +# Badge Interaction (React Native — Windows & macOS) + +## Keyboard navigation + +None — Badge is not focusable and is not part of the tab order. If the host control (Button, NavItem, ListItem, Avatar wrapper) is interactive, that wrapper owns the tab stop and keyboard handlers. Badge content folded into the wrapper's accessible name surfaces in the screen reader announcement; see `accessibility.md`. + +## Focus management + +None — Badge does not receive or manage focus and renders no focus ring. Wrappers that make the host control interactive must render their own focus ring on the wrapper, not on the Badge. + +## Animation + +None on the component itself — Badge has no Hover, Pressed, Focus, or Disabled state to transition between. If a parent control animates Badge in or out (a counter incrementing, a status changing), the parent owns that motion and its tokens. + +> **Reduced motion:** Because Badge has no animation, no reduce-motion accommodation is required on the component. Parents that animate Badge appearance must respect the OS reduce-motion setting themselves — typically by removing the transition entirely rather than swapping to a shorter duration. + +## Positioning + +Badge has no built-in anchoring behavior. Placement against a host control is the consumer's responsibility — typically via absolute positioning at the top-right corner of the host for counter-style Badges, or inline alongside a label for status-style Badges. The Badge container itself flows as a normal inline-block element. + +## Responsive behavior + +Badge sizes via the Size variant (Small or Medium), not via CSS scaling. Layouts that need a different visual scale must pick a different Size variant rather than applying CSS `transform: scale()` or width/height overrides — those would desync typography, padding, and icon size from the token system. diff --git a/packages/agentic-components/specs/badge/tokens.yaml b/packages/agentic-components/specs/badge/tokens.yaml new file mode 100644 index 0000000000..30a7ae1d1f --- /dev/null +++ b/packages/agentic-components/specs/badge/tokens.yaml @@ -0,0 +1,135 @@ +component: Badge +type: atomic + +variants: + Style: + values: [Outline, Tint] + default: Tint + Color: + values: [Brand, Danger, Success, Warning, Informative] + default: Brand + Size: + values: [Small, Medium] + default: Medium + Shape: + values: [Circular, Rounded] + default: Circular + Layout: + values: [Icon and text, Icon only] + default: Icon and text + State: + values: [Rest] + +tokens: + background: + container: + Tint: + Brand: colorBackgroundBrandSoft + Danger: colorBackgroundDangerSoft + Success: colorBackgroundSuccessSoft + Warning: colorBackgroundWarningSoft + Informative: colorBackgroundNeutralSoft + Outline: + all-colors: colorBackgroundNeutralTransparent + + foreground: + container: + all-styles: + Brand: colorForegroundBrandPrimary + Danger: colorForegroundDangerPrimary + Success: colorForegroundSuccessPrimary + Warning: colorForegroundWarningPrimary + Informative: colorForegroundNeutralPrimary + notes: Same foreground tokens drive label text and icon paint in both Tint and Outline. + + stroke: + container: + Tint: + width: 0 + color: no-stroke + Outline: + width: strokeWidthThin + color: + Brand: colorStrokeBrandLoud + Danger: colorStrokeDangerLoud + Success: colorStrokeSuccessLoud + Warning: colorStrokeWarningLoud + Informative: colorStrokeNeutralLoud + focus: n/a + + radius: + container: + Circular: + all-sizes: borderRadiusCircular + Rounded: + Small: borderRadiusBase100 + Medium: borderRadiusBase200 + notes: Rounded radius scales with Size so the corner reads as a softened rectangle at both heights. + + typography: + Small: textstyle-functional-caption + Medium: textstyle-functional-body-small + font-family: fontFamilyFunctional + + spacing: + container: + padding: + Icon and text: + Small: + inline: spacingComponentBase100 + block: 0 + Medium: + inline: spacingComponentBase150 + block: 0 + Icon only: + Small: + inline: 0 + block: 0 + Medium: + inline: 0 + block: 0 + gap: + Small: spacingComponentBase50 + Medium: spacingComponentBase50 + + elevation: n/a + + motion: + status: pending + guidance: Badge has no state transitions. If a parent animates the badge in or out (e.g. a counter incrementing), the parent owns that motion. Reduced motion — no accommodation required on Badge. + +sizes: + Small: + height: 16 + icon: 12 + Medium: + height: 20 + icon: 16 + notes: Heights and icon sizes are px literals — no --gnrc-* sizing tokens exist for these dimensions. + +properties: + component: + Label string: + type: TEXT + default: Badge + notes: Bound to the Label slot. Only visible when Layout = Icon and text. + Leading icon: + type: BOOLEAN + default: true + notes: Shows or hides the Leading icon slot on Layout = Icon and text. Inert on Icon only, where the slot is structurally always visible. + Trailing icon: + type: BOOLEAN + default: false + notes: Shows or hides the Trailing icon slot on Layout = Icon and text. Inert on Icon only. + Leading 16px Regular: + type: INSTANCE_SWAP + default: Image icon (16px Regular) + Leading 12px Regular: + type: INSTANCE_SWAP + default: Image icon (12px Regular) + Trailing 16px Regular: + type: INSTANCE_SWAP + default: Image icon (16px Regular) + Trailing 12px Regular: + type: INSTANCE_SWAP + default: Image icon (12px Regular) diff --git a/packages/agentic-components/specs/badge/usage.md b/packages/agentic-components/specs/badge/usage.md new file mode 100644 index 0000000000..8a780f4a1d --- /dev/null +++ b/packages/agentic-components/specs/badge/usage.md @@ -0,0 +1,58 @@ +--- +component: Badge +--- + +# Badge Usage + +## When to Use + +- To communicate a count of related items (unread messages, pending tasks, notification count) next to the host control they belong to. +- To label content with a short status or category (`New`, `Beta`, `Updated`, `Internal`). +- To surface a quiet, glanceable signal — Badge is read at a glance, not interacted with. + +### vs Tag + +Tag is a **dismissible, interactive** label for keywords, categories, or applied filters — clicking removes it. Badge is a **non-interactive** indicator that decorates another element and never receives focus. Use Tag when the element _is_ the content the user manipulates; use Badge when the element annotates something else. + +### vs Button + +Button performs an action. Badge does not. If users need to act on what the badge represents (e.g., open the inbox the badge counts), the action lives on the surrounding Button, NavItem, or ListItem — never on the Badge itself. + +### vs Message Bar + +Message Bar communicates page-level or section-level status with optional actions. Badge communicates per-item status inline. If the message needs to be read and acknowledged on its own, use Message Bar; if it's a small annotation on a host control, use Badge. + +--- + +## Behavior + +- **Never make Badge interactive.** Badge does not receive focus, does not respond to hover or pressed, and is not part of the tab order. Interactivity belongs to the host control. +- **Always anchor Badge to a host control.** A floating Badge with no obvious owner has no semantic context. Place Badge adjacent to or overlaid on the element it describes. +- **Never use color alone to communicate meaning.** A red Badge conveys nothing to screen readers and nothing to a user with red-green color vision deficiency. Pair color with a label, an icon, or text in the host control's accessible name. See `accessibility.md` for the patterns. +- **Always use the Fluent Iconography instance as the default INSTANCE_SWAP value.** Never use placeholder frames or custom vectors in the Icon slot. + +> **Icon style:** All Badge icons use the **Regular** style. There is no Filled icon variant in v1. + +--- + +## Layout + +- **Match Size to the host.** Small Badge on small icons or avatars (16–24px) and in dense rows; Medium Badge on standard icons or avatars (28–40px) and in regular-density layouts. +- **Pair Shape with the surrounding chrome.** Circular Badge next to circular avatars or icon buttons; Rounded Badge in rectangular contexts (cards, tables, rows of inputs). +- **Anchor counter Badges to the top-right corner of their host.** A counter Badge that floats elsewhere (top-left, bottom) breaks the convention users already have from notification systems. +- **Never resize Badge with CSS to fit a container.** If the size feels wrong, change the Size variant. Resizing breaks token-driven typography and padding ratios. +- **Never mix icon sizes within a Size.** Small uses 12px icons; Medium uses 16px icons. Do not swap a 20px icon into a Medium Badge. + +--- + +## Content + +- **Keep labels short — counter, single word, or two-word maximum.** Badge is read at a glance; long text reflows the host layout and defeats the purpose. For longer text, use Tag or inline body text. +- **Counter values that exceed legible width should truncate with `999+`** (or the locale-equivalent overflow token). Do not let a 4–5 digit count expand the Badge. +- **Use Color semantically, not decoratively.** + - **Brand** — featured, promoted, branded content + - **Danger** — errors, blocked items, destructive state + - **Success** — completed, healthy, confirmed + - **Warning** — caution, attention-needed, non-blocking issue + - **Informative** — quiet metadata, counts, neutral status +- **Avoid mixing colors in a single row.** A row of Badges with mixed Brand / Danger / Success colors creates visual noise. Pick the most relevant color per item or settle on one color across the row. diff --git a/packages/agentic-components/specs/breadcrumb/SPEC.md b/packages/agentic-components/specs/breadcrumb/SPEC.md new file mode 100644 index 0000000000..996588492d --- /dev/null +++ b/packages/agentic-components/specs/breadcrumb/SPEC.md @@ -0,0 +1,102 @@ +--- +name: breadcrumb +platform: react-native (Windows, macOS) +description: Molecular navigation trail that shows a user's hierarchical location; covers interactive BreadcrumbItem (extends Button Subtle), non-interactive CurrentItem, Separator, Overflow (Visible/Collapsed with Popover), Tooltip (overflow count + truncated labels), and Small/Medium/Large sizes. +argument-hint: "[variant axis or behavior question, e.g. 'What token does CurrentItem use?' or 'When should Collapsed overflow be used?']" +tokens: tokens.yaml +accessibility: accessibility.md +interaction: interaction.md +usage: usage.md +--- + +## Metadata + +| Field | Value | +| --------- | ---------- | +| Type | molecular | +| Component | Breadcrumb | + +This spec covers the Breadcrumb component for React Native (Windows & macOS). React Native tokens are in `tokens.yaml`, React Native interaction guidance (keyboard, focus, animation) is in `interaction.md`, React Native accessibility guidance (ARIA, WCAG, screen reader) is in `accessibility.md`, and shared usage guidance is in `usage.md` — read the relevant companion file before answering. + +Answer design questions directly — lead with rationale, then tokens. BreadcrumbItem is a Button instance (Subtle style); treat its size, focus ring, hover/pressed, and typography tokens as fully delegated to Button — do not redeclare them unless a value diverges. CurrentItem is never interactive and must never receive hover, pressed, or focus-ring treatment. + +--- + +# Breadcrumb + +## Spec + +## Dependencies + +- **Button** (`flex-components:button`) — BreadcrumbItem is a Button instance at Subtle style (Icon and text layout, or Icon only for the OverflowButton). Size axes map 1:1. Interaction states, focus ring, typography, padding, and icon slots all delegate to Button. +- **Popover** (`flex-components:popover`) — the overflow Popover surface. Activated by the OverflowButton in the Collapsed variant; all container tokens (surface, stroke, radius, shadow, elevation) come from Popover. +- **Menu / MenuItem** (`flex-components:menu`, `flex-components:menu-item`) — the overflow Popover's content. Hidden middle items render as MenuItem List Item instances; container padding follows Menu's override of Popover padding. +- **Tooltip** (`flex-components:tooltip`) — surfaces two types of contextual labels: (1) an item-count preview on hover/focus over the OverflowButton before the Popover is opened; (2) the full label of a truncated BreadcrumbItem on hover/focus. + +--- + +### Anatomy + +1. **Container** — outer `