| raw | true |
|---|---|
| title | Button |
| description | An action with a face: a hue, an emphasis, and a size, with every state taken from the element itself. |
| nav_group | Forms and Actions |
| nav_order | 2 |
An action with a face: a hue, an emphasis, and a size, with every state taken from the element itself.
View Code
<div class="buttons" role="group" aria-label="Emphasis">
<button class="button" type="button">Save</button>
<button class="button" type="button" data-emphasis="medium">Preview</button>
<button class="button" type="button" data-emphasis="low">Cancel</button>
</div>
<div class="buttons" role="group" aria-label="Map options">
<label class="button" data-emphasis="medium"><input type="checkbox" name="example-offline" checked> Offline</label>
<label class="button" data-emphasis="medium"><input type="checkbox" name="example-contours"> Contours</label>
</div>Anything the visitor does: submit, save, open, dismiss. Put the class on a button for an action and on an a for a link that should look like a button. One high-emphasis button per view is a good rule; the rest are medium or low, so the eye finds the main action.
Three attributes and no classes. data-variant picks a hue from the palette and the button reads that hue's ladder for its fill, its hover step, and its outline text. data-emphasis picks how much of the ladder shows: high fills, medium outlines, low is text that tints on hover. data-size scales the text and the padding together so the shape holds. Hover, active, focus, disabled, pressed, and busy come from the element's own state, so nothing needs a script to look right.
A button is at least the control height, and a large one grows by the same step a large field does, so the two match in a row. Above that minimum, --yeti-button-padding-block and --yeti-button-padding set the padding as multiples of the size's space step.
Two values of data-variant are not hues: black and white. They do not follow the theme and do not flip with the scheme, which is what a button on a painted band needs, where the band's own hue would vanish into itself. They keep every state: black steps to a dark grey under the pointer, its outline and text forms wash with a translucent black, and white mirrors it.
<section class="box" data-paint="warning">
<h2>Start the season early</h2>
<a class="button" href="#" data-variant="black">Book a place</a>
</section><a class="button" href="/docs" data-variant="secondary" data-emphasis="medium" data-size="lg">
<svg aria-hidden="true" viewBox="0 0 24 24"><path d="M5 12h14m-6-6 6 6-6 6" fill="none" stroke="currentColor" stroke-width="2"/></svg>
Read the docs
</a>A toggle needs no script when it is a native input. Put the class on a label wrapping a radio or checkbox: the input is hidden from sight but stays in Tab order, the label is filled while the input is checked, exactly as a pressed button is, draws the focus ring when the input has it, and dims when the input is disabled. The checked state submits with a form and, for radios sharing a name, the arrow keys move it. A real <button aria-pressed> looks the same, but moving aria-pressed from one button to the next is your script's job.
<label class="button" data-emphasis="medium">
<input type="checkbox" name="offline" checked>
Keep maps offline
</label>A link that is a button, <a class="button">, is drawn as a button and never underlined, since Yeti sets that inside its layer. A stylesheet of your own that underlines links on hover sits outside every layer and outranks it, so exclude buttons there: a:hover:not(.button).
A button is a button and an a is a link; the class changes the look, not the role, so use the element that matches what happens. An icon-only button needs an aria-label. A toggle is either a label.button wrapping a native radio or checkbox, whose own checked state is what a screen reader announces, or a button carrying aria-pressed, and the pressed look follows either. A button that is waiting on a request carries aria-busy="true" and aria-disabled="true" together: it dims, shows a progress cursor, and your handler ignores presses until the request returns. The focus ring is the page's ring and is never removed. Text over every fill meets AA in both color schemes; the test suite checks each variant.
| Attribute | Type | Values | Default | Description |
|---|---|---|---|---|
data-variant |
enum | primary, secondary, success, warning, alert, danger, neutral, black, white |
primary |
Which hue's ladder the button uses. |
data-emphasis |
enum | high, medium, low |
high |
How loud: high is a solid fill, medium an outline, low text alone. |
data-size |
enum | sm, md, lg |
md |
Scales the text and the padding together. |
> svg: 0 to 1. An optional icon, sized to the text.> input: 0 to 1. On a label.button, the radio or checkbox that makes it a toggle: hidden from sight, still focusable, pressed while checked.
| Token | Description |
|---|---|
--yeti-button-radius |
Corner radius. |
--yeti-button-weight |
Text weight. |
--yeti-button-padding |
Inline padding as a multiple of the size's space step. |
--yeti-button-padding-block |
Block padding as a multiple of the size's space step. |
--yeti-control-size |
Minimum height. |
--yeti-border-width |
Border width. |
--yeti-space-xs |
Gap between the icon and the label. |
--yeti-leading-tight |
Line height. |
--yeti-duration-fast |
How long a hover or press takes to settle. |
--yeti-ease |
The curve of that transition. |
--yeti-color-primary |
The default variant's color, when data-variant is absent. |
--yeti-color-primary-subtle |
The default variant's tint. |
--yeti-color-primary-soft |
The default variant's soft stop. |
--yeti-color-primary-strong |
The default variant's strong stop. |
--yeti-color-primary-text |
The default variant's text color. |
--yeti-on-primary |
Text on the default variant's color. |
--yeti-text-md |
Text size when data-size is absent. |
--yeti-space-sm |
The space step when data-size is absent. |
--yeti-opacity-muted |
Opacity when disabled or busy. |
--yeti-color-focus |
The focus ring a label.button toggle draws for its hidden input. |
Internal tokens (may change between minor versions)
--_yeti-variant--_yeti-variant-subtle--_yeti-variant-strong--_yeti-variant-text--_yeti-on-variant--_yeti-size-text--_yeti-size-space--_yeti-variant-soft
- Put the class on a button, an a, a submit input, or a label wrapping a radio or checkbox, nothing else. Use button for actions and a for navigation. An icon-only button needs an aria-label. A toggle is a label wrapping a native radio or checkbox, which needs no script, or a button with aria-pressed, which needs your script to move it; a button that is waiting sets aria-busy="true" together with aria-disabled="true", and your handler ignores presses while it waits.
| Key | Action |
|---|---|
Enter / Space |
Activates a button element; Enter follows a link. |
Space / Arrow keys |
On a label.button toggle, Space checks its input; the arrow keys move the choice within a radio group. |
- Used without guards: inline flexbox, logical properties
- Behind
@supports: nothing
None. This component is CSS only.
Available since 7.0.0.