Skip to main content

What it is

A control that performs an action in place: submitting, confirming, opening, applying, deleting. Its visual weight declares how much the interface wants you to press it. This page covers the whole button concept — with a label, with a label and an icon, with an icon alone, and the filter control. They are one thing to a reader and several exports in the package; the mapping is below.

Live example

Exports

One page, three exports. A designer can read the page as one component; an engineer needs to know which to import.
ButtonGroup is documented separately — see Button group. It governs the relationship between buttons rather than a button itself, which is a different decision.This grouping is a documentation choice, not a code change. Why the two structures differ is recorded in TITAN-DIV-10.

Vocabulary

The design team names buttons by prominence — how much the interface wants this pressed. That is the system’s vocabulary, it matches the naming specification’s prominence axis, and it is a variant property of the design library’s own Button component with exactly these three values. It is not a habit of speech; it is built into the library. Say primary, secondary, tertiary. They describe the decision — relative weight — rather than the treatment that happens to express it. A borderless button is still tertiary if the weight is what you meant.
“Primary” carries three meanings in this system. They are unrelated.Prominence and colour are independent axes. “Primary, error” is a legitimate combination: the highest-weight button in the destructive colour.The reader-facing name for the second axis is purpose, and it has two values — default and destructive. Code exposes the whole colour palette; the system sanctions two. Say “destructive”, not “error” or “red”.
tertiary is specified and never implemented. The naming specification lists it on the prominence axis, and no shipped token carries the word — the treatment exists in code only as variant="text". See TITAN-DIV-11.

Choose Button when

  • The control performs an action rather than retrieving a resource — submit, confirm, apply, delete, open a dialog.
  • The result of pressing it is a change in application state.
  • The user would not reasonably want to open the result in a new tab.

Choose something else when

Button vs. Link is the fork people get wrong most often. The test is not appearance — a Link can be styled to look like a button. The test is the result: does pressing it change state (Button), or does it take me to a resource that has a URL (Link)? If a URL exists, it must be reachable by right-click.

Anatomy

For an icon-only button the icon is the label, which is why it needs an accessible name supplied separately — see Accessibility.

Variants

Prominence encodes how much the interface wants this pressed, not how severe the consequence is. A destructive action is frequently the tertiary control on the screen.
Border adds to the box. The three levels carry different border widths, so they do not share internal geometry even at the same size. A row mixing them will not align on the label baseline by default.

The axes a Button actually has

Five, and naming them is what keeps a review from arguing about two different things at once. These are the design library’s own variant properties, and each maps to something in code: Two combinations the design library does not publish:
  • A trailing icon. The Type axis has a leading icon and no trailing one.
  • An icon-only button below primary prominence. Icon-only exists at primary, default purpose, and nowhere else.
TITAN-BTN-11 and TITAN-BTN-12 state both as rules, and both need confirming. The reasoning holds on its own, but the evidence is an absence in a published variant set — and a set of five axes has 180 combinations, so a missing one can mean ruled out or can mean never built. Ask before citing either as settled.

What Storybook publishes

Edge and failure states

The state everyone designs is a short label on a wide button. These are the others.

Tokens

Declared padding is not effective padding. Button declares padding: 8px 12px, but with a fixed control-height-md of 36px and a label line box of 24.5px the effective vertical padding is roughly 5.75px — the declared value is largely inert.Vertical space on a Button comes from the height token, not from the padding declaration. Do not reproduce the declared value in a design spec or in generated code.

Composition

Action group order — trailing-primary. The primary action sits last (rightmost in LTR), with dismissive actions to its left.
  1. The primary button is last.
  2. Dismissive actions (Cancel, Close) are first, always tertiary.
  3. Group separation uses a spacing token on the container, never a margin on the button.
Ordering is what compositions get wrong, not component selection. A group built from three correct Buttons in the wrong order is a worse outcome than the wrong variant, because the user’s muscle memory is in the position rather than the styling.

Content

Title case here means headline style, not “every word capitalized.” Lowercase short articles, conjunctions, and prepositions (a, the, to, of, and) unless one is the first or last word — “Save Changes to Draft,” not “Save Changes To Draft.” The theme’s rendering doesn’t know this exception on every label — see Open issues for where that bites.
Confirmation labels restate the action, never “Yes”/“No”. A dialog asking “Delete this campaign?” answered by “Yes” forces the user to re-read the question to know what “Yes” does. Delete Campaign is self-describing at the moment of commitment.

Accessibility

  • Renders a native <button>. Never apply role="button" to a div.
  • The accessible name comes from children. An icon-only button has no text, so it requires an explicit accessible name — without one it is announced as “button” and nothing else.
  • loading sets aria-busy="true" and disables the control.
  • Label contrast meets WCAG 2.2 AA (4.5:1). A tertiary button on a tinted surface is the historical failure case — verify it against the surface it actually sits on, not white.
TITAN-A11Y-04 requires a visible focus indicator. Button has no semantic focus token to draw it from (known issue) and inherits whatever the framework draws instead — never remove it. What this component does not handle: focus return after a dialog it opened closes, announcing the result of the action, and its own error state. Those belong to the composition.

Constraints

Retired

Known issues

Button: open issues

Divergences, open decisions, and undocumented gaps for Button.

Why it works this way

Labels are one line, by construction. A row of buttons reads as a row because the controls share a baseline and a height. Allowing one label to wrap trades that for the ability to write a longer label — and the longer label was the thing to fix. The theme holds the line so the decision does not depend on every author noticing it. Prominence is decoupled from severity. The most common request is to make destructive actions red and prominent so users “can’t miss them.” Prominence is an invitation. The interface should be easy to escape and deliberate to commit — so the destructive action takes a lower prominence and the escape route gets the weight. Severity is carried by the colour intent, the copy, and the confirmation step, never by prominence. The vocabulary is prominence, not treatment. “Primary” names the decision — this is the action the view exists to enable. “Contained” names one way of drawing it. Naming the decision means the rule survives a visual change: if primary buttons stopped being filled tomorrow, TITAN-BTN-01 would still say the right thing. Trailing-primary ordering matches the platform conventions users bring from elsewhere, and puts the primary action nearest the pointer’s resting position for the action taken most often. Two sizes, not three. A third size is another definition to maintain, another row in every matrix, and another decision at every call site. Two covers dense contexts and standard ones. If a case for a third emerges it gets added to the token source — not by passing an un-themed value through. One page for several exports. Someone deciding between a labelled button and an icon-only one is making a single decision about emphasis and space. Splitting that across two pages makes them read one, decide, and never learn the alternative existed.

Status

Last modified on September 7, 2026