Theme Contract

What a theme must override to restyle the library without touching components.

Themes override tokens, not selectors. The themes in src/css/themes/ are demo examples, not shipped by the default stylesheet; they exist to show valid ways to use this contract. A minimal recolor theme overrides the intent pairs, surfaces, text colors, border, focus, focus ring, and hover overlay. Shape, shadow, motion, typography, and soft-variant mix tokens are optional knobs. Override them only when the theme actually changes that part of the system. A full theme can override:

  • intent colors and foreground pairs
  • surface, text, border, focus, and overlay colors
  • radius tokens
  • optional shadow, motion, typography, and soft-variant mix tokens

CSS

[data-theme="brand"] {
  color-scheme: light;

  --primary: hsl(260 70% 54%);
  --primary-fg: white;
  --secondary: hsl(190 70% 38%);
  --secondary-fg: white;
  --success: hsl(150 58% 34%);
  --success-fg: white;
  --warning: hsl(42 92% 52%);
  --warning-fg: hsl(35 35% 10%);
  --danger: hsl(355 72% 52%);
  --danger-fg: white;
  --neutral: hsl(250 8% 42%);
  --neutral-fg: white;

  --surface: hsl(260 45% 98%);
  --surface-raised: white;
  --surface-subtle: hsl(260 35% 94%);
  --surface-solid: hsl(260 28% 16%);

  --text: hsl(260 24% 14%);
  --text-muted: hsl(260 10% 42%);
  --text-subtle: hsl(260 8% 56%);
  --border: hsl(260 24% 84%);
  --focus: var(--primary);
  --focus-ring: hsl(260 70% 54% / 0.22);
  --hover-overlay: hsl(260 30% 10% / 0.04);
  --soft-bg-mix: 84%;
  --soft-border-mix: 58%;

  --radius: 0.625rem;
  --radius-sm: 0.3125rem;
  --radius-lg: 0.875rem;
}

Without data-theme, the default theme advertises color-scheme: light dark and follows the user's OS preference in browsers that support light-dark(). Dark themes should set color-scheme: dark. Light themes should set color-scheme: light. Browsers without light-dark() receive the light fallback. Because the native select chevron is an SVG background image and cannot inherit currentColor, the select hands the arrow back to the browser under prefers-contrast: more (same treatment as forced-colors: active) instead of shipping a recolored chevron per theme — one native fallback covers every theme without a high-contrast icon variant to maintain. In forced-colors: active, the default theme maps the public color tokens to system colors (Canvas, CanvasText, ButtonText, Highlight, and HighlightText) and removes decorative shadows. Components should inherit that token mode by default; keep local forced-color rules only for custom control geometry, native bar parts, disabled states, or selected states that would otherwise be communicated only by background color.