Convert HEX colors to RGB, HSL, HSV, CMYK — with shades, tints, contrast checker & CSS export.
A HEX color like
#6F42C1
encodes three bytes in base-16. Split into pairs:
6F
= 111 red,
42
= 66 green,
C1
= 193 blue. Each pair converts: multiply the first digit by 16 and add the second (both in decimal). Short hex like
#FC0
expands to
#FFCC00
by doubling each digit.
HSL (Hue, Saturation, Lightness) maps RGB to a perceptual color wheel. Hue is the angle (0°–360°), Saturation is color intensity (0–100%), and Lightness is brightness (0% = black, 50% = pure color, 100% = white). HSL is more intuitive than RGB for designers adjusting color themes.
WCAG 2.1 requires a minimum contrast ratio of 4.5:1 for normal text (AA) and 3:1 for large text (18pt+ or 14pt bold). AAA compliance needs 7:1 for normal and 4.5:1 for large text. Ratios are computed from relative luminance values derived from linearized RGB channels.
CMYK (Cyan, Magenta, Yellow, Key/Black) is a subtractive model used in print. Converting from RGB: normalize each channel to 0–1, find K = 1 − max(R,G,B), then C = (1−R−K)/(1−K) and so on. Note that screen-to-print CMYK conversion is an approximation — professional print work requires ICC color profiles.
CSS happily accepts both — #6F42C1 and rgb(111, 66, 193) are interchangeable in a stylesheet. But step outside CSS and the format starts mattering. Canvas API and WebGL work natively with 0–1 float values derived from RGB. Android XML resources use #AARRGGBB with the alpha byte first. PHP's imagecolorallocate() takes three separate integers. Most charting libraries (D3, Chart.js) expect hex strings without the hash or want rgb() notation. Knowing the conversion math means you're not blocked when a library expects a format you don't have.
| Context | Format expected | Note |
|---|---|---|
| CSS / HTML attributes | #RRGGBB or rgb() or hsl() | All three are valid. Named colors too. Use whichever is most readable for the context. |
| HTML Canvas 2D API | #RRGGBB string | ctx.fillStyle = '#6F42C1'. The canvas will also parse most CSS color strings. |
| WebGL / shaders | Float vec3 or vec4 (0.0–1.0) | Divide each RGB channel by 255. R=111 becomes 0.435. |
| Android XML | #AARRGGBB | Alpha is the first byte, not the last. Easy to mix up when coming from CSS. |
| iOS / Swift UIColor | Float 0.0–1.0 per channel | UIColor(red: 0.435, green: 0.259, blue: 0.757, alpha: 1.0) |
| Python (Pillow / matplotlib) | RGB tuple (111, 66, 193) or float tuple | Pillow uses integers 0–255; matplotlib uses floats 0–1. |
| OpenCV | BGR tuple, not RGB | OpenCV reads color channels in Blue-Green-Red order. Swap R and B when converting. |
CSS Color 4 added 8-digit hex support: #RRGGBBAA where the last two digits are the alpha channel. So #6F42C180 is 50% transparent purple. Android XML does the opposite — #AARRGGBB. If you're copying hex values between CSS and Android code, the alpha byte is in a different position and you will get the wrong color if you don't swap it.
#F06 expands to #FF0066, not #F00066. Each single digit becomes a two-digit pair by repeating: F→FF, 0→00, 6→66. This means #F06 and #FF0066 are the same color, but #F16 has no 3-digit shorthand because you can't express F1 as a single repeated hex digit.
#6f42c1 and #6F42C1 are identical to every browser. But if your team uses a CSS linter like Stylelint with the color-hex-case rule, mixing cases will trigger warnings. Pick one convention for your project — most style guides prefer uppercase for HEX — and stick with it.
When passing a hex color to a JavaScript function or API, whether to include the # depends on the API, not on the color itself. ctx.fillStyle = '#6F42C1' needs the hash; parseInt('6F42C1', 16) doesn't. D3's d3.color('#6F42C1') accepts either. Check the docs for whatever you're calling.
B4: B=11, so 11×16=176, plus 4 = 180. With a bit of practice you can estimate values in the 80–200 range reasonably quickly without a tool.