Tier 1Tier 1 · Web (www / app / auth). The full design system: our component library and the components.css recipe. This is the one tier where the recipe is the contract, native tiers take the tokens and nothing else.--button-height-md · --tw-fs · --yellow-border · --nav-height
Three tiers, one axis, emphasis.btn-primary is the brand fill: at most one per action group; yellow is reserved for it. btn-default is the secondary tier: a quiet fill plus a --border step, so it reads to a hard edge exactly like a solid (--button-secondary-* aliases the same pair for shadcn imports). btn-ghost is the third tier: text only, no fill, no border.
--button-ghost-fg resting; hover paints --button-ghost-bg-hover and lifts the label to --button-ghost-fg-hover, so the faint fill is never the only cue. Use it for the header Sign in, toolbar text actions, Cancel beside a filled confirm. Outline is not a tier: the system ships no transparent-fill bordered button.
Pairing rule · never a border-only box beside a solid fill
btn-md
Never. The dashes mark the shared btn-md box, all three boxes are drawn from --button-height-md, so they are equal by construction; the read is not. A sub-20% hairline's soft edge resolves shorter beside the fill's hard edge, and a second bordered box competes with the CTA.
btn-md
Instead. Ghost beside the fill: at rest there is no second box to mis-read: one hard edge, hierarchy intact. Hover's quiet fill is transient and snaps to the same guides. If both must be boxed, both must fill (btn-default) with a border at --border: never a hairline on transparent.
Sizes · lg 36 · md 32 · sm 28 · xs 24, one box per size, tokenized: --button-height(-md/-sm/-xs) + padding + label + tracking
Quiet icon buttons · the ghost appearance on icon-only controls, the default inside rows & toolbars
Two appearances, one variant set. An icon button is quiet by default: transparent at rest, the variant colour in the glyph, the variant's component-bg-hover behind it on hover. That is what list rows, toolbars and dialog closes use. The filled appearance (btn-default btn-icon and friends) is for an icon button standing on its own as a target.
A destructive icon action is never a filled red disc: btn-quiet btn-danger paints a --red-solid glyph, and only a labelled btn-danger button fills. Disabled quiet buttons drop to --solid rather than fading the whole control.
With icon · icon + label, one size per action group
States · hover · focus · disabled · loading
Accessibility floor. White-on-green and white-on-red sit at 3.2–3.9 contrast, an accepted APCA Lc 60+ contract (medium-weight uppercase labels, tagged data-contrast-contract="apca"), not a WCAG 4.5:1 pass. Never use btn-sm or btn-xs for btn-success, btn-danger, or btn-info. (One known violation ships today, the transfer row's "Go to file" and "Stop recording" are sm; see the Transfers card.) For tight rows use btn-primary (yellow on black = 14:1 AAA at any size) or btn-default.