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
tomatoornavy. - 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.
ffis 255 and80is 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
colorproperty". - 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).
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.
.blue-100 { background: hsl(220 90% 92%); }
.blue-500 { background: hsl(220 90% 56%); }
.blue-900 { background: hsl(220 90% 20%); }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.
.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 {
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
Make a lighter and a darker purple
In the first example, change
.ftohsl(270 50% 70%)and.gtohsl(270 50% 20%). Predict which one turns pale before running. Only the lightness changed, so they stay the same purple family. - 2
Try an 8-digit hex
In the second example, change the
.alphabackground to#2563eb80. Predict what the console prints. (80in hex is 128 out of 255, about half.) Then run and compare. - 3
Add a third tag with one line
In the third example, add a third button with class
tag errorand give.error { color: #b91c1c; }. You write one colour; the border and icon follow without any extra CSS.
Code & diagrams
The browser stores every sRGB colour as red, green and blue numbers, whatever spelling you used.
Preview
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)Look at the text: in the first card it is solid, in the second it is faded with its background.
Preview
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)Hover a button: only `color` changes in the CSS, yet the border and the icon change with it.
Preview
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; }.
Break #2
Mixing old and new rgb syntax
Write color: rgb(255, 0, 0 / 50%); (commas and a slash together).
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)orrgba(r, g, b, a)with commas, whatever you wrote; that is whygetComputedStyleand exercise checks compare againstrgb(...)strings. Colours in newer spaces likeoklch()keep their own form. - ▸
opacitybelow 1 creates a new stacking context and forces the element to be painted as a group, which can change howz-indexworks for its children and costs some compositing work. Alpha colours do neither. - ▸
currentColoris resolved at computed-value time on each element, so an inheritedborder-color: currentColorfollows the child's owncolor, not the parent's. This is what makes it perfect for reusable icons. - ▸
Gradients from a colour to
transparentinterpolate in premultiplied alpha, solinear-gradient(red, transparent)fades smoothly instead of passing through grey, as it did in very old browsers.
Remember this
- 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
Named colours are about 150 keywords like
red,tomato,navy,rebeccapurpleandgray(also spelledgrey). They are quick for experiments but few match a real brand colour. The colourcolorproperty sets is the text colour;background-colorsets the fill behind the content;border-colorthe border.coloris inherited by children;background-coloris not (Topic 2.3). - 3
Hex (hexadecimal) colours write the three numbers in base 16, two digits each:
#RRGGBB. Digits go0–9thena–f, soff= 255 and00= 0.#ff0000is red,#663399is rebeccapurple. When each pair repeats, you may shorten it:#639=#663399. Add a fourth pair for alpha:#66339980(80hex = 128, about 50% opaque), and the short form#6398. - 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 formrgb(102, 51, 153)and thergba()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
Alpha is opacity per colour:
0(or0%) is fully see-through,1(or100%) is solid. It is different from theopacityproperty:opacity: 0.5fades 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 ofbackground-color. - 6
**
currentColor** means "whatever this element'scoloris".border: 2px solid currentColorkeeps the border the same colour as the text, and an SVG icon withfill="currentColor"takes the text colour too, so changing onecolor(say on:hover) recolours text, border and icon together. Borders and outlines already default tocurrentColorwhen you leave their colour out.
Explain it without notes
List four ways to write the same opaque colour and say how the browser stores it.
How do hsl()'s three numbers work, and why is it handy for making shades?
What is the difference between opacity: 0.5 and a colour with 50% alpha?
What is currentColor and where is it useful?
Practice
Write the colour rgb(255 165 0) (orange) as hex, and give a version with 25% alpha in both hex and rgb() form.
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()andhsl().I can read a hex code as red, green and blue amounts.
I can add transparency with alpha in hex,
rgb()andhsl().I can explain why alpha and
opacitylook different.I can use
currentColorto keep borders and icons matching the text.