Colors

Standard’s palette is built on OKLCH — a perceptually uniform color space where equal lightness values produce equal perceived brightness. The accent hue is 270° purple.


Grayscale

14 neutral steps from black to near-white. Every step is a pure OKLCH lightness value with zero chroma.

0
950
900
850
800
700
600
500
400
300
200
100
50
W
--black: oklch(0% 0 0);
--g950:  oklch(12% 0 0);
--g900:  oklch(17% 0 0);
--g850:  oklch(20% 0 0);
--g800:  oklch(24% 0 0);
--g700:  oklch(34% 0 0);
--g600:  oklch(44% 0 0);
--g500:  oklch(54% 0 0);
--g400:  oklch(64% 0 0);
--g300:  oklch(76% 0 0);
--g200:  oklch(86% 0 0);
--g100:  oklch(93% 0 0);
--g50:   oklch(97% 0 0);
--white: oklch(100% 0 0);

Purple Accent

The primary brand color at 270° hue. Seven stops from deep (600) to barely-there (50).

600
500
400
300
200
100
50
--pri-600: oklch(45% 0.18 270);
--pri-500: oklch(55% 0.20 270);  /* --accent */
--pri-400: oklch(65% 0.18 270);
--pri-300: oklch(75% 0.14 270);
--pri-200: oklch(85% 0.08 270);
--pri-100: oklch(92% 0.04 270);  /* --accent-s */
--pri-50:  oklch(96% 0.02 270);

Status Colors

Feedback colors for success, warning, and error. Each has a full-strength and soft variant.

Success
oklch(65% 0.18 145)
--ok-soft
Warning
oklch(75% 0.15 85)
--warn-soft
Error
oklch(60% 0.20 25)
--err-soft

Semantic Mapping

Tokens are aliased to semantic roles. The same token name resolves to different values per theme.

Surfaces & Text

Surfaces
--bg Primary
--bg-s Subtle
--bg-m Muted
Text
--fg Primary
--fg-2 Secondary
--fg-3 Tertiary

Light ↔ Dark

Token Light Dark
--bg white g950
--bg-s g50 g900
--bg-m g100 g800
--fg g950 g50
--fg-2 g700 g300
--fg-3 g500 g400
--fg-4 g400 g600
--bd g200 g800
--bd-s g300 g700
--bd-w g100 g900
--accent pri-500 pri-500
--accent-s pri-100 oklch(25% .08 270)

Why OKLCH?

Traditional HSL has a fundamental problem: two colors with the same “lightness” don’t actually look equally bright.

HSL — Same L (50%), different perceived brightness
Yellow
Blue
OKLCH — Same L (70%), consistent brightness ✓
Yellow
Blue

OKLCH values:

oklch(L  C  H)
       │  │  └─ Hue: 0–360° color wheel
       │  └──── Chroma: 0–~0.4 saturation
       └─────── Lightness: 0–100% perceived brightness

Extending

To add custom hues that harmonize with the palette:

:root {
  --teal-500: oklch(60% 0.15 180);
  --teal-100: oklch(92% 0.04 180);
  --teal-soft: oklch(95% 0.02 180);
}

Keep the same lightness values as the accent scale for visual consistency. The chroma and hue change — the lightness stays the same.


Best Practices

Do

  • Use semantic tokens, not primitivesvar(--fg) not var(--g950)
  • Maintain OKLCH lightness consistency — Match lightness values when extending the palette
  • Use soft variants for backgroundsvar(--ok-soft) behind success messages, not var(--ok)
  • Test both themes — Verify colors work in light and dark mode
  • Use status colors for meaning — Green for success, red for errors, yellow for warnings

Don’t

  • Hardcode hex/rgb values — Always use CSS custom properties
  • Use pure black for text — Use var(--fg) (OKLCH-based, softer)
  • Invent new grays — Stick to the 14-step grayscale
  • Mix color spaces — Stay in OKLCH for custom colors
  • Ignore contrast — Ensure text meets WCAG AA (4.5:1 for body, 3:1 for large text)

Quick Reference

/* Surfaces */
var(--bg)        var(--bg-s)      var(--bg-m)

/* Text */
var(--fg)        var(--fg-2)      var(--fg-3)      var(--fg-4)

/* Borders */
var(--bd-w)      var(--bd)        var(--bd-s)

/* Accent */
var(--accent)    var(--accent-h)  var(--accent-s)  var(--accent-fg)

/* Status */
var(--ok)        var(--ok-soft)
var(--warn)      var(--warn-soft)
var(--err)       var(--err-soft)