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.