Entity Header
{
{
"kind": "component"
},
{
"displayName": "Button"
},
{
"summary": "An interactive element that triggers an action when activated."
},
{
"description": "An interactive element that triggers an action when activated. Buttons communicate what will happen when the user interacts with them and are the primary mechanism for initiating actions within a surface."
},
{
"status": {
"overall": "stable",
"platforms": {
"react": {
"status": "stable",
"since": "1.0.0"
},
"web-component": {
"status": "experimental",
"since": "3.2.0",
"description": "Available as a Web Component wrapper. Native shadow DOM implementation planned for v4."
},
"ios": {
"status": "stable",
"since": "2.1.0"
},
"android": {
"status": "draft",
"description": "Compose implementation in progress. Expected in v4.0."
},
"figma": {
"status": "stable",
"since": "1.0.0"
}
}
}
},
{
"since": "1.0.0"
},
{
"tags": [
"action",
"form",
"interactive"
]
},
{
"category": "action"
},
{
"aliases": [
"btn",
"action-button",
"CTA"
]
}
}Button
An interactive element that triggers an action when activated.
An interactive element that triggers an action when activated. Buttons communicate what will happen when the user interacts with them and are the primary mechanism for initiating actions within a surface.
| Platform | Status | Since | Note |
|---|---|---|---|
| react | 1.0.0 | ||
| web-component | 3.2.0 | Available as a Web Component wrapper. Native shadow DOM implementation planned for v4. | |
| ios | 2.1.0 | ||
| android | — | Compose implementation in progress. Expected in v4.0. | |
| figma | 1.0.0 |
Anatomy
"description": "The Button is composed of a container, a text label, and an optional leading or trailing icon.", "parts": [ { "name": "container", "displayName": "Container", "description": "The outer boundary of the button. Receives background color, border, border radius, and padding. Defines the clickable area.", "required": true, "tokens": { "background": "button-background", "border-color": "button-border-color", "border-width": "button-border-width", "border-radius": "button-border-radius", "padding-horizontal": "button-padding-horizontal", "padding-vertical": "button-padding-vertical" } }, { "name": "label", "displayName": "Label", "description": "The text content of the button. Communicates the action that will occur on activation.", "required": true, "tokens": { "font-family": "button-font-family", "font-size": "button-font-size", "font-weight": "button-font-weight", "line-height": "button-line-height", "text-color": "button-text-color" } }, { "name": "icon", "displayName": "Icon", "description": "An optional icon displayed before (leading) or after (trailing) the label. Reinforces the label's meaning visually.", "required": false, "tokens": { "size": "button-icon-size", "color": "button-icon-color", "gap": "button-icon-gap" } }, { "name": "focus-ring", "displayName": "Focus Ring", "description": "A visible outline rendered when the button receives keyboard focus. Not displayed on mouse interaction.", "required": true, "tokens": { "color": "button-focus-ring-color", "width": "button-focus-ring-width", "offset": "button-focus-ring-offset" } } ] , { "preview": [ { "title": "Anatomy diagram", "presentation": { "kind": "image", "url": "https://design.acme.com/assets/button-anatomy.png", "alt": "An annotated diagram of a primary button with numbered callouts: 1. Container, 2. Label, 3. Icon (optional), 4. Focus ring (shown in dashed outline)." } } ] } , { "agents": { "intent": "Describe the visual parts that compose a Button so agents can map design tokens to the correct sub-elements.", "keywords": [ "parts", "container", "label", "icon", "focus ring", "structure" ] } }
Anatomy
The Button is composed of a container, a text label, and an optional leading or trailing icon.
| Part | Required | Description |
|---|---|---|
| Container | Yes | The outer boundary of the button. Receives background color, border, border radius, and padding. Defines the clickable area. |
| Label | Yes | The text content of the button. Communicates the action that will occur on activation. |
| Icon | No | An optional icon displayed before (leading) or after (trailing) the label. Reinforces the label's meaning visually. |
| Focus Ring | Yes | A visible outline rendered when the button receives keyboard focus. Not displayed on mouse interaction. |
Agent Context
Describe the visual parts that compose a Button so agents can map design tokens to the correct sub-elements.
API
"properties": [ { "name": "variant", "type": "'primary' | 'secondary' | 'ghost' | 'danger'", "schema": { "type": "string", "enum": [ "primary", "secondary", "ghost", "danger" ], "default": "primary" }, "description": "The visual style of the button. Determines background color, text color, and border treatment.", "required": false, "defaultValue": "primary", "since": "1.0.0" }, { "name": "size", "type": "'small' | 'medium' | 'large'", "schema": { "type": "string", "enum": [ "small", "medium", "large" ], "default": "medium" }, "description": "The size of the button. Affects padding, font size, icon size, and minimum target area.", "required": false, "defaultValue": "medium", "since": "1.0.0" }, { "name": "disabled", "type": "boolean", "schema": { "type": "boolean", "default": false }, "description": "When true, the button is non-interactive. The cursor changes to not-allowed, and the button is visually dimmed to 40% opacity.", "required": false, "defaultValue": false, "since": "1.0.0" }, { "name": "loading", "type": "boolean", "schema": { "type": "boolean", "default": false }, "description": "When true, the label is replaced with a spinner and the button is non-interactive. The button retains its dimensions to prevent layout shift.", "required": false, "defaultValue": false, "since": "2.1.0" }, { "name": "fullWidth", "type": "boolean", "schema": { "type": "boolean", "default": false }, "description": "When true, the button expands to fill the width of its parent container.", "required": false, "defaultValue": false, "since": "1.2.0" }, { "name": "iconStart", "type": "IconComponent", "description": "An icon component rendered before the label. When provided without a label, an aria-label is required.", "required": false, "since": "2.0.0" }, { "name": "iconEnd", "type": "IconComponent", "description": "An icon component rendered after the label.", "required": false, "since": "2.0.0" }, { "name": "type", "type": "'button' | 'submit' | 'reset'", "schema": { "type": "string", "enum": [ "button", "submit", "reset" ], "default": "button" }, "description": "The HTML button type attribute. Controls form submission behavior.", "required": false, "defaultValue": "button", "since": "1.0.0" } ], "events": [ { "name": "onClick", "description": "Fires when the button is activated via mouse click, touch tap, Enter key, or Space key. Does not fire when the button is disabled or loading.", "payload": "(event: MouseEvent) => void", "since": "1.0.0" }, { "name": "onFocus", "description": "Fires when the button receives focus.", "payload": "(event: FocusEvent) => void", "since": "1.0.0" }, { "name": "onBlur", "description": "Fires when the button loses focus.", "payload": "(event: FocusEvent) => void", "since": "1.0.0" } ], "slots": [ { "name": "default", "description": "The button's text label.", "acceptedContent": "Plain text or a text node. Do not nest interactive elements, headings, or block-level elements." } ] "cssCustomProperties": [ { "name": "--button-background", "description": "The background color of the button container.", "type": "color", "since": "1.0.0", "defaultValue": "var(--color-action-primary)" }, { "name": "--button-text-color", "description": "The color of the label text.", "type": "color", "since": "1.0.0", "defaultValue": "var(--color-text-on-action)" }, { "name": "--button-border-radius", "description": "The border radius of the button container.", "type": "dimension", "since": "1.0.0", "defaultValue": "var(--radius-medium)" } ] , { "agents": { "intent": "Define the code-level interface of the Button component — properties, events, slots, and CSS hooks.", "constraints": [ { "rule": "Always set the type attribute explicitly; never rely on the browser default.", "level": "should" }, { "rule": "When iconStart is used without a label, an aria-label must be provided.", "level": "must" } ], "keywords": [ "props", "events", "slots", "CSS custom properties", "interface", "API" ] } }
API
Properties
| Name | Type | Default | Description |
|---|---|---|---|
| The visual style of the button. Determines background color, text color, and border treatment. | |||
| The size of the button. Affects padding, font size, icon size, and minimum target area. | |||
| When true, the button is non-interactive. The cursor changes to not-allowed, and the button is visually dimmed to 40% opacity. | |||
| When true, the label is replaced with a spinner and the button is non-interactive. The button retains its dimensions to prevent layout shift. | |||
| When true, the button expands to fill the width of its parent container. | |||
| — | An icon component rendered before the label. When provided without a label, an aria-label is required. | ||
| — | An icon component rendered after the label. | ||
| The HTML button type attribute. Controls form submission behavior. |
Events
| Name | Payload | Description |
|---|---|---|
| Fires when the button is activated via mouse click, touch tap, Enter key, or Space key. Does not fire when the button is disabled or loading. | ||
| Fires when the button receives focus. | ||
| Fires when the button loses focus. |
Slots
| Name | Description |
|---|---|
| The button's text label. |
CSS Custom Properties
| Property | Type | Default | Description |
|---|---|---|---|
| color | The background color of the button container. | ||
| color | The color of the label text. | ||
| dimension | The border radius of the button container. |
Agent Context
Define the code-level interface of the Button component — properties, events, slots, and CSS hooks.
| Level | Rule |
|---|---|
| Always set the type attribute explicitly; never rely on the browser default. | |
| When iconStart is used without a label, an aria-label must be provided. |
Events
"items": [ { "name": "onClick", "description": "Fires when the button is activated via mouse click, touch tap, Enter key, or Space key. Does not fire when the button is disabled or loading.", "payload": "(event: MouseEvent) => void", "bubbles": true, "cancelable": false, "since": "1.0.0" }, { "name": "onFocus", "description": "Fires when the button receives focus via keyboard tab, programmatic focus, or mouse click. Use to show contextual help or tooltips.", "payload": "(event: FocusEvent) => void", "bubbles": false, "cancelable": false, "since": "1.0.0" }, { "name": "onBlur", "description": "Fires when the button loses focus. Use to dismiss contextual help or validate inline state.", "payload": "(event: FocusEvent) => void", "bubbles": false, "cancelable": false, "since": "1.0.0" } ] , { "agents": { "intent": "Document the events emitted by the Button so agents can wire up correct event handlers.", "constraints": [ { "rule": "onClick does not fire when disabled or loading — do not attach workaround handlers.", "level": "must-not" } ], "keywords": [ "events", "onClick", "onFocus", "onBlur", "handlers", "callbacks" ] } }
Events
| Event | Payload | Description |
|---|---|---|
| onClick (bubbles) | (event: MouseEvent) => void | Fires when the button is activated via mouse click, touch tap, Enter key, or Space key. Does not fire when the button is disabled or loading. |
| onFocus | (event: FocusEvent) => void | Fires when the button receives focus via keyboard tab, programmatic focus, or mouse click. Use to show contextual help or tooltips. |
| onBlur | (event: FocusEvent) => void | Fires when the button loses focus. Use to dismiss contextual help or validate inline state. |
Agent Context
Document the events emitted by the Button so agents can wire up correct event handlers.
| Level | Rule |
|---|---|
| onClick does not fire when disabled or loading — do not attach workaround handlers. |
Variants
"items": [ { { "kind": "enum", "name": "emphasis", "displayName": "Emphasis", "description": "Controls the visual weight of the button. Determines background fill, border treatment, and text color to establish a visual hierarchy among actions on a surface." }, "values": [ { "name": "primary", "displayName": "Primary", "description": "High-emphasis — the main action on the surface. Uses a solid, filled background. Limit to one primary button per surface.", "purpose": { "kind": "purpose", "useCases": [ { "description": "When the action is the most important on the surface — the one the user is most likely to take (e.g., Save, Submit, Confirm).", "kind": "positive" }, { "description": "When a surface already has a primary button. Adding a second dilutes visual hierarchy.", "kind": "negative", "alternative": { "name": "secondary", "rationale": "Secondary emphasis maintains importance without competing with the existing primary action." } } ] } }, { "name": "secondary", "displayName": "Secondary", "description": "Medium-emphasis — important but not the primary action. Uses a visible border and transparent background.", "purpose": { "kind": "purpose", "useCases": [ { "description": "When the action is important but secondary to a primary action on the same surface (e.g., Cancel alongside Save).", "kind": "positive" } ] } }, { "name": "ghost", "displayName": "Ghost", "description": "Low-emphasis — tertiary actions, toolbar actions, or dense layouts. No background or border in the default state.", "purpose": { "kind": "purpose", "useCases": [ { "description": "When the action is tertiary or supplementary — helpful but not essential to the user's primary task.", "kind": "positive" }, { "description": "When the action is the only action on the surface and needs to be clearly discoverable.", "kind": "negative", "alternative": { "name": "secondary", "rationale": "A ghost button on its own can be overlooked. Secondary emphasis provides enough visual presence to be discoverable." } } ] } }, { "name": "danger", "displayName": "Danger", "description": "High-emphasis destructive — signals an irreversible action. Uses the danger color. Pair with a confirmation dialog.", "purpose": { "kind": "purpose", "useCases": [ { "description": "When the action is destructive or irreversible — deleting a record, revoking access, removing a team member.", "kind": "positive" }, { "description": "When the action is not destructive, even if it feels important or urgent.", "kind": "negative", "alternative": { "name": "primary", "rationale": "The danger color is a strong signal reserved for destruction. Using it for non-destructive actions dilutes its meaning." } } ] } } ] }, { { "kind": "enum", "name": "size", "displayName": "Size", "description": "Controls the physical dimensions of the button — padding, font size, icon size, and minimum touch target area." }, "values": [ { "name": "sm", "displayName": "Small", "description": "Compact size for toolbars and dense layouts. 32px height." }, { "name": "md", "displayName": "Medium", "description": "Default size for most contexts. 40px height." }, { "name": "lg", "displayName": "Large", "description": "Touch-optimized size for mobile-first surfaces. 48px height." } ] } ] , { "agents": { "intent": "List the visual and behavioral dimensions along which the Button varies — emphasis and size.", "constraints": [ { "rule": "Limit each surface to one primary variant.", "level": "must" }, { "rule": "Use danger variant only for destructive or irreversible actions.", "level": "must" } ], "disambiguation": [ { "entity": "states", "distinction": "Variants are static design dimensions chosen at author time; states are dynamic conditions that change at runtime in response to user interaction." } ], "keywords": [ "emphasis", "primary", "secondary", "ghost", "danger", "size", "small", "medium", "large" ] } }
Variants
Emphasis (enum)
Controls the visual weight of the button. Determines background fill, border treatment, and text color to establish a visual hierarchy among actions on a surface.
Primary primary
High-emphasis — the main action on the surface. Uses a solid, filled background. Limit to one primary button per surface.
Secondary secondary
Medium-emphasis — important but not the primary action. Uses a visible border and transparent background.
Ghost ghost
Low-emphasis — tertiary actions, toolbar actions, or dense layouts. No background or border in the default state.
Danger danger
High-emphasis destructive — signals an irreversible action. Uses the danger color. Pair with a confirmation dialog.
Size (enum)
Controls the physical dimensions of the button — padding, font size, icon size, and minimum touch target area.
Small sm
Compact size for toolbars and dense layouts. 32px height.
Medium md
Default size for most contexts. 40px height.
Large lg
Touch-optimized size for mobile-first surfaces. 48px height.
Agent Context
List the visual and behavioral dimensions along which the Button varies — emphasis and size.
| Level | Rule |
|---|---|
| Limit each surface to one primary variant. | |
| Use danger variant only for destructive or irreversible actions. |
| Entity | Distinction |
|---|---|
| Variants are static design dimensions chosen at author time; states are dynamic conditions that change at runtime in response to user interaction. |
States
"items": [ { "name": "default", "displayName": "Default", "description": "The button's resting state when no interaction is occurring." }, { "name": "hover", "displayName": "Hover", "description": "Triggered when the user's pointer moves over the button. The background darkens by 8% to indicate interactivity. Not applicable on touch devices.", "tokens": { "button-background": "color-action-primary-hover" } }, { "name": "active", "displayName": "Active / Pressed", "description": "Triggered while the button is being pressed (mousedown or touch start). The background darkens by 16% from the default to indicate activation.", "tokens": { "button-background": "color-action-primary-active" } }, { "name": "focus", "displayName": "Focus", "description": "Triggered when the button receives keyboard focus. A 2px focus ring appears with a 2px offset from the container edge.", "tokens": { "button-focus-ring-color": "color-focus-ring", "button-focus-ring-width": "border-width-focus", "button-focus-ring-offset": "space-focus-offset" } }, { "name": "disabled", "displayName": "Disabled", "description": "The button is non-interactive. Opacity is reduced to 0.4. Pointer events are disabled. The button remains in the tab order when using aria-disabled instead of the HTML disabled attribute." }, { "name": "loading", "displayName": "Loading", "description": "The button label is replaced by a spinner animation. The button is non-interactive. The button maintains its dimensions from the default state to prevent layout shift." } ] , { "agents": { "intent": "Document the interactive states a Button can enter at runtime and the token overrides each state applies.", "disambiguation": [ { "entity": "variants", "distinction": "States are runtime conditions triggered by user interaction (hover, focus, disabled); variants are static design choices made at author time." } ], "keywords": [ "hover", "active", "focus", "disabled", "loading", "pressed", "interactive" ] } }
States
| State | Description | Token Overrides |
|---|---|---|
| Default | The button's resting state when no interaction is occurring. | — |
| Hover | Triggered when the user's pointer moves over the button. The background darkens by 8% to indicate interactivity. Not applicable on touch devices. | |
| Active / Pressed | Triggered while the button is being pressed (mousedown or touch start). The background darkens by 16% from the default to indicate activation. | |
| Focus | Triggered when the button receives keyboard focus. A 2px focus ring appears with a 2px offset from the container edge. | |
| Disabled | The button is non-interactive. Opacity is reduced to 0.4. Pointer events are disabled. The button remains in the tab order when using aria-disabled instead of the HTML disabled attribute. | — |
| Loading | The button label is replaced by a spinner animation. The button is non-interactive. The button maintains its dimensions from the default state to prevent layout shift. | — |
Agent Context
Document the interactive states a Button can enter at runtime and the token overrides each state applies.
| Entity | Distinction |
|---|---|
| States are runtime conditions triggered by user interaction (hover, focus, disabled); variants are static design choices made at author time. |
Purpose
"useCases": [ { "description": "When the user needs to trigger an action such as submitting a form, saving data, opening a dialog, or confirming a decision.", "kind": "positive" }, { "description": "When a destructive or irreversible action needs to be initiated, such as deleting a record or revoking access. Pair with a confirmation dialog.", "kind": "positive" }, { "description": "When the action navigates the user to a different page or URL.", "kind": "negative", "alternative": { "name": "link", "rationale": "Links carry native navigation semantics. Screen readers announce them as links, and browsers support standard navigation behaviors such as open-in-new-tab." } }, { "description": "When the user needs to select one option from a set of mutually exclusive choices.", "kind": "negative", "alternative": { "name": "radio-group", "rationale": "Radio groups communicate exclusivity through their semantic role. A set of buttons styled to look like a selector does not convey mutual exclusivity to assistive technology." } }, { "description": "When the only content is an icon with no visible text label.", "kind": "negative", "alternative": { "name": "icon-button", "rationale": "Icon buttons enforce an aria-label requirement and apply size adjustments for icon-only touch targets. A standard button with its label removed may fail accessibility requirements silently." } } ] , { "agents": { "intent": "Clarify when to use Button versus similar interactive elements.", "disambiguation": [ { "entity": "link", "distinction": "Button triggers an in-page action; Link navigates to a URL." }, { "entity": "icon-button", "distinction": "Button always has a visible text label; IconButton is icon-only and requires aria-label." } ], "keywords": [ "when to use", "use case", "action", "submit", "navigation" ] } }
Purpose
When to Use
When Not to Use
Agent Context
Clarify when to use Button versus similar interactive elements.
| Entity | Distinction |
|---|---|
| Button triggers an in-page action; Link navigates to a URL. | |
| Button always has a visible text label; IconButton is icon-only and requires aria-label. |
Guidelines
"items": [ { "guidance": "Limit each surface to one primary button.", "rationale": "Multiple primary buttons dilute visual hierarchy. When everything is emphasized, nothing is. A single primary button directs the user to the most important action.", "kind": "required", "category": "visual-design" }, { "guidance": "Place the primary button on the right side of a button group in left-to-right layouts.", "rationale": "Users scan in the direction of the layout's reading order. Placing the primary action at the natural endpoint aligns with the completion point of reading.", "kind": "encouraged", "category": "visual-design" }, { "guidance": "Do not use a Button when the action navigates the user to a different page or URL. Use a Link component instead.", "rationale": "Buttons and links have different semantic roles. Buttons trigger actions (submit, open, close). Links navigate. Screen reader users rely on element role to anticipate behavior.", "kind": "prohibited", "category": "visual-design" }, { "guidance": "Use the danger variant exclusively for destructive or irreversible actions. Pair danger buttons with a confirmation dialog.", "rationale": "Red is a strong signal. If danger styling is used for non-destructive actions, it dilutes the warning signal and conditions users to ignore it.", "kind": "required", "category": "visual-design" }, { "guidance": "Use the loading state instead of disabling the button during asynchronous operations.", "rationale": "A disabled button gives no feedback that an action is in progress. The loading state communicates that the action was registered and the system is working.", "kind": "encouraged", "category": "interaction" }, { "guidance": "Do not wrap a button's label text across multiple lines.", "rationale": "Multi-line button labels are harder to scan and create inconsistent button heights in groups. If the label is too long, rewrite it to be shorter.", "kind": "prohibited", "category": "visual-design" }, { "guidance": "Maintain a minimum tap target of 44x44 CSS pixels for all button sizes.", "rationale": "The WCAG 2.5.8 target size criterion requires a minimum 24x24px target, with 44x44px recommended. Touch devices require larger targets to prevent mis-taps.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#target-size-minimum" } ] }, { "guidance": "Use a verb or verb phrase that describes the action the button performs. Two words maximum.", "rationale": "Action-oriented labels set clear expectations about what will happen on activation. Short labels prevent truncation on narrow viewports.", "kind": "required", "category": "content", "target": "label" }, { "guidance": "Use sentence case capitalization.", "rationale": "Sentence case is easier to read than title case or all caps. It also localizes more predictably across languages where capitalization rules differ.", "kind": "required", "category": "content", "target": "label" }, { "guidance": "When using an icon-only button (no visible label), provide an aria-label that describes the action.", "rationale": "Screen readers announce button content as the accessible name. Without visible text, there is no accessible name. The aria-label provides one.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#name-role-value" } ] }, { "guidance": "Use the native <button> element. Do not recreate button behavior on a <div> or <span>.", "rationale": "Native buttons provide built-in keyboard interaction (Enter, Space), focus management, and form submission behavior. Recreating this on a non-semantic element is error-prone.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#name-role-value" } ] }, { "guidance": "Prefer aria-disabled=\"true\" over the HTML disabled attribute when the button should remain discoverable by screen reader users.", "rationale": "The HTML disabled attribute removes the button from the tab order, making it invisible to keyboard users. aria-disabled keeps the button focusable and announceable.", "kind": "encouraged", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#keyboard" } ] }, { "guidance": "The focus ring must be visible in all color modes (light, dark, high contrast).", "rationale": "Keyboard users depend on the focus indicator to track their position. If the focus ring is invisible against the background, navigation becomes impossible.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#focus-visible" } ] }, { "guidance": "Button label text must meet a minimum 4.5:1 contrast ratio against the button background.", "rationale": "Text contrast ensures readability for users with low vision.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#contrast-minimum" } ] } ] , { "agents": { "intent": "Enforce correct Button usage rules in generated UI code and design decisions.", "constraints": [ { "rule": "Never place two primary buttons on the same surface.", "level": "must-not" }, { "rule": "Always pair a danger button with a confirmation dialog.", "level": "must" } ], "keywords": [ "rules", "best practices", "visual design", "accessibility", "content" ] } }
Guidelines
Agent Context
Enforce correct Button usage rules in generated UI code and design decisions.
| Level | Rule |
|---|---|
| Never place two primary buttons on the same surface. | |
| Always pair a danger button with a confirmation dialog. |
Accessibility
{ "wcagLevel": "AA" }, "keyboardInteraction": [ { "key": "Enter", "action": "Activates the button." }, { "key": "Space", "action": "Activates the button." }, { "key": "Tab", "action": "Moves focus to the next focusable element in the tab order." }, { "key": "Shift+Tab", "action": "Moves focus to the previous focusable element in the tab order." } ], "ariaAttributes": [ { "attribute": "role", "value": "button", "description": "Applied automatically by the <button> element. Only set explicitly when using the 'as' prop to render a non-button element.", "required": false }, { "attribute": "aria-disabled", "value": "true | false", "description": "Set to 'true' when the button is non-interactive. Preferred over the HTML disabled attribute when the button should remain focusable for screen reader discoverability.", "required": false }, { "attribute": "aria-label", "value": "string", "description": "Provides an accessible name for icon-only buttons that lack visible text. Not needed when a visible label is present.", "required": false }, { "attribute": "aria-busy", "value": "true | false", "description": "Set to 'true' when the button is in the loading state. Communicates to assistive technology that the button's action is in progress.", "required": false } ], { "screenReaderBehavior": "Announced as '[label], button'. When disabled via aria-disabled, announced as '[label], button, dimmed' (VoiceOver) or '[label], button, unavailable' (NVDA/JAWS). When loading, announced as '[label], button, busy'." }, "colorContrast": [ { "foreground": "color-text-on-action", "background": "color-action-primary", "contrastRatio": 7.2, "level": "AAA", "context": "Label text on primary button background in light mode." }, { "foreground": "color-action-primary", "background": "color-background-default", "contrastRatio": 4.8, "level": "AA", "context": "Secondary button border/text against the default page background in light mode." }, { "foreground": "color-text-on-action", "background": "color-action-danger", "contrastRatio": 6.5, "level": "AAA", "context": "Label text on danger button background in light mode." } ] , { "agents": { "intent": "Provide keyboard interaction patterns, ARIA requirements, and contrast ratios for implementing an accessible Button.", "constraints": [ { "rule": "Use aria-disabled instead of the HTML disabled attribute when the button must remain in the tab order.", "level": "should" }, { "rule": "Focus ring must be visible with a minimum 3:1 contrast ratio against the surrounding background.", "level": "must" } ], "keywords": [ "WCAG", "keyboard", "ARIA", "screen reader", "focus", "contrast", "a11y" ] } }
Accessibility
Keyboard Interactions
| Key | Action |
|---|---|
| Enter | Activates the button. |
| Space | Activates the button. |
| Tab | Moves focus to the next focusable element in the tab order. |
| Shift+Tab | Moves focus to the previous focusable element in the tab order. |
ARIA Attributes
| Attribute | Value | Description |
|---|---|---|
| Applied automatically by the <button> element. Only set explicitly when using the 'as' prop to render a non-button element. | ||
| Set to 'true' when the button is non-interactive. Preferred over the HTML disabled attribute when the button should remain focusable for screen reader discoverability. | ||
| Provides an accessible name for icon-only buttons that lack visible text. Not needed when a visible label is present. | ||
| Set to 'true' when the button is in the loading state. Communicates to assistive technology that the button's action is in progress. |
Screen Reader
Announced as '[label], button'. When disabled via aria-disabled, announced as '[label], button, dimmed' (VoiceOver) or '[label], button, unavailable' (NVDA/JAWS). When loading, announced as '[label], button, busy'.
Color Contrast
| Foreground | Background | Ratio | Level | Context |
|---|---|---|---|---|
| 7.2:1 | Label text on primary button background in light mode. | |||
| 4.8:1 | Secondary button border/text against the default page background in light mode. | |||
| 6.5:1 | Label text on danger button background in light mode. |
Agent Context
Provide keyboard interaction patterns, ARIA requirements, and contrast ratios for implementing an accessible Button.
| Level | Rule |
|---|---|
| Use aria-disabled instead of the HTML disabled attribute when the button must remain in the tab order. | |
| Focus ring must be visible with a minimum 3:1 contrast ratio against the surrounding background. |
Links
[ { "kind": "source", "url": "https://code.acme.com/design-system/src/packages/components/src/button/button.tsx", "label": "React component source" }, { "kind": "source", "url": "https://code.acme.com/design-system/src/packages/components/src/button/button.test.tsx", "label": "Unit tests" }, { "kind": "design", "url": "https://design-tool.acme.com/file/abc123?node-id=1234:5678", "label": "Design file — component" }, { "kind": "design", "url": "https://design-tool.acme.com/file/abc123?node-id=1234:9999", "label": "Design file — variants" }, { "kind": "storybook", "url": "https://storybook.acme.com/?path=/docs/components-button--docs", "label": "Storybook docs" }, { "kind": "package", "url": "https://www.npmjs.com/package/@acme/components", "label": "npm package" }, { "kind": "alternative", "url": "https://design.acme.com/components/link", "label": "link (component)" }, { "kind": "child", "url": "https://design.acme.com/components/icon-button", "label": "icon-button (component)" }, { "kind": "parent", "url": "https://design.acme.com/components/button-group", "label": "button-group (component)" } ]
Links
Agent context
{
{
"intent": "Trigger a user-initiated action within the current view without causing page navigation."
},
{
"constraints": [
{
"rule": "Do not use for navigating to a different page or URL.",
"level": "must-not"
},
{
"rule": "Limit each surface to one primary-emphasis button.",
"level": "must"
},
{
"rule": "Always provide an accessible label via visible text or aria-label.",
"level": "must"
}
]
},
{
"disambiguation": [
{
"entity": "link",
"distinction": "Use button for in-page actions; use link for navigation to a URL."
},
{
"entity": "icon-button",
"distinction": "Use button when a visible text label is present; use icon-button for icon-only affordances."
}
]
},
{
"antiPatterns": [
{
"description": "Using a button to navigate to another page.",
"instead": "Use a link element with href."
},
{
"description": "Placing multiple primary buttons on the same surface.",
"instead": "Use one primary and one or more secondary or tertiary buttons."
}
]
},
{
"keywords": [
"action",
"submit",
"click",
"CTA",
"trigger",
"call-to-action",
"interactive"
]
}
}Agent context
Trigger a user-initiated action within the current view without causing page navigation.
Constraints
| Level | Rule | Context |
|---|---|---|
| Do not use for navigating to a different page or URL. | ||
| Limit each surface to one primary-emphasis button. | ||
| Always provide an accessible label via visible text or aria-label. |
Disambiguation
| Entity | Distinction |
|---|---|
| Use button for in-page actions; use link for navigation to a URL. | |
| Use button when a visible text label is present; use icon-button for icon-only affordances. |
Anti-patterns
Keywords
Entity Header
{
{
"kind": "token"
},
{
"name": "color-text-primary"
},
{
"summary": "Default body text color for light and dark surfaces."
},
{
"description": "The default color for body text, headings, and labels. Provides the highest-contrast text color for standard reading content on default background surfaces."
},
{
"status": {
"overall": "stable",
"platforms": {
"css": {
"status": "stable",
"since": "2.0.0"
},
"ios": {
"status": "stable",
"since": "2.1.0"
},
"android": {
"status": "stable",
"since": "2.2.0"
},
"figma": {
"status": "stable",
"since": "2.0.0"
}
}
}
},
{
"since": "2.0.0"
},
{
"tags": [
"color",
"text",
"body"
]
},
{
"tokenType": "color"
},
{
"category": "semantic"
},
{
"aliases": [
"color.text.default",
"color.text.body"
]
}
}color-text-primary
Default body text color for light and dark surfaces.
The default color for body text, headings, and labels. Provides the highest-contrast text color for standard reading content on default background surfaces.
| Platform | Status | Since |
|---|---|---|
| css | 2.0.0 | |
| ios | 2.1.0 | |
| android | 2.2.0 | |
| figma | 2.0.0 |
Purpose
"useCases": [ { "description": "When applying color to body text, headings, and form labels on default background surfaces.", "kind": "positive" }, { "description": "When building components that display primary reading content that must adapt across color modes.", "kind": "positive" }, { "description": "When placing text on dark or colored background surfaces.", "kind": "negative", "alternative": { "name": "color-text-inverse", "rationale": "This token is optimized for contrast against light backgrounds. Using it on dark or saturated surfaces will fail contrast requirements." } }, { "description": "When the text is inside a component that supplies its own scoped color tokens (e.g., text on a filled button).", "kind": "negative", "alternative": { "name": "color-text-on-action", "rationale": "Component-scoped tokens account for the specific background they sit on. Applying a general text token to a scoped context risks contrast failures." } } ] , { "agents": { "intent": "Clarify when to use the color-text-primary token versus other text color tokens.", "disambiguation": [ { "entity": "color-text-secondary", "distinction": "Use color-text-primary for body text and headings; use color-text-secondary for supplementary or de-emphasized text." }, { "entity": "color-text-on-action", "distinction": "Use color-text-primary on standard surfaces; use color-text-on-action on filled interactive elements like buttons." } ], "keywords": [ "when to use", "text color", "primary", "body text" ] } }
Purpose
When to Use
When Not to Use
Agent Context
Clarify when to use the color-text-primary token versus other text color tokens.
| Entity | Distinction |
|---|---|
| Use color-text-primary for body text and headings; use color-text-secondary for supplementary or de-emphasized text. | |
| Use color-text-primary on standard surfaces; use color-text-on-action on filled interactive elements like buttons. |
Guidelines
"items": [ { "guidance": "Use for all body text, headings, and form labels on default background surfaces.", "rationale": "A single primary text color ensures visual consistency and meets WCAG 2.1 AA contrast requirements against the system's default background.", "kind": "encouraged", "category": "visual-design" }, { "guidance": "Do not override this token's value at the component level. Use color-text-secondary or color-text-tertiary for reduced emphasis.", "rationale": "Overriding the primary text color creates inconsistency. The system provides lower-emphasis text tokens for visual hierarchy.", "kind": "prohibited", "category": "visual-design" }, { "guidance": "This color meets a 15.3:1 contrast ratio against color-background-default in light mode.", "rationale": "Exceeds WCAG 2.1 AAA requirements (7:1 for normal text), ensuring readability for users with low vision.", "kind": "informational", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#contrast-minimum" }, { "url": "https://www.w3.org/TR/WCAG22/#contrast-enhanced" } ] } ] , { "agents": { "intent": "Enforce correct usage rules for the color-text-primary token.", "constraints": [ { "rule": "Do not use color-text-primary on colored or image backgrounds without verifying contrast.", "level": "must-not" }, { "rule": "Always pair with a background token from the same surface family.", "level": "should" } ], "keywords": [ "rules", "contrast", "pairing", "surface" ] } }
Guidelines
Agent Context
Enforce correct usage rules for the color-text-primary token.
| Level | Rule |
|---|---|
| Do not use color-text-primary on colored or image backgrounds without verifying contrast. | |
| Always pair with a background token from the same surface family. |
Accessibility
{ "wcagLevel": "AAA" }, "colorContrast": [ { "foreground": "color-text-primary", "background": "color-background-default", "contrastRatio": 15.3, "level": "AAA", "context": "Primary text on default background in light mode." }, { "foreground": "color-text-primary", "background": "color-background-subtle", "contrastRatio": 13.8, "level": "AAA", "context": "Primary text on subtle background (card surface) in light mode." } ] , { "agents": { "intent": "Provide contrast ratios and WCAG compliance data for the color-text-primary token.", "keywords": [ "WCAG", "contrast", "AA", "AAA", "accessibility", "ratio" ] } }
Accessibility
Color Contrast
| FG | BG | Ratio | Level | Context |
|---|---|---|---|---|
| 15.3:1 | Primary text on default background in light mode. | |||
| 13.8:1 | Primary text on subtle background (card surface) in light mode. |
Agent Context
Provide contrast ratios and WCAG compliance data for the color-text-primary token.
Links
[ { "kind": "source", "url": "https://code.acme.com/design-system/src/tokens/color/text.tokens.json", "label": "Token source file" }, { "kind": "documentation", "url": "https://design.acme.com/tokens/color-text-primary", "label": "Token documentation" }, { "kind": "related", "url": "https://design.acme.com/tokens/color-text-secondary", "label": "color-text-secondary" }, { "kind": "related", "url": "https://design.acme.com/tokens/color-text-tertiary", "label": "color-text-tertiary" } ]
Links
Agent context
{
{
"intent": "Apply the primary text color for body content, headings, and labels on default background surfaces."
},
{
"constraints": [
{
"rule": "Do not use on dark or colored background surfaces.",
"level": "must-not"
},
{
"rule": "Do not override this token value at the component level.",
"level": "must-not"
}
]
},
{
"disambiguation": [
{
"entity": "color-text-secondary",
"distinction": "Use primary for default reading content; use secondary for supporting or reduced-emphasis text."
},
{
"entity": "color-text-on-action",
"distinction": "Use primary on default surfaces; use on-action inside filled interactive components."
}
]
},
{
"keywords": [
"color",
"text",
"primary",
"body",
"heading",
"label",
"foreground"
]
}
}Agent context
Apply the primary text color for body content, headings, and labels on default background surfaces.
Constraints
| Level | Rule | Context |
|---|---|---|
| Do not use on dark or colored background surfaces. | ||
| Do not override this token value at the component level. |
Disambiguation
| Entity | Distinction |
|---|---|
| Use primary for default reading content; use secondary for supporting or reduced-emphasis text. | |
| Use primary on default surfaces; use on-action inside filled interactive components. |
Keywords
Entity Header
{
{
"kind": "pattern"
},
{
"displayName": "Error Messaging"
},
{
"summary": "A pattern for communicating errors through inline validation, summary banners, and toast notifications."
},
{
"description": "Defines how errors are surfaced across the system — from inline field validation on forms to page-level error summaries and transient toast notifications. The pattern covers the lifecycle of an error from detection through resolution, ensuring that errors are perceivable, understandable, and actionable for all users including those relying on assistive technology."
},
{
"status": {
"overall": "stable",
"platforms": {
"react": {
"status": "stable",
"since": "2.0.0"
},
"web-component": {
"status": "experimental",
"since": "3.0.0",
"description": "Partial implementation — inline validation works, error summary not yet available."
},
"figma": {
"status": "stable",
"since": "2.0.0"
}
}
}
},
{
"since": "2.0.0"
},
{
"tags": [
"feedback",
"validation",
"forms",
"errors",
"accessibility"
]
},
{
"category": "feedback"
},
{}
}Error Messaging
A pattern for communicating errors through inline validation, summary banners, and toast notifications.
Defines how errors are surfaced across the system — from inline field validation on forms to page-level error summaries and transient toast notifications. The pattern covers the lifecycle of an error from detection through resolution, ensuring that errors are perceivable, understandable, and actionable for all users including those relying on assistive technology.
| Platform | Status | Since | Note |
|---|---|---|---|
| react | 2.0.0 | ||
| web-component | 3.0.0 | Partial implementation — inline validation works, error summary not yet available. | |
| figma | 2.0.0 |
Purpose
"useCases": [ { "description": "When a user submits a form and one or more fields fail validation — whether client-side or server-side.", "kind": "positive" }, { "description": "When an asynchronous operation fails and the user needs to be informed (e.g., save failed, API error, network timeout).", "kind": "positive" }, { "description": "When the user needs to correct input before proceeding, such as during account creation, checkout, or settings changes.", "kind": "positive" }, { "description": "When the feedback is a successful outcome (e.g., 'Changes saved', 'Account created').", "kind": "negative", "alternative": { "name": "success-messaging", "rationale": "Success feedback uses the success color and icon. Reusing the error pattern for positive outcomes confuses the visual language and dilutes the urgency of actual errors." } }, { "description": "When the feedback is an informational or warning message that does not block the user from proceeding.", "kind": "negative", "alternative": { "name": "notification-messaging", "rationale": "Informational messages use a neutral or warning color. Treating them as errors creates false urgency and trains users to ignore error styling." } }, { "description": "When the entire page or view fails to load (e.g., 404, 503).", "kind": "negative", "alternative": { "name": "empty-state", "rationale": "Full-page failures are better handled by empty state patterns that replace the entire content area with an explanation and recovery action, rather than overlaying error messages on a non-functional page." } } ] , { "agents": { "intent": "Clarify when the error messaging pattern applies versus inline validation or toast notifications.", "disambiguation": [ { "entity": "toast notification", "distinction": "Error messaging is for persistent, actionable form errors; toasts are for transient, informational feedback." } ], "keywords": [ "when to use", "form errors", "validation", "feedback" ] } }
Purpose
When to Use
When Not to Use
Agent Context
Clarify when the error messaging pattern applies versus inline validation or toast notifications.
| Entity | Distinction |
|---|---|
| Error messaging is for persistent, actionable form errors; toasts are for transient, informational feedback. |
Guidelines
"items": [ { "guidance": "Always display inline errors directly below the invalid field, not in a separate location or only in the summary.", "rationale": "Users scan forms top-to-bottom. An error placed away from its field forces the user to map the message to the field mentally. Proximity eliminates that cognitive load.", "kind": "required", "category": "visual-design" }, { "guidance": "Error messages must describe the problem and, when possible, suggest how to fix it.", "rationale": "A message like 'Invalid input' forces the user to guess what went wrong. A message like 'Enter a date in MM/DD/YYYY format' removes ambiguity and speeds correction.", "kind": "required", "category": "content" }, { "guidance": "When two or more fields have errors, display an error summary at the top of the form in addition to inline messages.", "rationale": "Users on long forms may not see all inline errors, especially those below the fold. The summary provides a single scannable list of everything that needs attention.", "kind": "required", "category": "visual-design" }, { "guidance": "Each entry in the error summary must link to the corresponding invalid field.", "rationale": "Keyboard and screen reader users need a way to navigate directly from the summary to the problematic field without tabbing through the entire form.", "kind": "required", "category": "interaction" }, { "guidance": "Move focus to the error summary when it appears.", "rationale": "Screen reader users will not discover the summary unless focus is moved to it. Sighted keyboard users benefit from the same behavior — their next Tab press lands them in the summary's link list.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#error-identification" } ] }, { "guidance": "Do not clear the form or reset field values when validation fails.", "rationale": "Clearing the form destroys the user's work. They must re-enter all data, including fields that were valid. This is hostile to all users and catastrophic for users with motor impairments who rely on slow, deliberate input.", "kind": "prohibited", "category": "interaction" }, { "guidance": "Use real-time inline validation only for fields where the constraint is unambiguous and immediate feedback is helpful (e.g., password strength, email format). Do not validate on every keystroke for fields that require the user to finish typing.", "rationale": "Premature validation interrupts the user. Showing 'Invalid email' while the user is still typing 'j' is noise. Wait for a blur event or a pause in typing.", "kind": "encouraged", "category": "interaction" }, { "guidance": "Pair every inline error message with an error icon. Do not rely on color alone.", "rationale": "Approximately 8% of men have some form of color vision deficiency. An icon provides a second, non-color signal that the field is in an error state. WCAG 1.4.1 requires that color is not the sole means of conveying information.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#use-of-color" } ] }, { "guidance": "Each inline error message must be programmatically associated with its field using aria-describedby.", "rationale": "Screen readers announce aria-describedby content when the field receives focus. Without it, the user hears the field label but not the error, and has no way to know the field is invalid.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#error-identification" }, { "url": "https://www.w3.org/TR/WCAG22/#info-and-relationships" } ] }, { "guidance": "Invalid fields must be marked with aria-invalid=\"true\".", "rationale": "The aria-invalid attribute allows screen readers to announce that a field's value is not accepted. It is the standard mechanism for communicating validation state in ARIA.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#error-identification" } ] }, { "guidance": "The error summary must use role=\"alert\" or be injected into an aria-live=\"assertive\" region so that screen readers announce it immediately when it appears.", "rationale": "Without a live region, screen readers will not announce dynamically injected content. The user will not know the summary exists unless they navigate to it manually.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#status-messages" } ] }, { "guidance": "Error summary anchor links must move focus to the corresponding field, not just scroll to it.", "rationale": "Keyboard users rely on focus position to interact. Scrolling without moving focus leaves the keyboard cursor at the summary, requiring the user to Tab through potentially many elements to reach the field.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#focus-order" } ] }, { "guidance": "Toast notifications for errors must persist until the user dismisses them or the error is resolved. Do not auto-dismiss error toasts.", "rationale": "Users with cognitive disabilities or screen reader users may need more time to read and understand the error. Auto-dismissing the toast removes the information before they can act on it.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#timing-adjustable" } ] } ] , { "agents": { "intent": "Enforce correct error message presentation and content rules.", "constraints": [ { "rule": "Error messages must describe what went wrong and how to fix it.", "level": "must" }, { "rule": "Do not use color alone to indicate an error state.", "level": "must-not" } ], "keywords": [ "rules", "content", "error text", "visual design" ] } }
Guidelines
Agent Context
Enforce correct error message presentation and content rules.
| Level | Rule |
|---|---|
| Error messages must describe what went wrong and how to fix it. | |
| Do not use color alone to indicate an error state. |
Accessibility
{ "wcagLevel": "AA" }, "keyboardInteraction": [ { "key": "Tab", "action": "Moves focus to the next focusable element. When the error summary is visible, focus moves into the summary's link list." }, { "key": "Enter", "action": "Activates a link in the error summary, moving focus to the corresponding invalid field." }, { "key": "Escape", "action": "Dismisses the toast notification, if one is visible." } ], { "screenReaderBehavior": "When validation fails, the error summary is announced immediately via aria-live='assertive'. Each summary link is announced as 'link, [error text]'. When an invalid field receives focus, the screen reader announces '[label], edit text, invalid entry, [error message]'." }, , { "agents": { "intent": "Provide ARIA and keyboard requirements for accessible error messaging.", "keywords": [ "WCAG", "aria-live", "focus management", "screen reader", "a11y" ] } }
Accessibility
Keyboard Interactions
| Key | Action |
|---|---|
| Tab | Moves focus to the next focusable element. When the error summary is visible, focus moves into the summary's link list. |
| Enter | Activates a link in the error summary, moving focus to the corresponding invalid field. |
| Escape | Dismisses the toast notification, if one is visible. |
Screen Reader
When validation fails, the error summary is announced immediately via aria-live='assertive'. Each summary link is announced as 'link, [error text]'. When an invalid field receives focus, the screen reader announces '[label], edit text, invalid entry, [error message]'.
Agent Context
Provide ARIA and keyboard requirements for accessible error messaging.
Links
[ { "kind": "component", "name": "form-field", "role": "Container for individual inputs. Displays inline validation messages directly below the field when validation fails.", "required": true }, { "kind": "component", "name": "alert", "role": "Page-level error summary. Appears at the top of the form or page listing all current errors with anchor links to each invalid field.", "required": true }, { "kind": "component", "name": "toast", "role": "Transient notification for server-side or asynchronous errors that cannot be tied to a specific field (e.g., network failure, timeout)" }, { "kind": "component", "name": "icon", "role": "Visual error indicator displayed alongside inline validation text and inside the error summary. Provides a non-color signal that reinforces the error state.", "required": true }, { "kind": "component", "name": "button", "role": "Submit trigger for the form. Initiates client-side validation before submission.", "required": true }, { "kind": "component", "name": "link", "role": "Anchor links within the error summary that navigate the user to the corresponding invalid field.", "required": true }, { "kind": "design", "url": "https://design-tool.acme.com/file/abc123?node-id=3000:1", "label": "Design file — error messaging pattern" }, { "kind": "storybook", "url": "https://storybook.acme.com/?path=/docs/patterns-error-messaging--docs", "label": "Storybook pattern docs" }, { "kind": "documentation", "url": "https://design.acme.com/patterns/error-messaging", "label": "Documentation site" }, { "kind": "alternative", "url": "https://design.acme.com/patterns/success-messaging", "label": "success-messaging (pattern)" }, { "kind": "alternative", "url": "https://design.acme.com/patterns/notification-messaging", "label": "notification-messaging (pattern)" }, { "kind": "related", "url": "https://design.acme.com/patterns/empty-state", "label": "empty-state (pattern)" }, { "kind": "related", "url": "https://design.acme.com/components/form-field", "label": "form-field (component)" }, { "kind": "related", "url": "https://design.acme.com/components/alert", "label": "alert (component)" }, { "kind": "related", "url": "https://design.acme.com/components/toast", "label": "toast (component)" }, { "kind": "related", "url": "https://design.acme.com/tokens/color-text-error", "label": "color-text-error (token)" } ]
Links
Agent context
{
{
"intent": "Guide multi-component composition for surfacing, communicating, and resolving form validation errors."
},
{
"constraints": [
{
"rule": "Always include both an inline field error and a summary when multiple fields fail.",
"level": "must"
},
{
"rule": "Do not use toast notifications as the sole error communication for form validation.",
"level": "must-not"
}
]
},
{
"disambiguation": [
{
"entity": "empty-state",
"distinction": "Use error-messaging for validation and system errors; use empty-state for the absence of content."
},
{
"entity": "alert",
"distinction": "Use error-messaging for form validation flows; use alert for non-form system or status messages."
}
]
},
{
"antiPatterns": [
{
"description": "Showing errors only on submit without inline field feedback.",
"instead": "Show inline errors on field blur and in a summary on submit."
}
]
},
{
"keywords": [
"error",
"validation",
"form",
"feedback",
"inline-error",
"error-summary",
"alert"
]
}
}Agent context
Guide multi-component composition for surfacing, communicating, and resolving form validation errors.
Constraints
| Level | Rule | Context |
|---|---|---|
| Always include both an inline field error and a summary when multiple fields fail. | ||
| Do not use toast notifications as the sole error communication for form validation. |
Disambiguation
| Entity | Distinction |
|---|---|
| Use error-messaging for validation and system errors; use empty-state for the absence of content. | |
| Use error-messaging for form validation flows; use alert for non-form system or status messages. |
Anti-patterns
Keywords
Entity Header
{
{
"kind": "style"
},
{
"displayName": "Spacing"
},
{
"summary": "A constrained spacing scale built on a 4px base unit."
},
{
"description": "A spatial system built on a 4px base unit. Defines the scale and rules for all whitespace, padding, margin, and gap values across the system. Eliminates ad hoc pixel values and reduces visual inconsistency caused by individual interpretation of spatial relationships."
},
{
"status": {
"overall": "stable",
"platforms": null
}
},
{
"since": "1.0.0"
},
{
"tags": [
"layout",
"spatial",
"whitespace",
"padding",
"margin",
"gap"
]
},
{
"category": "spacing"
},
{}
}Spacing
A constrained spacing scale built on a 4px base unit.
A spatial system built on a 4px base unit. Defines the scale and rules for all whitespace, padding, margin, and gap values across the system. Eliminates ad hoc pixel values and reduces visual inconsistency caused by individual interpretation of spatial relationships.
Purpose
"useCases": [ { "description": "When defining padding, margin, or gap values for any layout or component. All spatial values in production code must reference the spacing scale.", "kind": "positive" }, { "description": "When establishing vertical rhythm between content sections, form groups, or card layouts.", "kind": "positive" }, { "description": "When setting internal padding for containers such as cards, dialogs, panels, and page regions.", "kind": "positive" }, { "description": "When controlling the gap between sibling elements in Flexbox or Grid layouts.", "kind": "positive" }, { "description": "When defining border widths, outline offsets, or stroke values.", "kind": "negative", "alternative": { "name": "border-width", "rationale": "Border tokens are tuned for visual weight at sub-pixel and single-pixel sizes. Spacing tokens start at 0px and jump to values optimized for whitespace, which produce incorrect visual results when applied to borders." } }, { "description": "When setting font sizes or line heights.", "kind": "negative", "alternative": { "name": "typography", "rationale": "Typographic tokens follow a modular scale designed for readability and vertical rhythm. Spacing tokens follow a geometric scale designed for whitespace. The two scales serve different purposes and should not be interchanged." } }, { "description": "When sizing icons or illustration containers.", "kind": "negative", "alternative": { "name": "icon-size", "rationale": "Icon size tokens are calibrated to optical alignment with adjacent text at each type scale step. Spacing tokens do not account for optical sizing and produce misaligned icons." } } ] , { "agents": { "intent": "Clarify when the spacing style applies versus component-internal spacing.", "disambiguation": [ { "entity": "component padding", "distinction": "The spacing style governs space between elements in layouts; component-internal padding is defined in each component's design specifications." } ], "keywords": [ "when to use", "layout spacing", "margins", "gaps" ] } }
Purpose
When to Use
When Not to Use
Agent Context
Clarify when the spacing style applies versus component-internal spacing.
| Entity | Distinction |
|---|---|
| The spacing style governs space between elements in layouts; component-internal padding is defined in each component's design specifications. |
Guidelines
"items": [ { "guidance": "Use the spacing scale tokens for all padding, margin, and gap properties. Do not use hard-coded values.", "rationale": "Hard-coded values create visual inconsistency and are not responsive to system-wide spacing changes. Tokens enable global adjustment of spatial density from a single source of truth.", "kind": "required", "category": "visual-design" }, { "guidance": "Components must not apply external margin. Margin between components is the responsibility of the parent layout.", "rationale": "Components that own their margins create unpredictable spacing when composed. A button with built-in margin-bottom behaves differently depending on what follows it. Delegating margin to layout makes spacing composable and predictable.", "kind": "required", "category": "visual-design" }, { "guidance": "Select spacing based on the relationship between elements, not their size. Related elements use smaller spacing. Unrelated elements use larger spacing.", "rationale": "Gestalt proximity principle: objects that are closer together are perceived as related. Spacing encodes information hierarchy. A label 4px from its input is clearly associated with it. A label 32px from an input appears disconnected.", "kind": "encouraged", "category": "visual-design" }, { "guidance": "Do not use spacing tokens for non-spatial properties such as border-width, font-size, or icon-size. Use the tokens designated for those properties.", "rationale": "Spacing tokens are tuned for whitespace. A spacing scale step that works well as padding produces incorrect results as a border-width or icon-size. Using the wrong token category couples unrelated visual properties.", "kind": "prohibited", "category": "development" }, { "guidance": "Use CSS gap (in Flexbox or Grid) as the primary mechanism for spacing between sibling elements. Reserve margin for spacing between non-sibling elements or layout-level offset.", "rationale": "Gap applies spacing uniformly between children without affecting the first or last child. Margin requires :first-child/:last-child overrides to prevent unwanted space at container edges.", "kind": "encouraged", "category": "development" }, { "guidance": "For responsive layouts, reduce spacing density at smaller breakpoints by stepping down the scale. Do not introduce arbitrary breakpoint-specific values.", "rationale": "Stepping down the scale (e.g., space-7 at desktop becoming space-5 at mobile) maintains proportional relationships while conserving space. Arbitrary values break out of the scale and undermine system consistency.", "kind": "encouraged", "category": "visual-design" }, { "guidance": "Ensure all interactive elements maintain a minimum 44x44 CSS pixel touch target, inclusive of padding.", "rationale": "Users with motor impairments and touch device users require a minimum target size to interact reliably. WCAG 2.5.8 requires 24x24px minimum and recommends 44x44px. The spacing system's role is to ensure padding contributes to meeting this target.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#target-size-minimum" } ] }, { "guidance": "When users increase text size to 200% via browser settings, spacing must not collapse to zero or cause content to overlap.", "rationale": "Users with low vision enlarge text. If spacing is defined in fixed pixel units that do not scale, text enlargement causes overlap and loss of content. Use rem-based spacing tokens where possible, and test all layouts at 200% text zoom.", "kind": "required", "category": "accessibility", "criteria": [ { "url": "https://www.w3.org/TR/WCAG22/#resize-text" } ] } ] , { "agents": { "intent": "Enforce correct spacing usage rules in layouts and component composition.", "constraints": [ { "rule": "Use spacing tokens from the scale — never hard-code pixel values.", "level": "must" }, { "rule": "Apply comfortable density by default; compact density only in data-dense views.", "level": "should" } ], "keywords": [ "rules", "layout", "density", "tokens", "consistency" ] } }
Guidelines
Agent Context
Enforce correct spacing usage rules in layouts and component composition.
| Level | Rule |
|---|---|
| Use spacing tokens from the scale — never hard-code pixel values. | |
| Apply comfortable density by default; compact density only in data-dense views. |
Links
[ { "kind": "token-group", "name": "spacing", "role": "Provides all spatial tokens from space-0 (0px) through space-8 (48px), built on a 4px base unit." }, { "kind": "source", "url": "https://code.acme.com/design-system/src/tokens/spacing.tokens.json", "label": "Token source file" }, { "kind": "design", "url": "https://design-tool.acme.com/file/abc123?node-id=200:1", "label": "Design file — spacing variables" }, { "kind": "documentation", "url": "https://design.acme.com/style/spacing", "label": "Documentation site" }, { "kind": "related", "url": "https://design.acme.com/style/typography", "label": "Typography style" }, { "kind": "related", "url": "https://design.acme.com/components/stack", "label": "Stack component" } ]
Links
Agent context
{
{
"intent": "Document spacing scale tokens and enforce their consistent application across layout, components, and typography."
},
{
"constraints": [
{
"rule": "Do not substitute spacing tokens with typography or icon-size tokens.",
"level": "must-not"
},
{
"rule": "Always use the next scale step up or down rather than custom values.",
"level": "should"
}
]
},
{
"disambiguation": [
{
"entity": "typography style",
"distinction": "Spacing governs whitespace and layout gaps; typography governs type size, weight, and line height."
}
]
},
{
"keywords": [
"spacing",
"scale",
"gap",
"padding",
"margin",
"whitespace",
"layout",
"density"
]
}
}Agent context
Document spacing scale tokens and enforce their consistent application across layout, components, and typography.
Constraints
| Level | Rule | Context |
|---|---|---|
| Do not substitute spacing tokens with typography or icon-size tokens. | ||
| Always use the next scale step up or down rather than custom values. |
Disambiguation
| Entity | Distinction |
|---|---|
| Spacing governs whitespace and layout gaps; typography governs type size, weight, and line height. |
Keywords
Entity Header
{
{
"kind": "theme"
},
{
"name": "dark"
},
{
"summary": "A dark color mode theme for low-light environments and user preference."
},
{
"description": "The dark color mode theme. Inverts the default light surface/text relationship, using light text on dark backgrounds. Designed for low-light environments, user preference, and reduced eye strain. All override values have been validated to maintain WCAG AA contrast ratios against the dark background surfaces."
},
{
"status": {
"overall": "stable",
"platforms": {
"react": {
"status": "stable",
"since": "2.0.0"
},
"web-component": {
"status": "stable",
"since": "2.1.0"
},
"ios": {
"status": "stable",
"since": "2.0.0",
"description": "Automatically applied when the device is set to dark appearance."
},
"android": {
"status": "stable",
"since": "2.2.0",
"description": "Follows the system-level dark theme setting via AppCompat.DayNight."
},
"figma": {
"status": "stable",
"since": "2.0.0",
"description": "Available as a dedicated mode in the Figma variable collection."
}
}
}
},
{
"since": "2.0.0"
},
{
"tags": [
"dark-mode",
"color-mode",
"accessibility"
]
},
{},
{
"category": "color-mode"
},
{}
}dark
A dark color mode theme for low-light environments and user preference.
The dark color mode theme. Inverts the default light surface/text relationship, using light text on dark backgrounds. Designed for low-light environments, user preference, and reduced eye strain. All override values have been validated to maintain WCAG AA contrast ratios against the dark background surfaces.
| Platform | Status | Since |
|---|---|---|
| react | 2.0.0 | |
| web-component | 2.1.0 | |
| ios | 2.0.0 | |
| android | 2.2.0 | |
| figma | 2.0.0 |
Purpose
"useCases": [ { "description": "When the user's system preference is `prefers-color-scheme: dark` or they have explicitly selected dark mode in the application settings.", "kind": "positive" }, { "description": "When the application is used in low-light environments where a bright screen causes eye strain or discomfort.", "kind": "positive" }, { "description": "When embedding content in a context that is already dark (e.g., a media player, a code editor, or a presentation tool in dark mode).", "kind": "positive" }, { "description": "When the user has not expressed a preference for dark mode. Default to the light theme.", "kind": "negative", "alternative": { "name": "light", "rationale": "Dark mode can reduce readability for users with certain visual impairments such as astigmatism, where light text on dark backgrounds causes halation. Defaulting to light ensures the broadest accessibility baseline." } }, { "description": "When high-contrast accessibility is the primary concern rather than a dark aesthetic.", "kind": "negative", "alternative": { "name": "high-contrast", "rationale": "The dark theme is optimized for comfort in low-light environments, not maximum contrast. The high-contrast theme provides stronger contrast ratios that better serve users with low vision." } }, { "description": "When the content is primarily photographic or illustrative and the surrounding chrome should not compete visually.", "kind": "negative", "alternative": { "name": "light", "rationale": "Photographic content often assumes a neutral white surround for accurate color perception. A dark surround shifts the viewer's perception of brightness and color in the images." } } ] , { "agents": { "intent": "Clarify when to apply the dark theme versus the default or high-contrast themes.", "disambiguation": [ { "entity": "high-contrast theme", "distinction": "Dark theme reduces luminance for low-light environments; high-contrast theme maximizes contrast ratios for visual accessibility." } ], "keywords": [ "when to use", "dark mode", "low light", "user preference" ] } }
Purpose
When to Use
When Not to Use
Agent Context
Clarify when to apply the dark theme versus the default or high-contrast themes.
| Entity | Distinction |
|---|---|
| Dark theme reduces luminance for low-light environments; high-contrast theme maximizes contrast ratios for visual accessibility. |
Guidelines
"items": [ { "guidance": "Apply the dark theme at the application root when the user's system preference is `prefers-color-scheme: dark` or when they explicitly select dark mode in the application settings.", "rationale": "Respecting the user's color scheme preference improves comfort in low-light environments and can reduce eye strain. Forcing a color mode against the user's preference creates friction.", "kind": "required", "category": "development" }, { "guidance": "In dark mode, layers become one step lighter with each added layer. Do not apply components that are darker than their background surface.", "rationale": "The dark theme's layering model uses increasing lightness to communicate depth. Reversing this — placing darker elements on lighter surfaces — breaks the spatial hierarchy and confuses the visual relationship between layers.", "kind": "required", "category": "visual-design" }, { "guidance": "Do not mix light-mode and dark-mode semantic tokens on the same surface. Use inline theme switching if a component must appear in the opposite mode.", "rationale": "Mixing tokens from different themes produces unpredictable contrast pairings. Semantic tokens are only validated for contrast within their own theme context.", "kind": "prohibited", "category": "visual-design" }, { "guidance": "Shadows (elevation tokens) are replaced with surface color differentiation in dark mode. Do not add custom box-shadow values to create depth.", "rationale": "Dark backgrounds absorb shadow, making traditional drop shadows invisible or visually muddy. The dark theme communicates depth through lighter surface values instead.", "kind": "discouraged", "category": "visual-design" }, { "guidance": "Ensure all custom illustrations, charts, and images remain legible in dark mode. Provide dark-mode variants of images that use light backgrounds or thin strokes.", "rationale": "Illustrations designed for light backgrounds may become invisible or illegible on dark surfaces. Custom visual content requires the same level of dark-mode adaptation as UI components.", "kind": "encouraged", "category": "visual-design" } ] , { "agents": { "intent": "Enforce correct dark theme implementation rules.", "constraints": [ { "rule": "Always test all component variants under the dark theme — do not assume light-theme contrast ratios transfer.", "level": "must" }, { "rule": "Respect the user's prefers-color-scheme OS setting as the default.", "level": "should" } ], "keywords": [ "rules", "testing", "prefers-color-scheme", "implementation" ] } }
Guidelines
Agent Context
Enforce correct dark theme implementation rules.
| Level | Rule |
|---|---|
| Always test all component variants under the dark theme — do not assume light-theme contrast ratios transfer. | |
| Respect the user's prefers-color-scheme OS setting as the default. |
Accessibility
{ "wcagLevel": "AA" }, "colorContrast": [ { "foreground": "color-text-default", "background": "color-background-default", "contrastRatio": 15.9, "level": "AAA", "context": "Primary text (#FFFFFF) on default dark background (#111111)." }, { "foreground": "color-text-subtle", "background": "color-background-default", "contrastRatio": 4.6, "level": "AA", "context": "Secondary/subtle text (#A5A5A5) on default dark background (#111111)." }, { "foreground": "color-text-link", "background": "color-background-default", "contrastRatio": 5.7, "level": "AA", "context": "Link text (#45A3FE) on default dark background (#111111)." }, { "foreground": "color-text-error", "background": "color-background-default", "contrastRatio": 5.1, "level": "AA", "context": "Error text (#F47171) on default dark background (#111111)." }, { "foreground": "color-text-default", "background": "color-background-elevation-accent", "contrastRatio": 14.4, "level": "AAA", "context": "Primary text (#FFFFFF) on accent surface (#191919)." }, { "foreground": "color-text-default", "background": "color-background-elevation-floating", "contrastRatio": 11.3, "level": "AAA", "context": "Primary text (#FFFFFF) on floating surface (#2B2B2B)." } ] , { "agents": { "intent": "Provide WCAG compliance data and contrast verification requirements for the dark theme.", "keywords": [ "WCAG", "contrast", "dark surfaces", "accessibility", "AA" ] } }
Accessibility
Color Contrast
| FG | BG | Ratio | Level | Context |
|---|---|---|---|---|
| 15.9:1 | Primary text (#FFFFFF) on default dark background (#111111). | |||
| 4.6:1 | Secondary/subtle text (#A5A5A5) on default dark background (#111111). | |||
| 5.7:1 | Link text (#45A3FE) on default dark background (#111111). | |||
| 5.1:1 | Error text (#F47171) on default dark background (#111111). | |||
| 14.4:1 | Primary text (#FFFFFF) on accent surface (#191919). | |||
| 11.3:1 | Primary text (#FFFFFF) on floating surface (#2B2B2B). |
Agent Context
Provide WCAG compliance data and contrast verification requirements for the dark theme.
Links
[ { "kind": "design", "url": "https://www.figma.com/file/abc123/color-system?node-id=100:200", "label": "Figma — Dark mode variable collection" }, { "kind": "documentation", "url": "https://design.example.com/foundations/color/dark-mode", "label": "Dark mode usage guidelines" }, { "kind": "source", "url": "https://github.com/example/design-system/blob/main/tokens/themes/dark.tokens.json", "label": "Dark theme token source (DTCG format)" } ]
Links
Agent context
{
{
"intent": "Apply a dark color palette for low-light environments and user preference, maintaining WCAG AA contrast throughout."
},
{
"constraints": [
{
"rule": "Always apply at the application root, not on individual components.",
"level": "must"
},
{
"rule": "Do not mix dark-mode and light-mode semantic tokens on the same surface.",
"level": "must-not"
}
]
},
{
"disambiguation": [
{
"entity": "high-contrast",
"distinction": "Use dark for low-light comfort; use high-contrast for users with low vision requiring maximum contrast."
}
]
},
{
"antiPatterns": [
{
"description": "Applying dark theme only to part of a page while the rest remains light.",
"instead": "Use inline theme switching for isolated components; apply the theme at the root for global coverage."
}
]
},
{
"keywords": [
"dark-mode",
"color-mode",
"night",
"low-light",
"theme",
"override"
]
}
}Agent context
Apply a dark color palette for low-light environments and user preference, maintaining WCAG AA contrast throughout.
Constraints
| Level | Rule | Context |
|---|---|---|
| Always apply at the application root, not on individual components. | ||
| Do not mix dark-mode and light-mode semantic tokens on the same surface. |
Disambiguation
| Entity | Distinction |
|---|---|
| Use dark for low-light comfort; use high-contrast for users with low vision requiring maximum contrast. |
Anti-patterns
Keywords
Design System Documentation Standard (DSDS) 0.1 — Interactive samples