Skip to content

Latest commit

 

History

History
159 lines (116 loc) · 9.43 KB

File metadata and controls

159 lines (116 loc) · 9.43 KB
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

Button

An action with a face: a hue, an emphasis, and a size, with every state taken from the element itself.

Example

<iframe title="Button, live" srcdoc="<base href="/yeti/"><link rel="stylesheet" href="/yeti/yeti.css"><script type="module" src="/yeti/yeti.js"></script><script>addEventListener("click",function(e){var a=e.target.closest&&e.target.closest('a[href="#"]');if(a)e.preventDefault();});</script><body style="margin:0;padding:var(--yeti-space-md)"><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>"></iframe>
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>

When to use it

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.

How it works

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).

Accessibility

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.

Attributes

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.

Children

  • > 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.

Tokens

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

Accessibility

  • 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.

Browser support

  • Used without guards: inline flexbox, logical properties
  • Behind @supports: nothing

JavaScript

None. This component is CSS only.

Available since 7.0.0.