← Back to Blog

Color Conversion for Frontend: HEX, RGB, HSL and Contrast Ratios

A small frontend color dilemma

Given the requirement "make the main color a bit softer", you'd be stuck inside RGB—touching R drifts the hue, changing G/B can't say how much lighter things get. But switch to HSL and the answer becomes one number: drop saturation S by 20%. That is why understanding color models matters.

This article explains the three models most used on the frontend and lands on contrast and accessible color picking.

Reading the three color models

HEX = RGB in hex notation

#FF8800
 ├R=0xFF→255   ├G=0x88→136   └B=0x00→0

There's also the 3-digit shorthand #RGB: #f80 = #ff8800. It's the most compact CSS notation, but you can't eyeball lightness from it.

RGB: mixing three primaries

rgb(255, 136, 0)
R=255  G=136  B=0

Displays use this underneath; the numbers are literal but picking colors isn't intuitive.

HSL: splitting the feeling into three axes

hsl(30, 100%, 50%)
 H=30°(orange)   S=100%   L=50%
  • H hue 0–360°: red 0, yellow 60, green 120, cyan 180, blue 240, magenta 300;
  • S saturation 0–100%: gray to vivid;
  • L lightness 0–100%: black to white.

Conversion in a glance

From → to Mechanics
HEX → RGB decode each 2 hex digits to decimal
RGB → HSL normalize → max/min spread sets S, mean sets L, extreme position sets H
HSL → RGB reconstruct by H interval

Most pickers (color-picker) let you flip between HEX/RGB/HSL—handy for verifying conversions.

An accessibility-aware color workflow

Color design isn't only about aesthetics; contrast must meet thresholds. The full flow:

  1. Pick the primary: use a picker/palette to settle the brand color;
  2. Build neutrals: generate light/dark variants (same hue, different L/S);
  3. Compute contrast: compare each 'foreground text vs background' pair;
  4. Check WCAG: body ≥4.5 (AA) / large ≥3; stricter teams target AAA 7.0.

Contrast formula

L = 0.2126·R̄ + 0.7152·Ġ + 0.0722·B̄     (R̅/G̅/B̅ are linearized luminance)
contrast = (L_light + 0.05) / (L_dark + 0.05)

WCAG thresholds:

Level Normal text Large/bold/UI components
AA ≥ 4.5 ≥ 3
AAA ≥ 7 ≥ 4.5

A common pitfall: on the same background, different text lightness gives wildly different contrast. Test dark and light versions of the same hue separately—often only one passes.

How palette generators work

Many palette tools (color-palette-generator / color-picker) internally follow this logic:

  1. Pick a base hue H0;
  2. Generate analogous/complementary candidates at 60° intervals;
  3. For each candidate, produce a lightness gradient using "same H, varying S/L";
  4. Return a usable 10–12 color set with primary, neutral and accent colors.

That way the family is harmonious (hues are intrinsically related) yet contains high-contrast colors usable for text.

Self-check checklist

  • [ ] You can convert between HEX ↔ RGB ↔ HSL by hand/tool for the same color;
  • [ ] You can say what each of HSL's three axes controls, and explain why adjusting L is more predictable than adjusting RGB;
  • [ ] You can compute contrast and recall the two AA/AAA thresholds for body and large text;
  • [ ] You can confirm color values with a picker and quickly build a set of light/dark variants with a palette.

String conversion, picking, palette and contrast into one workflow, and choosing colors stops being a lottery.

Frequently Asked Questions

What is the relationship between HEX, RGB and HSL?

They describe the same color in different coordinate systems. **HEX** is RGB's hex shorthand: `#FF8800` = R=0xFF, G=0x88, B=0x00. **RGB** uses three 0–255 primaries. **HSL** uses hue H (0–360°), saturation S and lightness L (0–100%). HEX↔RGB is pure base conversion; RGB→HSL normalizes and computes max/min difference and lightness. The intuition: RGB answers 'what the color is', HSL answers 'how light/pure it feels and toward which hue'.

Why is HSL better for picking colors?

Because HSL splits hue/saturation/lightness into three independently adjustable axes: to darken or lighten, change L; to mute, change S; to shift to a neighbor, nudge H. In RGB these are entangled—you can't predict what tweaking one number will do visually. In practice you pick a primary hue, then derive a harmonious family of light/dark and saturated/desaturated variants by holding H and varying S and L—exactly how many palette generators build 'same hue, multiple lightness' sets.

How is contrast computed? What are the WCAG AA/AAA levels?

Contrast = (brighter relative luminance + 0.05) / (darker relative luminance + 0.05). Relative luminance uses linearized RGB with sRGB gamma correction (≈ L = 0.2126R+0.7152G+0.0722B). WCAG levels (normal text): **AA ≥ 4.5 for body, ≥3 for large/bold; AAA ≥7 (≥4.5 for large)**. To decide whether a color pair is usable for body text, compare against these thresholds—the same number a color tool reports is directly matched to the levels.

← Back to Blog