Skip to content

← All writing

Hex, RGB, HSL: How CSS Color Formats Work

· by Andergrove Software

#7c3aed, rgb(124 58 237) and hsl(258 83% 58%) all describe the exact same purple. They are three ways of writing one colour, each useful for a different job. Once you see what each format actually encodes, converting between them — and, more usefully, adjusting a colour by hand — stops being guesswork.

You can convert between all of them live in the color converter, which also checks contrast. This post explains what the numbers mean.

Hex is just RGB in base 16

A hex colour like #7c3aed is three pairs of hexadecimal digits: 7c, 3a, ed. Each pair is one colour channel — red, green, blue — written in base 16. 7c in hex is 124 in decimal, 3a is 58, ed is 237. So #7c3aed is exactly rgb(124, 58, 237); they are the same numbers in different bases. Hex is popular because it is compact and copy-pastes cleanly, but there is nothing magic about it — it is RGB wearing a different hat. (If base 16 is unfamiliar, the number base converter shows the hex↔decimal step.)

A few hex shorthands are worth knowing. #fff is shorthand for #ffffff (white): each digit is doubled. And an eight-digit hex like #7c3aedcc adds a fourth pair for alpha (opacity) — cc is about 80%.

RGB and what 8-bit colour means

RGB describes a colour by how much red, green and blue light to mix, each from 0 to 255. That range is not arbitrary: 255 is the largest value a single byte (8 bits) can hold, so each channel is "8-bit," and three channels give 24-bit colour — about 16.7 million combinations. rgb(0 0 0) is black (no light), rgb(255 255 255) is white (full light), and equal amounts of all three give a grey.

Modern CSS lets you write RGB with spaces instead of commas — rgb(124 58 237) — and add alpha with a slash: rgb(124 58 237 / 80%). The old comma form (rgba(124, 58, 237, 0.8)) still works everywhere.

The catch with RGB is that it is not intuitive to adjust. If you want "the same purple but a bit lighter," there is no obvious way to nudge three light channels to get there. That is what HSL is for.

HSL: the format you can actually tune

HSL describes a colour as hue, saturation, lightness — much closer to how people think about colour:

  • Hue is the colour itself, as an angle on a wheel from 0 to 360°: 0 is red, 120 is green, 240 is blue, and back to red at 360. Our purple sits at 258°.
  • Saturation is how vivid it is, 0% (grey) to 100% (full colour).
  • Lightness is how light or dark, 0% (black) to 100% (white), with 50% being the "pure" colour.

So hsl(258 83% 58%) is "a vivid purple, slightly lighter than mid." The power of HSL is that each part changes one intuitive thing. Want a darker shade for a hover state? Drop the lightness: hsl(258 83% 48%). Want a muted version? Lower the saturation. Want a matching accent colour? Spin the hue. Building a palette by hand is far easier in HSL than in hex, which is why designers reach for it — then often ship the hex.

Alpha and the modern syntax

Every format has a way to add transparency. The clean, current syntax uses a slash inside the function:

color: rgb(124 58 237 / 80%);
color: hsl(258 83% 58% / 80%);
background: #7c3aedcc;   /* 8-digit hex, cc ≈ 80% */

Alpha of 0 is fully transparent, 100% (or 1) fully opaque. The space-separated syntax with a slash is the direction CSS is heading; the older rgba()/hsla() functions do the same thing and remain supported, so use whichever your codebase already uses.

A note on oklch and perceptual colour

There is a newer family worth knowing about. RGB and HSL have a quirk: equal numeric steps do not look like equal changes to the eye. Two HSL colours with the same lightness can look noticeably different in brightness, because the maths does not match human perception. oklch (and its cousin oklab) fixes this — it is a perceptually uniform space, so oklch(58% 0.2 290) behaves the way you expect: adjust the lightness and the perceived brightness tracks it, and colours at the same lightness genuinely look equally bright.

oklch is now supported in every current browser and is excellent for generating accessible palettes and consistent shade ramps. For everyday work, HSL is still the intuitive default; reach for oklch when you need colours that are perceptually consistent, such as a data-visualisation scale or a design-system palette.

Which to use

  • Hex for sharing and pasting fixed brand colours — compact and universal.
  • RGB when you are working from raw channel values or an image's pixel data.
  • HSL when you are tuning a colour by hand — shades, tints, hover states, palettes.
  • oklch when perceptual consistency matters, especially palettes and scales.

They all describe the same colours; pick the one that makes the change you are trying to make easy. Convert between them — and check the contrast ratio for accessibility — in the color converter, and build a full gradient from your stops in the gradient generator.

Open the color converter →