CMYK to HSL: from a print swatch to a CSS palette
If your starting point is a printed brand color, a CMYK to HSL converter gives you something you can reason about on screen. In HSL the hue tells you where the color sits on the wheel, and lightness gives you a single knob for creating hover states, borders and backgrounds from the same brand color.
How the conversion runs
- CMYK to RGB:
R = 255 × (1 − C) × (1 − K), and the same for G with M and B with Y. Round to whole numbers. - Find the largest and smallest channel as fractions.
- Lightness is their average:
L = (max + min) / 2. - Saturation is
(max − min) / (1 − |2L − 1|), and hue comes from which channel is largest.
Worked example: cmyk(67%, 0%, 37%, 45%)
RGB: R = 255 × 0.33 × 0.55 = 46.3 → 46
G = 255 × 1.00 × 0.55 = 140.3 → 140
B = 255 × 0.63 × 0.55 = 88.4 → 88
max = 140/255 = 0.549, min = 46/255 = 0.180
L = (0.549 + 0.180) / 2 = 0.365 → 36.5%
S = 0.369 / (1 − |0.729 − 1|) = 0.505 → 50.5%
H = 60 × ((88 − 46) / 94 + 2) = 146.8 → 147°
→ hsl(147, 50.5%, 36.5%)
What black ink does to lightness
K scales every RGB channel by the same factor, so it lowers lightness while keeping the hue. Reducing the color inks does the opposite: it raises lightness.
cmyk(0%, 100%, 100%, 0%) → hsl(0, 100%, 50%)
cmyk(0%, 100%, 100%, 50%) → hsl(0, 100%, 25.1%)
cmyk(0%, 50%, 50%, 0%) → hsl(0, 100%, 75.1%)
cmyk(0%, 0%, 0%, 50%) → hsl(0, 0%, 50.2%)
The last line is a pure gray, so saturation is 0 and the hue is reported as 0° by convention.
Building screen variants
Once you have hsl(147, 50.5%, 36.5%), keep the hue and saturation and step the lightness, for instance 28% for a pressed state and 92% for a pale background. Every variant stays recognisably the same green, which is harder to achieve by editing CMYK or hex values directly.
Keep in mind the CMYK source values are converted with the simple unmanaged formula, not an ICC profile, so the HSL you get is a good screen approximation rather than a colorimetric match to the printed sample.
For the reverse direction use HSL to CMYK. If you work with picker values, CMYK to HSV suits better, and RGB to HSL covers colors you already have as channels.