Color
Defines color roles, contrast minimums, and how palettes are built.
Principles
- Use roles, not values. A role resolves in every theme, and a copied value breaks in the next one.
- Set lightness first. Contrast depends mostly on OKLCH lightness, so tune hue and chroma afterward and check the ratio again.
- Meet 4.5:1 for text and 3:1 for marks. Text needs 4.5:1 (WCAG 1.4.3), and control edges, focus indicators and state icons need 3:1 (WCAG 1.4.11).
- Never rely on color alone. Name every status in words, beside its icon, so it reads with color vision deficiency and in forced colors (WCAG 1.4.1).
- Save chroma for small areas. Large surfaces stay near gray, and the strongest color goes to small marks such as badges.
Color roles
Each row shows a role in the light theme, then the dark. Values and sources are in Design tokens.
Surfaces
Text
Brand
Lines and focus
Status
Trend
Markets that read red as a rise swap --surf-trend-up and --surf-trend-down.
Charts
Series use ink with dashes and markers. Categories use hue, and each keeps a label.
In CSS, read a role through var().
Contrast
Text needs 4.5:1, and large text 3:1: 24px, or 18.67px bold. Control edges, focus indicators and selection marks need 3:1 against the color next to them. Decorative dividers and disabled controls are exempt.
Each surface keeps at least 1.05:1 against the one it sits on, so no plane disappears.
The tables measure the current theme's computed colors as WCAG 2 luminance in sRGB. They skip transparency, gradients and overlap, so recheck borderline values with a separate tool.
Text on surfaces 4.5:1 minimum
Status and badges 4.5:1 minimum
Trends 4.5:1 minimum
Text in controls 4.5:1 minimum
Control edges and marks 3:1 minimum
Chart colors 3:1 minimum
Code syntax 4.5:1 minimum
0 / 72 pairs meet their threshold. Forced colors and unsupported colors are excluded.
WCAG 1.4.3 — Text contrast / WCAG 1.4.11 — Non-text contrast
Status and selection
Show a status with a label and an icon, and fill its whole surface or border, not one rounded side. Success is green, apart from the brand, and the label still names it.
Selected text takes an opaque gray and its own text color, so it stays readable on any surface. Select this sentence to see it.
OKLCH palette
OKLCH names a color by lightness L (0–1), chroma C and hue H. L tracks perceived lightness, and a hue keeps its look from light to dark, unlike HSL. So the system fixes L for contrast and lets hue and chroma vary.
Palette lab
Pick hues on the wheel. The lab builds ramps, checks a categorical set for color vision deficiency, and measures any pair. Copy the CSS when a palette works. The site's theme doesn't change.