Colours, units and values

Introduction

CSS has several ways of saying the same thing. A colour might be written four different ways, and a size in nearly a dozen units. These are not random variety: each exists because it solves a real problem, and the wrong choice shows up later, when a visitor zooms in or opens your page on a small phone.

How it works

Colours. A screen makes each colour by mixing red, green and blue light.

  • Names: tomato, teal. Easy, but only a limited set exists.
  • Hex: #ff6347. Three pairs of base-16 digits, for red, green and blue, each from 00 to ff. A short form like #f00 doubles each digit.
  • RGB: rgb(255 99 71). The same three numbers from 0 to 255, written in decimal.
  • HSL: hsl(9 100% 64%). Hue as an angle on a colour wheel, then saturation and lightness. HSL makes it easy to produce related shades: keep the hue and change the lightness.

Add transparency with a fourth value called alpha: rgb(255 99 71 / 0.5), or in hex, #ff634780. You may still see the older comma style, rgba(255, 99, 71, 0.5), and it works the same way.

Contrast. Text must stand out from its background. WCAG asks for a ratio of at least 4.5 to 1 for normal text. Light grey text on white may look elegant and be unreadable to many people.

Absolute and relative units.

UnitRelative toGood for
pxa fixed reference sizeborders, small details
remthe font size of htmlfont sizes, spacing
emthe current element's font sizepadding that scales with its text
%usually the parentwidths
vw, vh1% of the viewport's width or heightlarge sections

Why rem for fonts? Browsers let people set a larger default text size, and the default is normally 16px. A font size written in rem scales with that setting. A size in px ignores it, so it fails the visitors who changed it for a reason. For the same reason, change the root with a percentage, as in html { font-size: 125%; }, and not in pixels.

em compounds. On most properties, em means the element's own font size. On font-size itself it has to mean the parent's. So if a list item is set to 1.2em, and one list is inside another, the inner items become 1.2 times 1.2. This surprises people. rem always refers back to the root, so there is no compounding.

calc() does arithmetic with mixed units, which a plain value cannot do: width: calc(100% - 40px). Spaces around + and - are required.

Syntax

color: #ff6347;
background: rgb(0 0 0 / 0.5);
font-size: 1.25rem;
width: calc(100% - 2rem);

Example

console.log output from the page shows here.

Line by line

  1. .box sets its text to 2rem, which is 32px by default.
  2. .a is 1.5rem, or 24px, since rem looks at the root and ignores .box.
  3. .b is 1.5em of its parent's 32px, or 48px.
  4. .wide subtracts a fixed 40px from a percentage, which is only possible with calc().

Expected result

A medium-sized "1.5rem" line, a much larger teal-coloured "1.5em" line, and a pale blue bar that stops 40px short of the right side.

In the real world

Design systems usually define colours in a small palette and base their sizes on rem, so that an entire interface can scale from a single value. Many teams write colours in HSL or newer spaces because shades are easier to calculate than in hex.

Common mistakes

  • Using px for all text, ignoring the visitor's chosen size.
  • Forgetting spaces in calc(): calc(100%-40px) is invalid.
  • Low-contrast text that only looks fine on your screen.
  • Using em deep in nested elements and being surprised by the size.

Best practices

  • Use rem for font sizes.
  • Use px for thin borders.
  • Check contrast with a tool, not by eye.
  • Keep a small, named set of colours.

Summary

Colours can be written by name, hex, RGB or HSL, with optional transparency. rem follows the root size, em follows the current size and compounds, % usually follows the parent, and calc() can combine them.

Quick check

1. With the default root size of 16px, how large is 2rem?
2. Which is written correctly?
3. Why is rem usually better than px for font sizes?

Key takeaways

  • Hex and rgb describe the same colours in different notation.
  • Use rem for font sizes, so that a reader's settings are respected.
  • em compounds when nested, and rem does not.
  • calc() can mix units, such as a percentage minus pixels.

Practice problems for this lesson

My notes

Write down what you want to remember from this lesson. Notes are saved on this device as you type, and appear on your dashboard.