breakstuff
Design System
Theme:
The tokens, primitives, and rules that build every surface. Switch themes to preview.
Semantic Colors
Spacing
--sp-1 4px --sp-2 8px --sp-3 12px --sp-4 16px --sp-5 20px --sp-6 24px --sp-8 32px --sp-10 40px --sp-12 48px --sp-16 64px --sp-20 80px Radius
radius-badge 4px radius-input 6px radius-card 8px radius-md 6px radius-modal 12px radius-pill 9999px Typography
text-micro
The quick brown fox
0.625rem text-sm
The quick brown fox
0.75rem text-body
The quick brown fox
0.875rem text-base
The quick brown fox
1rem text-lead
The quick brown fox
1.125rem text-heading
The quick brown fox
1.375rem text-title
The quick brown fox
1.75rem text-display
The quick brown fox
2.25rem text-hero
The quick brown fox
3rem text-giant
The quick brown fox
4rem Motion
dur-fast · ease dur-fast · ease-io dur-base · ease dur-base · ease-io dur-slow · ease dur-slow · ease-io Rules for consumers
@breakstuff/tokens
--- description: How to use @breakstuff/tokens semantic CSS custom properties in apps --- # @breakstuff/tokens — Consumer Rules Import the package once in your app's entry point: ```ts import '@breakstuff/tokens/tokens.css'; ``` Set the theme on the `<html>` element in `index.html`: ```html <html data-theme="dadbod"> ``` Available themes: `dadbod`, `dark-orange`, `heartthrob-crimson`, `heartthrob-vitalscan`. ## Hard rules - **Never use raw hex or px values in CSS.** Every color, spacing, radius, typography, and motion value must come from a CSS custom property. - **Use semantic tokens only.** Base tokens are source inputs and are not emitted to app-facing CSS. - If a semantic token doesn't exist for your use case, add one (see maintainer rules) — do not hardcode. ## Available CSS Custom Properties ### Color | Variable | Use | |----------|-----| | `--bg` | Page/app background | | `--surface` | Card, panel, sheet background | | `--surface-raised` | Elevated surface (dropdown, popover) | | `--on-bg` | Primary text/icon on page background | | `--on-bg-muted` | Secondary text on page background | | `--on-bg-subtle` | Tertiary/disabled text on page background | | `--on-surface` | Primary text/icon on surface | | `--on-surface-muted` | Secondary text on surface | | `--on-surface-subtle` | Tertiary text on surface | | `--accent` | Primary accent — interactive elements, highlights | | `--accent-hover` | Accent on hover state | | `--accent-press` | Accent on active/press state | | `--accent-muted` | Low-emphasis accent tint (background fills) | | `--on-accent` | Text/icon on accent-colored backgrounds | | `--border` | Default border color | | `--border-strong` | Emphasis border | | `--focus-ring` | Focus indicator color | | `--success` | Success state | | `--success-muted` | Low-emphasis success tint | | `--warning` | Warning state | | `--warning-muted` | Low-emphasis warning tint | | `--error` | Error/danger state | | `--error-muted` | Low-emphasis error tint | | `--info` | Informational state | | `--info-muted` | Low-emphasis info tint | | `--achievement` | Gold achievement/PR highlight | | `--achievement-muted` | Low-emphasis achievement tint | | `--spark-glow` | Lightning/spark effect — outer aura and mid-bolt glow | | `--spark-core` | Lightning/spark effect — bright bolt core | | `--spark-hot` | Lightning/spark effect — white-hot bolt center | | `--spark-branch` | Lightning/spark effect — branch filaments / secondary accent | | `--spark-flash` | Lightning/spark effect — full-screen impact flash | | `--chart-1` through `--chart-5` | Chart series colors (in order) | | `--overlay`, `--overlay-light` | Overlay backgrounds | | `--shadow-raised`, `--shadow-overlay` | Elevation shadows | ### Typography | Variable | Use | |----------|-----| | `--font` | Body/UI font family | | `--font-display` | Display/heading font family | | `--text-micro` | Smallest label text | | `--text-sm` | Small supporting text | | `--text-body` | Default body text | | `--text-base` | Base size (1rem) | | `--text-lead` | Lead/subheading text | | `--text-heading` | Section heading | | `--text-title` | Page title | | `--text-display` | Large display number/stat | | `--text-hero` | Hero-scale text | | `--text-giant` | Hero display | | `--weight-normal` | 400 | | `--weight-medium` | 500 | | `--weight-semibold` | 600 | | `--weight-bold` | 700 | | `--leading-tight` | Tight line height | | `--leading-normal` | Default line height | | `--tracking-tight` | Tight letter spacing | | `--tracking-normal` | Normal letter spacing | | `--tracking-wide` | Loose letter spacing | | `--tracking-wider` | Extra-loose letter spacing | ### Spacing | Variable | Use | |----------|-----| | `--sp-1` through `--sp-20` | Raw spacing scale (multiples of 4px) | | `--inset-sm` | Compact component padding | | `--inset-md` | Default component padding | | `--inset-lg` | Generous component padding | | `--component-gap` | Gap between items in a component group | | `--item-gap` | Gap between items in a list | | `--section-gap` | Gap between page sections | | `--page-padding` | Horizontal page padding | ### Radius | Variable | Use | |----------|-----| | `--radius-pill` | Pill-shaped elements | | `--radius-input` | Input fields | | `--radius-card` | Cards and panels | | `--radius-md` | Generic medium radius | | `--radius-modal` | Modals and dialogs | | `--radius-badge` | Badge/chip elements | ### Control Size | Variable | Use | |----------|-----| | `--height-control-sm` | Small control minimum height | | `--height-control-md` | Default control minimum height | | `--height-control-compact` | Compact form control minimum height | | `--height-control-lg` | Large control minimum height | ### Motion | Variable | Use | |----------|-----| | `--dur-fast` | Quick transitions (hover, toggle) | | `--dur-base` | Default transition duration | | `--dur-slow` | Slow transitions (modals, drawers) | | `--ease` | Default easing curve | | `--ease-io` | In/out easing curve | ## Machine-readable token list `@breakstuff/tokens/semantic-vars.json` exports the public CSS custom property names. Stylelint should use that list when enforcing allowed design-system variables. `@breakstuff/tokens/semantic-manifest.json` exports the public semantic token catalog with purpose descriptions. Agents should read this file before choosing a token so they select by intended use, not by color value or name similarity. ## iOS-specific rule Interactive inputs (`<input>`, `<textarea>`, `<select>`) must have `font-size` ≥ 16px (`var(--text-body)` or larger). Anything smaller triggers iOS Safari auto-zoom on focus. Use `var(--text-body)` as the minimum.
@breakstuff/primitives-react
# @breakstuff/primitives-react — consumer rule
You are editing code that depends on `@breakstuff/primitives-react`. Follow these rules.
## Setup (one-time, at the app root)
```ts
import '@breakstuff/tokens/tokens.css';
import '@breakstuff/primitives-react/styles.css';
```
Both imports must happen exactly once each, and **tokens.css must come first**.
## Token selection
When writing or reviewing component CSS, read `@breakstuff/tokens/semantic-manifest.json` before choosing CSS custom properties. The manifest documents each public semantic token's intended purpose. Choose tokens by purpose, not by matching the current color value.
Use `@breakstuff/tokens/semantic-vars.json` only as the allowed-variable list for tooling; it does not explain when to use a token.
## Primitives available
- `Button` (single component). Props: `variant: "primary" | "secondary" | "ghost"`, `size: "sm" | "md" | "lg"`, `tone: "accent" | "neutral" | "danger" | "info" | "warning" | "success"`, `asChild`, plus native `<button>` attributes. Use `tone="warning"` (amber/gold, `--warning` token) for AI-related actions. Use `tone="info"` (indigo/violet, `--info` token) for informational actions.
- `IconButton` (single component). Props: required `aria-label: string`, `variant: "primary" | "secondary" | "ghost"` (default `"ghost"`), `size: "sm" | "md" | "lg"` (default `"md"`), `tone: "accent" | "neutral" | "danger" | "info" | "warning" | "success"` (default `"neutral"`), plus native `<button>` attributes. Use for icon-only actions such as menus, settings, close/delete affordances, and compact toolbar actions. Children should be an icon or visually hidden content; the accessible name must come from `aria-label`.
- `ButtonGroup` (compound): `ButtonGroup.Root`, `ButtonGroup.Button`. `Root` props: `orientation: "horizontal" | "vertical"` (default `"horizontal"`), `equalWidth?: boolean` (default `true`), plus native `<div>` attributes. `Button` accepts `Button` props and defaults to `variant="secondary"`, `tone="neutral"`, `size="md"`.
- `Pill` (single component). Props: `size: "sm" | "md"` (default `"md"`), `trailingIcon?: ReactNode`, plus native `<button>` attributes. Use for compact filter chips and removable labels, not for primary actions.
- `Dialog` (compound): `Dialog.Root`, `Dialog.Trigger`, `Dialog.Content` (requires `title: string`, optional `description`, `titleLevel: "h2" | "h3"`, `titleSize: "default" | "compact"`, `variant: "modal" | "sheet"`), `Dialog.Footer`, `Dialog.Close`.
- `Tabs` (compound): `Tabs.Root`, `Tabs.List`, `Tabs.Trigger`, `Tabs.Panel`.
- `Input` (single component). Props: `label: string` (required), `hint?: string`, `state: "idle" | "ok" | "error" | "disabled"` (default `"idle"`), `size: "sm" | "md" | "lg"` (default `"md"`), `density: "normal" | "compact"` (default `"normal"`), `variant: "default" | "number"` (default `"default"`), `selectOnFocus?: boolean`, plus native `<input>` attributes except native `disabled` and `size`. Set `--input-label-bg` on the containing element when the input sits on `--surface` or `--surface-raised` (defaults to `--bg`). Match `size` to the `Button` size used alongside the input for visual symmetry.
- `Textarea` (single component). Props: `label: string` (required), `hint?: string`, `state: "idle" | "ok" | "error" | "disabled"` (default `"idle"`), `className?` (layout only), plus native `<textarea>` attributes except native `disabled`. Use for multi-line text entry; hints in error state render as alerts.
- `Select` (single component). Props: `label: string` (required), `options: Array<{ value: string; label: string; disabled?: boolean }>`, `placeholder?: string`, `hint?: string`, `state: "idle" | "ok" | "error" | "disabled"` (default `"idle"`), `density: "normal" | "compact"` (default `"normal"`), `size: "sm" | "md" | "lg"` (default `"md"`), `disabled?: boolean`, `className?` (layout only), `triggerClassName?` (layout only), `id?`, plus Radix Select root props except native disabled handling.
- `SearchableSelect` (single component). Use when an option set can grow beyond easy scanning. Props: `label: string` (required), `options: Array<{ value: string; label: string; description?: string; disabled?: boolean }>`, `value?: string`, `onValueChange?: (value: string) => void`, `placeholder?: string`, `searchPlaceholder?: string`, `emptyText?: string`, `hint?: string`, `state: "idle" | "ok" | "error" | "disabled"` (default `"idle"`), `size: "sm" | "md" | "lg"` (default `"md"`), `disabled?: boolean`, `className?` (layout only), `triggerClassName?` (layout only), `id?`, plus Radix Popover root open props. Prefer over `Select` for long prototype lists, large entity pickers, or any picker where users may know the item by name.
- `Slider` (single component). Wraps `@radix-ui/react-slider` for accessible single-value range controls. Props: `label: string` (required), `value?: number`, `defaultValue?: number`, `onValueChange?: (value: number) => void`, `onValueCommit?: (value: number) => void`, `valueLabel?: ReactNode`, `hint?: string`, `minLabel?: ReactNode`, `maxLabel?: ReactNode`, `state: "idle" | "disabled"` (default `"idle"`), `disabled?: boolean`, plus Radix Slider root props except array-based value/change props and disabled handling. Keep domain math in app code; pass the computed value/min/max/step into the primitive.
- `Stack` (single component). Props: `direction: "row" | "column"` (default `"column"`), `gap: "none" | "xs" | "sm" | "md" | "lg" | "xl"` (default `"md"`), `align: "start" | "center" | "end" | "stretch"` (default `"stretch"`), `justify: "start" | "center" | "end" | "between" | "around"` (default `"start"`), `wrap?: boolean`, `asChild`, plus native `<div>` attributes. Gap maps to `--sp-1/2/4/6/8`.
- `ReorderableStack` (single component). Props: `items`, `getKey`, `renderItem`, `onReorder`, `direction`, `gap`, `align`, `justify`, `activationDelay`, `disabled`, `itemClassName`, plus native `<div>` attributes except `children`. Use when app code needs keyboard- and drag-driven reordering; `renderItem` receives `{ isDragging, isDropTarget }` state.
- `Drawer` (compound): `Drawer.Root`, `Drawer.Trigger`, `Drawer.Content` (requires `title: string`, optional `description`), `Drawer.Close`. `Content` also supports `minimized`, `onMinimize`, `onExpand`, `outsideInteraction: "close" | "minimize" | "none"`, and `swipeDownAction: "close" | "minimize" | "none"`. Renders as a bottom sheet that slides up from the bottom edge. Has a drag-handle indicator and safe-area-inset-bottom padding built in. Use for contextual overlays that don't need to be full-screen (e.g. timers, quick-action panels). Stacks correctly on top of a full-screen `Dialog` sheet.
- `Card` (single component). Props: `asChild`, plus native `<div>` attributes. Renders a surface-raised container with `--border-strong` border, `--radius-card` radius, and `--inset-sm` padding. Use `asChild` to render as a `Link` or `button` for clickable cards. Apply layout-only `className` for width, flex children, or positional tweaks — never restyle the card surface.
- `MobileBottomNav` (single component). Props: native `<nav>` attributes. Renders a `position: fixed` bottom nav shell with safe-area-inset-bottom padding, surface background, and top border. Defines `--bottom-nav-height: 3.5rem` on `:root` — use `calc(var(--bottom-nav-height) + env(safe-area-inset-bottom))` anywhere content needs to clear the nav. Place app-specific tab items as children.
- `SearchInput` (single component). Props: `value: string` (required), `onChange: (value: string) => void` (required, receives the string directly — not a synthetic event), `placeholder?: string` (default `"Search…"`), plus all native `<input>` attributes except `type` and `onChange`. Always renders a magnifying-glass icon on the left and a × clear button on the right. The × is always visible but dimmed when the field is empty. Suppresses the native browser search-cancel button. Font size is clamped to ≥ 16px to prevent iOS Safari viewport zoom.
- `Checkbox` (single component). Wraps `@radix-ui/react-checkbox`. Props: `checked?: boolean | "indeterminate"`, `defaultChecked?: boolean`, `onCheckedChange?: (checked: boolean | "indeterminate") => void`, `disabled?`, `required?`, `name?`, `value?`, `id?`, `className?` (layout only), plus all Radix CheckboxProps. Renders as a square box (`--sp-5` × `--sp-5`) with an accent-filled background and check icon when checked. The indicator is a `fa-solid fa-check` icon — Font Awesome Kit must be loaded. Always associate with a `<label>` via `id`/`htmlFor` or wrap in a `<label>`.
- `SegmentedControl` (compound): `SegmentedControl.Root`, `SegmentedControl.Item`. Use for small mutually exclusive option sets. It wraps Radix RadioGroup, so `Root` takes RadioGroup root props such as `value`, `defaultValue`, `onValueChange`, and `aria-label`; `Item` takes RadioGroup item props such as `value` and `disabled`.
Always use the primitive from this package in preference to raw `@radix-ui/*` or hand-rolled HTML controls. If something is missing, **add it to this package** — do not one-off it in an app.
## Composition
- Use the **compound API** (`Dialog.Trigger`, `Tabs.Panel`). Never import sub-pieces by name from `@radix-ui/*` directly.
- `asChild` is the only supported way to change the rendered element:
```tsx
<Button asChild>
<a href="/go">Go</a>
</Button>
```
Never add an `as` prop.
- Disabled state uses the native `disabled` attribute. Never set `aria-disabled` manually.
## className
`className` is a **layout escape hatch only** — use it for margin, grid placement, or positional tweaks. Never use it to:
- Re-skin colors, backgrounds, borders, or text styles of the primitive.
- Change padding or font-size of the primitive.
If you need to restyle a primitive visually, add a new variant / tone / size to the primitive in this package.
## Focus
Focus styling is handled by the `.ds-app` class from `@breakstuff/tokens`. Do not add component-level focus styles.
Primitives
Button
ButtonGroup
Pill
Input
Looks good
Required
Textarea
SearchInput
Select
Checkbox
unchecked
checked
indeterminate
disabled
SegmentedControl
Card
Total Volume
12,400 kg
Sessions
47
Stack
row / gap sm / align center
A
B
C
column / gap md / align stretch
Set 1
Set 2
Set 3
Tabs
Today's session goes here.