Color system
A color system is a few ramps, each step doing one job, named by role and measured as rendered. Sources: Jakub Krehel's better-colors and oklch-skill, each fact tagged, checked against W3C, APCA, Radix, culori, MDN and Tailwind's docs and source; where a source was wrong (white text on 600, v3 hex sold as Tailwind's palette, @theme without inline, APCA's sign, Lc 60 for body text, 9 steps, five out-of-gamut example values, a hex fallback that is another color, cyan always lowest, the opacity modifier's output), the file gives the official form and says what the source said. Numbers marked "computed" were checked with an OKLab, Display P3, WCAG 2 and APCA implementation. The CSS features themselves (oklch(), color-mix(), relative color, light-dark(), contrast-color(), P3 declarations) and Adam Argyle's recipes are in modern-css COLOR.md. The owner's own token design in app-ui TOKENS.md and landing-page-ui SYSTEM.md wins wherever it differs; each difference is a one-line note in the topic file.
Apply only the routes and checks for the selected job. Read a linked skill's named file from its installed folder when available, or from the linked source. Report an unavailable required reference instead of inventing it. [skill-authoring, DISTRIBUTION.md#composing-skills]
| The job | Read |
|---|---|
Choosing a notation, why OKLCH, channels and formatting, HSL hue drift, converting and bulk conversion, color-mix() and relative color in tokens, gradient interpolation spaces |
FORMATS-AND-OKLCH.md |
| Which ramps a system needs, the role of every step (Tailwind and Radix), white text on the solid fill, neutrals, status hues, the brand color, a correct ramp, culori, the chroma-percentage algorithm, scale labels, several hues at once | PALETTES.md |
| Measuring the rendered pair, WCAG 2 and APCA thresholds, Lc's sign, fixing a pair through lightness, the lightness gap, text polarity, translucent and computed colors | CONTRAST.md |
| Compute WCAG 2 contrast between hex colors with unrounded pass/fail checks | scripts/contrast.py |
| Primitive and semantic tiers, the role inventory, naming grammar and anti-patterns, tokens only in their role, one color one meaning, one filled action, culture | TOKENS-AND-NAMING.md |
| The dark palette, the switching mechanism, increased contrast per appearance, gamut, max chroma by L and H, the clamp, P3 as an enhancement, hex fallbacks | DARK-MODE-AND-GAMUT.md |
Tailwind v4's OKLCH palette and 11 steps, a scale in @theme, semantic tokens and the doubled utility, .dark and @theme inline, the opacity modifier, migrating a hex theme |
TAILWIND.md |
| Audit stance, palette inventory, the before-you-finish scan, P0 to P3 severity, verification tiers, the report table and verdict | AUDIT.md |
The rules
- Match the project's notation. A consistent hex system beats hex with
oklch()scattered through it; useoklch()by default only for a new system, and convert only on request or in an agreed migration [Jakub Krehel, better-colors] (FORMATS-AND-OKLCH.md). - A system is one neutral ramp, one accent ramp and only the status ramps the product renders, and every step exists because a role consumes it [Jakub Krehel, better-colors] (PALETTES.md).
- Hold the hue, peak chroma mid-ramp, step finer at the light end, and keep every step inside sRGB at its L and H; across hues, match L and the same percentage of each hue's maximum chroma [Jakub Krehel, better-colors; Jakub Krehel, oklch-skill] (PALETTES.md).
- Measure white text on every solid fill. It fails 4.5:1 on every Tailwind v3
500hue and on nine600hues; the light hues take dark text (computed; Radix) (PALETTES.md). - Components use semantic tokens only, named by role in one grammar, each used only in its role [Jakub Krehel, better-colors] (TOKENS-AND-NAMING.md).
- Measure against the background actually rendered, report WCAG 2 and add APCA (Lc 75 for body text, Lc 30 the floor; positive Lc is dark on light), and never report a value you did not measure (APCA in a Nutshell) (CONTRAST.md).
- Fix a failing pair only when asked, through lightness, holding the hue, re-clamping chroma into the gamut, then remeasure [Jakub Krehel, better-colors] (CONTRAST.md).
- Dark is designed, not reversed: swap roles, lower the accent's chroma, separate the dark end, use one switching mechanism and give increased contrast its own value per appearance [Jakub Krehel, better-colors] (DARK-MODE-AND-GAMUT.md).
- Declare the sRGB value first and P3 inside
@media (color-gamut: p3); any fallback must be the same color [Jakub Krehel, better-colors] (DARK-MODE-AND-GAMUT.md). - In Tailwind v4, the palette is OKLCH with 11 steps, a semantic token referencing a repointed variable needs
@theme inline, and/50compiles tocolor-mix(in oklab, ...)(tailwindcss.com/docs/theme) (TAILWIND.md). - An audit reports, it does not repaint: inventory first, one P0 to P3 row per root cause with every location,
Blockon any P0 or P1 [Jakub Krehel, better-colors] (AUDIT.md).
Prove it
Every color in component code resolves through a semantic token; every ramp step has a role, a constant hue and a chroma inside sRGB at its L and H (or sits behind a P3 query after its sRGB value); every text pair, measured against its rendered background in light and dark, meets its WCAG 2 ratio unrounded and its APCA level with the correct sign; dark mode uses one switching mechanism and its own tuned values; every hex fallback converts to the color it stands in for; the audit report has a P0 to P3 row per root cause and lists what was Not verified; and every topic file's own Prove it checks pass for the parts you touched.