How every value is calculated
Every number on this site comes from one file, lib/color.ts, and every function in it has a tested inverse. This page states which formula each tool uses, which specification defines it, and where the answer is an approximation rather than a fact.
Hex and RGB
A six digit hex color is three bytes written in base 16, two digits per channel. Conversion in both directions is exact and lossless: there is no rounding step and no color space assumption involved, because hex and RGB are two notations for the same numbers. Three digit shorthand expands by doubling each digit, so #0af is #00aaff, which is what CSS Color Module Level 4 specifies.
RGB to HSL and HSV
Both use the standard cylindrical transformations of the RGB cube. Hue comes from which channel is the maximum and how far the other two sit from it; saturation and lightness or value come from the maximum and minimum channels. These are geometric rearrangements of sRGB numbers, not perceptual models. Two colors with the same HSL lightness can look very different in brightness, which is the reason OKLCH exists.
HueKit stores HSL and HSV rounded to two decimals. Converting back to RGB can therefore move a channel by at most one step out of 255. The round trip is verified for twenty sample colors in scripts/verify-color.mjs.
RGB to CMYK, and why it is approximate
HueKit uses the naive conversion: K = 1 - max(R', G', B'), then C = (1 - R' - K) / (1 - K) and the same for M and Y, where each channel has first been divided by 255.
This is a formula, not a color management step. It assumes no ICC profile, no ink limit, no paper white and no dot gain. A real press has all four. Treat the CMYK values on this site as a starting point for an on screen preview, and get the final values from the printer's profile. This is why the CMYK numbers here say approximate: not as a disclaimer, but because a device independent CMYK does not exist.
CIE XYZ, CIELAB and LCH
Conversion to XYZ starts by linearising sRGB with the piecewise transfer function from IEC 61966-2-1, with its 0.04045 breakpoint, then applies the standard sRGB to XYZ matrix for a D65 white point. CIELAB follows from XYZ with the usual cube root function and its epsilon of (6/29)³. LCH is CIELAB in cylindrical form: chroma is the distance from the neutral axis, hue is the angle.
Anchor values that any correct implementation must reproduce, and which this one does: white is L* 100 with a* and b* at zero, black is L* 0, and any grey has chroma zero.
OKLab and OKLCH
OKLab is Bjorn Ottosson's 2020 space, adopted by CSS Color Module Level 4 as oklab() and oklch(). HueKit uses the published matrices: linear sRGB to an LMS-like space, a cube root, then a second matrix to Lab. The inverse is the exact reverse.
The practical reason it is here: equal steps in OKLab lightness look like equal steps, which is not true of HSL. A ramp built in HSL has yellows that look far brighter than blues at the same lightness value. A ramp built in OKLCH does not.
Cross check: converting the Tailwind CSS 4.3.1 palette, which is published in OKLCH, through this implementation reproduces the hex values Tailwind itself ships, including blue-600 as #155DFC and slate-900 as #0F172B. That is an independent check on the matrices, not a claim by us about our own code.
Color difference: CIE76, and what it is not
Where this site says how far apart two colors are, it uses CIE76: the straight Euclidean distance in CIELAB. It is named explicitly on every page that shows it, because it is not CIEDE2000. CIE76 overstates differences in saturated blues and understates some others. It is used here for one job only, ranking which named color is nearest, where the ordering matters more than the exact figure.
A rule of thumb often quoted is that a CIE76 distance below roughly 2.3 is around the threshold at which people notice a difference side by side. That is a rough guide, not a threshold to design against.
WCAG contrast
Contrast uses the relative luminance formula exactly as the WCAG specification writes it, including its 0.03928 breakpoint, which differs very slightly from the 0.04045 in the sRGB specification. The two are kept separate in the code on purpose: one drives the accessibility thresholds, the other drives colorimetry. Merging them would change published pass and fail results by a hair for no reason.
The ratio is (L1 + 0.05) / (L2 + 0.05), lighter over darker, so the range runs from 1:1 to 21:1. The thresholds are set by success criteria, not by us: 1.4.3 Contrast (Minimum) requires 4.5:1 for normal text and 3:1 for large text, 1.4.6 Contrast (Enhanced) requires 7:1 and 4.5:1, and 1.4.11 Non-text Contrast requires 3:1 for interface components and meaningful graphics.
WCAG 2.2 is a W3C Recommendation, most recently revised on 12 December 2024, and it carries all three criteria over from 2.1 with the luminance formula unchanged. A tool that says 2.1 and a tool that says 2.2 compute the same number. APCA, the perceptual contrast algorithm discussed for a future version, is a different calculation with different thresholds and is not used here.
Shades, tints and harmonies
Shades mix toward black and tints toward white, in linear steps in sRGB. That is the common definition and it is what most design tools do, but it is not perceptually even. Complementary, analogous and triadic pairings rotate hue by 180, 30 and 120 degrees in HSL. These are geometric conventions from the color wheel, useful as starting points rather than as rules.
Color names
Two different things are called a color name on this site and they are not equally solid. The 148 CSS keywords are normative: CSS Color Module Level 4 fixes both the names and their values, and HueKit uses them unaltered. The names in the color library are a curated list of common usage, which is convention rather than standard. Where a library name and a CSS keyword disagree, the keyword is the one to cite.
What is not modelled
- No ICC color management. Everything assumes sRGB, which is right for the web and wrong for print and for wide gamut work.
- No color vision deficiency simulation. Those are models of a condition, not a view through someone's eyes, and publishing one without naming the model would be misleading.
- No APCA scores. Adding them next to WCAG numbers invites people to mix two incompatible scales.
Reproduce it
The conversions are in one file and the checks in another. Every value on the site, and every value in the JSON dataset, is produced by those functions at build time, so a number in the copy cannot drift away from a number in a table.
Related
Last updated by FusionStudios. Every value on this page is computed by the same functions the tools use. How this is calculated.