Command Palette

Search for a command to run...

Hectal
PHASE 3Beginner ~21 min· topic 4 of 7

Topic 3.4

Colours

In one line

CSS can write the same colour many ways (names, hex, rgb(), hsl()), add transparency with an alpha value, follow the text colour with currentColor, and erase colour with transparent; the browser turns them all into the same red, green and blue numbers.

Think of it like this

Mixing paint for a wall. You can ask the shop for "sky blue" (a name), read the code from a paint chart (a hex code), give the exact amounts of red, green and blue pigment (rgb), or say "a blue, not very strong, quite light" (hsl). All four can describe the same tin of paint. Adding water to make it see-through is the alpha.

Words you'll meet

New words in this topic, in plain English. Come back here whenever one feels fuzzy.

sRGB
The standard set of colours ordinary screens can show, made from red, green and blue light.
Named colour
A colour written as a word, like tomato or navy.
Hex colour
A colour written as # and six (or three) base-16 digits, two each for red, green and blue.
Hexadecimal
Counting in base 16: 0–9 then a–f. ff is 255 and 80 is 128.
rgb()
A colour written as amounts of red, green and blue from 0 to 255, like rgb(255 99 71).
hsl()
A colour written as hue (an angle on a colour wheel), saturation (how vivid) and lightness (how light).
Alpha
How see-through a colour is: 0 is invisible, 1 is solid.
currentColor
A keyword that means "the value of this element's color property".
transparent
A fully see-through colour. The default background of most elements.

Step by step

01One colour, many spellings

All of these are the same purple. The browser converts each one to red, green and blue numbers; DevTools and getComputedStyle show the result as rgb(102, 51, 153).

purple.csswhole filecss
color: rebeccapurple;
color: #663399;
color: #639;
color: rgb(102 51 153);
color: rgb(102, 51, 153);   /* older comma syntax */
color: hsl(270 50% 40%);

02Reading a hex code

Split #663399 into pairs: 66, 33, 99. In base 16, 66 = 6 × 16 + 6 = 102, 33 = 51, 99 = 153. Those are the red, green and blue amounts.

You don't need to do this maths in daily work; colour pickers do it. Knowing how it works helps you spot that #ff0000 is all red and #808080 is a middle grey (all three equal).

03Build shades with hsl

Keep the hue and saturation, change the lightness, and you get a family of matching shades. This is how many design systems make their 50–900 colour scales.

shades.csswhole filecss
.blue-100 { background: hsl(220 90% 92%); }
.blue-500 { background: hsl(220 90% 56%); }
.blue-900 { background: hsl(220 90% 20%); }
Build shades with hsldiagram
Rendering diagram…

04See-through: alpha versus opacity

A card with background: rgb(37 99 235 / 15%) gets a pale blue tint and its text stays fully dark. A card with opacity: 0.15 fades everything inside it, text included, which usually makes it unreadable.

tint.csswhole filecss
.tinted { background: rgb(37 99 235 / 15%); }  /* only the background */
.faded  { opacity: 0.15; }                     /* the whole element */

05Let currentColor do the matching

Set color once and point everything else at it. On hover, change only color and the border and icon follow.

tag.csswhole filecss
.tag {
  color: #15803d;
  border: 2px solid currentColor;
}
.tag svg { fill: currentColor; }
.tag:hover { color: #b91c1c; }   /* text, border and icon all turn red */

Try it yourself

  1. 1

    Make a lighter and a darker purple

    In the first example, change .f to hsl(270 50% 70%) and .g to hsl(270 50% 20%). Predict which one turns pale before running. Only the lightness changed, so they stay the same purple family.

  2. 2

    Try an 8-digit hex

    In the second example, change the .alpha background to #2563eb80. Predict what the console prints. (80 in hex is 128 out of 255, about half.) Then run and compare.

  3. 3

    Add a third tag with one line

    In the third example, add a third button with class tag error and give .error { color: #b91c1c; }. You write one colour; the border and icon follow without any extra CSS.

Code & diagrams

Seven spellings, one colour New tab

The browser stores every sRGB colour as red, green and blue numbers, whatever spelling you used.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

rebeccapurple -> rgb(102, 51, 153)
#663399 -> rgb(102, 51, 153)
#639 -> rgb(102, 51, 153)
rgb(102 51 153) -> rgb(102, 51, 153)
rgb(102, 51, 153) -> rgb(102, 51, 153)
hsl(270 50% 40%) -> rgb(102, 51, 153)
hsl(270deg, 50%, 40%) -> rgb(102, 51, 153)
Alpha versus opacity, and transparent New tab

Look at the text: in the first card it is solid, in the second it is faded with its background.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

alpha background: rgba(37, 99, 235, 0.5)
opacity card opacity: 0.5
transparent is: rgba(0, 0, 0, 0)
currentColor keeps text, border and icon in step New tab

Hover a button: only `color` changes in the CSS, yet the border and the icon change with it.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

color: rgb(21, 128, 61)
border: rgb(21, 128, 61)
icon fill: rgb(21, 128, 61)

Break it on purpose

Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.

Break #1

Faded text with opacity

To make a pale overlay card, write .card { background: navy; opacity: 0.3; }.

terminal
$ Look at the card
── what you'll see ──
The background is pale, but the card's text, buttons and images are pale and hard to read too.

Break #2

Mixing old and new rgb syntax

Write color: rgb(255, 0, 0 / 50%); (commas and a slash together).

terminal
$ DevTools → Styles
── what you'll see ──
The declaration is crossed out with "Invalid property value"; the text keeps its previous colour.

Myth vs fact

Myth

You need rgba() for transparency.

Fact

Modern rgb() and hsl() take alpha after a slash, and hex takes a fourth pair. rgba() and hsla() are older aliases that still work.

Myth

opacity: 0.5 and a 50% alpha background look the same.

Fact

opacity fades the entire element including text and children. Alpha in a colour affects only what that colour paints.

Myth

hsl() can show more colours than hex.

Fact

rgb(), hex and hsl() all describe the same sRGB colours. To reach the brighter colours of modern screens, you need oklch() or color() (Topic 3.5).

Pro corner

Extra depth for experienced readers. New to this? Skip it for now and come back later.

  • ▸

    Computed colours in sRGB formats serialise as rgb(r, g, b) or rgba(r, g, b, a) with commas, whatever you wrote; that is why getComputedStyle and exercise checks compare against rgb(...) strings. Colours in newer spaces like oklch() keep their own form.

  • ▸

    opacity below 1 creates a new stacking context and forces the element to be painted as a group, which can change how z-index works for its children and costs some compositing work. Alpha colours do neither.

  • ▸

    currentColor is resolved at computed-value time on each element, so an inherited border-color: currentColor follows the child's own color, not the parent's. This is what makes it perfect for reusable icons.

  • ▸

    Gradients from a colour to transparent interpolate in premultiplied alpha, so linear-gradient(red, transparent) fades smoothly instead of passing through grey, as it did in very old browsers.

Remember this

  1. 1

    Screens make every colour by mixing red, green and blue light, each from 0 (off) to 255 (full). rgb(255 0 0) is pure red, rgb(0 0 0) black, rgb(255 255 255) white. These are colours in the sRGB colour space, the standard range of colours ordinary screens show. Every format in this topic is a different way to write an sRGB colour.

  2. 2

    Named colours are about 150 keywords like red, tomato, navy, rebeccapurple and gray (also spelled grey). They are quick for experiments but few match a real brand colour. The colour color property sets is the text colour; background-color sets the fill behind the content; border-color the border. color is inherited by children; background-color is not (Topic 2.3).

  3. 3

    Hex (hexadecimal) colours write the three numbers in base 16, two digits each: #RRGGBB. Digits go 0–9 then a–f, so ff = 255 and 00 = 0. #ff0000 is red, #663399 is rebeccapurple. When each pair repeats, you may shorten it: #639 = #663399. Add a fourth pair for alpha: #66339980 (80 hex = 128, about 50% opaque), and the short form #6398.

  4. 4

    **rgb()** writes the three numbers directly: rgb(102 51 153). Modern syntax separates them with spaces and adds alpha after a slash: rgb(102 51 153 / 50%) or / 0.5. The older comma form rgb(102, 51, 153) and the rgba() name still work everywhere and mean the same thing. **hsl() describes colour the way people think: hue (the angle on a colour wheel: 0 red, 120 green, 240 blue), saturation (0% grey to 100% vivid) and lightness** (0% black, 50% normal, 100% white). hsl(270 50% 40%) is rebeccapurple again. To make a lighter or darker version, change only the lightness.

  5. 5

    Alpha is opacity per colour: 0 (or 0%) is fully see-through, 1 (or 100%) is solid. It is different from the opacity property: opacity: 0.5 fades the whole element, including its text and children, while a background with alpha fades only that background and leaves the text solid. **transparent** is a keyword for fully see-through black, rgb(0 0 0 / 0); it is the initial value of background-color.

  6. 6

    **currentColor** means "whatever this element's color is". border: 2px solid currentColor keeps the border the same colour as the text, and an SVG icon with fill="currentColor" takes the text colour too, so changing one color (say on :hover) recolours text, border and icon together. Borders and outlines already default to currentColor when you leave their colour out.

Explain it without notes

01

List four ways to write the same opaque colour and say how the browser stores it.

02

How do hsl()'s three numbers work, and why is it handy for making shades?

03

What is the difference between opacity: 0.5 and a colour with 50% alpha?

04

What is currentColor and where is it useful?

Practice

01

Write the colour rgb(255 165 0) (orange) as hex, and give a version with 25% alpha in both hex and rgb() form.

02

Make a .badge with text colour #be123c, a 2px border in the same colour, and a background of the same red at 10% alpha.

Trade-offs

  • ↔

    Hex is compact and what design tools export; hsl() is easier to adjust by hand; names are readable but rarely match a brand.

  • ↔

    Alpha colours blend with whatever is behind them, so the same token can look different on different backgrounds; solid colours are predictable but can't let a background show through.

Done when you can

  • I can write one colour as a name, hex, rgb() and hsl().

  • I can read a hex code as red, green and blue amounts.

  • I can add transparency with alpha in hex, rgb() and hsl().

  • I can explain why alpha and opacity look different.

  • I can use currentColor to keep borders and icons matching the text.