Hex Color Codes Explained: RGB to Hex Conversion, CSS Formats, and When HSL Wins
A practical breakdown of hex color codes — how the RGB-to-hex conversion works, when to use hex vs. HSL vs. RGB in CSS, and which format fits each workflow.
Hex Color Codes Explained: RGB to Hex Conversion, CSS Formats, and When HSL Wins
Hex color codes appear in every design file, stylesheet, and DevTools pane. Most developers treat them as opaque strings to copy and paste. Knowing how they actually work speeds up debugging, makes color theming predictable, and helps you pick the right format for each job.
What a Hex Color Code Encodes
A hex code like #FF5733 packs three separate byte values — one each for red, green, and blue — into six characters written in base-16 (hexadecimal). Each two-character pair maps to a number from 0 to 255:
FF→ 255 (maximum red)57→ 87 (medium green)33→ 51 (low blue)
That gives rgb(255, 87, 51) — a vivid orange-red. Because two hex digits cover all 256 values (0x00 through 0xFF), six characters encode a complete 24-bit color, yielding 16,777,216 possible values (256³). This is the full sRGB gamut you see in standard CSS.
CSS also accepts an 8-character form: #FF573380. The last two characters encode alpha. FF is fully opaque; 00 is invisible. That 8-digit form behaves identically to rgba() but is more compact.
The 3-digit shorthand #ABC expands to #AABBCC — each digit is doubled. It only works when both digits in each channel pair are the same, so #FF5733 has no shorthand form, but #AABBCC does.
How RGB-to-Hex Conversion Works — Step by Step
The conversion is just repeated division by 16. Take each channel value, divide by 16 for the high nibble, take the remainder for the low nibble, then map any value above 9 to its letter (A=10, B=11, C=12, D=13, E=14, F=15).
Example: rgb(18, 52, 86) → hex
- Red 18: 18 ÷ 16 = 1 remainder 2 →
12 - Green 52: 52 ÷ 16 = 3 remainder 4 →
34 - Blue 86: 86 ÷ 16 = 5 remainder 6 →
56 - Output:
#123456
This one is clean because 18, 52, and 86 each divide without producing a value above 9. A messier case: rgb(200, 100, 50):
- Red 200: 200 ÷ 16 = 12 (C) remainder 8 →
C8 - Green 100: 100 ÷ 16 = 6 remainder 4 →
64 - Blue 50: 50 ÷ 16 = 3 remainder 2 →
32 - Output:
#C86432— a warm dark orange.
I worked both examples by hand to build the mental model, then verified them in Toolora's CSS Color Format Converter, which confirmed the hex output and also showed the HSL and oklch equivalents in the same step. Manual calculation is worth doing once; after that, automation is faster and eliminates transcription errors.
The Three Mainstream CSS Color Formats
CSS supports hex, rgb(), and hsl() natively, each with a different ergonomic profile.
Hex is compact and universally accepted. Design tools (Figma, Sketch, Affinity) export it by default. It's unambiguous and copy-paste ready. The trade-off: #2E8B57 gives no immediate visual cue about hue, saturation, or brightness without a converter.
rgb() is useful when you think in channels or when the calculation requires raw numeric values. The WCAG 2.1 contrast-ratio formula — which mandates a minimum 4.5:1 ratio for body text at AA level (per the W3C specification) — operates on relative luminance derived from linearized RGB values. Staying in rgb() keeps your mental model close to that math.
hsl() encodes color as hue (degree on the color wheel), saturation (%), and lightness (%). hsl(146, 50%, 36%) is seagreen — the same as #2E8B57 — but expressed in terms a human can reason about directly. Hue 0 is red, 120 is green, 240 is blue, and values in between are the colors you'd expect on a standard color wheel.
When HSL Makes More Sense Than Hex
Hex is the right choice for static values. HSL is the right choice when you need to reason about or generate colors.
Hover and focus states. To lighten a button on hover in hex you need a mental or visual reference point. In HSL you just add points to the L channel: hsl(146, 50%, 36%) → hsl(146, 50%, 46%). The hue stays exact, the saturation stays exact, only the brightness shifts.
Design token families. I needed nine shades of a brand blue for a component library — from a near-white tint to a deep pressed state. Instead of picking values by feel, I fixed the hue at 218° and saturation at 62%, then stepped the lightness from 95% down to 20% in equal increments. The result was nine shades with visually consistent "blue-ness" because only one dimension changed. Doing the same thing in hex would have required an iterative trial-and-error loop.
CSS custom properties. HSL separates naturally into three independent variables:
:root {
--brand-h: 218;
--brand-s: 62%;
--brand-l: 40%;
}
.button {
background: hsl(var(--brand-h), var(--brand-s), var(--brand-l));
}
.button:hover {
background: hsl(var(--brand-h), var(--brand-s), calc(var(--brand-l) + 10%));
}
Changing the brand hue across the whole site now means updating one number. Hex can't do this without JavaScript.
Where HSL falls short: equal lightness values don't look equally bright across different hues. Yellow at hsl(60, 100%, 50%) looks far brighter than blue at hsl(240, 100%, 50%) even though both are nominally 50% lightness. For data visualizations or large design systems where perceptual uniformity matters, oklch is a better choice — but that's a separate topic. For typical UI component work, HSL is precise enough.
Contrast Is Part of the Color Decision
Once you settle on foreground and background colors, check whether the pairing is accessible. WCAG 2.1 level AA requires a 4.5:1 contrast ratio for normal-size body text and 3:1 for large text or UI components. Level AAA raises the bar for body text to 7:1.
The calculation requires converting both colors to relative luminance in linearized RGB space — not something you want to do manually. Toolora's Color Contrast Checker accepts hex input and shows the exact ratio, the WCAG grade, and a live text preview. I run it every time I finalize a palette, especially for light-mode/dark-mode pairs where a color that passes in one mode can fail in the other.
Which Format to Reach For
| Situation | Best format | |---|---| | Copying from Figma | Hex | | Static stylesheet values | Hex | | Hover / active state adjustments | HSL | | Design token families | HSL | | JavaScript color math | RGB | | WCAG contrast calculation | RGB (or use a tool) | | Perceptual uniformity (charts, viz) | oklch |
If you bounce between formats often, a multi-format converter is worth bookmarking. Paste in any hex value at Toolora's CSS Color Format Converter and it returns the hex, rgb, hsl, and oklch equivalents simultaneously, along with Tailwind class suggestions and CSS variable output — which is a common workflow when porting a design file into a token-based system.
Made by Toolora · Updated 2026-06-27