ConvertAny Color ↔ Tailwind CSS
Paste any color (HEX, RGB, HSL, OKLab/OKLCH, or Tailwind like blue-500) or pick a format—we'll convert it to HEX, RGB, HSL, OKLab, and OKLCH,
and show the closest Tailwind tokens with side-by-side swatch diffs.
HEX, RGB, HSL, OKLab, and OKLCH are shown for precision.
Complementary Color Schemes
Explore shades that complement your base color
Base color: blue-500 → Using family blue, shade 500
How Any Color ↔ Tailwind works
The converter behaves like a color-aware assistant, not just a formatter. It does four things in order: parse whatever you paste, normalize into a perceptual color space, emit common formats, and rank the closest Tailwind tokens using a perceptual distance (ΔE). Below is a practical walk-through.
1) Parsing & normalization
We accept #RRGGBB
/#RGB
, rgb()
(with/without alpha), hsl()
,oklab()
, oklch()
, or a Tailwind token like emerald-600
. Inputs normalize into OKLab (L, a, b
), which tracks perceived lightness better than RGB/HSL. That gives us a stable base: “one unit of lightness” ≈ “one unit brighter to a human,” which is what you want when creating shade ladders or measuring differences.
2) Output formats (copy what your stack needs)
From OKLab we derive: HEX (ubiquitous in CSS), RGB (APIs/canvas), HSL (intuitive knobs), and OKLab/OKLCH (perceptual, great for programmatic tints/shades). Everything is clamped to sRGB so results look consistent across devices.
3) Closest Tailwind token (ΔE in OKLab)
Tailwind palettes are curated families (e.g., sky
, emerald
, neutral
) with steps from 50 → 950
. For each entry we compute a simple ΔE in OKLab and pick the smallest.
type OKLab = { L: number; a: number; b: number };
export function deltaE(a: OKLab, b: OKLab) {
const dL = a.L - b.L, da = a.a - b.a, db = a.b - b.b;
return Math.sqrt(dL * dL + da * da + db * db);
}
The lowest distance is the best visual match. We display ΔE so you can see when the top two or three candidates are essentially ties—useful when brand guidance prefers one family over another.
4) Edge cases & guardrails
- Very light / very dark inputs naturally map near
50/100
or900/950
. We gently ease chroma near extremes to avoid washed lights or muddy darks. - Near-neutrals (tiny chroma) often land in
neutral/*
,stone/*
, orzinc/*
. Hue becomes unstable as chroma → 0 (expected). - Wide-gamut (P3) inputs are clamped to sRGB before comparison, so the “closest match” is always renderable on common displays.
5) Building a 10-shade scale around your anchor
When proposing a ladder around the best match, we move primarily along L
(lightness) and adjust C
(chroma) slightly so lights don’t wash out and darks don’t crush. Mid tones remain tighter (great for text/icons), while extreme steps spread more for clear contrast.
6) Practical snippet (OKLCH → HEX → Tailwind class)
// Given OKLCH components, convert to sRGB hex (pseudocode-ish):
function oklchToHex(L: number, C: number, hDeg: number): string {
// ...convert L,C,h -> OKLab -> linear RGB -> clamp -> gamma-encode -> hex
return "#0ea5e9"; // example (sky-500-ish)
}
// Then pick Tailwind class from nearest match:
const nearest = findNearestTailwind({ L: 0.78, a: -0.08, b: -0.21 })[0];
console.log(nearest.token, nearest.className); // "sky-500", "bg-sky-500"
- Body text on backgrounds: aim for ≥ 4.5:1 contrast.
- Large text and UI chrome: ≥ 3:1 is acceptable.
- On light UIs use
*-600/700
for text; on dark UIs go a step lighter (e.g.,*-500
) for readability. - Focus rings should hit ≥ 3:1 against both light and dark surfaces; prefer a distinct hue.
- Badges/chips on color fills: add a subtle border (e.g.,
border-white/10
in dark mode). - Don’t rely only on color for state; add icons, patterns, or copy.