Button

elements man style/components/button Read as markdown

A bare <button> is a finished secondary button: bordered, correct height, hover and active states, a focus ring. Add is- modifiers to change intent, size, or state. Each modifier owns one axis, so they combine freely and order never matters. This is button.css in @layer components.

Buttons
<button>Cancel</button> <button class="is-primary is-lg is-block">Sign up</button>

Submit Inputs

input[type=submit], input[type=reset], and input[type=button] get the same treatment with no class at all, and take the same modifiers. A submit input is a button to whoever meets it, so it looks and behaves like one:

Submit inputs
<input type="submit" value="Save"/> <input type="submit" class="is-primary is-lg" value="Sign up"/>

Use a <button> for is-loading and is-icon. Both draw content inside the element, and an input is void, with no children and no ::before.

Coercion

To render a non-button element (typically an <a>) as a button, add .button. It gets the identical treatment and takes all the same modifiers:

Coercion
<a class="button is-primary" href="/install">Install</a>

Intents

Class Look
(bare <button>) Bordered secondary
is-primary Accent-filled call to action (monochrome until branded)
is-ghost Borderless, quiet
is-danger Red destructive fill

is-primary fills with --accent and uses --accent-ink for its label, so it takes your brand color automatically. is-danger fills with --danger and uses white text deliberately (in dark mode --danger-ink is a light text tint, not a fill foreground) and swaps in the danger focus ring.

Intents
<button class="is-primary">Save</button> <button class="is-ghost">Skip</button> <button class="is-danger">Delete</button>

Sizes

Class Height
is-sm 32px
(default) 40px
is-lg 48px
is-hero 56px

Width, chrome, and state

Class Effect
is-block Full width
is-bare Strips all chrome: height, padding, border, background
is-loading Progress cursor, muted, no pointer events
is-disabled Disabled look (alongside the :disabled attribute)

<button disabled> and aria-disabled="true" also get the disabled treatment.

Text alignment

A button centers its label, and text-align overrides that. This matters when the button is wider than its text, which is what is-block, width: 100% or flex: 1 in a row all do:

style.css
.todo-title { flex: 1; text-align: start; }

A button holding elements rather than bare text, an icon beside a label, keeps its parts centered as a group and ignores text-align.

Icon-only buttons

A button whose only child is a single <svg> goes bare automatically: square padding, no border, a subtle hover fill. No class needed:

Icon-only button
<button> <svg><!-- icon --></svg> </button>

Button group

.button-group joins adjacent buttons so they share borders. The group rounds only its outer corners and collapses the seams between buttons:

Button group
<div class="button-group"> <button>Day</button> <button class="is-primary">Week</button> <button>Month</button> </div>

Composing a button into a control

A bare <button> is a finished control with its own height, padding, border and radius. To build one into a larger control, such as a stepper with a button inside a bordered wrapper next to an <input>, hand those to the wrapper:

style.css
.stepper button { height: 100%; /* --button-height (2.75rem) otherwise wins even inside a */ min-height: 0; /* wrapper of the same height, and the hover fill paints */ /* over the wrapper's own border */ padding: 0; /* --button-padding-x survives `width`, pushing a single */ width: 2.75rem; /* glyph off centre under border-box */ } .stepper input { height: 100%; min-height: 0; padding: 0; border: none; border-radius: 0; background: none; }

When a composed control looks uneven, such as a hover fill that sits over the wrapper's border or a glyph off centre, set the bare element's height, padding, border or radius to fit the wrapper, as above. Here setting padding is part of composing the control.