Colours in CSS: hex, rgb, hsl and oklch

Every way to write colour in CSS, how transparency works, and why oklch makes palettes easier.

  • Beginner
  • 6 min read
  • Updated 17 Oct 2026

What you will learn

  • Write colours in several formats
  • Use transparency correctly
  • Explain why oklch gives more predictable lightness than hsl
  • Use currentColor to keep colours in sync

Colour appears in dozens of CSS properties: color, background-color, border-color, box-shadow, outline-color and more. All of them accept the same colour values, so learning the formats once pays off everywhere.

Where colours go

CSS
.alert {
    color: #5c1a1a;              /* text */
    background-color: #fde8e8;   /* background */
    border: 2px solid #c53030;   /* border */
}

color sets the text colour (and is inherited). Most other colour properties apply only to the element they are set on.

Named colours

CSS has around 140 named colours, from red and navy to rebeccapurple and papayawhip:

CSS
.note {
    color: darkslategray;
}

Names are handy for quick demos, but a real design needs exact values, so you will rarely use them in production. One keyword you will use is transparent, which is fully see-through.

Hexadecimal

Hex colours are the most common format you will see in design tools and existing code:

CSS
.brand {
    color: #0b5394;     /* red 0b, green 53, blue 94 */
    background: #fff;   /* short form of #ffffff */
}

Each pair is a channel from 00 (none) to ff (full). Three-digit hex is a shorthand where each digit is doubled: #f60 means #ff6600.

Add a fourth or eighth digit for transparency:

CSS
.overlay {
    background-color: #00000080;  /* black at about 50% opacity */
}

Hex is compact but hard to adjust by hand. Making #0b5394 "a bit lighter" means guessing at three numbers.

rgb()

rgb() uses the same red, green and blue channels, written as numbers from 0 to 255:

CSS
.brand {
    color: rgb(11 83 148);
}

.overlay {
    background-color: rgb(0 0 0 / 50%);
}

Modern CSS separates the channels with spaces and adds transparency after a slash. You will also see the older comma syntax, rgb(11, 83, 148) and rgba(0, 0, 0, 0.5). Both still work, and rgba() is now just an alias of rgb().

hsl()

hsl() describes colour the way people tend to think about it: hue, saturation and lightness.

CSS
.brand {
    color: hsl(210 86% 31%);
}

.brand-light {
    color: hsl(210 86% 60%);
}
  • Hue is an angle on the colour wheel: 0 is red, 120 green, 240 blue.
  • Saturation goes from 0% (grey) to 100% (full colour).
  • Lightness goes from 0% (black) to 100% (white).

HSL makes variations easier: keep the hue, change the lightness. It has one big flaw, though. Its lightness is not perceptual. A yellow and a blue at the same HSL lightness look very different in brightness:

CSS
.yellow { background-color: hsl(60 100% 50%); }  /* looks very bright */
.blue   { background-color: hsl(240 100% 50%); } /* looks much darker */

That makes HSL unreliable for building palettes where every colour should feel equally light, or for predicting text contrast.

oklch()

oklch() is a newer format designed so that equal changes in its numbers look like equal changes to the eye.

CSS
.brand {
    color: oklch(45% 0.12 250);
}
  • L is perceived lightness, from 0% (black) to 100% (white). You can also write it as a number from 0 to 1.
  • C is chroma, roughly how colourful it is. 0 is grey; most usable colours sit below about 0.37.
  • H is the hue angle, as in HSL, though the angles do not line up exactly with HSL's.

Because lightness is perceptual, you can build a consistent scale by changing only L:

CSS
:root {
    --blue-300: oklch(75% 0.12 250);
    --blue-500: oklch(58% 0.15 250);
    --blue-700: oklch(42% 0.12 250);

    --green-500: oklch(58% 0.15 150);  /* same lightness, looks equally bright */
}

oklch can also express colours outside the classic sRGB range, which wide-gamut screens can show. If a screen cannot display a colour, the browser maps it to the nearest one it can.

oklch() is supported in all current major browsers, but it is newer than hex, rgb and hsl. Check its Baseline status on web.dev before relying on it if you must support older browsers. For a safe fallback, declare a hex value first and the oklch value after it: browsers that do not understand oklch ignore that declaration and keep the hex.

CSS
.button {
    background-color: #1d5fa8;
    background-color: oklch(48% 0.14 255);
}

The --blue-300 lines above are custom properties, which you will learn properly later. For now, read them as named colour values.

Transparency: alpha versus opacity

There are two ways to make something see-through, and they behave differently.

HTML
<div class="panel panel-alpha"><p>Readable text</p></div>
<div class="panel panel-opacity"><p>Faded text</p></div>
CSS
.panel-alpha {
    background-color: rgb(11 83 148 / 20%);  /* only the background */
}

.panel-opacity {
    background-color: rgb(11 83 148);
    opacity: 0.2;                            /* the whole element, text included */
}
  • An alpha channel in a colour affects only that colour. The text stays solid.
  • The opacity property fades the entire element and everything inside it.

Using opacity to get a pale background is a common mistake. The text fades too and can fail contrast requirements. Use a colour with alpha for the background instead.

Every format accepts an alpha value after a slash: hsl(210 86% 31% / 0.5), oklch(45% 0.12 250 / 50%).

currentColor

The keyword currentColor means "whatever the color property is on this element":

CSS
.tag {
    color: #0b5394;
    border: 1px solid currentColor;
}

.tag-danger {
    color: #b42318;   /* the border follows automatically */
}

Many properties default to it already. Borders and SVG icons with fill: currentColor pick up the text colour, so one change updates both.

Which format to use

FormatGood for
HexCopying values from design tools; compact constants
rgb()Adding transparency to an existing colour
hsl()Quick manual tweaks of hue or lightness
oklch()Building palettes and scales with consistent lightness
currentColorBorders, icons and outlines that match the text

Pick one main format per project for consistency. Many modern design systems choose oklch for their source tokens.

Colour and accessibility

Colour choices must leave enough contrast between text and background, and colour must never be the only way you communicate something, such as an error. The Web Accessibility Essentials course covers contrast ratios and testing in detail. DevTools helps right away: click a colour swatch in the Styles pane and Chrome or Edge shows the contrast ratio against the background.

Summary

  • Hex, rgb(), hsl() and oklch() all describe the same colours in different ways.
  • Add transparency with an alpha value after a slash; use opacity only to fade a whole element.
  • HSL lightness is not perceptual; oklch lightness is, which makes palettes predictable.
  • Provide a fallback declaration before newer formats if you support older browsers.
  • currentColor keeps borders and icons in sync with the text colour.

Further reading