Skip to content

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

CategoryPurpose
BackgroundSurface elevation hierarchy (base → surface → elevated)
ForegroundText hierarchy (emphasized → default → muted)
AccessoryBorders and decorative elements
ImportanceSemantic CTA colors (primary, secondary, destructive, neutral)
StatusFeedback states (ok, warning, error, inactive)
TagTag colors — 10 hues with solid and inverse (tint) variants

Swatches

General

Foreground

Emphasized
--foreground-emphasized
Default
--foreground-default
Muted
--foreground-muted

Foreground Colorless

Colorless
--foreground-colorless
Colorless Inv.
--foreground-colorless-inverse

Background

Base
--background-base
Surface
--background-surface
Elevated
--background-elevated

Accessory (borders, dividers)

Emphasized
--accessory-emphasized
Default
--accessory-default
Muted
--accessory-muted

Importance

Importance — Foreground

Primary Foreground
--importance-primary-foreground
Secondary Foreground
--importance-secondary-foreground
Destructive Foreground
--importance-destructive-foreground
Neutral Foreground
--importance-neutral-foreground

Importance — Background

Primary
--importance-primary-background
Secondary
--importance-secondary-background
Destructive
--importance-destructive-background
Neutral
--importance-neutral-background

Importance — Accessory

Primary Accessory
--importance-primary-accessory
Secondary Accessory
--importance-secondary-accessory
Destructive Accessory
--importance-destructive-accessory
Neutral Accessory
--importance-neutral-accessory

Status

Status — Foreground

OK Foreground
--status-ok-foreground
Warning Foreground
--status-warning-foreground
Error Foreground
--status-error-foreground
Inactive Foreground
--status-inactive-foreground

Status — Background

OK background
--status-ok-background
Warning background
--status-warning-background
Error background
--status-error-background
Inactive background
--status-inactive-background

Status — Background Muted

OK Background Muted
--status-ok-background-muted
Warning Background Muted
--status-warning-background-muted
Error Background Muted
--status-error-background-muted
Inactive Background Muted
--status-inactive-background-muted

Status — Accessory

OK Accessory
--status-ok-accessory
Warning Accessory
--status-warning-accessory
Error Accessory
--status-error-accessory
Inactive Accessory
--status-inactive-accessory

Status — Accessory Muted

OK Accessory Muted
--status-ok-accessory-muted
Warning Accessory Muted
--status-warning-accessory-muted
Error Accessory Muted
--status-error-accessory-muted
Inactive Accessory Muted
--status-inactive-accessory-muted

Status — Background Inverse

OK Background Inverse
--status-ok-background-inverse
Warning Background Inverse
--status-warning-background-inverse
Error Background Inverse
--status-error-background-inverse
Inactive Background Inverse
--status-inactive-background-inverse

Tag

Tag palette — Foreground

Orange Foreground
--tag-orange-foreground
Brown Foreground
--tag-brown-foreground
Green Foreground
--tag-green-foreground
Teal Foreground
--tag-teal-foreground
Cyan Foreground
--tag-cyan-foreground
Blue Foreground
--tag-blue-foreground
Indigo Foreground
--tag-indigo-foreground
Purple Foreground
--tag-purple-foreground
Pink Foreground
--tag-pink-foreground
Rose Foreground
--tag-rose-foreground

Tag palette — Background

Orange Background
--tag-orange-background
Brown Background
--tag-brown-background
Green Background
--tag-green-background
Teal Background
--tag-teal-background
Cyan Background
--tag-cyan-background
Blue Background
--tag-blue-background
Indigo Background
--tag-indigo-background
Purple Background
--tag-purple-background
Pink Background
--tag-pink-background
Rose Background
--tag-rose-background

Tag palette — Accessory

Orange Accessory
--tag-orange-accessory
Brown Accessory
--tag-brown-accessory
Green Accessory
--tag-green-accessory
Teal Accessory
--tag-teal-accessory
Cyan Accessory
--tag-cyan-accessory
Blue Accessory
--tag-blue-accessory
Indigo Accessory
--tag-indigo-accessory
Purple Accessory
--tag-purple-accessory
Pink Accessory
--tag-pink-accessory
Rose Accessory
--tag-rose-accessory

Tag palette — Background Inverse

Orange Background Inverse
--tag-orange-background-inverse
Brown Background Inverse
--tag-brown-background-inverse
Green Background Inverse
--tag-green-background-inverse
Teal Background Inverse
--tag-teal-background-inverse
Cyan Background Inverse
--tag-cyan-background-inverse
Blue Background Inverse
--tag-blue-background-inverse
Indigo Background Inverse
--tag-indigo-background-inverse
Purple Background Inverse
--tag-purple-background-inverse
Pink Background Inverse
--tag-pink-background-inverse
Rose Background Inverse
--tag-rose-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

GroupReach for when…
BackgroundSetting surface elevation — page (base), toolbars/headers (surface), cards/list items (elevated)
ForegroundColoring text — sub-variant sets hierarchy (muteddefaultemphasized); use colorless-inverse for text on any colored fill
AccessoryColoring borders and dividers — muted for subtle separators, default for standard, emphasized for interactive
ImportanceExpressing semantic intent (primary, secondary, destructive, neutral) — sub-variant selects fill (background), colored text (foreground), or outline border (accessory)
StatusShowing feedback states (ok, warning, error, inactive) — sub-variant selects fill intensity, text, or border; see detail below
TagStyling 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/selected

Never 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.