Browse all of Kata

Buttons

Source docs/design/patterns/buttons.mdMarkdown

Primary, secondary, link — and the one-filled-primary budget.

Part of the Konjo design language. The laws in the spine apply here unless this chapter contradicts them.

Kata (2026-09-29). Red is the action colour and buttons are pills (interview, V4). This table is the Kata version; <Button> implements it.

Role Fill Height Radius Label
Primary brand.red, label brand.onRed (5.01:1, both themes). Studio too 48 radius.pill rowTitle 16/600
Primary inside a hero block brand.red 48 radius.pill label 13/700, caps (hero voice)
Secondary none, 1px border.control in ink 44 radius.pill label 13/700, text.primary
Destructive the secondary style — an ink outline, never red — with a plain verb ("Delete event") behind the serious-action confirmation 44 radius.pill label 13/700, text.primary
Text link none 44 target — pad 12 vertical + hitSlop when the label is short — label 13/700, accent.red
Chip surface.card + border.control; selected fills brand.red on surface.card, label brand.onRed 32 radius.pill label 13/700

label 13/700 is for chips, secondary buttons and links — not for a 48pt primary. A 48pt button carrying 13pt type is anti-pattern #13, observed in Mindbody's "BOOK NOW": target size and label size decided independently. A full-width primary takes rowTitle 16/600.

Every button is a pill, full-width primaries included. The August rule was the opposite (a full-width pill "reads as a toy"); the founder chose pill buttons with soft cards in Kata V4, over square and fully rounded alternatives, and the reason is recorded there.

Red on a button means "tap here" and nothing else. That is why a destructive action is an ink outline: a red Delete would read as the thing to press. Errors use feedback.errorText, a different red, and always with an icon and words.

"Ink" as a text colour means text.primary, not brand.ink. Ink is for section labels, eyebrows and arrows (links are red now), and that is right — but brand.ink is theme-invariant #111111, which is 1.021:1 on the dark page. It is the same trap the hero law names, and it applies to a link label exactly as much as to a surface. brand.ink is a fill on a light surface, or the hero surface itself. Text that must survive both themes uses text.primary.

Loading state. A button in flight keeps its fill, swaps its label ("Submitting…"), shows a 16pt spinner to the left of the label at spacing.s8, and ignores taps. It is never greyed — there is no legal disabled text colour in this system.

Heights come from controlHeight, not from a spacing token: primary 48, control 44 (secondary and inputs), chip 32, row 64. A spacing token doing a sizing job is how 48 ends up written as spacing.s48.