HEX, RGB, HSL, HSV: Understanding Color Formats for Web Development
Why So Many Color Formats?
If you’ve ever styled a button, you’ve probably written something like color: #3b82f6 or background: rgb(59, 130, 246). Both describe the exact same blue. So why do we have so many ways to say “blue”?
Each color format was designed to solve a different problem. HEX is compact for hand-editing. RGB maps directly to how screens emit light. HSL and HSV describe colors the way humans think about them—by hue, saturation, and lightness. Understanding the differences helps you pick the right tool for the job, whether you’re writing CSS, building a design system, or generating colors programmatically.
You can convert between all of these formats with the Color Converter on CodeKit.
HEX: The Compact Classic
HEX (hexadecimal) notation represents each of the red, green, and blue channels as a two-digit base-16 number, from 00 to FF (0 to 255 in decimal).
.button {
background-color: #3b82f6; /* R=59, G=130, B=246 */
}
A HEX color is always six digits (or three, in shorthand). The shorthand #abc expands to #aabbcc. Modern CSS also supports 8-digit HEX with an alpha channel: #3b82f6ff is fully opaque, #3b82f680 is 50% transparent.
.card {
background: #3b82f680; /* blue at 50% opacity */
}
When to use HEX:
- You’re copying a color from a design tool like Figma or Photoshop.
- You want the most compact, readable representation.
- You’re working in a codebase where HEX is the established convention.
RGB and RGBA: The Additive Model
RGB describes a color by how much red, green, and blue light it contains. It’s the format that maps most directly to how displays work—each pixel is literally three tiny LEDs.
.button {
background-color: rgb(59, 130, 246);
color: rgba(255, 255, 255, 0.9); /* white at 90% opacity */
}
The modern rgb() and rgba() syntaxes are interchangeable in current browsers. You can write rgb(59 130 246 / 0.5) to get 50% opacity without switching to rgba.
.overlay {
background: rgb(59 130 246 / 50%); /* modern space-separated syntax */
}
When to use RGB:
- You need to manipulate individual channels programmatically.
- You’re working with canvas or WebGL, which expect RGB values.
- You want alpha transparency without switching notation.
HSL: The Human-Friendly Format
HSL (Hue, Saturation, Lightness) describes colors the way a painter thinks: pick a hue, decide how vivid it is, then decide how light or dark.
- Hue (0–360): The position on the color wheel. 0 is red, 120 is green, 240 is blue.
- Saturation (0–100%): How intense the color is. 0% is gray; 100% is full color.
- Lightness (0–100%): How bright. 0% is black, 50% is normal, 100% is white.
.button {
background-color: hsl(217, 91%, 60%); /* same blue as #3b82f6 */
}
/* Creating a lighter shade is trivial—just bump the lightness */
.button:hover {
background-color: hsl(217, 91%, 70%);
}
This is HSL’s killer feature: creating tints and shades is intuitive. Want a hover state that’s 10% lighter? Change one number. With HEX, you’d have to pull up a color picker and eyeball it.
/* A complete button color system using HSL */
:root {
--hue: 217;
--primary: hsl(var(--hue), 91%, 60%);
--primary-hover: hsl(var(--hue), 91%, 70%);
--primary-active: hsl(var(--hue), 91%, 50%);
--primary-disabled: hsl(var(--hue), 10%, 70%);
}
When to use HSL:
- You’re building a design system with color variants.
- You need to generate shades programmatically.
- You want readable, intention-revealing color values.
HSV (HSB): The Designer’s Model
HSV (Hue, Saturation, Value)—also called HSB (Brightness)—is similar to HSL but defines the third channel differently. In HSV, 100% Value means the pure color, while in HSL, 100% Lightness means white.
The difference matters most in color pickers. Most design tools (Photoshop, Figma, Sketch) use HSV internally because it maps cleanly to how the picker UI works: pick a hue, then adjust saturation and brightness on a 2D square.
HSL: hsl(217, 91%, 60%) → Lightness 60% (a medium blue)
HSV: hsv(217, 76%, 96%) → Value 96% (a bright blue)
HSV is not part of the CSS color spec, so you can’t write hsv() in a stylesheet. But it’s the format you’ll encounter in design tools and image-processing libraries.
// Converting HSV to RGB (since CSS doesn't accept HSV directly)
function hsvToRgb(h, s, v) {
s /= 100; v /= 100;
const c = v * s;
const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
const m = v - c;
let r, g, b;
if (h < 60) [r, g, b] = [c, x, 0];
else if (h < 120) [r, g, b] = [x, c, 0];
else if (h < 180) [r, g, b] = [0, c, x];
else if (h < 240) [r, g, b] = [0, x, c];
else if (h < 300) [r, g, b] = [x, 0, c];
else [r, g, b] = [c, 0, x];
return [
Math.round((r + m) * 255),
Math.round((g + m) * 255),
Math.round((b + m) * 255)
];
}
Modern CSS Color Functions
CSS Color Module Level 4 and 5 introduced powerful new ways to work with color:
/* oklch: perceptually uniform—50% lightness looks the same across hues */
.button {
background: oklch(62% 0.19 255);
}
/* color-mix: blend two colors in any color space */
.gradient {
background: color-mix(in oklch, blue, red);
}
/* relative colors: derive a new color from an existing one */
.dark-button {
background: oklch(from var(--primary) calc(l - 0.2) c h);
}
oklch is particularly exciting because it’s perceptually uniform: a 10% change in lightness looks the same whether you’re adjusting a yellow or a blue. HSL doesn’t have this property—a “50% lightness” yellow looks much brighter than a “50% lightness” blue.
Color and Accessibility
Color format choice affects accessibility in two ways:
1. Contrast ratios
WCAG requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text. The ratio depends on the relative luminance of the foreground and background, which is calculated from RGB values.
/* Good: dark text on light background */
.body-text {
color: #1f2937; /* rgb(31, 41, 55) */
background: #f9fafb; /* rgb(249, 250, 251) */
/* Contrast ratio: ~17:1 âś“ */
}
/* Bad: light gray on white */
.disabled-text {
color: #d1d5db; /* rgb(209, 213, 219) */
background: #ffffff;
/* Contrast ratio: ~1.4:1 âś— */
}
2. Never rely on color alone
About 8% of men have some form of color vision deficiency. A red error message on a green background is invisible to many users. Always pair color with an icon, text label, or pattern.
<!-- Bad: relies on color alone -->
<span style="color: red">Error</span>
<!-- Good: color + icon + text -->
<span style="color: red">
<svg aria-hidden="true"><!-- warning icon --></svg>
Error: Please enter a valid email address
</span>
Choosing the Right Format
| Format | Best for | CSS support |
|---|---|---|
| HEX | Compact values, design handoff | Yes |
| RGB | Channel manipulation, canvas | Yes |
| HSL | Design systems, tints/shades | Yes |
| HSV | Color pickers, image processing | No (convert first) |
| oklch | Perceptually uniform adjustments | Modern browsers |
Conclusion
There’s no single “best” color format—each has a purpose. HEX is compact and universal. RGB maps to how screens work. HSL makes design systems readable. HSV powers color pickers. And oklch is the future of perceptually uniform color manipulation.
The practical advice: use HSL (or oklch where supported) when you’re building a system that needs color variants, and use HEX when you’re copying a specific color from a design. Whatever you choose, always check your contrast ratios and never communicate meaning through color alone.
Need to convert a color between formats? Try the Color Converter on CodeKit—it handles HEX, RGB, HSL, and HSV instantly, right in your browser.