Every color system makes three choices: the color model it works in, whether a step has the same lightness in every hue, and how much chroma each hue gets. Tailwind gives each hue its own lightness, so as text on white its yellow-500 reaches 1.91:1 and its blue-500 3.76:1. ColorTokensKit holds one lightness per stop, so every _500 has the same contrast, and yellow turns darker, toward olive, to match. Here's where other well-known systems land, and what each choice gets you. (Tailwind's colors are shown as an sRGB screen draws them, and ours in Display P3.)
What each system gives you#
| System | Same contrast in every hue | Same vividness in every hue | Display P3 colors | sRGB fallback | Dark mode built in | A job for each color | Any hue or brand color | Ready for Swift |
|---|---|---|---|---|---|---|---|---|
| Material 3 | Yes | No | No | Partly, not stated, or doesn't apply | Yes | Yes | Yes | Yes |
| Stripe | Yes | No | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | No | No | No |
| Radix Colors | No | No | Yes | Yes | Yes | Yes | Yes | No |
| Tailwind CSS 4 | No | No | Yes | No | No | No | No | No |
| Adobe Leonardo | Yes | No | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Yes | Partly, not stated, or doesn't apply | Yes | No |
| Accessible Palette | Yes | No | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Yes | No |
| Huetone | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Yes | No |
| Matt Ström-Awn | Partly, not stated, or doesn't apply | No | No | Partly, not stated, or doesn't apply | Yes | No | Yes | No |
| Linear | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Yes | Yes | Yes | No |
| Apple system colors | No | No | Partly, not stated, or doesn't apply | Partly, not stated, or doesn't apply | Yes | Yes | No | Yes |
| ColorTokensKit | Yes | Yes | Yes | No | Yes | Yes | Yes | Yes |
Most systems hold lightness steady across hues and let chroma vary. Tailwind and Radix let both vary, and tune each hue to look its best.
What each one gets right and gives up#
| System | Works in | What you get | What it costs |
|---|---|---|---|
| Material 3 | HCT: CAM16 hue and chroma, CIELab L* as tone | Contrast you can read from tone numbers, and light and dark schemes with named roles built from a single color, in Swift too. | Chroma belongs to a palette, not a tone, so two hues at one tone can differ in vividness. Tones 50 apart can land just under 4.5:1 (4.48:1 for tone 50 on white, by our math). No wide-gamut colors. |
| Stripe, 2019 | CIELAB | The same contrast at each level in every hue, and a rule for pairs: five levels apart for small text. | It's built for Stripe's own products, and each hue is adjusted in their tool, so a new hue takes the same work. |
| Radix Colors | Tuned by hand | Scales tuned by eye, 12 steps with a job each, light and dark scales, and text steps guaranteed to Lc 60 and Lc 90 in APCA. A tool makes a scale from your brand color. | Contrast differs between scales: Sky, Mint, Lime, Yellow and Amber need dark text on their solid steps. |
| Tailwind CSS 4 | OKLCH | Each hue at the lightness and chroma that suit it, and wide-gamut color on screens that show it. | The same step has a different contrast in every hue, and sRGB screens clip the wide-gamut colors instead of using a designed fallback. Dark mode and your own colors are yours to pick, shade by shade. |
| Adobe Leonardo | CAM02, LCH, OKLCH and more | Colors generated from the contrast ratio you need, in the color model you choose, for a light or dark background. | You pick key colors for every hue and chroma follows them, so hues can differ in vividness. |
| Accessible Palette, 2021 | CIELCh | The same lightness at each step, with more saturation where you want warnings to stand out. | Chroma differs by hue on purpose, and CIELCh blues drift toward purple as they get lighter. |
| Huetone | CIELCh or OKLCH | An open-source tool for building your own palette with predictable contrast. | It's a tool, not a palette, so how even the result is depends on your choices. |
| Matt Ström-Awn, 2024 | Okhsl | One formula for any hue, with lightness on one curve, chroma that tapers toward the ends, and dark mode from the same formula. | A share of each hue's maximum is a different amount of chroma per hue, so light yellows get far more than light blues. Okhsl's lightness isn't luminance, so contrast shifts a little by hue. sRGB only for now. |
| Linear, 2024 | LCH | Whole light and dark themes from three inputs, a base color, an accent color and contrast, including high-contrast themes. | It's Linear's own theme system rather than a palette, and LCH's equal lightness only looks equal at similar chroma. |
| Apple system colors | Chosen color by color | Colors named by their job that adapt to dark mode, vibrancy and accessibility settings on their own. | Each color has its own lightness, so as text on white, red, green and blue all miss 4.5:1. |
| ColorTokensKit | CIELab L* for lightness, OKLCH for hue and chroma | The same contrast and vividness at a stop in every hue, in Display P3. | Quieter colors than Tailwind's, and no sRGB fallback. |
Where ColorTokensKit sits#
We use the split Material uses: CIELab L* for lightness, so a stop has the same contrast in every hue, and a newer model for hue and chroma. Ours is OKLCH, which keeps a blue looking blue from light to dark. Then we hold chroma too: at each stop every hue gets the same chroma, and only a hue that can't reach it in Display P3 gets less. How the ramps are built shows the recipe.
You get the same contrast and the same vividness at a stop in every hue, across 36 hues in Display P3. The cost is brightness: our colors are quieter than Tailwind's, and dark yellows read as olive. Like Tailwind's, they have no sRGB fallback, so screens that only show sRGB clip the most vivid ones. What does equal lightness cost? covers each trade-off and what to do about it.
Sources#
- Material Color Utilities, hct.ts and tonal_palette.ts, which define HCT, tones and tonal palettes, and its Swift port, with dark schemes and roles like
primaryandonPrimaryinMaterialDynamicColors. - Stripe, Designing accessible color systems (2019).
- Radix Colors, Understanding the scale, Installation, which lists the dark scales, and Custom palettes.
- Tailwind CSS, the v4.0 announcement (2025), theme.css (version 4.3.3), the source of the swatches above, and the docs on dark mode and colors.
- Adobe, Leonardo, whose contrast-colors package takes a background
lightnessfor light and dark themes. - Eugene Fedorenko, Accessible Palette: stop using HSL for color systems (2021).
- Alexey Ardov, Huetone.
- Matt Ström-Awn, Generating color palettes (2024).
- Linear, How we redesigned the Linear UI (2024).
- Apple, Human Interface Guidelines: Color.
- W3C, CSS Color Module Level 4, on blues in CIE LCH turning purple.
- High, Green and Nussbaum, on the Helmholtz–Kohlrausch effect (2023), which is strong in blues and negligible in yellows.
See also
- Why use ColorTokensKit over native colors? What you gain over SwiftUI's system colors, asset catalogs and hex values, and what it costs.
- How did we choose and build these colors? Why every hue shares one lightness and one chroma at each stop, and how the 36 hues were named.
- Fitting colors into Display P3 Stay inside Display P3 without losing contrast: chroma drops, lightness holds.
- How accessible is it? What the palette guarantees for contrast in every hue and mode, and what you still need to check.