Button
The primary action control. Renders a <button>, or an <a> when given href. Orange darkens
on hover/press β no scale, no bounce.
import Button from '@repo/ui/Button';
<Button variant="primary" onClick={submit}>Submit order</Button><Button variant="primary" patch>Get started</Button> // patch corner β brand moment<Button href="/inbox" variant="outline">Open inbox</Button>| Prop | Type | Default | Notes |
|---|---|---|---|
variant | primary | secondary | outline | ghost | destructive | primary | primary is the only orange one β keep it to ~one per view |
size | sm | md | lg | md | 32 / 40 / 48px tall |
patch | boolean | false | Asymmetric patch corner (8px 8px 8px 0). Reserve for brand moments |
href | string | β | Renders an <a> instead of <button> |
className / class | string | β | Merged via tailwind-merge |
Plus all native <button> / <a> attributes (onClick, disabled, type, aria-*, β¦).
All states are token-driven: default β hover (--om-orange-hover) β active
(--om-orange-press) β :focus-visible (orange ring) β disabled (50% opacity, no pointer).
Do / donβt
Section titled βDo / donβtβ- Do use one
primaryper view β orange is ~2% of the layout. - Do use
patchsparingly for hero CTAs. - Donβt add scale/bounce on hover, or use orange for secondary actions.
- Donβt put body-length text in a button; itβs an action, sentence case, no β!β.
Accessibility
Section titled βAccessibilityβ- Keyboard-operable as a native button/link; visible
:focus-visiblering. - Icon-only buttons must pass an
aria-label. - Verified with
jest-axe(zero violations). Contrast note: orange-on-white is ~2.3:1;outline/ghostrings use--om-orange-pressfor β₯3:1 on light. SeeDECISIONS.md.