oklch-skill
Practical OKLCH color workflow skill for building consistent, accessible, and tunable color systems in modern UIs.
Install
npx skills add https://github.com/jakubkrehel/oklch-skill --skill oklch-skillPalette Generation
The scale convention
Design system palettes use a numeric scale from 50 (lightest) to 950 (darkest). The standard labels by palette size:
9 steps is the standard default (matches Tailwind).
Algorithm
Given a base color with lightness (L), chroma percentage, and hue (H):
Step 1 — Lightness bounds:
Lightness is clamped to [0.05, 0.95] to avoid pure black/white which have zero chroma.
Step 2 — Distribute lightness evenly from maxL (lightest, label 50) to minL (darkest, label 950).
Step 3 — Clamp chroma per step. Each lightness level has a different maximum chroma for a given hue and color space:
This ensures every step is within gamut. High-chroma base colors will have lower chroma at the lightest and darkest ends — this is correct and expected.
CSS variable output
Multi-hue palettes
When generating palettes for multiple hues, use the same lightness and chroma percentage for all. Same L guarantees equal perceived brightness. Same chroma percentage (not absolute chroma) guarantees equal vividness relative to each hue's maximum.
Different hues have different max chroma at the same lightness. Using the same absolute C value across hues would make some appear more vivid than others.
Dark mode
Reverse the palette mapping so that the lightest step becomes the darkest and vice versa:
This works because oklch's perceptual uniformity means equal L steps in both directions produce equally readable results.
Why not HSL palettes?
Hue drift: hsl(240, 80%, 20%) and hsl(240, 80%, 90%) are not the same perceptual hue. The light variant shifts ~18° toward purple. OKLCH hue is stable.
Brightness inconsistency: hsl(60, 100%, 50%) (yellow) and hsl(240, 100%, 50%) (blue) have the same HSL lightness but wildly different perceived brightness.