Skip to content

Button

A button starts an action on the screen you are on, such as sending an offer or publishing a job. Its label says what will happen.

Kinds

Primary moves the task forward.

Secondary carries Cancel, or the main action when nothing leads.

Quiet carries the everyday actions of toolbars and table rows.

Outline has the fill of a text field, for an action among fields.

Danger confirms a step that deletes or withdraws.

Danger quiet offers a destructive action among others.

When to use it

Use a button when the result happens on this screen, such as sending an offer or opening a dialog. When the result is another page, use a link.

Use an icon button where a familiar glyph says enough, a select to pick an option, and a switch to turn a setting on or off.

One primary action

A screen has one primary button, and so does each dialog or panel that opens over it. Other actions take quieter buttons.

In a row of actions the primary comes last, 8 px after the others. Table and list rows carry no primary button.

Schedule an interview

Maria Santos, care assistant. Thursday 1 October at 10:00.

Anatomy and sizes

A button holds one line of label and at most one icon. A label that does not fit is rewritten, never wrapped or cut.

An icon before the label names the action, such as Plus in Add note. An icon after it says where the button leads: a chevron opens a menu.

VariantSmall, 36 pxMedium, 44 pxLarge, 48 px
Primary
Secondary
Quiet
Outline
Danger
Danger quiet
Height
36, 44 and 48 px, and at least as wide
Side padding
12, 16 and 20 px
--space-3, --space-4, --space-5
Label
13 or 14 px on 20 px, weight 500
--type-caption, --type-nav
Icon
16 px, 8 px from the label
--space-2
Corner radius
8 px
--radius-lg

States

VariantRestHoverPressedFocusDisabledLoading
Primary--brand-true--on-brand
Secondary--surface-control--text
Quietno fill--text-muted
Outline--glass-fill-hover--glass-fill-input--text
Danger--error--badge-alert--on-brand
Danger quietno fill--error

Every enabled label reads at 4.5 to 1 or more, and the focus ring at 3 to 1, in both themes.

Hover darkens a filled button one step, fades the red one to 90 percent, and gives an unfilled one a faint fill. A press keeps that color and shrinks the button to 98 percent, unless motion is reduced.

Keyboard focus draws a 2 px outline 2 px outside the button, which forced colors mode keeps.

A toggle is a quiet button that stays pressed, for a filter such as Assigned to me. While on, it takes the selected gray.

OffOn

Loading and disabled

While an action runs, a spinner takes the place of the start icon, or of the label. The button keeps its width and focus and ignores presses.

Leave a button enabled when you can, and say on press what is missing. A disabled button stays in the tab order, with the reason beside it.

Pick a candidate to send the offer to.

Spinner
16 px, one turn per second
Spinner delay
300 ms
--duration-slow
Disabled
The button at 50 percent opacity

Destructive actions

Start a destructive action with a danger quiet button. The confirmation carries a danger button named with the verb of its question.

A danger button is never the default: Enter does not press it, and focus starts on Cancel. When the action can be undone, act at once and offer Undo.

Delete the CV of Sita Gurung?

The file leaves her profile for everyone on the project.

Keyboard and touch

Tab reaches every button, a disabled one included, and Enter or Space presses it. On a menu button, the arrows open the menu at its first or last item.

On a touch screen, even a small button answers to a touch 44 px tall. Keep 8 px between neighbors.

Writing the label

Start with a verb, and add the object when the verb alone is vague: Send offer, Publish job. Keep to two or three words in sentence case.

A dialog that asks Delete the CV? ends in Delete CV and Cancel, not Yes and No.

Cancel stops a task and keeps nothing. Close dismisses what changed nothing, such as an error message.

Permitted and not permitted

Permitted

  • One primary button on each screen, dialog and panel

  • Delete CV inside its confirmation

Not permitted

  • Two primary buttons side by side

    Neither reads as the next step

  • Submit, OK or Yes as a label

    It does not say what happens

  • Quiet or outline over a picture

    The label sinks into it

NextIcon button

Button

Starts an action on the current screen, such as saving a form or sending an offer.

  • Kind: component
  • Page: https://design.werklist.com/components/button
  • Version: 2.1.2

When to use it

  • The result happens on this screen, such as a save or a dialog that opens.
  • Primary for the one action per surface (page, dialog, sheet, panel) that moves the task forward.
  • Secondary beside a primary, or for the main action when none leads.
  • Quiet for frequent minor actions in toolbars and rows.
  • Outline for an action that sits among fields, such as Add filter beside a search field: it has the fill of a text field.
  • Danger for the confirming button of a destructive step, inside its confirmation.
  • Danger quiet for a destructive action among other actions. It leads to a confirmation or offers undo.
  • Toggle: a quiet button with aria-pressed, for filters and view options in a toolbar.
  • Menu button: a chevron after the label and aria-haspopup menu, for a family of at least three related actions.
  • A call to action that opens another page: a link drawn as a button.

When not to use it

  • Navigation in running text: use Link.
  • A second primary button on the same surface.
  • A primary button in a table or list row.
  • A danger button as the default of a form or a dialog, or with initial focus.
  • Quiet, outline or danger quiet over a picture.
  • A setting that persists: use Switch.
  • Choosing one option from several: use Select or Segmented control.
  • A label that wraps, truncates or has no verb.
  • Two icons in one button, except the start icon and chevron of a menu button.

Anatomy

  • Container: height 36, 44 or 48 px, minimum width equal to the height, radius 8 px, as wide as its content (--radius-lg)
  • Side padding: 12, 16 or 20 px (--space-3, --space-4, --space-5)
  • Label: one line, no wrap, no truncation. 13 px on 20 px at small, 14 px on 20 px at medium and large, weight 500 (--type-caption, --type-nav)
  • Icon: 16 px, at most one, at the start or the end, 8 px from the label, currentColor (--space-2)
  • Spinner: 16 px, one turn per second, replaces the start icon or sits over the transparent label, appears after 300 ms (--duration-slow)
  • Focus ring: outline 2 px, offset 2 px (--focus-ring, --focus-ring-width, --focus-ring-offset)
  • Touch target: 44 px on coarse pointers, the small size extending its hit area 4 px above and below
  • Group: 8 px between buttons, the primary at the trailing end. Below 640 px a form or dialog ends in a full width stack with the primary on top (--space-2)

Variants

  • primary: The one action per surface that moves the task forward.
  • secondary: Cancel beside a primary, or the main action when none leads.
  • quiet: Frequent minor actions in toolbars and rows. The only variant that toggles.
  • outline: An action among fields, with the fill of a text field.
  • danger: The confirming button of a destructive step, inside its confirmation.
  • danger-quiet: A destructive action among others, leading to a confirmation or undo.

Sizes

  • sm: height 36 px, padding 0 12 px, type 13 px on 20 px, weight 500
  • md: height 44 px, padding 0 16 px, type 14 px on 20 px, weight 500
  • lg: height 48 px, padding 0 20 px, type 14 px on 20 px, weight 500

States

  • rest: Primary: brand blue fill and white label in both themes. Secondary: gray fill and text ink. Quiet: no fill and muted ink. Outline: the field material. In dark that is --glass-fill-input with an inset 1 px --input-border edge and --glass-ring, in light the 5 percent ink wash of --glass-fill-hover with no edge. Danger: --error in light and --badge-alert in dark, white label. Danger quiet: no fill and --error ink. (--brand-true, --on-brand, --surface-control, --text, --text-muted, --glass-fill-input, --input-border, --glass-ring, --glass-fill-hover, --badge-alert, --error)
  • hover: Primary darkens to --brand-blue-dark in light, and to the brand blue mixed 72 percent with --navy in dark. Danger drops to opacity 0.9. Secondary and outline lay --glass-fill-hover over their fill. Quiet gains --glass-fill-hover and text ink. Danger quiet gains --error at 10 percent. Only on a pointer that can hover. (--brand-blue-dark, --navy, --glass-fill-hover, --error)
  • pressed: The hover paint, on touch too, and a scale of 0.98 over 150 ms unless motion is reduced. (--duration-fast, --ease-standard)
  • focus-visible: Outline 2 px at offset 2 px in the focus ring color. As an outline it survives forced colors mode. (--focus-ring, --focus-ring-width, --focus-ring-offset)
  • disabled: aria-disabled true on a focusable button, drawn at opacity 0.5 in the colors of its variant. Presses and implicit form submission are canceled, and hover does not apply. Contrast minimums do not apply to a disabled button. The reason is visible text, linked with aria-describedby.
  • loading: aria-busy and aria-disabled true at once, focus kept, presses ignored. After 300 ms a 16 px spinner in the label ink replaces the start icon, or sits centered over the label, which turns transparent. Width and label do not change. Under reduced motion the spinner fades between opacity 1 and 0.5 each second instead of turning. (--duration-slow)
  • toggle on: Quiet variant with aria-pressed true: --seg-selected fill and text ink. On hover --glass-fill-hover lies over it. In a board toolbar a pressed tool takes the brand tint instead (see Toolbar). (--seg-selected, --text, --glass-fill-hover)
  • menu open: aria-expanded true. The button holds its hover paint and the chevron turns 180 degrees in 150 ms, without the turn under reduced motion. (--duration-fast)

Keyboard

  • Tab, Shift+Tab: Move focus in document order, disabled and loading buttons included.
  • Enter: Press. On a menu button, open the menu with focus on the first item. On a button that opens a page, follow the link.
  • Space: Press on release, flip a toggle, or open a menu button's menu. Not on a button that opens a page.
  • Down arrow: On a menu button, open the menu with focus on the first item.
  • Up arrow: On a menu button, open the menu with focus on the last item.
  • Escape: Close an open menu and return focus to the menu button.

Accessibility

  • A native button element with type button, or type submit in a form. An a element with href when the button opens a page.
  • The name is the visible label. Icons are aria-hidden.
  • Label on fill at 4.5 to 1, icon and focus ring at 3 to 1, in every enabled state and both themes.
  • Disabled and loading use aria-disabled, not the disabled attribute, so focus stays and the reason is reachable.
  • States exposed with aria-pressed, aria-expanded, aria-haspopup, aria-busy, aria-disabled and aria-describedby.
  • Targets of 44 px on coarse pointers.
  • Forced colors: a 1 px ButtonText border, ButtonText on ButtonFace, the toggle on in Highlight, focus as an outline in Highlight.

Content

  • A verb first, plus the object when the verb alone is vague: Send offer, Publish job, Add candidate.
  • Two or three words, sentence case, no final period, no exclamation.
  • Answer the question of the dialog: Delete CV and Cancel, not Yes and No.
  • Cancel stops a task and keeps nothing. Close dismisses what changed nothing.
  • No OK, no Submit, no ellipsis at the end.
  • The label does not change while loading.

Tokens

  • --brand-true
  • --brand-blue-dark
  • --navy
  • --on-brand
  • --surface-control
  • --text
  • --text-muted
  • --glass-fill-hover
  • --glass-fill-input
  • --input-border
  • --glass-ring
  • --badge-alert
  • --error
  • --seg-selected
  • --focus-ring
  • --focus-ring-width
  • --focus-ring-offset
  • --radius-lg
  • --space-2
  • --space-3
  • --space-4
  • --space-5
  • --type-caption
  • --type-nav
  • --duration-fast
  • --duration-slow
  • --ease-standard

JSON

{
  "title": "Button",
  "path": "/components/button",
  "url": "https://design.werklist.com/components/button",
  "text": "https://design.werklist.com/agent/components/button",
  "summary": "A button starts an action on the screen you are on, from sending an offer to deleting a CV.",
  "version": "2.1.2",
  "kind": "component",
  "name": "Button",
  "purpose": "Starts an action on the current screen, such as saving a form or sending an offer.",
  "use": [
    "The result happens on this screen, such as a save or a dialog that opens.",
    "Primary for the one action per surface (page, dialog, sheet, panel) that moves the task forward.",
    "Secondary beside a primary, or for the main action when none leads.",
    "Quiet for frequent minor actions in toolbars and rows.",
    "Outline for an action that sits among fields, such as Add filter beside a search field: it has the fill of a text field.",
    "Danger for the confirming button of a destructive step, inside its confirmation.",
    "Danger quiet for a destructive action among other actions. It leads to a confirmation or offers undo.",
    "Toggle: a quiet button with aria-pressed, for filters and view options in a toolbar.",
    "Menu button: a chevron after the label and aria-haspopup menu, for a family of at least three related actions.",
    "A call to action that opens another page: a link drawn as a button."
  ],
  "avoid": [
    "Navigation in running text: use Link.",
    "A second primary button on the same surface.",
    "A primary button in a table or list row.",
    "A danger button as the default of a form or a dialog, or with initial focus.",
    "Quiet, outline or danger quiet over a picture.",
    "A setting that persists: use Switch.",
    "Choosing one option from several: use Select or Segmented control.",
    "A label that wraps, truncates or has no verb.",
    "Two icons in one button, except the start icon and chevron of a menu button."
  ],
  "anatomy": [
    {
      "part": "Container",
      "measure": "height 36, 44 or 48 px, minimum width equal to the height, radius 8 px, as wide as its content",
      "token": "--radius-lg"
    },
    {
      "part": "Side padding",
      "measure": "12, 16 or 20 px",
      "token": "--space-3, --space-4, --space-5"
    },
    {
      "part": "Label",
      "measure": "one line, no wrap, no truncation. 13 px on 20 px at small, 14 px on 20 px at medium and large, weight 500",
      "token": "--type-caption, --type-nav"
    },
    {
      "part": "Icon",
      "measure": "16 px, at most one, at the start or the end, 8 px from the label, currentColor",
      "token": "--space-2"
    },
    {
      "part": "Spinner",
      "measure": "16 px, one turn per second, replaces the start icon or sits over the transparent label, appears after 300 ms",
      "token": "--duration-slow"
    },
    {
      "part": "Focus ring",
      "measure": "outline 2 px, offset 2 px",
      "token": "--focus-ring, --focus-ring-width, --focus-ring-offset"
    },
    {
      "part": "Touch target",
      "measure": "44 px on coarse pointers, the small size extending its hit area 4 px above and below",
      "token": ""
    },
    {
      "part": "Group",
      "measure": "8 px between buttons, the primary at the trailing end. Below 640 px a form or dialog ends in a full width stack with the primary on top",
      "token": "--space-2"
    }
  ],
  "variants": [
    {
      "name": "primary",
      "use": "The one action per surface that moves the task forward."
    },
    {
      "name": "secondary",
      "use": "Cancel beside a primary, or the main action when none leads."
    },
    {
      "name": "quiet",
      "use": "Frequent minor actions in toolbars and rows. The only variant that toggles."
    },
    {
      "name": "outline",
      "use": "An action among fields, with the fill of a text field."
    },
    {
      "name": "danger",
      "use": "The confirming button of a destructive step, inside its confirmation."
    },
    {
      "name": "danger-quiet",
      "use": "A destructive action among others, leading to a confirmation or undo."
    }
  ],
  "sizes": [
    {
      "name": "sm",
      "height": "36 px",
      "padding": "0 12 px",
      "type": "13 px on 20 px, weight 500"
    },
    {
      "name": "md",
      "height": "44 px",
      "padding": "0 16 px",
      "type": "14 px on 20 px, weight 500"
    },
    {
      "name": "lg",
      "height": "48 px",
      "padding": "0 20 px",
      "type": "14 px on 20 px, weight 500"
    }
  ],
  "states": [
    {
      "name": "rest",
      "change": "Primary: brand blue fill and white label in both themes. Secondary: gray fill and text ink. Quiet: no fill and muted ink. Outline: the field material. In dark that is --glass-fill-input with an inset 1 px --input-border edge and --glass-ring, in light the 5 percent ink wash of --glass-fill-hover with no edge. Danger: --error in light and --badge-alert in dark, white label. Danger quiet: no fill and --error ink.",
      "tokens": [
        "--brand-true",
        "--on-brand",
        "--surface-control",
        "--text",
        "--text-muted",
        "--glass-fill-input",
        "--input-border",
        "--glass-ring",
        "--glass-fill-hover",
        "--badge-alert",
        "--error"
      ]
    },
    {
      "name": "hover",
      "change": "Primary darkens to --brand-blue-dark in light, and to the brand blue mixed 72 percent with --navy in dark. Danger drops to opacity 0.9. Secondary and outline lay --glass-fill-hover over their fill. Quiet gains --glass-fill-hover and text ink. Danger quiet gains --error at 10 percent. Only on a pointer that can hover.",
      "tokens": [
        "--brand-blue-dark",
        "--navy",
        "--glass-fill-hover",
        "--error"
      ]
    },
    {
      "name": "pressed",
      "change": "The hover paint, on touch too, and a scale of 0.98 over 150 ms unless motion is reduced.",
      "tokens": [
        "--duration-fast",
        "--ease-standard"
      ]
    },
    {
      "name": "focus-visible",
      "change": "Outline 2 px at offset 2 px in the focus ring color. As an outline it survives forced colors mode.",
      "tokens": [
        "--focus-ring",
        "--focus-ring-width",
        "--focus-ring-offset"
      ]
    },
    {
      "name": "disabled",
      "change": "aria-disabled true on a focusable button, drawn at opacity 0.5 in the colors of its variant. Presses and implicit form submission are canceled, and hover does not apply. Contrast minimums do not apply to a disabled button. The reason is visible text, linked with aria-describedby.",
      "tokens": []
    },
    {
      "name": "loading",
      "change": "aria-busy and aria-disabled true at once, focus kept, presses ignored. After 300 ms a 16 px spinner in the label ink replaces the start icon, or sits centered over the label, which turns transparent. Width and label do not change. Under reduced motion the spinner fades between opacity 1 and 0.5 each second instead of turning.",
      "tokens": [
        "--duration-slow"
      ]
    },
    {
      "name": "toggle on",
      "change": "Quiet variant with aria-pressed true: --seg-selected fill and text ink. On hover --glass-fill-hover lies over it. In a board toolbar a pressed tool takes the brand tint instead (see Toolbar).",
      "tokens": [
        "--seg-selected",
        "--text",
        "--glass-fill-hover"
      ]
    },
    {
      "name": "menu open",
      "change": "aria-expanded true. The button holds its hover paint and the chevron turns 180 degrees in 150 ms, without the turn under reduced motion.",
      "tokens": [
        "--duration-fast"
      ]
    }
  ],
  "keys": [
    {
      "key": "Tab, Shift+Tab",
      "action": "Move focus in document order, disabled and loading buttons included."
    },
    {
      "key": "Enter",
      "action": "Press. On a menu button, open the menu with focus on the first item. On a button that opens a page, follow the link."
    },
    {
      "key": "Space",
      "action": "Press on release, flip a toggle, or open a menu button's menu. Not on a button that opens a page."
    },
    {
      "key": "Down arrow",
      "action": "On a menu button, open the menu with focus on the first item."
    },
    {
      "key": "Up arrow",
      "action": "On a menu button, open the menu with focus on the last item."
    },
    {
      "key": "Escape",
      "action": "Close an open menu and return focus to the menu button."
    }
  ],
  "accessibility": [
    "A native button element with type button, or type submit in a form. An a element with href when the button opens a page.",
    "The name is the visible label. Icons are aria-hidden.",
    "Label on fill at 4.5 to 1, icon and focus ring at 3 to 1, in every enabled state and both themes.",
    "Disabled and loading use aria-disabled, not the disabled attribute, so focus stays and the reason is reachable.",
    "States exposed with aria-pressed, aria-expanded, aria-haspopup, aria-busy, aria-disabled and aria-describedby.",
    "Targets of 44 px on coarse pointers.",
    "Forced colors: a 1 px ButtonText border, ButtonText on ButtonFace, the toggle on in Highlight, focus as an outline in Highlight."
  ],
  "content": [
    "A verb first, plus the object when the verb alone is vague: Send offer, Publish job, Add candidate.",
    "Two or three words, sentence case, no final period, no exclamation.",
    "Answer the question of the dialog: Delete CV and Cancel, not Yes and No.",
    "Cancel stops a task and keeps nothing. Close dismisses what changed nothing.",
    "No OK, no Submit, no ellipsis at the end.",
    "The label does not change while loading."
  ],
  "tokens": [
    "--brand-true",
    "--brand-blue-dark",
    "--navy",
    "--on-brand",
    "--surface-control",
    "--text",
    "--text-muted",
    "--glass-fill-hover",
    "--glass-fill-input",
    "--input-border",
    "--glass-ring",
    "--badge-alert",
    "--error",
    "--seg-selected",
    "--focus-ring",
    "--focus-ring-width",
    "--focus-ring-offset",
    "--radius-lg",
    "--space-2",
    "--space-3",
    "--space-4",
    "--space-5",
    "--type-caption",
    "--type-nav",
    "--duration-fast",
    "--duration-slow",
    "--ease-standard"
  ]
}

Raw file: https://design.werklist.com/agent/components/button