Colors, Typography, and Units
CSS units and color formats each have trade-offs — choosing the right one depends on whether you want absolute or relative sizing.
- Write colors using hex, rgb(), and hsl() notations
- Distinguish between absolute units (px) and relative units (em, rem, %)
- Set font properties — font-family, font-size, font-weight, line-height
- Explain when to use rem vs em for font sizing
CSS lets you express colours in multiple formats and sizes in multiple units. The variety is not arbitrary — each format has a distinct mental model that makes certain tasks easier or harder. Knowing which tool to reach for produces cleaner stylesheets and fewer "why is this element the wrong size?" moments.
Colour formats
Hex
The most common format. A # followed by six hexadecimal digits encoding
red, green, and blue as pairs:
color: #3a7bd5; /* red=0x3a, green=0x7b, blue=0xd5 */
background: #ffffff; /* white */
background: #000000; /* black */A three-digit shorthand works when each pair is a repeated digit:
#aabbcc → #abc. Hex is precise and copy-pasteable from design tools, but
adjusting "make this 10% lighter" requires converting to a different format.
rgb()
Expresses each channel as a decimal 0–255:
color: rgb(58, 123, 213); /* same blue as above */
color: rgb(255, 255, 255); /* white */rgba() adds an alpha (opacity) channel from 0.0 (transparent) to 1.0
(fully opaque):
background: rgba(0, 0, 0, 0.5); /* 50% transparent black overlay */Modern CSS lets you write rgb(58 123 213 / 0.5) without commas — both
syntaxes work.
hsl()
Hue, Saturation, Lightness — the format most intuitive for designers and the easiest to adjust by feel:
color: hsl(218, 63%, 54%); /* same blue again */- Hue — the colour wheel position, 0–360 degrees. 0 = red, 120 = green, 240 = blue.
- Saturation — how vivid the colour is. 0% is grey; 100% is fully vivid.
- Lightness — how light or dark. 0% is black; 100% is white; 50% is the pure colour.
To create a lighter variant of a colour, increase lightness. To mute it, reduce saturation. This is much harder to do intuitively with hex. Many design systems define a colour as an HSL base and then generate a scale by varying lightness.
hsl() also accepts an optional alpha: hsl(218 63% 54% / 0.7).
All three formats — hex, rgb(), and hsl() — describe exactly the same colour space and can represent any of the same colours. Pick based on how you will be working with the colour: hex from a design file, hsl when you want to derive tints and shades in code.
Units
Absolute: px
A CSS pixel is a fixed unit. 16px is 16 pixels regardless of context:
border-width: 2px;
border-radius: 4px;Pixels are predictable and appropriate for things that should not scale with
text size: borders, shadows, fine details. They are a poor choice for font
sizes because they ignore the user's browser preference. A user who has set
their browser's default text size to 20px (for readability) will have that
preference overridden by font-size: 16px.
Relative: em
1em is equal to the font-size of the current element. If an element has
font-size: 20px, then 1em = 20px for that element:
.card {
font-size: 18px;
padding: 1em; /* 18px — matches the font size */
}em scales with the element's own font size, which is useful for padding and
spacing that should grow with the text. The trap: if elements are nested, each
level multiplies the parent's font size:
.outer { font-size: 1.2em; } /* 1.2 × browser default */
.inner { font-size: 1.2em; } /* 1.2 × 1.2 × browser default = 1.44× */The compounding effect makes em hard to manage for font sizes in nested
components.
Relative: rem
1rem is always equal to the font-size of the root element (<html>),
not the current element. It does not compound:
html { font-size: 16px; } /* browser default — usually don't set this */
h1 { font-size: 2rem; } /* always 32px, regardless of nesting */
p { font-size: 1rem; } /* always 16px */Because rem roots itself at the <html> element's size, it respects the
user's browser font size preference. If the user sets their browser to 20px
default, 1rem = 20px and everything scales proportionally. Use rem for
font sizes and most spacing.
Relative: %
Percentage is relative to the parent element's corresponding property:
.container {
width: 80%; /* 80% of parent's width */
}
p {
font-size: 90%; /* 90% of parent's font-size — use rem instead */
}Percentages are most useful for widths and layout (width: 100% to fill the
parent), and less useful for font sizes where rem is clearer.
Typography properties
p {
font-family: 'Inter', 'Helvetica Neue', Arial, sans-serif;
font-size: 1rem;
font-weight: 400;
line-height: 1.6;
letter-spacing: 0.01em;
}font-family
A comma-separated list of fonts — the browser uses the first available. Always
end with a generic family (serif, sans-serif, monospace) as a final
fallback. Quote names that contain spaces: 'Times New Roman'.
font-size
Set in rem for body text. The browser default is typically 16px.
font-weight
Use numeric values for precision: 100 (thin) through 900 (black), in
increments of 100. 400 is normal; 700 is bold. Named keywords (bold,
normal) also work.
line-height
Controls the vertical space between lines of text. Use a unitless number
like 1.6 rather than a pixel value — it multiplies by the element's own
font size and stays proportional if the font size changes. A common range
is 1.4 for headings to 1.7 for body text.
line-height: 1.5 means each line of text gets 150% of the font size as
its allotted vertical space. Unitless values are inherited as the multiplier,
not a computed pixel value — which is why they are preferred over
line-height: 24px.
letter-spacing
Fine-tunes the space between characters. Common uses: uppercase headings often
benefit from letter-spacing: 0.05em to 0.1em; body text rarely needs
adjustment. Use em so the spacing scales with the font size.
Where to go next
Next: Lab — Style a Page — apply everything you have learned in the Styling with CSS module by taking a semantic HTML document and writing a full stylesheet from scratch.