Colors v0.1.0

Color Theory

Sveltopia Colors is built on three foundations: the OKLCH color space, the Radix 12-step scale system, and APCA contrast validation. Here's why each matters:

Why OKLCH?

Most color tools use HSL or RGB. These color spaces have a fundamental problem: they aren't perceptually uniform. A yellow and a blue at the same HSL lightness value look completely different in brightness to the human eye.

OKLCH (Oklab Lightness, Chroma, Hue) solves this. It's a perceptual color space where:

  • L (Lightness) — 0 to 1. Two colors with the same L value actually look equally bright.
  • C (Chroma) — 0 to ~0.4. How vivid the color is. Zero is gray.
  • H (Hue) — 0 to 360 degrees. The color wheel position.

Practical impact: When you generate a 12-step scale in OKLCH, step 5 of every hue (orange, blue, green) looks the same brightness. In HSL, they'd look wildly different. This is what makes the scales feel consistent across your whole palette.

OKLCH also supports the P3 wide-gamut color space, giving you access to more vivid colors on modern displays. The generated CSS includes P3 fallbacks automatically.

The Radix 12-step scale

Radix Colors pioneered a 12-step scale where each step has a specific semantic purpose. Sveltopia Colors follows this system, mapped to a 50–950 naming convention for Tailwind compatibility:

StepTailwindPurposeExample use
150App backgroundPage body, full-bleed background
2100Subtle backgroundSidebar, striped table rows
3200Component backgroundCard, input field, code block background
4300Hovered componentCard hover, button hover background
5400Active/selectedActive tab, selected list item
6500Subtle bordersDividers, card borders
7600Strong bordersInput borders, focus rings
8700Hovered bordersFocus rings, hovered input borders
9800Primary solidButtons, links, primary actions
10850*Hovered solidButton hover states
11900Low-contrast textSecondary text, labels, captions
12950High-contrast textHeadings, primary body text

*850 is not part of Tailwind's default color scale. We added it to preserve the full Radix 12-step system — without it, the "hovered solid" state (step 10) would have no Tailwind equivalent.

Why 12 steps? Fewer steps (like 5 or 7) don't provide enough granularity for real UI work — you end up needing "between" values. More than 12 creates decision paralysis without meaningful perceptual differences. Twelve is the sweet spot that covers every UI need with clear semantic purpose for each step.

APCA contrast

WCAG 2.x uses a contrast ratio (like 4.5:1) that has known problems. It rates some readable combinations as failing and some hard-to-read combinations as passing, especially with colored text.

APCA (Accessible Perceptual Contrast Algorithm) is the replacement being developed for WCAG 3. It accounts for:

  • Polarity — dark text on light backgrounds is perceived differently than light text on dark backgrounds
  • Font size and weight — larger, bolder text needs less contrast than small body text
  • Perceptual lightness — uses the OKLCH model for accurate luminance calculations

Sveltopia Colors uses these APCA thresholds:

Use caseAPCA thresholdTypical context
Body text7514–16px paragraph text
Large text / UI6018px+ headings, buttons, icons
Decorative45Non-text elements, borders, dividers

Critical text contrast pairs — steps 11–12 (text colors) against steps 1–2 (backgrounds) — are validated and auto-adjusted during generation to meet these thresholds. Button solid steps (9–10) are validated but flagged as warnings rather than auto-corrected, since saturated hues naturally have lower contrast that APCA accounts for through font size and weight. See the Accessibility page for safe combination guidelines.

Further reading