Colors
The color system uses OKLCH for all token values, providing perceptually uniform color representation. All hue colors are normalized to the same lightness level (L=65% light / L=75% dark) to guarantee WCAG AA contrast across all 10 hues without per-color tuning.
The tokens are defined in apps/web/src/design-tokens/theme.css and imported directly into the design guide — no copy-paste.
Token categories
| Category | Purpose |
|---|---|
| Background | Surface elevation hierarchy (base → surface → elevated) |
| Foreground | Text hierarchy (emphasized → default → muted) |
| Accessory | Borders and decorative elements |
| Importance | Semantic CTA colors (primary, secondary, destructive, neutral) |
| Status | Feedback states (ok, warning, error, inactive) |
| Tag | Tag colors — 10 hues with solid and inverse (tint) variants |
Swatches
General
Foreground
Foreground Colorless
Background
Accessory (borders, dividers)
Importance
Importance — Foreground
Importance — Background
Importance — Accessory
Status
Status — Foreground
Status — Background
Status — Background Muted
Status — Accessory
Status — Accessory Muted
Status — Background Inverse
Tag
Tag palette — Foreground
Tag palette — Background
Tag palette — Accessory
Tag palette — Background Inverse
Usage
Tokens are consumed through Tailwind utility classes via @theme inline mappings.
// Background layers<div className="bg-background-base"> {/* Page background */}<div className="bg-background-surface"> {/* Toolbar / header */}<div className="bg-background-elevated"> {/* Card / list item */}
// Importance colors — base for fills, foreground for colored text on neutral bg<div className="bg-importance-primary-background" /> {/* badge / button fill */}<span className="text-importance-primary-foreground" /> {/* colored label on page */}<div className="border-importance-destructive-background text-importance-destructive-foreground" />
// Status feedback<div className="bg-status-ok-background-inverse text-status-ok-foreground" />
// Hue colors (tags)<Badge className="bg-tag-teal-background text-tag-teal-background-inverse" /><Badge className="bg-tag-teal-background-inverse border-tag-teal-accessory text-tag-teal-foreground" />When to use each group
| Group | Reach for when… |
|---|---|
| Background | Setting surface elevation — page (base), toolbars/headers (surface), cards/list items (elevated) |
| Foreground | Coloring text — sub-variant sets hierarchy (muted → default → emphasized); use colorless-inverse for text on any colored fill |
| Accessory | Coloring borders and dividers — muted for subtle separators, default for standard, emphasized for interactive |
| Importance | Expressing semantic intent (primary, secondary, destructive, neutral) — sub-variant selects fill (background), colored text (foreground), or outline border (accessory) |
| Status | Showing feedback states (ok, warning, error, inactive) — sub-variant selects fill intensity, text, or border; see detail below |
| Tag | Styling tag badges exclusively — hue-based colorization only; see detail below |
Background group — three elevation layers for visual hierarchy without shadows. Base is the page; surface is for toolbars, sticky headers, and popovers; elevated is for cards and list items.
Foreground group — muted for metadata and placeholder text, default for body copy, emphasized for headings and high-contrast labels. foreground-colorless-inverse is white-in-light / white-in-dark — use it for text on any colored fill. foreground-colorless is the reverse (dark on light fill). Never hardcode text-white or text-black.
Accessory group — borders, dividers, and decorative rules. Use muted for subtle separators, default for standard borders, emphasized for interactive or prominent borders.
Importance group — three independent sub-variants, each for a different use case:
- Background (
importance-*-background) — fill color for badges, buttons, and indicators - Foreground (
importance-*-foreground) — colored text on a neutral page background, e.g. a section label - Accessory (
importance-*-accessory) — border color for outline-style buttons and badges
Status group — six sub-variants covering fills, text, and borders at two intensities each:
- Background — bold fill for icons, compact indicators, and badge fills
- Background muted — softened fill for progress bar tracks and subtle highlights
- Background inverse — tinted section background for status-aware card layouts
- Foreground — colored status text on a neutral background
- Accessory — status-colored border at normal intensity
- Accessory muted — subtle status-colored border (e.g. a low-key warning outline)
Tag group — exclusively for tag badge colors. Ten hues cover the full spectrum so each tag type can have a distinct color. Four sub-variants per hue:
- Background — solid fill for active or selected badges
- Background inverse — light tint background for default (non-selected) badges
- Foreground — colored text inside an inverse badge
- Accessory — border color for an inverse badge
Semantic vs primitive
Always reach for semantic tokens when building components. Semantic tokens carry intent — they adapt to light/dark mode automatically and communicate meaning (e.g. “this is a primary action” or “this item is expiring”).
// Correct — semantic intent, adapts to theme<button className="bg-importance-primary-background text-foreground-colorless-inverse" />
// Wrong — raw tag hue value used on a button; carries no semantic meaning<button className="bg-tag-teal-background" />The one exception is tag badges. Tag tokens (orange, teal, blue, etc.) are the correct primitive choice for tag color assignment because the purpose is to assign a visual hue, not to express a semantic state. Use the full set of tag variants (base, -inverse, -foreground, -accessory) to style the badge itself.
// Correct — tag color tokens used intentionally for tag colorization<Badge className="bg-tag-teal-background-inverse border-tag-teal-accessory text-tag-teal-foreground" /><Badge className="bg-tag-teal-background" /> // solid variant for active/selectedNever use raw OKLCH values or Tailwind’s built-in color scale (e.g. bg-blue-500) — those bypass the token system and will not respond to theme changes.
Dark mode
All tokens are defined in both :root (light) and .dark (dark) selectors.
The theme is controlled by the dark class on <html> — toggled via localStorage and OS preference detection.