Design
Color Conversion Explained: HEX, RGB and HSL Without the Confusion
9 min read
A client sends back a design with a precise brand blue and asks, "can you export this as a color code?". The designer wrote it as HSL(225, 80%, 47%), the developer's CSS wants #2f6fed, and both people are talking about the exact same color. This is the entire reason color conversion exists: three notations, one reality. This guide walks through what each format means, how to convert between them confidently, and the few rules that stop you making unreadable color choices.
The three models you will actually meet
Every color on a screen is produced by mixing red, green and blue light. What differs is how we describe the mix:
HEXis the web's shorthand — the RGB values squeezed into six hex digits.RGBspells the red, green and blue amounts out as three 0–255 numbers.HSLnames the color by its hue, its saturation and its lightness — the way a human tends to think about it.
None of these is more "correct". They are different tools: pick the one that makes the current task easiest to reason about.
HEX: where the six digits come from
A value like #2f6fed is just three two-digit numbers glued together. Split it: 2f is the red amount, 6f the green, ed the blue. The catch is the digits go from 0–9 to a–f (hexadecimal), so 2f means 2 × 16 + 15 = 47. That number then maps directly onto the 0–255 scale: hex 2f → 47 → rgb(47, ...).
When the three pairs are all doubled digits — like #aabbcc — you can shorten it to three digits: #abc. Browsers treat the two forms as identical, and the short form is standard in Tailwind's bg-red-500 utilities. A common beginner trap is expecting #ffffff to be grey; it is pure white, while #808080 is mid grey.
RGB: the 0–255 numbers screens actually use
When you see rgb(47, 111, 237), read it as "47 units of red light, 111 of green, 237 of blue". Zero is off, 255 is full brightness, and a modern display mixes those three channels to produce the color. Everything else — including HEX and HSL — is a convenience layer over these three numbers.
The numbers look meaningless until you connect them to experience. A color where all three channels are equal — rgb(200,200,200) — is a shade of grey. The brightness of any RGB color is roughly the average of the three channels: raise them together and the color lightens toward white. Once that sticks, reading RGB gets much easier.
HSL: the model your intuition wants
HSL describes a color the way people actually describe colors. Hue is the angle on the color wheel — 0° red, 120° green, 240° blue — so you can pick "the red over there" without computing numbers. Saturation is how vivid the color is, from grey (0%) to pure color (100%). Lightness runs from black (0%) to white (100%), with 50% being the "normal" vivid version of the color.
This makes HSL the right model for design work: darken a hover state by dropping lightness a few points, or mute an accent by lowering saturation, without touching hue. Trying the same tweak in HEX means recomputing three unrelated digits. That is why most color pickers, CSS gradients and design tools show HSL somewhere.
Converting between models
The conversions are mechanical. HEX → RGB is handled above; RGB → HSL just requires finding the max/min channel and deriving hue from which channel wins. In practice you will rarely write this by hand — a converter does it instantly — but the formula demystifies it:
function rgbToHex(r, g, b) {
const toHex = (n) => n.toString(16).padStart(2, "0");
return "#" + [r, g, b].map(toHex).join("");
}
function hexToRgb(hex) {
const h = hex.replace("#", "");
const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
return [0, 2, 4].map((i) => parseInt(full.slice(i, i + 2), 16));
}Keep the original values
Conversion is lossless in principle but each model rounds differently. If a color is being reused across many files, keep the canonical value in one place and convert from it, rather than converting a converted value and letting rounding drift accumulate. Nothing matches the original hex better than the original hex.
Accessibility: checking contrast with math
Picking colors by eye is how unreadable interfaces happen. WCAG defines a contrast ratio between a text color and its background, from 1 (no contrast) to 21 (black on white). The practical thresholds: normal text needs 4.5:1, large text 3:1. The ratio comes from the relative luminance of the two colors, which weights the green channel heaviest because human eyes are most sensitive to it — a pure #00ff00 background is far brighter to a person than #ff0000.
The honest shortcut is not doing the luminance math by hand but understanding what moves the number: increasing the lightness difference between text and background almost always helps. That is why HSL is invaluable here — two colors sharing a hue are automatically harmonious, and you can raise one lightness until the contrast passes. When a client insists on a pale yellow brand color, contrast is the conversation you need to be able to have: it is not a taste question, it is a 4.5:1 question.
Building and extracting palettes
The same color knowledge powers palette work. A Tailwind-style palette starts from one base color and spreads steps at fixed lightness intervals; a palette extractor takes an existing image and pulls out its dominant, visually balanced colors. Both rely on converting the source colors into a space where "similar" and "distinct" can be measured — which is why tools that let you hop between HEX, RGB and HSL, or pull colors straight from an image, save so much fiddling.
Color tools in Convrs
Convrs keeps all of this in the browser: the color converter translates any HEX, RGB or HSL value into the other notations with a live preview, the color palette extractor pulls a balanced palette out of any image, and the Tailwind palette generator turns a single color into a full shade ramp for your design system. Your colors never leave your device, and you can copy the result straight into CSS or Tailwind configuration.