Contents Purpose Anatomy Variants States Tokens Used Accessibility Behavior Platform Notes Component: btn Purpose Trigger an action or navigation. Anatomy label — required, text content icon — optional, leading or trailing (Lucide on web, SF Symbols on macOS) Variants Class Visual Usage btn Base class, always applied Shared structure (inline-flex, alignment, min-height, transitions) btn-primary Accent bg, surface-primary text, no border High-emphasis CTA — one per section max btn-secondary Surface-primary bg, border-default border, text-primary Medium-emphasis — most buttons btn-ghost No bg, no border, text-secondary Low-emphasis — toolbar actions, dismissals btn is always combined with a variant class. A <button> without .btn receives base treatment from @layer base. States All canonical interactive states apply: rest, hover, focus-visible, active, disabled, loading. Hover (transition: --duration-normal ease-out): btn-primary: background-color → --color-accent-hover btn-secondary: background-color → --color-surface-secondary btn-ghost: background-color → --color-surface-secondary Active (instant, no transition): btn-primary: background-color → one step darker than hover (use filter: brightness(0.9)) btn-secondary: background-color → --color-surface-tertiary btn-ghost: background-color → --color-surface-tertiary Focus-visible (all variants): outline: 2px solid var(--color-accent-base) outline-offset: 2px Disabled (all variants): opacity: 0.4 cursor: not-allowed No hover/active/focus changes Loading (is-loading class): Preserve element dimensions Label hidden visually, replaced by a CSS-only pulse animation Disabled pointer events Tokens Used --font-sans, --text-base, --leading-normal --space-2, --space-4 --color-accent-base, --color-accent-hover --color-surface-primary, --color-surface-secondary, --color-surface-tertiary --color-text-primary, --color-text-secondary --color-border-default --duration-normal --icon-sm Accessibility Semantic element: <button> for actions, <a> for navigation Keyboard: Enter/Space activates the button aria-disabled="true" alongside visual disabled state when the element must remain focusable aria-busy="true" during loading state Loading label hidden with visibility: hidden (not display: none) to preserve screen reader text Minimum target size: 32px (pointer), 44px (touch) — met via min-height + padding Behavior No additional interaction behavior beyond the universal state model. is-loading disables pointer events and sets aria-busy="true". Platform Notes Web: CSS in @layer components. Builds on base <button> styles from @layer base — .btn is an opt-in upgrade, not a replacement. macOS: Map to SwiftUI Button with style modifiers corresponding to variants.