Role and semantics
Section titled “Role and semantics”A native <button> is rendered; screen readers announce “Save, button”. The default type="button" means it never submits a form by accident. Rendered as an <a> with render, it is announced as a link. The label is the Button’s accessible name; icons are hidden from screen readers with aria-hidden. Icon-only Buttons are not supported in this version; every Button has a visible label.
Keyboard
Section titled “Keyboard”| Key | Behaviour |
|---|---|
| Tab | Moves focus to the Button. A disabled Button is skipped unless focusableWhenDisabled is set or the Button is loading. |
| Enter | Starts the action; inside a form with type="submit", submits the form. |
| Space | Starts the action. |
The focus ring is 2 px thick, drawn 2 px outside the Button, and shows only when the Button is reached with the keyboard, not on a mouse click. Its contrast against the page background is in the Contrast section below. A loading Button keeps focus.
Screen reader output
Section titled “Screen reader output”| State | Announced |
|---|---|
| Default | “Save, button” |
Disabled (disabled) |
Leaves the tab order; when browsing with a virtual cursor, “Save, button, unavailable”. |
Disabled but focusable (focusableWhenDisabled) |
Carries aria-disabled="true"; announced as “Save, button, unavailable” and can be focused. |
Loading (loading) |
The Button’s name stays its label. When loading starts, the polite status region next to the Button (role="status") has screen readers read loadingLabel once, or “Loading” in the Provider’s language without it. aria-busy is not used. When it is loading and disabled, the Button has aria-disabled="true" instead of the disabled attribute and stays in the tab order, so focus is not lost. |
Exact wording varies a little between screen readers and browsers; the above is the expected form.
Contrast
Section titled “Contrast”On an enabled Button, label and background reach at least 4.5:1 for every variant and tone, including hover and pressed, in every brand and mode. The values below were computed from the token sources (packages/tokens/src) with the WCAG relative luminance formula; the lowest value across the two brands (bigbang, fixture) is shown. A test in the token package checks the default pairs of every variant and tone, the primary hover pair and the danger hover and press fills on every build; the other hover and press pairs are computed here. Disabled Buttons are exempt.
| Variant and tone | Pair | Light mode | Dark mode |
|---|---|---|---|
primary neutral |
Label / default, hover, pressed background | 6.17:1 | 8.09:1 |
secondary neutral |
Label / default, hover, pressed background | 14.40:1 | 11.57:1 |
tertiary neutral |
Label / page, hover, pressed background | 13.24:1 | 11.57:1 |
primary danger |
Label / default, hover, pressed background | 5.63:1 | 8.83:1 |
secondary danger |
Label / default, hover, pressed background | 5.76:1 | 4.81:1 |
tertiary danger |
Label / page, hover, pressed background | 5.76:1 | 4.81:1 |
| Focus ring | color-focus-ring / color-surface-default |
5.97:1 | 8.09:1 |
secondary danger border |
color-border-danger / page |
5.45:1 | 6.15:1 |
secondary neutral border |
color-border-default / page |
1.38:1 | 1.49:1 |
The border of a neutral secondary Button is below 3:1. The label and its verified contrast identify the Button; the border is only visual support. Product teams should not rely on the border alone to set a secondary Button apart from the page surface.
WCAG 2.2
Section titled “WCAG 2.2”| Criterion | How Button meets it | What the product team must do |
|---|---|---|
| 1.4.1 Use of colour | Meaning is not carried by colour alone; the danger tone also relies on the label text. | Make sure the label says what the action does (“Delete”); do not depend on red alone. |
| 1.4.3 Contrast (minimum) | Label contrast is at least 4.81:1 in all enabled states (table above). | Do not change colour classes through className. |
| 1.4.11 Non-text contrast | The focus ring is at least 5.97:1; the danger border at least 5.45:1. The neutral secondary border is below 3:1, but the label identifies the Button. | Do not place a secondary Button where only its border sets it apart from the background. |
| 2.1.1 Keyboard | A native <button>; Enter and Space start the action. |
Use Button instead of a clickable <div>. |
| 2.4.7 Focus visible | A 2 px ring is drawn with :focus-visible. |
Do not remove the focus ring through className. |
| 2.4.11 Focus appearance (minimum) | The ring is drawn outside the Button at a 2 px offset; nothing else covers it. | Do not put a Button at the edge of an overflow: hidden container that clips the ring. |
| 2.5.3 Label in name | The accessible name equals the visible label; while loading, the label stays the name. | Do not give a name different from the visible label with aria-label. |
| 2.5.8 Target size (minimum) | The smallest size is 32 px tall. | Leave space between adjacent Buttons; do not shrink the size in narrow toolbars. |
| 4.1.2 Name, role, value | A native <button>; disabled or aria-disabled when unavailable. |
When rendering a link with render, set nativeButton={false}. |
| 4.1.3 Status messages | The polite status region announces once when loading starts. | Announce the outcome (success, error) separately; Button only reports that loading started. |