← Front-End Developer — all chapters  ·  Kelick Tech Hub

Chapter 7 — Colours, Typography and Text

Section 1 — Colour is a CSS property like any other

Colour in CSS is just a value you give to a property. color sets text colour, background-color sets the colour behind an element, and border-color sets a border's colour. The same colour values work in all three places. This is a reassuring starting point, because it means you do not need to learn a separate colour system for each property: learn the formats once, in Sections 2 to 8, and you can use them everywhere a colour is accepted. A value like #0066cc means the same blue whether it colours text, a background, a border, or a shadow.

The distinction between property and value matters here. The property names what changes, and the value says how. color is the property; red, #ff0000 and rgb(255, 0, 0) are all values for it, and all three describe the same colour in different notations. Beginners sometimes think of "red" as a property, or of color as only accepting colour names, and both confusions clear up once the property-value split is firm. If a declaration is not working, check both halves separately: is the property the one you meant, and is the value written in a format that property accepts?

Colour properties also follow the same cascade and inheritance rules as everything else in Chapter 6. color inherits, so setting it on body colours most of the page's text, while background-color does not inherit — a child with no background simply shows through to its parent's background, which looks similar but is not the same mechanism. And a colour set by a class rule beats one set by an element rule, exactly as any other property would. Colour is not a special case in CSS's architecture; it is an ordinary value, governed by ordinary rules, which is why the foundations from Chapter 6 carry straight over.

Finally, colour choices are design decisions, not just code. The same blue can look professional as a link colour and garish as a full-page background, because context — how much of it there is, what sits next to it, how large the text is — changes how a colour reads. Learning the formats is the technical half of this chapter; learning restraint, contrast and purpose, in Sections 12 to 14 and 44 to 46, is the design half. Both halves matter, and neither substitutes for the other. A final habit worth building early is to test every colour in context, not in isolation. A colour swatch on a white background tells you little about how that colour will behave as small text on a tinted card, or as a large heading over a photograph. Put the colour where it will actually live, at the size it will actually appear, next to the neighbours it will actually sit beside, and judge it there. Colours are relational: they look different against light than against dark, different large than small, and different beside one colour than beside another. Testing in context is what turns colour theory into colour judgement.

Check: Which property sets the colour of the text itself?

Section 2 — Named colours

CSS has a set of named colours you can write as plain words: red, navy, teal, gold, tomato and many more. They are easy to read, but the set is limited, so real projects usually move on to hex or rgb for exact shades. Named colours are the friendliest way to start, because they read like English: color: red; needs no decoding, and anyone reading the stylesheet knows immediately what was intended. For learning, sketching, and quick experiments, they are ideal — you can try a colour without looking anything up or doing any arithmetic.

The limitation is precision. The named set contains 148 colours, which sounds like a lot until you need a specific brand blue or a muted grey-green that is not in the list. There is no named colour for "slightly darker than sky blue but less saturated", and guessing from names like steelblue or cornflowerblue is unreliable — different people picture different shades from the same name, and the name gives no hint of how light or saturated the colour actually is. Two colours that sound similar, like darkgrey and dimgrey, are surprisingly different in practice, and the only way to know is to look. Named colours are therefore a poor tool for matching a design exactly.

There is also a consistency problem. If a design uses the same blue in ten places, writing the name ten times is fine, but if the design later needs a slightly different blue, there may be no name for it, and the stylesheet ends up mixing names and hex codes for what should be one palette. Starting with hex or hsl from the beginning avoids that mixture, and it makes the palette explicit: the exact values are visible in the code, not hidden behind names. That said, a few names remain useful even in professional stylesheets, particularly white, black and transparent, whose meanings are unambiguous and whose values are exactly what you would write anyway.

A sensible approach is to use named colours while learning and prototyping, and to switch to hex, rgb or hsl when a design needs to be exact, consistent, or shared with a team. The names never stop working — they are part of CSS and will always be valid — so there is no penalty for using them where they fit. The skill is recognising when they no longer fit: when you find yourself hunting for a name that does not exist, or when two named colours clash in a way you cannot adjust, that is the moment to move to a numeric format, where every shade is reachable by changing a number. One more practical note: named colours are case-insensitive in CSS, so RED, Red and red all mean the same colour, though lowercase is the convention and the form you will see in almost all real stylesheets. Consistency matters more than case here, but following the lowercase convention makes your code look familiar to other developers, which helps them read it quickly and trust it. When you move from named colours to numeric formats, that same convention — lowercase letters in hex codes, for instance — keeps the stylesheet visually consistent.

Check: Name one advantage and one disadvantage of named colours.

Section 3 — Hex colours

A hex colour starts with # followed by six hexadecimal digits: two for red, two for green, two for blue. #ff0000 is pure red, #00ff00 is pure green, and #0000ff is pure blue. Each pair runs from 00 (none) to ff (full). Hexadecimal is base sixteen, using the digits 0 to 9 and the letters a to f, so each pair can express 256 levels, from 0 to 255 in decimal. That gives over sixteen million possible colours — every shade a screen can reasonably display — from a compact six-character code.

Reading a hex colour becomes second nature with practice. The first pair is the red channel, the second green, the third blue, and the value of each pair says how much of that channel is present. #ff0000 is full red, no green, no blue. #000000 is none of anything, which is black. #ffffff is full everything, which is white. #808080 is half of everything, which is a mid-grey — grey being what you get when all three channels are equal. And #336699 is a muted blue: a little red, a medium amount of green, and a lot of blue, which is why it reads as a dusty, desaturated blue rather than a vivid one.

Hex colours are the most common format in real projects, for several practical reasons. They are compact, they are easy to copy from design tools — which almost always display hex codes — and they are unambiguous: there is exactly one hex code for each colour, so a team sharing #336699 is sharing the same colour, with no room for interpretation. They are also easy to search for in a codebase: finding every use of a colour is a simple text search for its code, which matters when a brand colour changes and every instance must be updated.

The main drawback of hex is that it is not intuitive to adjust. If a colour is too dark, you cannot tell from #336699 which digits to change or by how much, without converting to another format in your head or in a tool. Darkening a hex colour means lowering all three pairs by some amount, and the relationship between the digits and the perceived colour is not obvious. That is where hsl, covered in Section 7, earns its place: it describes colours in terms humans can reason about — hue, saturation, lightness — so adjusting a shade is a matter of changing one number. Many developers therefore pick colours in a design tool, copy the hex code into their stylesheet, and switch to hsl when they need to generate shades. Hex is the format for specifying, hsl the format for exploring, and knowing both lets you choose the right one for each task.

Check: In #336699, which pair controls the amount of red?

Section 4 — Short hex colours

When each pair has two identical digits, you can shorten a hex colour to three digits: #ff0000 becomes #f00, and #336699 becomes #369. The short form means exactly the same colour — it is just less typing. The rule is mechanical: each pair of identical digits collapses to one digit, so #aabbcc becomes #abc, #112233 becomes #123, and #ffffff becomes #fff. The browser expands the short form by doubling each digit, which is why the two forms are identical — #369 is not an approximation of #336699, it is the same value written more briefly.

Not every hex colour can be shortened. The pairs must each contain two identical digits; #336699 qualifies because 33, 66 and 99 are all doubles, but #345678 does not, because none of its pairs repeat. Trying to shorten a colour that does not qualify changes the colour: #345 expands to #334455, which is a different shade from #345678. So the short form is only available for colours whose digits happen to line up, which is a minority of colours — though a significant minority, because designers often choose "round" colours whose hex codes are doubles, and because the most common colours, black, white and the pure primaries, all shorten neatly.

Whether to use the short form is largely a matter of team convention. Some stylesheets use it wherever possible, valuing brevity; others always write six digits, valuing consistency and searchability — a search for #ff0000 will not find #f00, which can matter when updating a colour across a large codebase. Neither convention is wrong, but mixing them arbitrarily makes a stylesheet harder to scan, because the same colour can appear in two forms. Pick one convention for your project and stick to it, and if you inherit a project, follow its existing convention rather than imposing your own.

There is also an eight-digit hex form, which adds an alpha pair for transparency: #ff000080 is red at about half opacity. The alpha pair works like the other pairs, from 00 (transparent) to ff (opaque), and there is a four-digit short form for it as well, where each of the four pairs must be a double. Eight-digit hex is less widely used than rgba() for transparency, partly because it is newer and partly because rgba() reads more clearly, but it is valid CSS and you will encounter it in design tools. For this chapter, the key point is the three-digit form: a convenience for the colours that qualify, identical in meaning to the six-digit form, and best used consistently. A related habit is to be careful when copying hex codes from design tools, because tools sometimes display the short form and sometimes the long form for the same colour, and mixing the two in one stylesheet can hide duplicates. If a palette colour appears as both #369 and #336699 in different rules, a search for one form will miss the other, and a later update may change one instance while leaving the other untouched. Normalising every colour to a single form when you first write it — whichever form your project prefers — prevents that quiet inconsistency from taking root.

Check: What is the short form of #aabbcc?

Section 5 — RGB colours

rgb() writes a colour as three decimal numbers from 0 to 255: rgb(255, 0, 0) is red, rgb(0, 0, 0) is black, and rgb(255, 255, 255) is white. It says the same thing as hex, in a form some people find easier to read. The three numbers are the red, green and blue channels, in the same order as hex pairs, but expressed in decimal — the numbering system you already know — instead of hexadecimal. For many people, rgb(51, 102, 153) is easier to reason about than #336699, because the numbers map directly onto "how much": 51 out of 255 red, 102 green, 153 blue, which is the same muted blue as before, now readable as quantities.

The equivalence between rgb and hex is exact, not approximate. Each hex pair converts to one decimal number: ff is 255, 00 is 0, 33 is 51, 66 is 102, 99 is 153. So #336699 and rgb(51, 102, 153) are the same colour, and a design tool will show you both, letting you choose whichever your stylesheet convention prefers. Some teams prefer rgb because it reads as quantities; others prefer hex because it is shorter and easier to copy. There is no technical advantage to either for opaque colours — the browser treats them identically — so the choice is about readability and team habit.

Rgb has one practical advantage that hex does not: it extends naturally to transparency. Adding a fourth number, the alpha, gives rgba(), covered in the next section, and modern CSS also allows the newer space-separated syntax, rgb(255 0 0 / 0.5), where the alpha follows a slash. Hex gained an alpha form only later, with eight digits, and it is less familiar to most developers. So if your palette involves transparency, rgb is often the more natural format, and a stylesheet that mixes opaque hex with transparent rgba is perfectly normal — the formats coexist, and each is used where it reads best.

There is also a percentage form of rgb, where each channel is a percentage instead of a number from 0 to 255: rgb(100%, 0%, 0%) is the same red as rgb(255, 0, 0). Percentages are rarely used in practice, because the 0-to-255 scale is more familiar, but they are valid, and they illustrate that rgb is a system with options, not a single rigid syntax. The important skill is not memorising every syntax variant, but understanding the underlying model: three channels, each from none to full, combining to make any colour. Once that model is clear, every rgb variant — decimal, percentage, with or without alpha — is just a different way of writing the same three quantities, and converting between them is a matter of arithmetic, not of learning a new concept.

Check: Write pure blue as an rgb value.

Section 6 — Adding transparency with alpha

rgba() adds a fourth number, the alpha, from 0 (fully transparent) to 1 (fully opaque). rgba(0, 0, 0, 0.5) is black at half strength, so whatever sits behind it shows through. Modern CSS also lets you write the alpha in rgb() itself, as rgb(0 0 0 / 0.5). Alpha is best understood as a dimmer switch for a colour: at 1, the colour is fully itself and hides everything behind it; at 0, the colour vanishes entirely and whatever is behind shows through untouched; at 0.5, the result is an even blend of the colour and whatever lies behind it. The blend is computed by the browser, so a semi-transparent black over a photograph darkens the photograph evenly, and over a white background it produces grey — the same declaration, different results, depending on context.

That context-dependence is both the power and the caution of alpha. A semi-transparent overlay is a flexible tool: a dark overlay at 0.5 works over any image, light or dark, because it always darkens by the same proportion, whereas a solid colour chosen for one image might clash with another. Similarly, a semi-transparent white panel over a coloured background tints the panel with the background's hue, creating a layered, glass-like effect that adapts to whatever sits behind it. Designers use these effects for image captions, modal backdrops, and hover states, where the underlying content should remain partly visible.

But the same flexibility means the result cannot be judged from the declaration alone. rgba(0, 0, 0, 0.5) over white is mid-grey; over a busy photograph it is a darkened photograph; over another semi-transparent layer it is a blend of blends. When you use alpha, you must test over the actual backgrounds the element will sit on, not just on a white test page. And remember that alpha applies only to that colour, not to the whole element: text on top of a semi-transparent background stays fully opaque and readable, which is the crucial difference from the opacity property, covered in Section 11. If you want a tinted background with solid text, alpha is the tool; if you use opacity instead, the text fades too, and readability suffers.

Alpha values are decimals, and small differences matter: 0.1 is barely there, 0.9 is nearly solid, and the most useful range for overlays and tints is usually between 0.3 and 0.7. There is no need for great precision — 0.5 versus 0.55 is rarely a meaningful difference — so round numbers are fine, and adjusting by tenths while looking at the result is the practical workflow. Note also that alpha can be written as a percentage in the modern syntax, rgb(0 0 0 / 50%), which some people find clearer. Whichever syntax you use, the concept is the same: a colour plus a strength, blended with whatever lies behind it.

Check: What does an alpha of 0.25 mean?

Section 7 — HSL colours

hsl() describes a colour by hue (an angle from 0 to 360 around a colour wheel), saturation (a percentage, from grey to vivid) and lightness (a percentage, from black to white). hsl(210, 100%, 50%) is a vivid blue, and raising the lightness gives you lighter shades of the same colour. HSL is organised around how humans think about colour, rather than how screens produce it, and that makes it the most intuitive format for designing. Instead of mixing red, green and blue quantities — a process that requires mental arithmetic to predict the result — you pick a colour on a wheel, decide how vivid it should be, and decide how light or dark it should be. Each of those decisions is independent, which is what makes HSL so adjustable.

Hue is the colour itself, expressed as an angle. Imagine a wheel with red at 0 degrees, yellow at 60, green at 120, cyan at 180, blue at 240, and magenta at 300, returning to red at 360. A hue of 210 sits between cyan and blue, in the sky-blue to steel-blue region, which is why hsl(210, 100%, 50%) is a vivid blue. Changing the hue rotates around the wheel, so adding 30 degrees shifts a colour towards its neighbour, and adding 180 gives the complementary colour on the opposite side of the wheel — a classic technique for finding contrasting accent colours. Hue is the only part of HSL that is an angle rather than a percentage, and the degree unit is usually written without a symbol in CSS, as a plain number.

Saturation controls vividness. At 100%, the colour is as vivid as it can be; at 0%, it is a pure grey, with no hue at all, regardless of the hue value. Most real designs use saturations between 40% and 90% for accents, because fully saturated colours can be harsh in large areas, while moderately saturated colours feel richer and more professional. Lightness controls brightness: at 0%, the colour is black, whatever the hue and saturation; at 100%, it is white; at 50%, it is the "pure" version of the hue at full vividness. The combination of saturation and lightness is what produces the range from pastel (high lightness, low saturation) to deep and muted (low lightness, moderate saturation) to neon (mid lightness, full saturation).

HSL also has an alpha form, hsla(), and a modern syntax with a slash, hsl(210 100% 50% / 0.5), exactly parallel to rgb. The two systems are interchangeable — every colour can be written in either — and the choice between them is about which is easier to work with for the task at hand. For specifying an exact colour from a design tool, hex or rgb is often what the tool provides. For exploring, adjusting, and building palettes, HSL is usually easier, because its three numbers correspond to three independent design decisions. Learning to think in HSL, even if you write most colours in hex, improves your colour judgement, because it gives you a vocabulary for what makes colours relate to each other.

Check: In hsl(0, 100%, 50%), what colour do you get?

Section 8 — Choosing shades of one colour

HSL makes shade families easy: keep the hue and saturation the same and change only the lightness. hsl(210, 80%, 30%), hsl(210, 80%, 50%) and hsl(210, 80%, 70%) are a dark, medium and light version of the same blue — a simple way to build a consistent palette. This technique is one of the most practical in this chapter, because a coherent palette is rarely a collection of unrelated colours; it is usually a few hues, each expressed in several shades for different purposes. A button might use the medium shade, its hover state the dark shade, and a background tint the light shade, and because all three share a hue, they look related without any effort.

The method is mechanical, which is its virtue. Choose a hue that suits the design — a blue for trust and calm, a green for growth, a warm orange for energy — and choose a saturation that feels right, perhaps 70% or 80% for a modern accent. Then generate shades by stepping the lightness: 20% or 30% for dark text or dark backgrounds, 50% for the main accent, 70% or 80% for light backgrounds, 90% or more for subtle tints. Each step of 10% or 20% in lightness is a visible but not jarring change, so a palette built this way has a natural rhythm, with shades that are clearly different yet clearly related. Design tools can generate these scales automatically, but understanding the principle lets you do it by hand and, more importantly, lets you judge whether a tool's output actually looks coherent.

Shade families also solve a common beginner problem: colours that clash because they were chosen independently. Picking a dark blue from one place, a bright blue from another, and a pale blue from a third often produces a set that feels disjointed, because the hues and saturations differ in unnoticed ways. Building from a single hue guarantees harmony, because the only variable is lightness, and lightness variation is inherently harmonious — it is how a single object looks under different lighting. That guarantee holds even for people with little colour intuition, which makes the technique especially valuable early in your career: you can produce a professional-looking palette by following a procedure, while your eye for colour is still developing.

There are refinements worth knowing as you gain confidence. Very light shades often look better with slightly lower saturation, because a fully saturated colour at 90% lightness can look washed out or garish, while a gentler saturation reads as a clean tint. Very dark shades sometimes benefit from slightly higher saturation, to avoid looking muddy or grey. And for text, the darkest shade in a family — 20% lightness or below — usually provides the best contrast on light backgrounds, while the lightest shades are best reserved for backgrounds, not text, because they lack contrast. These are adjustments to the basic method, not replacements for it: start with a single hue and stepped lightness, then fine-tune saturation at the extremes if the result needs it. The result is a palette that looks designed rather than assembled, and that can be extended — a new shade is just another lightness step — without breaking its coherence.

Check: How would you make a darker version of hsl(200, 70%, 60%)?

Section 9 — The currentColor keyword

currentColor means "whatever this element's text colour is". border-color: currentColor; makes a border match the text colour automatically, so if the text colour changes later, the border follows without a second edit. The keyword is a small piece of indirection with a large practical benefit: it expresses a relationship — "this border should be the same colour as the text" — instead of a fixed value, so the relationship survives changes that a fixed value would not. If a component is restyled from dark text to light text, its currentColor borders follow automatically, while borders with a fixed colour would need a separate edit and might be forgotten.

The most common use is for borders and outlines that should match text. An alert box with coloured text and a coloured left border, for example, can set the text colour once and use currentColor for the border, guaranteeing that the two always agree. Similarly, an icon drawn with CSS, or an underline effect created with a border, can use currentColor to stay in sync with the text it accompanies. The alternative — repeating the colour value in two declarations — works, but it creates two places to update, and the two values can drift apart over time, producing a border that is almost but not quite the text colour, which looks like a mistake.

currentColor resolves to the element's computed color value, which itself may come from inheritance, a rule, or a parent's colour. That means it works through the cascade normally: if a link inside a footer inherits the footer's light text colour, a border set to currentColor on that link will be light too, because the link's computed colour is light. It also works with pseudo-classes: a hover rule that changes an element's text colour will also change its currentColor borders, because the borders resolve against the new computed colour. This dynamic behaviour is what makes the keyword feel alive — it tracks the text colour through every state and context, without any extra rules.

There are limits. currentColor only refers to the element's own text colour, not to a parent's or a sibling's, so it cannot express "match the colour of that other element" — for that, you need a shared variable or an explicit value. And it only makes sense for properties that accept a colour; using it where a length or a font is expected is invalid. But within its scope, it is a clean, expressive tool, and it illustrates a broader CSS idea: values can be relative, not just absolute. Just as em units are relative to font size, currentColor is relative to text colour, and relative values keep designs coherent under change in a way that absolute values cannot. Whenever you catch yourself writing the same colour twice for a text element and its decoration, consider whether currentColor says what you mean more honestly.

Check: If an element's text is green, what colour is a border set to currentColor?

Section 10 — Background colour

background-color paints the area behind an element's content and padding, up to its border. body { background-color: #f5f5f5; } gives the whole page a light grey background, while a card can have a white background sitting on top of it. The background area is defined by the box model, which Chapter 8 covers in full, but the essential fact for colour is this: the background covers the content and padding areas, stops at the border, and does not extend into the margin. The margin is transparent space outside the element, so the parent's background shows through it — which is why a card with a margin on a grey page appears to sit on grey, with the grey visible in the gap around it.

Backgrounds create the layered look of most modern pages. A page background — often a very light grey or a soft off-white — establishes the overall tone, and cards, panels and sections sit on top of it in white or a contrasting tint, creating depth through contrast. That layering is a visual hierarchy device: the eye reads the page background as "behind" and the cards as "in front", even though both are flat rectangles, because the colour difference implies depth. Without background contrast, a page can look flat and undifferentiated, with sections running into each other; with it, the structure is visible at a glance.

A common beginner mistake is to set a background colour on an element and expect it to fill the whole screen or the whole container, when in fact it fills only that element's own box. A div with a background colour but no content and no height collapses to zero height, so its background is invisible — not because the colour failed, but because there is no box to paint. Similarly, a background on body does paint the whole viewport, but only because the browser extends the body's background to fill the canvas when the html element has no background of its own — a special rule worth knowing, because it means body { background-color: ...; } behaves more generously than backgrounds on other elements. For other elements, the background is exactly as large as the box, no larger.

Background colours also interact with text contrast, which is the subject of Section 12: every background choice implies a text colour choice, because the two must contrast enough to read. A white card on a light grey page needs dark text; a dark section needs light text. Setting a background without considering the text that sits on it is a frequent source of readability problems, especially when backgrounds are changed during a redesign and the text colour is forgotten. The habit to build is to always set background and text colour as a pair, thinking of them as a combination rather than as independent decisions, so that contrast is designed in rather than discovered later.

Check: Does a background colour extend into an element's margin? Why or why not?

Section 11 — Opacity versus alpha

opacity: 0.5; makes a whole element, including its text and children, half transparent. That is different from an alpha colour, which makes only the colour transparent — text on an rgba() background stays fully solid and readable. The distinction is one of scope: alpha applies to a single colour value, while opacity applies to an entire element and everything inside it. When you set opacity on a container, the browser renders the whole container — its background, its text, its borders, its children — and then blends that rendered result with whatever lies behind it, at the given strength. Nothing inside escapes the fading, because the fading happens to the finished picture, not to any individual colour.

That behaviour makes opacity the right tool for some effects and the wrong tool for others. It is right for fading an element in or out as a whole — a modal appearing, a notification dissolving, a disabled panel receding — because in those cases you genuinely want everything, text included, to fade together. Animating opacity is also efficient and smooth, which is why fade transitions almost always use opacity rather than animating colours. And for decorative elements with no text, such as a background image or a coloured shape, opacity and alpha are interchangeable in effect, so either will do.

It is wrong, however, for the very common case of a semi-transparent background behind solid text. A caption over a photograph, a card over a coloured section, a tooltip over content — all of these want the background to be translucent so the content behind shows through a little, while the text on top stays crisp and fully legible. Using opacity on the caption container fades the text as well, making it washed out and hard to read, especially at lower opacities. Using an alpha background colour, like rgba(0, 0, 0, 0.6), darkens the photograph behind the caption while leaving the white text at full strength, which is exactly the effect intended. This is the single most common opacity mistake, and recognising it — "the text is fading, but I only wanted the background to fade" — immediately tells you to switch from opacity to an alpha colour.

There is a second, subtler difference: opacity creates a stacking context, which affects how the element layers against other elements, particularly with positioned elements and z-index. That is beyond this chapter's scope, but it is worth knowing that opacity less than 1 changes layering behaviour in ways an alpha colour does not, which is another reason to prefer alpha when only a colour needs to be translucent. The rule of thumb is simple: if you want to fade a colour, use alpha; if you want to fade an element, use opacity. Keeping that sentence in mind prevents the most frequent confusion between the two, and it makes your intentions clear to anyone reading the stylesheet.

Check: Why is rgba() usually better than opacity for a semi-transparent background behind text?

Section 12 — Contrast keeps text readable

Contrast is the difference in brightness between text and its background. Dark text on a light background, or light text on a dark background, is easy to read; mid-grey text on a slightly different mid-grey is not. When in doubt, increase the difference. Contrast is the most important single factor in text readability, more important than font choice or size, because if the eye cannot distinguish the letters from the background, no other typographic virtue matters. The letters are recognised by their edges, and edges are visible only when the letter and its surroundings differ enough in brightness for the visual system to detect the boundary.

The failure mode is almost always the same: a designer chooses a subtle, elegant colour combination — light grey text on white, or pale blue on pale grey — that looks refined in a design tool at large size, but becomes illegible at body-text size on a real screen, in bright light, or for a reader with ageing eyes. Subtlety that works for a large heading fails for small text, because small letters have less area to signal their shape, and they need stronger contrast to be recognised quickly. A good habit is to test text at its actual size, on a real device, in less-than-ideal light, rather than judging it in a design tool at a comfortable zoom. If you have to squint, the contrast is too low, however beautiful the colours look in isolation.

There are formal guidelines for contrast, expressed as ratios between the text and background brightness, with higher ratios required for smaller text and for the highest level of accessibility. You do not need to memorise the numbers for this chapter, but you should know that free contrast-checking tools exist, that they take a text colour and a background colour and report a ratio and a pass or fail, and that using one is a normal part of professional workflow, not an admission of uncertainty. Checking contrast is like checking spelling: a quick, mechanical verification that catches errors the eye misses, and it should be done for every text-and-background combination on a page, not just for body text — buttons, captions, footnotes and placeholder text all need to pass.

Increasing contrast is usually simple: darken the text, lighten the background, or both, moving away from the middle of the brightness range towards the extremes. Pure black on pure white is the maximum, but Section 46 explains why a slightly softened version is often preferable; the point is not to maximise contrast at all costs, but to ensure it is comfortably above the threshold where reading becomes effortful. When a design calls for muted text — for secondary information, captions, or metadata — keep it muted by using a darker grey rather than a light one, and reserve the lightest greys for large text or decorative purposes where legibility is less critical. Contrast is a constraint that good design works within, not a limitation on beauty: the most elegant pages are usually those whose colours are both harmonious and high in contrast, because the designer chose hues that harmonise while keeping their brightness far apart.

Check: Why is light grey text on a white background a bad choice?

Section 13 — Accessibility and colour

Colour alone must never be the only way information is given, because some readers cannot tell colours apart. An error message should say "Error" in words and use an icon as well as red, not rely on red alone. This principle — never encode meaning in colour alone — is one of the most widely agreed rules in accessible design, and it exists because colour vision varies. A significant minority of people have some form of colour vision deficiency, most commonly difficulty distinguishing red from green, and others may view a page in greyscale, in bright sunlight, or on a screen with poor colour reproduction. For all of them, a distinction that exists only as a colour difference may simply not exist.

The examples are everywhere once you look for them. A form that marks invalid fields only with a red border gives no information to someone who cannot see the red; adding an error message in words, and perhaps an icon, makes the error perceivable to everyone. A chart that distinguishes data series only by colour is unreadable to someone who cannot distinguish those colours; adding different line styles, patterns, or direct labels makes it readable. A "required field" marked only by red text is invisible as a requirement to someone who cannot see the red; adding an asterisk and the word "required" makes it explicit. In each case, the fix is not to abandon colour — colour remains a useful, immediate signal — but to pair it with a second channel: text, shape, icon, or pattern, so that the information survives the loss of colour.

This principle also applies to interactive states. A link distinguished from body text only by colour — blue text among black text, with no underline — is hard to identify for someone with low colour vision, and even for others, it is less scannable than an underlined link. Keeping the underline, or adding another cue, makes links identifiable by shape as well as by colour. Similarly, a button's hover state that changes only its colour may be imperceptible to some users; changing its brightness substantially, or adding an underline or shadow, makes the state change visible more broadly. The general rule is to ask: if this page were printed in black and white, would the information still be there? If the answer is no, something needs a second channel.

Beyond colour vision, there are other reasons to avoid colour-only meaning. Screen readers, which read pages aloud, do not announce colours unless the author explicitly provides text, so a status conveyed only by a coloured dot is silent to a screen reader user. And colour meanings are cultural as well as perceptual: red means danger in many contexts, but it means good fortune in others, so relying on a colour's connotation without words risks miscommunication across cultures. Words, icons and patterns are more universal than colour connotations, and they travel better across devices, cultures and abilities. Colour should enhance meaning, never carry it alone.

Check: Why should an error message not rely on red colour alone?

Section 14 — A small palette goes a long way

Most good-looking pages use only a handful of colours: a background, a text colour, one main accent, and maybe one colour for warnings or success. Restraint looks more professional than a rainbow, and it keeps the page consistent. A small palette is a design discipline, not a limitation. When every element draws from the same few colours, the page feels unified, because the eye learns the colour language quickly: this colour means "link", that colour means "action", that one means "warning", and everything else is neutral background or text. When every element introduces a new colour, the eye must learn a new meaning for each one, and the page feels scattered, however attractive the individual colours may be.

A practical starting palette has four or five roles. First, a background colour, usually white or a very light neutral, which covers most of the page. Second, a text colour, usually a very dark neutral, which provides contrast against the background. Third, an accent colour, used sparingly for links, buttons and highlights — the colour that draws the eye to what matters. Fourth, optionally, a secondary accent or a surface colour for cards and panels, to create layering. And fifth, semantic colours for states: a red for errors, a green for success, an amber for warnings, used only where those meanings apply. That is the whole palette for most sites, and it is enough, because the variety in a design comes from layout, typography and spacing as much as from colour.

Restraint also makes maintenance easier. A palette of five colours can be documented in a few lines, shared with a team, and updated in one place when a brand changes. A palette of fifty colours, accumulated one element at a time, cannot be documented, cannot be reasoned about, and cannot be updated without hunting through the whole stylesheet. Modern CSS custom properties make palettes even more manageable, by letting you name colours once and reuse them, but even without variables, the discipline of reusing existing colours instead of inventing new ones keeps a stylesheet coherent. When a new element needs a colour, the first question should be "which existing colour fits this role?" and only if none fits should a new colour be added — and then it should be added deliberately, as a new role in the palette, not as a one-off value.

Finally, a small palette is more accessible, because it is easier to check. Five colours mean a manageable number of text-and-background combinations to test for contrast, while fifty colours mean hundreds of combinations, most of which will never be checked. Restraint is therefore not just an aesthetic preference; it is a practical strategy that improves consistency, maintainability and accessibility at once. Start small, add colours only when a new role genuinely demands one, and let the design's richness come from how those few colours are combined, sized and spaced, rather than from how many there are.

Check: How many colours does a typical tidy palette use?

Section 15 — What typography means

Typography is the art of arranging type so it is readable and pleasant: which font, how big, how much space between lines, and how text is aligned. Good typography is mostly invisible — readers notice it only when it is bad. The word comes from the world of print, where typographers arranged metal letters on a page, but the concerns are the same on screen: choosing a typeface, setting its size, spacing its lines and letters, and arranging it on the page so that reading is effortless and the structure of the content is clear. A page with good typography does not draw attention to its type; it draws attention to its content, because the type gets out of the way.

Typography matters because reading is the primary activity on most web pages. Visitors read articles, instructions, product descriptions, messages and documentation, and the ease of that reading determines whether they stay, understand, and act. Poor typography — text too small, lines too long, spacing too tight, fonts that clash — creates friction that readers experience as tiredness, confusion, or a vague sense that the page is unprofessional, even if they cannot name the cause. Good typography removes that friction, so that the content's quality is what readers judge, not the presentation's flaws. It is, in that sense, a form of respect for the reader's time and attention.

The components of typography are the subjects of the rest of this chapter: the typeface (Sections 16 to 19), the size (Sections 20 to 22), the weight and style (Sections 23 and 24), the spacing (Sections 25 to 27), the alignment and transformation (Sections 28 to 31), and the handling of overflow and decoration (Sections 32 to 34). Each component is a separate CSS property, but they interact: a font that is readable at one size may be cramped at another, a line height that suits body text may be too generous for headings, and a letter-spacing that flatters a heading may slow down body text. Typography is therefore a system, not a list of independent choices, and the skill lies in balancing the components so that they support each other.

A useful way to approach typography is to start with the body text, because that is what readers spend most time with, and to get it right before worrying about headings or decorations. Choose a readable font, a comfortable size, a generous line height, and a moderate line length, and test them with real content at real sizes. Then build the hierarchy outward: headings larger and bolder, captions smaller and muted, labels in a contrasting style — each level distinct enough to be recognised, but related enough to feel like one system. That body-first approach prevents the common mistake of designing headings in isolation and then discovering that the body text, which carries the actual information, was never properly considered.

Check: In one sentence, what does typography cover?

Section 16 — Font families and fallbacks

font-family takes a list of fonts in order of preference, and the browser uses the first one available: font-family: Georgia, "Times New Roman", serif;. The list should always end with a generic family like serif or sans-serif as a safety net. The reason for a list, rather than a single font, is that fonts are not guaranteed to be present on a visitor's device. A font you have installed may be absent from another person's computer or phone, and without a fallback list, the browser would substitute its own default — which may be a completely different style — instead of your next preference. The list is a chain of preferences: try this, then this, then this, and if none is available, use any font in this generic category.

Font names with spaces must be quoted, as in "Times New Roman", while single-word names like Georgia or Arial do not strictly need quotes, though quoting them is harmless. The generic families — serif, sans-serif, monospace, cursive and fantasy — are not specific fonts but categories, and every browser can supply a font for each category, so ending the list with one guarantees that the browser always has something to use. That guarantee is the point of the fallback: it ensures the text is always rendered in the intended style category, even if the exact typeface is unavailable. A list without a generic family at the end is fragile, because if every named font is missing, the browser falls back to its default font, which may be a serif when you wanted a sans-serif, changing the page's character entirely.

The order of the list matters, and it should reflect both preference and similarity. List your preferred font first, then fonts that look similar to it, so that the fallback is as close as possible to the intended appearance. A list like Georgia, "Times New Roman", serif works because Georgia and Times New Roman are both serif fonts with a similar feel, so the substitution is graceful. A list that jumps from a serif to a sans-serif as a fallback would produce a jarring change if the first font were missing, because the whole character of the text would shift. Good fallback lists are therefore curated, not random: each entry should be a plausible substitute for the one before it.

Web fonts, covered in Section 19, reduce the need for fallbacks by delivering the font with the page, but they do not eliminate it. A web font may fail to load — because of a network problem, a slow connection, or a blocking setting — and in that case the fallback list is what the visitor sees, either temporarily while the font loads or permanently if it never arrives. Fallbacks are also what visitors see before the web font arrives, during the brief period when the page is already rendered but the font is still downloading. Writing a thoughtful fallback list is therefore not an old-fashioned precaution but a current necessity, and it is one of the small details that separates a page that degrades gracefully from one that flashes between styles or falls back to an unintended default.

Check: Why does a font-family list end with a generic family?

Section 17 — Serif versus sans-serif

Serif fonts have small decorative strokes at the ends of letters, like Georgia and Times New Roman. Sans-serif fonts do not, like Arial and Verdana. Neither is "better" in general — the choice depends on the mood of the page and how it reads on screen. The word "serif" refers to those small strokes, called serifs, which in print were thought to guide the eye along a line of text. "Sans-serif" is French for "without serif", and sans-serif fonts have clean, even letter ends. The distinction is the most basic classification in typography, and it is the first choice you make when choosing a typeface, because it sets the overall character of the page.

In print, serif fonts were traditionally preferred for long body text, on the theory that serifs help the eye track along lines. On screen, the picture is less clear: early low-resolution screens rendered serifs poorly, which favoured sans-serif fonts for digital reading, but modern high-resolution screens render both well, and the choice has become largely a matter of tone and context. Serif fonts tend to feel traditional, formal, literary or editorial — they suit newspapers, books, academic sites and luxury brands. Sans-serif fonts tend to feel modern, clean, neutral or technical — they suit software interfaces, startups, and sites that want a contemporary, uncluttered feel. Neither association is a rule, and both categories contain fonts of many moods, but the broad tendency is a useful starting point.

The practical advice is to choose based on the content and the audience, and to test at the sizes you will actually use. A serif that looks elegant at heading size may be tiring at body size on a phone, while a sans-serif that looks crisp in an interface may feel bland in a long article. Many sites use both: a serif for headings, to convey character and tradition, and a sans-serif for body text, to maximise readability — or the reverse, a sans-serif heading with a serif body, for a modern editorial feel. That pairing, covered in Section 37, works because the contrast between the categories signals hierarchy: the eye distinguishes headings from body partly by the change in typeface category, not just by size.

Finally, remember that the serif-versus-sans-serif choice is expressed through the generic family at the end of the font stack, which is what guarantees the category even when the specific font is missing. A stack ending in serif promises a serif font; a stack ending in sans-serif promises a sans-serif font. That promise is part of the design contract: if you choose a serif for its traditional feel, the fallback should also be a serif, so that the feel survives a missing font. The category is therefore not just a classification for discussion; it is a functional part of the CSS, and choosing it deliberately is part of choosing the typeface itself.

Check: Which of these is a serif font: Georgia or Verdana?

Section 18 — Monospace fonts

In a monospace font, every character takes up the same width, like letters on an old typewriter. font-family: "Courier New", monospace; is the classic choice for code, because columns of code line up neatly. In proportional fonts — the serif and sans-serif fonts of the previous section — each character has its own natural width: an "i" is narrow, an "m" is wide, and the spacing adjusts to fit. In a monospace font, that adjustment is abandoned: every character, from the narrowest punctuation to the widest letter, occupies exactly the same horizontal space, as if each character sat in a fixed-width box. The result looks mechanical and regular, which is precisely its virtue for certain content.

Code is the primary use, and the reason is alignment. Program code relies on indentation and column alignment to show structure: nested blocks are indented, comments line up, tables of values align in columns. In a proportional font, those alignments break, because characters of different widths shift the columns unpredictably, and the visual structure of the code becomes harder to follow. In a monospace font, a space is the same width as any other character, so indentation is exact, columns stay aligned, and the code's structure is visible. That is why code editors, terminals and documentation sites all default to monospace for code, and why the HTML <code> element is conventionally styled monospace by browsers.

Monospace fonts have other uses too. They suit tabular data where digits should align, such as prices or times in a column, because equal-width digits line up vertically in a way proportional digits do not — though modern proportional fonts often include tabular figures as a feature, monospace guarantees it. They evoke a technical, typewriter or terminal aesthetic, which some designs use deliberately for a retro or hacker feel. And they are useful for displaying user input, file paths, or any text where the exact characters and spacing matter, because the fixed width makes each character distinct and countable.

The drawback of monospace is readability for long prose. Fixed-width text is wider and more spaced out than proportional text, so lines hold fewer words, the eye must travel further, and long passages feel slower and more fragmented. That is why monospace is reserved for code and short technical snippets, not for articles or body text, and why a page set entirely in monospace quickly feels like a terminal rather than a document. Use monospace where alignment matters, use proportional fonts where reading matters, and let each category do what it does best. Monospace fonts also appear in a less obvious place: the browser's default styling for form inputs and text areas, which historically used monospace, though modern browsers have moved towards proportional fonts for most controls. That history explains why a plain text area can look different from the surrounding body text if you do not set its font explicitly — a reminder, from Section 40, to set font inheritance on form controls deliberately. Whenever text alignment or character counting matters, monospace is the safe, predictable choice.

Check: Why are monospace fonts used for code?

Section 19 — Web fonts

A web font is a font file the browser downloads with the page, so visitors see the same typeface even if it is not installed on their device. Font services host these files; you link to one, and the browser fetches it like any other page resource. Before web fonts, designers were limited to the handful of fonts commonly installed on computers — the so-called web-safe fonts — and typographic choice was severely constrained. Web fonts removed that constraint: any typeface can be delivered with the page, so the designer's choice is no longer limited by what visitors happen to have installed. That freedom transformed web typography, and it is why modern sites can have distinctive, branded typefaces rather than the same few defaults.

The mechanism is straightforward. A font service, or your own server, hosts font files in formats browsers understand, and your page references them — typically by linking to a stylesheet provided by the service, which in turn points to the font files. The browser downloads the font files along with the page's other resources, and once they arrive, it renders the text in the web font. Until they arrive, the browser renders the text in the fallback font from your font stack, and then swaps to the web font when it loads — a behaviour that can cause a visible shift in layout, known as a flash of unstyled text, or a brief invisibility, depending on the browser's loading strategy. Managing that swap — by choosing fonts that are similar in size to their fallbacks, or by controlling the display behaviour — is part of professional web font use.

Web fonts come with costs that must be weighed. Each font file adds to the page's download size and loading time, and a page using several weights and styles of a typeface — regular, bold, italic, bold-italic — downloads several files, which can noticeably slow the first visit, especially on slow connections. Fonts are also a dependency: if the font service is unavailable, or the visitor blocks third-party resources, the page falls back to its stack, so the design must still work in the fallback — which returns to the importance of a good fallback list from Section 16. And licensing matters: many typefaces are commercial products, and using them as web fonts requires a licence that permits web embedding, which is why free font services are popular for small projects and why large organisations often license fonts specifically for web use.

Despite those costs, web fonts are now standard practice, and the skill is using them judiciously rather than avoiding them. Limit the number of typefaces — usually one or two, as Section 37 advises — and limit the weights to those you actually use, rather than loading an entire family. Prefer a reputable service or self-hosting from a trusted source, and always provide a strong fallback stack so the page remains readable if the font fails. Used that way, web fonts give a site a distinctive voice without compromising performance or reliability, and they are the reason typography on the web is now a design discipline in its own right, rather than a choice among a few defaults.

Check: What problem do web fonts solve?

Section 20 — Font size in pixels

font-size: 16px; sets text to 16 pixels tall. Pixels are simple and predictable, and 16 pixels is the browser's default body size and a sensible starting point for paragraph text. A pixel, in CSS, is a unit of measurement that corresponds roughly to a single dot on a typical screen, though modern high-resolution screens pack several physical dots into one CSS pixel, so the browser scales accordingly. For practical purposes, you can think of a pixel as a small, fixed unit, and a font size in pixels as an absolute instruction: "make this text this many pixels tall". That absoluteness is both the advantage and the limitation of pixels.

The advantage is predictability. A heading set to 32 pixels will be 32 pixels on every device, regardless of the user's settings or the surrounding context, which makes layout calculations straightforward and designs consistent. For a beginner, that predictability is comforting: what you specify is what you get, and there is no compounding or inheritance puzzle to solve, as there is with em units. For fixed elements — a logo, a button label, a caption that must fit a precise space — pixels give the control needed to fit text into a designed layout.

The limitation is that pixels do not adapt. If a visitor has set their browser to use larger text because of poor eyesight, a pixel-sized font ignores that preference, because pixels are absolute: 16 pixels is 16 pixels, whatever the user asked for. Relative units like rem, covered in Section 22, respect the user's base size, because they scale from it, so a visitor who needs larger text gets larger text throughout a rem-based design. For that reason, accessibility guidance favours relative units for body text, and many professional stylesheets use rem for text sizes while reserving pixels for borders, spacing, and other fixed measurements where adaptability is less important.

The default size deserves emphasis: browsers render body text at 16 pixels unless told otherwise, and that default is a well-tested compromise between readability and screen economy. Starting from 16 pixels for body text, and scaling headings upward from there — 20 or 24 for subheadings, 32 or more for main headings — gives a hierarchy that feels natural, because it builds on a size readers are already accustomed to. Going much smaller than 16 pixels for body text is rarely advisable: it strains the eyes, especially on phones, and it signals that the text is unimportant, which is seldom the intent. Pixels are a fine tool for font size, provided you use them knowingly, with 16 as your anchor and with an awareness of when a relative unit would serve readers better. One more consideration: pixel sizes interact with browser zoom in a way that is easy to misunderstand. Zooming the whole page scales pixel text along with everything else, so a 16-pixel font at 200% zoom renders at 32 device pixels and remains readable. What pixels do not respect is the browser's text-size setting, which is a separate preference from zoom. A visitor who sets larger default text without zooming will not benefit from a pixel-based design, which is the accessibility argument for relative units covered in Section 47.

Check: What is the browser's default font size for body text?

Section 21 — Relative sizes with em

An em is relative to the font size of the element's parent: font-size: 2em; on a heading inside a 16-pixel parent makes it 32 pixels. Ems compound when nested, which can surprise you — a list inside a list inside a list keeps growing if each level uses ems. The em is named after the letter "M", which in metal type was a square unit equal to the type size, but in CSS it simply means "times the parent's font size". A value of 1em equals the parent's size, 1.5em is one and a half times it, and 0.875em is seven-eighths of it. That relativity is the point: an em-based size adapts to its context, so a component designed in ems scales gracefully when its container's size changes.

The classic use of em is for scaling within a component. If a card's base font size is set, and its heading is 1.5em and its caption 0.875em, then changing the card's base size scales all three proportionally, preserving their relationships. That is a powerful property for reusable components, which may appear at different sizes in different contexts: a card in a sidebar at a smaller base size and the same card in the main content at a larger size will both look correct, because their internal proportions are expressed relatively. Pixels cannot do this; a pixel-based card looks the same size wherever it appears, which is sometimes what you want, but not when a component needs to adapt to its context.

Compounding is the trap. Because each em is relative to its parent, and the parent's size may itself be expressed in ems, the multiplications stack. A list whose items are 1.2em, nested inside another list whose items are also 1.2em, produces second-level items at 1.44 times the base size, third-level items at 1.73 times, and so on, growing with every level. The effect is often discovered by accident, when a deeply nested menu or comment thread appears progressively larger for no apparent reason, and the cause — compounding ems — is not obvious from any single rule. The same compounding affects padding and margins expressed in ems, since those are also relative to the element's own font size, which compounds the surprise.

The remedy, for font sizes at least, is rem, covered in the next section, which anchors to the root instead of the parent and therefore does not compound. But em still has legitimate uses, particularly for spacing within a component, where scaling with the component's own font size is desired: padding expressed in ems grows when the text grows, keeping the proportions consistent, which is a desirable property for buttons and badges. The skill is to use em deliberately, knowing whether you want parent-relative scaling or root-relative stability, and to avoid em for font sizes in nested structures where compounding will occur. When in doubt about font size, rem is the safer default; when you want a measurement to scale with the local text size, em is the right tool, used with eyes open about nesting.

Check: If a parent is 16px, how big is a child set to 1.5em?

Section 22 — Root ems, or rem

A rem is relative to the root element's font size, usually the <html> element, so it does not compound through nesting. font-size: 1.5rem; is one and a half times the root size wherever it appears, which makes rem the safer relative unit for most sizing. The "r" in rem stands for "root", and that single letter is the whole difference from em: while em asks "how big is my parent?", rem asks "how big is the root?", and the root's size is the same everywhere on the page, so the answer is the same everywhere too. A heading set to 2rem is twice the root size whether it sits at the top level or nested five levels deep, and a list item set to 1rem is the root size at any depth.

That stability makes rem the preferred unit for font sizes in most modern stylesheets. It combines the two advantages that pixels and ems each offer separately: like pixels, it is predictable — a rem value means the same size everywhere, so you can reason about it without tracing the nesting chain; like ems, it is relative — it scales with the root size, so a visitor who has set a larger base font size in their browser gets larger text throughout the page, which pixels would deny them. Setting the root size, usually by leaving it at the browser default of 16 pixels or by setting it explicitly on html, establishes the scale for the whole site, and every rem value is a multiple of that scale.

Rem also makes design systems easier to maintain. A scale like 0.875rem for captions, 1rem for body, 1.25rem for subheadings, 1.5rem for section headings, and 2rem or more for page titles is a clear, consistent ladder, where each step is a simple multiple of the base. Changing the base size — for example, increasing the root to 18 pixels for a site aimed at older readers — scales the entire ladder proportionally, preserving all the relationships. With pixels, that change would require editing every size individually; with ems, it would require checking every nesting level for compounding. With rem, it is a one-line change, which is the kind of leverage that makes a stylesheet maintainable.

Rem is not the answer to everything. For spacing that should scale with the local text size — the padding of a button that should grow with its label, for instance — em is still more appropriate, because it tracks the element's own font size rather than the root's. And for fixed measurements like borders, pixels remain the natural unit. But for font sizes, and increasingly for spacing in layout, rem is the default that most professional developers reach for, and the habit of thinking in rem — "how many times the base size should this be?" — is a good one to build. It gives you the predictability of absolute units with the adaptability of relative ones, which is the best of both worlds for text.

Check: Why does rem avoid the compounding problem of em?

Section 23 — Font weight

font-weight controls how bold text is. Keywords like normal and bold work everywhere, and numbers from 100 (thinnest) to 900 (thickest) give finer control, with 400 equal to normal and 700 equal to bold — but only if the font actually includes that weight. Font weight is about the thickness of the strokes that make up each letter. At normal weight, the strokes are of a standard thickness; at bold, they are thicker and darker, which makes the text stand out and appear heavier on the page. That visual heaviness is why bold is used for emphasis and hierarchy: a bold heading draws the eye before regular body text, and a bold word inside a sentence draws the eye to that word.

The numeric scale runs from 100 to 900 in steps of 100, with 100 the thinnest, 400 normal, 700 bold, and 900 the thickest. The keywords normal and bold are simply aliases for 400 and 700, and they are the safest choices, because every font includes at least those two weights — or can simulate bold by thickening its normal strokes if it lacks a true bold design. The intermediate numbers, however, depend on the font actually containing those weights. A typeface may offer only regular and bold, in which case requesting 300 or 600 will produce the nearest available weight, not a genuinely lighter or heavier design, and the result may look identical to regular or bold. A variable font or a large family may offer the full range, in which case the numbers give fine control over the exact heaviness.

That dependence on the font is worth understanding, because it explains why a weight change sometimes appears to do nothing. If you set font-weight: 500 on text in a font that has only 400 and 700, the browser picks the closest available weight — 400 — and the text looks unchanged, with no error to explain why. The fix is either to use a weight the font actually provides, or to load additional weights of a web font, which increases download size, so weights should be loaded only when they are genuinely needed. A common professional practice is to load regular and bold, plus perhaps one lighter or heavier weight for display headings, and to design within those weights rather than requesting the full scale.

Weight should also be used with restraint. Bold is effective for emphasis precisely because it contrasts with regular text; if everything is bold, nothing stands out, and the page becomes tiring to read, because heavy strokes demand more visual effort. A hierarchy that uses weight sparingly — bold for headings and key labels, regular for body, perhaps a lighter weight for large display text — is clearer than one that bolds indiscriminately. And weight interacts with size: a large heading at normal weight can be more elegant than the same heading bold, while a small label may need bold to remain legible. Judging weight together with size, as part of the overall hierarchy, is the skill, and it is developed by looking at real pages and noticing how weight guides the eye.

Check: Which font-weight number equals bold?

Section 24 — Font style

font-style: italic; slants text, and font-style: normal; turns slanting off — useful when you want to un-italicise something the browser italicises by default, such as an <em> element inside a heading you want upright. Italic is a slanted version of a typeface, originally designed as a separate, more flowing script for emphasis and for certain kinds of text, such as book titles, foreign words, and thoughts. In CSS, font-style has three main values: normal for upright text, italic for a true italic design if the font provides one, and oblique for a mechanically slanted version of the upright design, which browsers use as a fallback when no true italic exists.

The distinction between italic and oblique is subtle but real. A true italic is a distinct design, with different letter shapes — a single-storey "a", a flowing "f" — created by the type designer to complement the upright font. An oblique is the upright font slanted by the browser, with the same letter shapes merely tilted. Most readers cannot tell the difference at a glance, and for most fonts the browser will use the true italic when it is available and synthesise an oblique when it is not, so authors rarely need to choose between them explicitly. What matters is the effect: slanted text reads as distinct from upright text, which is why it signals emphasis, quotation, or a change in voice.

The browser's defaults make font-style practically useful. The <em> element, for emphasis, is italic by default, and the <cite> element, for citations, is also italic by default. Sometimes you want to override those defaults: a heading that contains an <em> element might look better with the emphasis expressed by colour or weight rather than slant, or a citation in a particular design might need to be upright. Setting font-style: normal; on the element removes the slant cleanly, without changing the HTML or losing the semantic meaning of the element — the <em> still means emphasis to screen readers and search engines, even though it no longer looks slanted. That separation of meaning and appearance is a recurring theme in good front-end work: the HTML says what something is, and the CSS decides how it looks, and the two can differ.

Like bold, italic should be used sparingly. Long passages in italic are harder to read than upright text, because the slanted, flowing shapes are less distinct and the eye must work harder to recognise words. Italic is best for short stretches — a word or phrase for emphasis, a title, a brief quotation — where its distinctiveness is an asset and its readability cost is minimal. A page that italicises whole paragraphs, or that combines italic with bold and underline for emphasis, quickly becomes difficult and looks undisciplined. One form of emphasis at a time, applied to a short span, is the professional standard, and italic, used that way, is an elegant tool.

Check: How would you remove the italic slant from an <em> element?

Section 25 — Line height

line-height is the height of each line of text, and it is the single biggest readability lever you have. A unitless value like line-height: 1.5; means one and a half times the font size, and it scales if the font size changes. Body text around 1.5 is a good default. Line height is the vertical space from the baseline of one line to the baseline of the next. It determines how much breathing room each line has, and that breathing room is critical: lines packed too tightly blur together, and the eye loses its place when moving from the end of one line to the start of the next, while lines spaced too generously disconnect from each other, and the paragraph stops feeling like a coherent block.

The unitless form is the recommended way to write line height, and the reason is inheritance. A unitless value like 1.5 is inherited as a multiplier, not as a fixed measurement: each element multiplies its own font size by 1.5 to get its own line height. So a paragraph at 16 pixels gets a 24-pixel line height, while a heading at 32 pixels inside the same container gets a 48-pixel line height, both from the same inherited 1.5. A line height written in pixels, like line-height: 24px;, is inherited as 24 pixels regardless of font size, so the 32-pixel heading would have lines tighter than its own text, causing overlap or cramped appearance. The unitless form is therefore the only form that scales correctly through inheritance, which is why it is the professional default.

The right value depends on the text. Body text benefits from generous line height, around 1.5 to 1.7, because long lines of small text need room to be tracked. Headings, which are larger and often single lines, can use tighter line height, around 1.1 to 1.3, because their lines are short and their size already provides presence, and too much spacing would make a multi-line heading feel disconnected. Captions and footnotes, which are small and secondary, can also use tighter spacing. The pattern is that line height should be inversely related to font size and line length: smaller, longer lines need more room; larger, shorter lines need less. There is no single correct value for all text, but 1.5 for body text is a reliable starting point from which to adjust by eye.

Line height also affects the overall rhythm of a page. Consistent line heights create a vertical rhythm, where baselines align across columns and sections, giving the page a calm, ordered feel. Inconsistent line heights — a paragraph at 1.5 next to a list at 1.2 next to a caption at 2.0 — break that rhythm and make the page feel unsettled, even if each value is reasonable in isolation. Setting a base line height on body and letting it inherit, then overriding it only where a different rhythm is genuinely needed, is the way to maintain that consistency. Line height is a quiet property — readers rarely notice it when it is right — but it is the difference between text that flows and text that fights the reader, and it deserves the attention this section gives it.

Check: Why is a unitless line-height better than one fixed in pixels?

Section 26 — Letter spacing

letter-spacing adds or removes space between characters: letter-spacing: 0.05em; opens text up slightly, which suits headings and all-caps labels. Use it sparingly on body text, where extra spacing slows reading down. Letter-spacing, also called tracking, adjusts the horizontal gaps between characters uniformly, adding the same amount of space after each character. A positive value spreads letters apart; a negative value squeezes them together. The effect is subtle at small values and dramatic at large ones, which is why it is a powerful tool for headings and a dangerous one for body text.

The reason it suits headings and labels is that those are short, large, and meant to be noticed. A heading with slightly increased letter-spacing feels more open, more deliberate, and more premium — the letters have room to breathe, and the heading reads as a designed object rather than as ordinary text. All-caps labels benefit even more, because capital letters are wider and denser than lowercase, and without extra spacing they can feel cramped and blocky. A tracking value of 0.05em to 0.1em on an all-caps label is a standard professional touch, opening the caps just enough to let each letter stand clear. The value is given in ems so that it scales with the font size: 0.05em at 32 pixels is 1.6 pixels of extra space, while at 12 pixels it is 0.6 pixels, preserving the proportional effect at any size.

Body text is a different matter. Readers recognise words partly by their overall shape, and the shape depends on letters being close enough to group into a word. Extra letter-spacing breaks that grouping: the letters drift apart, the word shape dissolves, and the reader must assemble the word letter by letter, which is slower and more tiring. Even a small increase, applied to a whole article, accumulates into a noticeably harder read, and a large increase makes body text feel like a puzzle. For that reason, body text should normally have no letter-spacing at all, or at most a tiny value to correct a font that feels tight. If body text feels cramped, the remedy is usually line height or font size, not letter-spacing.

Negative letter-spacing has its own uses and risks. Tightening a large heading by a small negative amount — say, -0.02em — can make it feel more compact and modern, especially for very large display type where the natural spacing looks loose. But negative values on small text cause letters to collide, particularly in fonts with wide characters or at small sizes, and they harm legibility quickly. As with positive tracking, the effect should be judged at the actual size and in the actual font, because the same value looks different in different typefaces. The general principle is that letter-spacing is a seasoning for display text, not a staple for body text, and like any seasoning, a little enhances and a lot overwhelms.

Check: Where is extra letter-spacing most appropriate: body text or headings?

Section 27 — Word spacing

word-spacing adjusts the gaps between words rather than between letters. It is rarely needed, but it can help justify the look of a short heading or match a particular design. Like letter-spacing, it is usually given in ems so it scales with the text. Word spacing is the horizontal space that separates one word from the next, normally determined by the font itself — each font defines a standard space width that feels natural for its design. The word-spacing property adds to or subtracts from that standard space, uniformly across the text. A positive value widens the gaps between words; a negative value narrows them.

Because the standard space is already well-chosen by the type designer, word spacing rarely needs adjustment, and when it does, the adjustment is usually small and for a specific purpose. One such purpose is a short heading or display line where the designer wants a particular rhythm — wider word gaps can make a headline feel more spacious and deliberate, in the same way that letter-spacing does, but operating at the word level rather than the character level. Another is matching a design mockup exactly, where the spacing in the mockup differs slightly from the font's default. And in justified text, where the browser already stretches word spacing to align both edges, an explicit word-spacing value can set a baseline that the justification adjusts from, though this is an advanced use.

The difference from letter-spacing is worth being precise about, because the two are often confused. Letter-spacing adds space after every character, including characters within a word, so it affects the internal structure of words. Word spacing adds space only between words, at the space characters, so it leaves the internal structure of each word untouched and changes only how far apart the words sit. The visual results are therefore different: letter-spacing makes text feel airier within words, while word spacing makes it feel more spread out as a sequence of blocks. For body text, both should normally be left alone, because both disrupt the word shapes and spacing rhythms that readers rely on, but word spacing is the less disruptive of the two for short display text, because it preserves word shapes.

Negative word spacing is occasionally used to tighten a heading or to fit a line into a constrained space, but it risks words running together visually, especially at small sizes or in fonts with narrow spaces. As with letter-spacing, the value should be judged in context, at the actual size, and the adjustment should be the smallest that achieves the effect. In practice, most stylesheets never set word-spacing at all, and that is fine — it is a property to know about for the occasional case where it is needed, not a property to reach for routinely. Its presence in this chapter is for completeness and for the distinction from letter-spacing, which is a distinction worth making clearly, because the two properties sound alike and do different things.

Check: What is the difference between letter-spacing and word-spacing?

Section 28 — Text alignment

text-align positions lines of text: left, right, center or justify. Left-aligned text is the easiest to read for long passages, because every line starts in the same place; centred text suits short headings and captions, not whole articles. Alignment determines where each line sits within its container, and that placement has a direct effect on readability. In left-aligned text, every line begins at the same left edge, so the eye, returning from the end of one line, always knows exactly where the next line starts. That predictability is what makes left alignment the default and the most readable choice for extended reading: the eye's return sweep is a practiced, automatic movement to a known location.

Centred text breaks that predictability. Each line is centred independently, so lines of different lengths start at different horizontal positions, and the eye must search for the start of each new line. For a short heading or a caption, that search is trivial, and the symmetry of centred text can look elegant and formal. For a long article, the search repeats on every line, accumulating into fatigue and slower reading, and the ragged left edge also makes the text block look less structured. Centred paragraphs are therefore a common beginner mistake: they look attractive in a short preview, where only a few lines are visible, but they become tiring over a full article. Reserve centring for headings, captions, short quotations, and other brief elements where its formality is an asset and its cost is negligible.

Right alignment mirrors left alignment, with a consistent right edge and a ragged left edge, and it shares the same readability cost for long text, plus the additional issue that most readers, in left-to-right languages, expect text to start on the left. Right alignment is appropriate for specific cases: numbers in a table column, where right edges should align for comparison; short labels in a right-to-left layout; or a deliberate design effect in a small element. It is not appropriate for body text in a left-to-right language, where it forces the eye to search for each line's start in an unfamiliar place.

Justified text aligns both edges, by stretching the spaces between words on each line so that every line reaches the right edge. That produces a neat, block-like appearance, familiar from newspapers and books, but on the web it often creates uneven word spacing — wide gaps on lines with few words, tight gaps on others — known as rivers of white space running through the paragraph. Those rivers distract the eye and slow reading, especially on narrow screens where justification has less room to distribute spacing evenly. Justification can work on wide, carefully set text, but for most web content, left alignment is the safer, more readable choice, and justification should be used deliberately, with an awareness of its cost, rather than as a default.

Check: Why is left-aligned text easier to read than centred text for long passages?

Section 29 — Text transform

text-transform changes how text is displayed without changing the HTML: uppercase makes everything capitals, lowercase makes everything small, and capitalize uppercases the first letter of each word. Screen readers still read the original text. The key idea is separation: the HTML contains the text as it was written, and the CSS changes only its displayed form. That means the underlying content — what search engines index, what screen readers announce, what is copied when a visitor selects the text — remains the original, while the visual presentation follows the design. If the HTML says "hello world" and the CSS says text-transform: uppercase, the visitor sees "HELLO WORLD", but the document still contains "hello world".

That separation is useful for several reasons. First, it keeps the HTML clean and semantic: a heading can be written in normal sentence case, which is correct for indexing and accessibility, while being displayed in capitals for visual impact, which is a design decision that belongs in CSS. Second, it makes the transformation consistent: every heading with the transform gets the same treatment, without authors having to remember to type in capitals, and without the risk of inconsistent capitalisation in the source. Third, it makes the transformation reversible: removing the CSS rule restores the original case everywhere at once, whereas text typed in capitals in the HTML would need to be retyped to restore it.

Each value has its uses and cautions. uppercase is the most common, used for headings, labels, buttons and navigation, where capitals convey importance and formality — but Section 41 explains why long passages in capitals are hard to read, so uppercase should be reserved for short text. lowercase is less common, used occasionally for a stylistic effect where a design wants everything small, though it can look affected if overused. capitalize uppercases the first letter of each word, which suits titles in some styles, but it does not understand grammar — it capitalises every word, including small words like "and" and "the" that title case would normally leave lowercase, so its output may not match editorial title case, and it should be checked against the intended style.

There is also a none value, which removes any transformation, useful for overriding a transform inherited from a parent — for example, a heading inside an uppercase container that should itself be in normal case. And screen reader behaviour deserves a note: because the transformation is purely visual, screen readers announce the original text, which is usually correct, but it means that a transformation that changes meaning — such as capitalising for emphasis in a way that a listener would miss — should be accompanied by a semantic marker as well. Text transform is a presentation tool, and like all presentation tools, it should not be the only carrier of meaning.

Check: If the HTML says "hello world" and CSS says text-transform: uppercase, what does the visitor see?

Section 30 — Text decoration

text-decoration adds lines to text: underline under it, line-through through it, or none to remove a line. Links are underlined by default, so removing underlines from links also removes a clue that they are links — keep another clue, like a distinct colour, if you do. Text decoration is one of the oldest text effects, dating from the days when underlining was done by hand, and its meanings are deeply ingrained in readers' expectations. An underline means "link" on the web, a strikethrough means "deleted" or "no longer valid", and an overline is rare and usually decorative. Those associations are not arbitrary; they are conventions that readers rely on to interpret a page, and breaking them carelessly causes confusion.

Links are the central case. Browsers underline links by default, and that underline is a crucial affordance: it tells visitors, at a glance and without colour vision, that this text can be clicked. Many designs remove the underline for aesthetic reasons, preferring a clean look with colour alone distinguishing links. That can work, but only if the colour distinction is strong and consistent — links in a clearly different, saturated colour, used only for links, so that colour becomes a reliable signal. If links are the same colour as body text, or if the link colour is also used for non-link text, removing the underline leaves visitors unable to tell what is clickable without hovering over everything, which is a poor experience. A safer modern pattern is to remove the underline in the normal state but restore it on hover and focus, so that links look clean yet remain discoverable when interacted with.

Strikethrough has a specific, useful meaning: it marks text as deleted, superseded, or no longer applicable, while keeping it visible for reference. A sale price showing the old price struck through beside the new price is the classic example, and it works because readers understand the convention instantly. Using strikethrough merely for decoration, or for emphasis, misuses that convention and confuses readers who interpret it as deletion. Similarly, underlining ordinary text for emphasis — a habit from typewriter days, when bold and italic were unavailable — now confuses readers into thinking the text is a link, which is why Section 42 advises bold or italic for emphasis instead.

The modern text-decoration shorthand can also set the line's colour, style and thickness separately, as in text-decoration: underline wavy red;, and properties like text-underline-offset control how far below the text the underline sits. Those refinements let underlines be both functional and beautiful — a coloured, offset underline that clearly marks a link without the harshness of the default. The principle remains: decoration should support meaning, not obscure it, and the strongest convention — underline means link — should be respected unless a clear alternative signal replaces it.

Check: Why should you be careful about removing underlines from links?

Section 31 — Text indent

text-indent pushes the first line of a paragraph in from the left, like the indented first line of a printed book: p { text-indent: 2em; }. It is a paragraph-level effect; it does not indent the whole block. Text indent is a traditional print convention, where the first line of each paragraph is indented to signal "a new paragraph begins here", in place of the blank space between paragraphs that is more common on the web. On screen, the blank-space convention dominates, because spacing between blocks is easier to scan than a small indent, but indent still has uses, particularly for long-form reading that mimics print, or for specific elements like block quotations or first lines that need a typographic signal.

The property applies to the first line only, and that "only" is the point to understand. Setting text-indent: 2em; on a paragraph moves its first line two ems to the right, while all subsequent lines start at the normal left edge. The paragraph as a block does not move; its left edge, its background, and its border all stay where they were, and only the first line's text shifts. That is different from margin-left or padding-left, which move or pad the whole block, and confusing the two is a common mistake: an author who wants a whole block indented and uses text-indent will be puzzled when only the first line moves, while an author who wants only the first line indented and uses margin-left will be puzzled when the whole block shifts.

The value is usually given in ems, so that it scales with the font size: 2em is twice the font size, which is a traditional indent width, though 1em to 1.5em is also common for a subtler effect. A negative value creates a hanging indent, where the first line extends to the left of the rest — the effect used in bibliographies and reference lists, where the author's name starts at the margin and subsequent lines are indented, making entries easy to scan. Hanging indents are usually achieved with a combination of a negative text-indent and a positive padding or margin on the block, so that the block's content area is indented while the first line hangs back to the margin. That combination is a good example of how two simple properties compose into a sophisticated typographic effect.

Text indent interacts with alignment in ways worth noting. In centred text, indenting the first line shifts it relative to the centre, which usually looks like a mistake rather than a design choice, so indent is rarely combined with centring. In right-aligned text, indent works from the right edge, which is logical but uncommon. And in the first paragraph after a heading, print convention often omits the indent, because the heading itself already signals a new section — a refinement that can be expressed with the adjacent sibling selector from Chapter 6, as in h2 + p { text-indent: 0; }, showing how selectors and typography combine. For most web writing, spacing between paragraphs is the clearer paragraph signal, and indent is a specialist tool for print-like or reference layouts, but knowing it completes your typographic vocabulary.

Check: Does text-indent move the whole paragraph or just its first line?

Section 32 — Controlling white space

white-space controls how the browser treats spaces and line breaks in the source. The default, normal, collapses runs of spaces into one and wraps lines. nowrap stops wrapping, which is handy for short labels that must stay on one line. White space in HTML source — spaces, tabs, line breaks — is not preserved literally by default. The browser collapses any sequence of white-space characters into a single space, and it breaks lines at spaces to fit the container, ignoring the line breaks in the source. That collapsing is why you can format HTML source readably, with indentation and line breaks, without those formatting choices affecting the rendered page: the source's white space is a convenience for authors, not a set of instructions for layout.

The white-space property changes that default handling, and its values form a small system worth learning. normal is the default described above: collapse spaces, wrap lines, ignore source line breaks. nowrap collapses spaces but does not wrap, so a long line overflows its container rather than breaking — useful for labels, buttons, or navigation items that must stay on one line, but dangerous for long text, which will overflow and may be cut off or break the layout. pre preserves spaces and line breaks exactly, and does not wrap, which is how the <pre> element displays code: what you type is what you get, including indentation and blank lines. pre-wrap preserves spaces and line breaks but also wraps, giving the fidelity of pre with the safety of wrapping. pre-line collapses spaces but preserves line breaks, and wraps — a middle ground for text where author line breaks matter but extra spaces do not.

Each value suits a different content type. normal suits ordinary prose, where the browser should manage wrapping. nowrap suits short, indivisible labels, table headers, or any text that would be confusing if split across lines. pre suits code, poetry, and ASCII art, where exact spacing is part of the content. pre-wrap suits code that should also wrap on narrow screens, so that long lines do not overflow on a phone. And pre-line suits text pasted from another source where line breaks are meaningful — an address, a poem, a formatted message — but where extra spaces are not. Choosing the right value is a matter of asking what the white space in the content means: is it formatting for the author, or is it part of the content itself?

A caution about nowrap: because it prevents wrapping, it can cause overflow, and overflow in a narrow container can break a layout or hide content, especially on phones. Always pair nowrap with an overflow strategy — such as the ellipsis technique in the next section, or a container that can scroll — and test at narrow widths. White space handling is one of those areas where the browser's default is well-chosen for the common case, and the property exists for the exceptions. Knowing the values lets you handle those exceptions deliberately, rather than fighting the default with non-breaking spaces or manual line breaks, which are fragile hacks compared to a single, clear property.

Check: What does white-space: nowrap; do to a long line of text?

Section 33 — Text overflow and ellipsis

When text is too long for its box, text-overflow: ellipsis; shows "..." at the cut-off point — but only when combined with overflow: hidden; and white-space: nowrap;. All three declarations are needed; ellipsis alone does nothing. The ellipsis is a visual signal that text has been cut off, and it is used for previews, titles, labels and table cells where the full text cannot fit but the reader should know that more exists. Without the ellipsis, cut-off text simply ends mid-word or mid-letter, which looks like an error; with it, the cut is clearly deliberate, and the reader understands that the text continues beyond what is shown.

Why three declarations? Each one does a distinct job, and the effect requires all three. white-space: nowrap; prevents the text from wrapping to a second line, forcing it into a single line that is longer than its container. overflow: hidden; clips that overlong line at the container's edge, hiding the excess instead of letting it spill out or scroll. And text-overflow: ellipsis; replaces the clipped edge with "...", so that the cut is marked. Remove any one, and the effect fails: without nowrap, the text wraps and there is no overflow to clip; without hidden, the text spills out and the ellipsis has nothing to replace; without ellipsis, the text is clipped silently, with no signal that anything was cut. The trio is a recipe, and like any recipe, omitting an ingredient changes the result.

The ellipsis has limitations that matter for accessibility and usability. The hidden text is still in the document — it is clipped visually, not removed — so screen readers will read the full text, which is good, but sighted readers cannot see it, and they cannot access it unless the design provides a way, such as a tooltip on hover, an expand control, or a link to the full content. An ellipsis without any way to reveal the full text is therefore a dead end for sighted readers: they know text was cut, but they cannot read it. For titles and previews, that is often acceptable, because the full content is one click away; for essential information, it is not, and the text should be allowed to wrap or the container should be enlarged instead.

There are also variations worth knowing. The ellipsis works on a single line, as described; multi-line ellipsis — showing "..." after, say, three lines — requires newer techniques and is less universally supported, though it is increasingly available. And the ellipsis character itself can be customised in some contexts, though the default "..." is standard and recognisable. The key habit is to treat ellipsis as a design decision with a trade-off — space saved versus content hidden — and to pair it with a way to reach the full text whenever the hidden content matters. Used that way, it is a polished, professional touch; used carelessly, it hides information behind three dots with no way back.

Check: Name the three declarations needed for a working ellipsis.

Section 34 — Text shadow

text-shadow adds a shadow behind text, with horizontal offset, vertical offset, blur and colour: text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.4);. Used lightly, it can lift text off a busy background; used heavily, it hurts readability. A text shadow is a copy of the text's shape, offset from the original, blurred by a given amount, and coloured as specified, drawn behind the original text. The four values, in order, are: how far right the shadow is offset (negative values move it left), how far down it is offset (negative values move it up), how blurred it is (zero means a sharp copy, larger values mean a softer, more diffused shadow), and the shadow's colour, which can include alpha for a soft, translucent effect.

The classic legitimate use is text over an image. White text directly on a bright, varied photograph can disappear where the photograph is light, because there is not enough contrast in those areas. A soft dark shadow behind the text creates a halo of contrast around each letter, separating it from the background regardless of what lies behind it, and making the text readable without a solid background panel. That is why hero headings over photographs so often have a subtle shadow: it is a readability device, not merely a decoration, and it preserves the photograph's visibility while ensuring the text can be read. The shadow should be soft and dark, with a modest blur, so that it reads as a gentle separation rather than as a distinct, duplicated text.

Multiple shadows can be listed, separated by commas, to build more complex effects: a light shadow above and a dark shadow below can create a letterpress or embossed effect, and layered shadows can create a glow. Those effects are fun, but they quickly become gimmicky, and they should be used sparingly, for display headings or special occasions, not for body text. On body text, any shadow reduces readability, because it adds visual noise around each letter, blurring the edges that the eye uses to recognise shapes. A shadow on small text is therefore almost always a mistake, however subtle it seems in a design tool.

The values also deserve care. An offset of 1 or 2 pixels with a blur of 2 to 4 pixels and a semi-transparent black is a typical subtle shadow; larger offsets and blurs produce a floating, disconnected look, and a fully opaque shadow colour produces a harsh duplicate that competes with the original. As with other decorative effects, the test is to view the result at actual size, on the actual background, and to ask whether the text is easier or harder to read with the shadow than without it. If it is harder, remove the shadow and solve the contrast problem another way — with a background panel, a darker overlay, or a different text colour — rather than relying on a shadow to rescue an unreadable combination.

Check: What do the four values in a text-shadow mean?

Section 35 — Line length and readability

Very long lines tire the eyes, because the reader loses their place coming back to the next line. Keeping paragraphs in a container of moderate width — rather than stretching across a very wide screen — makes long articles noticeably easier to read. Line length, measured in characters per line, is a major readability factor that is often overlooked in favour of font size and line height. When a line is very long, the eye must travel a long way from the end of one line back to the start of the next, and during that return sweep it is easy to land on the wrong line — skipping one, or re-reading one — especially if the lines are also tightly spaced. That loss of place interrupts reading, forces backtracking, and accumulates into fatigue over a long article.

The comfortable range for line length is often given as roughly 45 to 75 characters per line, with 60 to 70 as a sweet spot for body text. That range is not a strict rule, and it varies with font, size and audience, but it captures the principle: lines should be long enough that the eye does not return too often, which is tiring in the opposite direction, but short enough that the return sweep is reliable. Very short lines — under 40 characters — force frequent returns and make the text feel choppy, while very long lines — over 90 characters — make returns unreliable and the text feel daunting. The moderate range balances those two costs, and it is why books, newspapers and well-designed sites all keep their text columns to a moderate width, even on wide screens.

On the web, the practical implication is to constrain the width of text containers, rather than letting paragraphs stretch to the full width of a wide monitor. A common technique is to give the article container a maximum width, such as max-width: 65ch;, where the ch unit is roughly the width of the character "0" in the current font, so 65ch is about 65 characters wide, whatever the font size. That single declaration keeps lines in the comfortable range on any screen, from a phone, where the container is naturally narrow, to a desktop monitor, where it would otherwise stretch to hundreds of characters. Sidebars, images and other elements can still use the full width; only the text column is constrained, which is exactly the point.

Line length interacts with the other readability factors. A longer line needs more line height, because the return sweep is longer and the eye benefits from more vertical separation between lines. A larger font can support a slightly longer line, because the characters are bigger and the line's shape is clearer. And a narrow column on a phone, where lines are necessarily short, needs less line height than the same text on a desktop. These interactions mean that line length should be considered as part of a system — width, size, line height and font together — rather than in isolation. But the starting point is simple: do not let body text stretch across a very wide screen, and your readers will thank you with longer, more comfortable reading sessions.

Check: Why are extremely long lines of text hard to read?

Section 36 — Font size and hierarchy

Size creates hierarchy: a large heading, medium subheadings and smaller body text tell the reader what matters most before they read a word. If everything is the same size, nothing stands out; if everything is large, nothing stands out either. Visual hierarchy is the arrangement of elements so that their relative importance is clear at a glance, and font size is its most immediate tool. A reader scanning a page should be able to identify the main title, the section headings, and the body text within a second, without reading anything, purely from the sizes. That scanning is how readers decide what to read, in what order, and whether the page is worth their time, so hierarchy is not decoration — it is navigation.

A clear hierarchy has distinct levels with clear gaps between them. A common scale might be: page title at 2.5rem, section headings at 1.75rem, subheadings at 1.25rem, and body text at 1rem, with captions at 0.875rem. The gaps between levels matter as much as the values: if the section heading is only slightly larger than the body, the distinction is weak and the hierarchy feels muddy; if the gaps are too large, the page feels disjointed, with headings shouting and body text whispering. A scale where each level is roughly 1.25 times the previous one — a modular scale — produces gaps that feel natural, because the ratios are consistent, and it is a technique worth learning as you design more complex pages.

Size hierarchy also works with weight and colour, and the three should be used together rather than relying on size alone. A heading can be larger, bolder, and in a darker or accent colour, while body text is smaller, regular, and in a neutral colour — the combination of differences makes the hierarchy unmistakable, even for readers who cannot perceive one of the differences, such as a colour difference. Relying on size alone is fragile, because a reader with low vision who zooms the page may lose the relative size differences, while weight and colour differences survive zooming. Hierarchy, like other information, should be conveyed by more than one channel where possible.

The failure modes are instructive. A page where everything is the same size has no hierarchy at all: the reader cannot tell titles from content, and must read everything to find anything, which is exhausting. A page where everything is large — headings, body, captions all competing — has the opposite problem: everything shouts, so nothing is heard, and the reader is overwhelmed rather than guided. And a page with too many levels — six or seven distinct sizes — has a hierarchy too complex to learn, so readers stop trying to interpret it. Three to five levels, clearly separated, consistently applied, is the professional standard for most pages, and it is enough to structure even a long, complex document without overwhelming the reader.

Check: How does font size create a visual hierarchy?

Section 37 — Combining fonts

Most pages need at most two typefaces: one for headings and one for body text. Pair fonts that contrast enough to look deliberate — a serif heading with a sans-serif body, for example — rather than two similar fonts that look like a mistake. The two-typeface rule is a discipline that prevents a common beginner error: using a different font for every element, in the belief that variety looks interesting, when in fact it looks chaotic. Each additional typeface adds a new visual voice to the page, and voices that are not clearly distinct compete and confuse, while voices that are clearly distinct can harmonise by contrast. Two voices, well-chosen, are enough for almost any page: one to announce, one to explain.

The pairing principle is contrast with harmony. The two fonts should be different enough that the change between heading and body is clearly intentional, not an accident. A serif heading with a sans-serif body is the classic contrast: the serif's traditional, detailed character against the sans-serif's clean, neutral character signals "heading versus body" instantly. A sans-serif heading with a serif body reverses the roles but keeps the contrast, giving a modern heading with a literary body. What does not work is pairing two similar sans-serifs, or two similar serifs, because the difference is too subtle to read as intentional — it looks like a mistake, a font that failed to load, or an inconsistency, rather than a design choice. If two fonts look almost alike, use only one of them.

Beyond the heading-body pair, a third typeface is occasionally justified for a specific purpose: a monospace font for code, which is a functional choice rather than a stylistic one, or a display font for a logo or a special headline, used sparingly. But each addition must earn its place by serving a distinct role that the existing fonts cannot serve, and it must be used consistently for that role. A monospace font used only for code is a clear role; a third decorative font used for occasional headings is not, because it competes with the heading font without a clear reason. The discipline is to ask, for each new typeface: what job does this do that the current fonts do not, and is that job important enough to add a voice to the page?

Pairing also involves practical considerations: the fonts should have similar proportions, so that a heading and body at their respective sizes feel balanced, and they should both be available as web fonts with the weights you need, without excessive download cost. Testing the pair with real content — a heading, a paragraph, a caption, a button — at real sizes, is the only reliable way to judge it, because fonts that look good in isolation can clash in combination. When a pair works, the page feels designed: headings announce, body explains, and the contrast between them guides the eye without drawing attention to itself. That quiet guidance is the goal of typographic pairing.

Check: How many typefaces does a typical page need?

Section 38 — Colour and typography together

Colour choices and type choices interact: a thin, light font in a pale colour on a white background may be unreadable, while the same font in a dark colour is fine. Always judge a font and a colour together, at the size they will actually appear. Typography and colour are often taught as separate subjects — this chapter separates them for clarity — but in practice they are inseparable, because readability is a product of both. A typeface's legibility depends on its weight, its size, and the contrast between its colour and its background, and changing any one of those changes the readability of the whole combination. A combination that passes in one configuration may fail in another, and the only reliable test is the combination itself, at its real size, on its real background.

Thin fonts are the clearest example. A light-weight font — 300 on the weight scale — has thin strokes that need strong contrast to remain visible, because there is less ink, so to speak, to signal each letter's shape. In a dark colour on white, a light font can look elegant and refined, especially at large sizes where the thin strokes are still substantial. In a pale grey on white, the same font becomes ghostly: the thin strokes plus low contrast leave almost no signal for the eye to detect, and the text dissolves into the background. The same font, the same size, the same background — only the colour changed — and readability collapses. That is why colour and type must be judged together: neither alone determines whether text can be read.

Size interacts similarly. Small text needs more contrast than large text, because small letters have less area to carry the contrast signal, and a colour combination that works for a large heading may fail for small body text or captions. Accessibility guidelines reflect this by requiring higher contrast for smaller text, but the principle is intuitive: the smaller and thinner the text, the stronger the contrast it needs. When you choose a muted colour for secondary text — a caption, a footnote, a metadata line — check it at its actual small size, not at heading size, because the muted colour that looks sophisticated large may be illegible small.

The practical habit is to test combinations, not components. When you choose a font, test it in its intended colours and sizes; when you choose a colour, test it in its intended fonts and sizes; and when you change one, re-check the other. Design tools that show a font in black on white are showing you the font, not the design, and the design is the combination. A page whose fonts and colours were chosen together — each font tested in its colours, each colour tested in its fonts — feels coherent in a way that a page assembled from separately chosen parts does not, because the parts were never separate in the reader's experience. They were always one thing: text, in a colour, at a size, on a background, being read by a person.

Check: Why should you judge a font and a colour together rather than separately?

Section 39 — Shorthand font property

The font shorthand sets several font properties at once: font: italic bold 20px/1.5 Georgia, serif;. The size and family are required, and the line height goes after the size with a slash. Shorthand is compact, but it resets any font properties you do not mention. Shorthand properties exist throughout CSS as a way to set several related properties in one declaration, and font is one of the most powerful — and most dangerous — of them, because it sets style, variant, weight, size, line height and family in a single line. The order is fixed: style, variant and weight first, in any order among themselves, then size, then optionally line height after a slash, then family. So font: italic bold 20px/1.5 Georgia, serif; means italic style, bold weight, 20-pixel size, 1.5 line height, Georgia family with serif fallback.

The requirement to include size and family is strict: a font declaration without both is invalid and ignored entirely, which is a common source of confusion when a shorthand that looks reasonable does nothing. The line height is optional, but if it is included, it must follow the size with a slash and no space around the slash in the size/line-height pair — 20px/1.5, not 20px / 1.5, though browsers are forgiving about spaces in practice. The style, variant and weight are also optional, and can appear in any order before the size, but they must appear before it, not after. Learning that order takes a little memorisation, but it is a fixed pattern, and once learned, it reads naturally: how the text looks, how big it is, how it is spaced, and what typeface it uses.

The danger is the reset behaviour. A shorthand sets every property it covers, including those you do not mention, resetting them to their initial values. So font: 20px Georgia, serif; does not merely set size and family; it also resets style to normal, weight to normal, and line height to normal, discarding any italic, bold or custom line height set by earlier rules. That reset is by design — shorthand means "set all of these" — but it surprises authors who expect it to mean "set only these". If an element was bold through a class rule, and a later shorthand sets its font without mentioning weight, the bold is lost, not because the shorthand opposed it, but because the shorthand reset it. The fix is either to include the weight in the shorthand, or to avoid shorthand where a reset would be harmful and set the individual properties instead.

For that reason, shorthand is best used where you intend to set the whole font description at once — typically in a base rule for body text, or in a component rule that defines a complete typographic style — and avoided where you intend to change only one aspect, such as making a single element bold, where font-weight: bold; alone is clearer and safer. Shorthand is compact and expressive when used deliberately, and a trap when used casually. Knowing both sides — the convenience and the reset — lets you choose the right form for each situation, which is the professional approach to all shorthand properties, not just font.

Check: In font: 16px/1.5 Arial, sans-serif;, what does the 1.5 mean?

Section 40 — Inheriting typography from body

Setting font-family, font-size, line-height and color on body styles nearly all the text on a page at once, because those properties inherit. Headings and form controls are the main exceptions that set their own defaults, so they may need explicit rules. This is the practical application of inheritance from Chapter 6, and it is the standard starting point for a page's typography. A single rule on body — for example, body { font-family: Arial, sans-serif; font-size: 16px; line-height: 1.5; color: #222; } — establishes the baseline for the entire document, and every paragraph, list item, table cell and caption that does not set its own values inherits that baseline automatically. Without inheritance, each of those elements would need its own rule, and a site-wide typography change would be a major edit; with it, the change is a one-line edit to the body rule.

The exceptions are instructive. Headings have browser defaults for size and weight that override inheritance, because the user-agent stylesheet targets headings directly, and a direct rule beats an inherited value, as Chapter 6 explained. So a body font-size of 16 pixels does not make headings 16 pixels; they remain at their default sizes — larger and bolder — unless you set them explicitly, which you normally will, as part of building the hierarchy from Section 36. Form controls — inputs, buttons, selects — are a similar exception in many browsers: they do not inherit font properties by default, but use the operating system's form styling instead, so a form inside a page with a custom body font may display its text in a different font unless you explicitly set font-family: inherit; on the controls, or set their font directly. That quirk is well-known, and handling it — usually with a rule like input, button { font-family: inherit; } — is a standard part of setting up a page's typography.

The pattern that emerges is: set the baseline on body, then set the exceptions explicitly, and let inheritance carry the baseline everywhere else. Headings get their sizes and weights in a grouped rule, form controls get their font inheritance, and everything else follows the body. That structure is efficient, because the baseline is stated once, and it is clear, because the exceptions are visible as explicit rules rather than hidden as overrides. It also makes the design easy to change: a new body font, a new base size, or a new line height propagates everywhere that inherits, and only the deliberate exceptions — headings, forms — need separate consideration, which is exactly the behaviour you want when a design evolves.

There is a final, subtle benefit: setting typography on body makes the design's intentions explicit. A reader of the stylesheet can see, in one rule, what the page's default text looks like, and can reason about variations as departures from that default. That clarity is valuable for maintenance, for onboarding new developers, and for auditing accessibility, because contrast and size can be checked against the baseline rather than hunted through dozens of rules. Typography inheritance is not just a convenience; it is a way of organising a design around a single, visible baseline, with variations stated as exceptions — which is the same layered thinking that the cascade encourages throughout CSS.

Check: Why does setting a font on body style most of a page's text?

Section 41 — Uppercase and readability

All-caps text is harder to read than normal text, because words lose their distinctive shapes and every word becomes a rectangle. Reserve uppercase for short labels and headings, and never set long paragraphs in capitals. Readers recognise words not only letter by letter, but also by their overall shape — the pattern of ascenders (the tall parts of letters like "h" and "l") and descenders (the tails of "p" and "y") that gives each word a distinctive silhouette. In normal mixed-case text, those silhouettes vary from word to word, and the eye uses them as a shortcut to recognise words quickly. In all-caps text, every letter is the same height, with no ascenders or descenders, so every word becomes a uniform rectangle, and the shape shortcut disappears. The reader must then decode each word letter by letter, which is slower and more tiring.

That cost is acceptable for short text, where the number of words is small and the visual impact of capitals — formality, importance, a sense of announcement — outweighs the slower reading. A navigation label, a button, a short heading, or a section title in capitals reads clearly, because there are only a few words to decode, and the capitals signal that this text is a label rather than content. For those uses, capitals are a legitimate design choice, especially when combined with letter-spacing, which opens the uniform rectangles just enough to let each letter stand clear, as Section 26 explained.

For long text, the cost accumulates and becomes prohibitive. A paragraph in capitals forces letter-by-letter reading for every word, over dozens of lines, and the result feels like shouting — which is the cultural association of capitals online — as well as being physically harder to read. A page that sets articles, descriptions, or messages in capitals is therefore both less readable and less polite than one that reserves capitals for labels. The rule is simple to state and easy to follow: capitals for short, structural text; mixed case for everything that must actually be read. And when capitals are used, they should be applied with text-transform: uppercase in CSS, as Section 29 explained, so that the underlying HTML remains in normal case for screen readers and search engines, and the transformation can be removed or changed in one place.

There is a related point about emphasis: capitals are sometimes used to emphasise a word or phrase inside a sentence, but that emphasis comes at the cost of readability and tone, and bold or italic usually emphasises more gracefully, without changing the word's shape or shouting at the reader. Reserve capitals for labels and headings, where their formality is an asset, and use other forms of emphasis inside sentences. That restraint keeps capitals meaningful: when everything is in capitals, capitals mean nothing, but when only labels are in capitals, capitals mean "this is a label", which is a useful signal for scanning a page.

Check: Why is a long paragraph in all capitals hard to read?

Section 42 — Underlines are for links

On the web, an underline means "this is a link" to most readers. Underlining ordinary text for emphasis confuses people into clicking something that is not a link, so use bold or italics for emphasis instead. This convention is one of the strongest on the web, because it dates from the earliest browsers, which underlined links by default, and it has been reinforced by decades of use. Readers have learned, unconsciously, that underlined text can be clicked, and they act on that learning: they move the pointer over underlined text, they expect a pointer cursor, they expect a new page or action. When underlined text is not a link, that expectation is violated, and the reader experiences a small moment of confusion and frustration — a click that does nothing, or a hesitation before clicking something that should be clickable.

The confusion is not trivial, because it undermines trust in the page's affordances. If some underlined text is clickable and some is not, readers cannot tell which is which without trying, and they learn to distrust underlines, which makes real links harder to discover. The page's interactive language becomes unreliable, and that unreliability spreads: readers become less confident about buttons, menus, and other controls, because the page has already shown that its visual signals cannot be trusted. Keeping underlines exclusively for links preserves the reliability of that signal, which benefits every link on the page.

Emphasis has better tools, as Sections 23 and 24 explained. Bold makes a word stand out by weight, without implying clickability, and it works for short and long spans alike. Italic makes a word stand out by slant, signalling a change in voice or a title, again without implying a link. Colour can also emphasise, provided it is not the link colour — using the link colour for non-link emphasis reintroduces the same confusion in a different form. The principle is that each visual treatment should have one meaning, and emphasis should use treatments that mean emphasis, not treatments that mean something else. Bold and italic mean emphasis; underline means link; keeping those meanings separate keeps the page's language clear.

There are narrow exceptions. In some contexts, such as legal documents or academic citations, underlining has a traditional meaning unrelated to links, and readers in those contexts understand it. But on the general web, the link convention dominates, and even in specialised contexts, the risk of confusion usually outweighs the benefit of following a print convention. The safe, professional default is: underline only links, emphasise with bold or italic, and let readers trust that what looks clickable is clickable, and what does not look clickable is not. That trust is a small thing, but it is built from many small consistencies, and underlines are one of the most visible of them.

Check: What should you use for emphasis instead of underlining plain text?

Section 43 — Colour keywords and system colours

Beyond named colours, CSS has keywords like transparent (fully see-through) and currentColor (covered in Section 9). transparent is useful for borders and backgrounds you want to keep structurally present but invisible. Keywords are colour values that are not colours in the usual sense, but instructions about how a colour should behave. transparent means "no colour at all, fully see-through", which is equivalent to rgba(0, 0, 0, 0) — a colour with zero alpha — but clearer to read and to intend. It is the initial value of background-color, which is why elements have no background by default: their background is transparent, and whatever lies behind them shows through.

The practical use of transparent is for structure without visibility. A border set to transparent still occupies space — it still affects layout, pushing content inward by the border's width — but it is invisible, so it can be used as a placeholder that prevents layout shift when the border later becomes visible, for example on hover. Without the transparent border, a hover rule that adds a border would push the content inward by the border's width, causing a visible jump; with a transparent border in the normal state, the space is already reserved, and the hover change is only a colour change, with no movement. That technique — reserving space with a transparent border — is a standard way to make hover effects smooth, and it illustrates how a "colour" can be used for layout purposes.

transparent is also useful for backgrounds that should reveal what lies behind them while still being explicitly set, such as a button over an image that should show the image through it, or a panel that should not obscure a background pattern. Setting the background to transparent states that intention explicitly, which is clearer than omitting the background property and relying on the default, because the explicit value documents that the transparency is deliberate, not an oversight. And in gradients, transparent as a colour stop creates a fade to see-through, which is a common technique for vignettes and overlays that darken an image gradually rather than abruptly.

Other keywords exist, including system colours that follow the user's operating system theme, such as colours for buttons, windows, and highlighted text. Those system colours are less commonly used today, because designs increasingly define their own palettes, but they remain valid, and they illustrate the same idea as currentColor and transparent: colour values can be relative — to the text, to the system, to nothing at all — rather than absolute. Knowing the keywords completes your colour vocabulary: named colours for familiarity, hex and rgb for exactness, hsl for exploration, alpha for translucency, and keywords for the special cases where a colour is really a relationship or an absence.

Check: What does the keyword transparent do as a background colour?

Section 44 — Choosing colours with a purpose

Give each colour a job: one for text, one for backgrounds, one for actions like buttons and links, and one for warnings. When a new element needs a colour, reuse an existing one with a job that fits, instead of inventing a new shade each time. This is the design-system version of the small-palette advice from Section 14, and it turns a palette from a list of colours into a set of roles. A role-based palette answers the question "what is this colour for?" rather than "what does this colour look like?", and that shift makes decisions easier: a new element does not need a new colour, it needs an existing colour whose job matches its purpose.

The roles in a typical palette are few and stable. Text colours: a primary text colour for body content, and a secondary, muted text colour for captions and metadata. Background colours: a page background, a surface colour for cards and panels, and perhaps a dark surface for footers or hero sections. Action colours: a primary accent for buttons and links, and possibly a secondary accent for less prominent actions. Semantic colours: success, warning, and error, used only where those meanings apply, and never for decoration, so that their meanings stay clear. Each role has one colour, and each colour has one role, so the palette is a small, comprehensible system rather than a growing collection of shades.

Role-based thinking also prevents a common drift: the gradual accumulation of near-identical colours. Without roles, a developer adding a new button might pick a blue that is slightly different from the existing button blue, because it looked right in isolation, and over time the stylesheet accumulates five blues that are almost but not quite the same, creating a subtle inconsistency that is hard to clean up. With roles, the developer asks "is this a primary action?" and, if yes, uses the primary action colour, exactly, without variation. The result is consistency by construction, not by vigilance, and it survives team changes and time, because the roles are documented and the colours are tied to them.

Documenting the roles is part of the practice. Even a simple comment at the top of a stylesheet — listing each colour, its value, and its role — turns the palette into a shared reference that a team can follow and a future maintainer can understand. Modern CSS custom properties make this documentation executable, by letting you name a colour once, as a variable, and use the variable everywhere, so that changing the role's colour updates every use automatically. But even without variables, the discipline of assigning roles and reusing colours is the foundation, and it is what separates a designed palette from an accumulated one. Start with roles, not with colours, and let each new element find its colour by its job.

Check: What goes wrong when every new element gets a brand-new colour?

Section 45 — Testing text on real screens

Text that looks fine on a large monitor can be too small on a phone or too faint in bright light. Check your pages on a real phone, zoom the browser in, and read the page at arm's length — those three checks catch most readability problems. Testing on real devices is the part of typography that cannot be done in a design tool, because a design tool shows text under ideal conditions: a large screen, a comfortable viewing distance, good lighting, and a zoom level chosen for inspection. Real reading happens under worse conditions, and text that survives those conditions is text that is genuinely readable, not just text that looks readable in a mockup.

The phone check is the most important, because phones are where much web reading now happens, and they impose the hardest constraints: a narrow screen, which forces short lines and small text if the design is not responsive; a small physical size, which makes text smaller in absolute terms; and variable lighting, from indoor rooms to bright sunlight, which washes out low-contrast text. Opening your page on a real phone — not just in a desktop browser's device emulator, though that is a useful first step — reveals problems that the emulator misses, such as text that is technically legible but practically too small to read comfortably while holding the phone at a normal distance, or colours that look fine indoors but vanish outdoors.

Zooming the browser in is the second check, and it simulates low vision and ageing eyes. Browsers let visitors increase text size or zoom the whole page, and a well-designed page should remain readable and usable at 150% or 200% zoom, without text overflowing its containers, overlapping, or being cut off. Testing at high zoom reveals fixed-height containers that clip enlarged text, layouts that break when text wraps to more lines, and font sizes that were set in a way that ignores the visitor's preferences. A page that breaks at high zoom is a page that excludes readers who need larger text, and fixing it is usually a matter of using flexible containers and relative units, as earlier sections advised.

Reading at arm's length is the simplest check, and it requires no device at all: hold your screen at arm's length, or step back from a monitor, and try to read the page. Text that is readable at arm's length has enough size and contrast for comfortable reading; text that dissolves into a grey blur does not, however elegant it looked up close. That test approximates the experience of a reader with reduced acuity, a reader in bright light, or a reader glancing at a page from a distance, and it is a humbling check, because many designs that look refined up close fail it. Together, these three checks — phone, zoom, arm's length — take a few minutes and catch the majority of readability problems before visitors do, which is the point of testing: to find problems while they are still cheap to fix.

Check: Name three quick checks for text readability.

Section 46 — Avoiding pure black on pure white

Pure black text (#000) on a pure white background (#fff) can feel harsh, especially on bright screens. Many designers soften one side slightly — very dark grey text on white, or white text on very dark grey — which reads just as clearly with less glare. The harshness of pure black on pure white is a consequence of maximum contrast: the difference between the brightest white a screen can produce and the darkest black is as large as possible, and on a bright, backlit screen, that extreme difference can feel glaring, especially over long reading sessions. The eye must constantly adjust between the brilliant background and the dark letters, and that adjustment, repeated thousands of times across a page, contributes to fatigue.

Softening one side reduces the extreme without sacrificing readability, provided the softening is modest. Dark grey text — such as #222 or #333 — on a white background retains almost all the contrast of pure black, because the difference between very dark grey and white is still large, but it removes the harshest edge, making the text feel gentler over long passages. Similarly, an off-white background — such as #fafafa or #f5f5f5 — with pure black or dark grey text reduces the background's glare while keeping the text dark and clear. Either softening, or both together in moderation, produces a combination that readers describe as more comfortable, without any measurable loss in legibility, as long as the contrast remains well above the thresholds discussed in Section 12.

The caution is not to soften too far. A mid-grey text on white, or a dark grey text on a mid-grey background, crosses from comfortable into illegible, because the contrast that was barely reduced at #333 is substantially reduced at #777, and the text becomes hard to read, especially at small sizes. The safe range for softening is narrow: text should stay in the very dark range, roughly #111 to #333, and backgrounds should stay in the very light range, roughly #fafafa to #fff, so that the combination remains high-contrast. Beyond those ranges, comfort is bought at the price of legibility, which is a bad trade, because a comfortable page that cannot be read is not comfortable at all.

This advice is also a matter of taste and context, not a rule. Some designs — particularly those aiming for a stark, high-impact look — deliberately use pure black on pure white, and they can succeed, especially for short, large text where glare is less of an issue. And some screens, particularly e-ink displays or printed pages, do not have the backlight glare that motivates softening, so pure black on white is ideal there. The principle is to consider the screen and the reading duration: for long-form reading on backlit screens, a slight softening is usually kinder; for short, bold statements, maximum contrast is fine. Judging that trade-off, rather than following a blanket rule, is the professional approach.

Check: What is a softer alternative to pure black text on a pure white background?

Section 47 — Numbers and units in typography

Font sizes can be given in pixels, ems, rems or percentages, and spacing in ems or pixels. The unit matters: a pixel size stays fixed when the user zooms text settings, while em and rem sizes adapt, which is one reason relative units are preferred for body text. Units in CSS are not interchangeable labels; they encode different relationships to the user's settings, to the surrounding context, and to the physical screen, and choosing a unit is choosing one of those relationships. A pixel says "this many screen units, regardless of context". An em says "this many times the parent's font size". A rem says "this many times the root font size". A percentage, for font size, says "this percentage of the parent's font size", which is equivalent to em for that purpose. Each unit answers a different question, and the right unit depends on which question you are asking.

For body text, the question is usually "how big should this be relative to the reader's preferred size?", and the answer is a relative unit — rem in most modern stylesheets, because it is stable across nesting, as Section 22 explained. A visitor who has set their browser to 20 pixels as a base size, because of poor eyesight, will get 20-pixel body text from a 1rem rule, 25-pixel subheadings from a 1.25rem rule, and so on, with all proportions preserved. A pixel-based design would give that visitor 16-pixel body text regardless of their setting, ignoring a preference they set precisely because they need larger text. That is not just a technical difference; it is a difference in respect for the reader, and it is why accessibility guidance favours relative units for text.

For other measurements, the question is different. A border width of 1 pixel should be 1 pixel on any screen, because it is a hairline, not a proportion of text size. Letter-spacing in ems should scale with the font size, because it is a proportion of the text, not an absolute gap. Line height as a unitless multiplier should scale with the font size, as Section 25 explained, because it is a ratio, not a measurement. Each property has a natural unit, determined by what the measurement means, and using that natural unit makes the design behave correctly when conditions change — when the font size changes, when the user zooms, when the page is viewed on a different device.

Percentages deserve a final note, because they appear in several contexts with different meanings. For font size, a percentage is relative to the parent's font size, like em. For line height, a percentage is relative to the element's own font size, but it is computed to a fixed value before inheritance, unlike a unitless line height, which is inherited as a multiplier — a subtle difference that causes real bugs. For width, a percentage is relative to the containing block's width, which is a layout meaning, covered in Chapter 8. The lesson is that a unit's meaning depends on the property it is used with, and the documentation for each property specifies what its units are relative to. When in doubt, check the property, not just the unit, because the same unit can mean different things in different places.

Check: Why are relative units often preferred over pixels for body text?

Section 48 — One font rule, many elements

A grouped selector can set the same font for several elements at once: h1, h2, h3 { font-family: Georgia, serif; }. This is the same grouping idea from Chapter 6, applied to typography, and it keeps heading styles consistent. Grouping, introduced in Chapter 6, Section 7, is the technique of listing several selectors, separated by commas, in a single rule, so that they share the same declarations. Applied to typography, it is the standard way to give a family of elements — headings, for instance — a shared font, without repeating the font stack in several rules that could then drift apart. A single grouped rule states the design decision once: "headings use this font", and every heading level follows it.

The consistency benefit is the main point. Headings form a family: they should look related, differing in size and weight to signal hierarchy, but sharing a typeface to signal that they belong together. If each heading level had its own font rule, a change to the heading font would require editing each rule, and missing one would produce a heading level in the old font — a subtle inconsistency that looks like a mistake. With a grouped rule, the font is stated once, and a change updates all levels together, while size and weight remain in separate, per-level rules, so that hierarchy and typeface are controlled independently. That separation — shared properties grouped, differing properties separate — is a clean way to organise typographic rules.

Grouping also applies to other typographic decisions. A rule like h1, h2, h3, h4 { line-height: 1.2; } gives all headings a tighter line height than body text, in one place. A rule like p, li, td { font-size: 1rem; } gives body-like elements a shared size. And a rule like input, button, select { font-family: inherit; } solves the form-control inheritance quirk from Section 40 for all control types at once. In each case, the group expresses a real shared decision, which is the test from Chapter 6, Section 44: group what shares a decision, not what merely shares a value by coincidence. Headings sharing a font is a shared decision; a heading and a button sharing a colour by accident is not.

There is a limit to grouping's usefulness in typography, which is that over-grouping can hide differences that matter. If headings and body text are grouped for font family, but should in fact use different families — a serif heading with a sans-serif body, as Section 37 discusses — then grouping them is wrong, and separate rules are correct. The group should reflect the design's actual families: if headings share a font, group them; if headings and body share a font, group them together; if they differ, do not. Grouping is a tool for expressing sameness, and it should only be used where sameness is the design intent. Used that way, it keeps typographic stylesheets short, consistent, and easy to change, which is exactly what a typographic system needs as it grows.

Check: How would you give all headings the same font in one rule?

Section 49 — When text styles conflict

Text styles follow the same cascade rules as everything else: a class rule beats an inherited body font, and a later equal rule beats an earlier one. If a heading ignores your font, check whether a more specific rule — or a browser default — is winning. This section is a reminder that typography does not have its own cascade; it uses the same one as all of CSS, from Chapter 6, and the same debugging approach applies. When a text style is not what you expected, the explanation is almost always one of the cascade's three steps — origin, specificity, or source order — or inheritance, which is the fallback when no rule targets the element. Knowing that, you can debug typography with the same checklist as any other style, rather than treating fonts as mysteriously different.

Headings are the most common case, because they have browser defaults that surprise beginners. A heading's font size and weight come from the user-agent stylesheet, which targets headings directly, so those defaults beat an inherited body font — not because they are more specific in a contest with your body rule, but because they target the heading directly while your body rule only reaches the heading by inheritance, and a direct rule always beats an inherited value, as Chapter 6, Section 33 explained. The fix is to target the heading directly yourself, with a rule at least as specific as the default needs — in practice, any author rule beats a user-agent rule on origin, so even a simple element rule like h2 { font-family: Georgia, serif; } is enough. The confusion arises only when the author expects inheritance to override a default, which it never does.

Form controls are the second common case, for the opposite reason: they do not inherit font properties by default in many browsers, so a body font rule does not reach them at all, and they display in the operating system's form font instead. The fix, as Section 40 noted, is to set their font explicitly, usually with font-family: inherit;, which forces inheritance where the default blocks it. That fix looks like a cascade solution, but it is really an inheritance solution: you are not outbidding a competing rule, you are enabling a mechanism that was disabled. Distinguishing those two cases — a competing rule versus a blocked inheritance — is the key to debugging text styles efficiently, because the fixes are different.

The general debugging workflow for text, then, is the same as for any style, from Chapter 6, Section 49: check that the stylesheet is loaded, check that the selector matches, check whether a more specific rule is winning (in developer tools, look for your rule crossed out), and check that the property and value are valid. For text specifically, add two checks: is a browser default targeting this element directly, and is this element a form control that does not inherit fonts? Those two checks cover the majority of "my font is not applying" reports, and they both have simple fixes — a direct rule for headings, an inherit rule for controls. Typography, like all of CSS, rewards systematic debugging over guessing, and the system you learned in Chapter 6 is the system that solves typographic problems too.

Check: A heading ignores your font-family rule. What is the most likely reason?

Section 50 — Chapter 7 in one paragraph

Colour in CSS is a value — named, hex, rgb or hsl, with alpha for transparency — and typography is the set of choices that make text readable: family, size, weight, line height, spacing and alignment. Used with restraint and good contrast, they make a page both attractive and easy to read. That summary, expanded, is the whole chapter: a colour system with four notations, each suited to a different task, and a typographic system with a handful of properties, each controlling one aspect of how text looks and reads. The colour notations are interchangeable in what they can express, but they differ in how easy they are to read, adjust, and share, which is why professionals use several of them, choosing the right one for each situation. The typographic properties are independent in what they control, but they interact in their effect on readability, which is why they must be judged together, at real sizes, on real screens.

The design principles that run through the chapter are as important as the properties themselves. Restraint in palette — a handful of colours, each with a job — produces pages that feel unified and are easy to maintain. Contrast as a non-negotiable constraint produces text that can actually be read, by everyone, in varied conditions. Hierarchy through size, weight and colour produces pages that can be scanned and navigated, not just read word by word. And testing on real devices, at zoom, and at arm's length produces typography that survives contact with real readers, rather than merely looking good in a design tool. Those principles are not specific to any one property; they are habits of mind that apply to every colour and type decision you will ever make, and they are what separate a page that looks designed from a page that merely has colours and fonts applied to it.

Looking forward, the next chapter builds on this one in a direct way. Chapter 8 covers the box model — how every element is a box with content, padding, border and margin — and typography lives inside those boxes: line height determines how tall a text box is, font size determines how much content fits, and spacing properties determine how text sits within its container. A typographic choice that ignores the box — a font size that overflows its container, a line height that makes a box taller than intended — creates layout problems, and a box choice that ignores typography — a narrow container that makes lines too short, a padding that crowds the text — creates readability problems. The two chapters together, colour and type plus boxes and spacing, give you the complete vocabulary for making a page look good and read well, which is the foundation for layout, the subject of the chapters that follow. For now, do the practical task below, which asks you to build an article page with a restrained palette and a clear hierarchy, and to test it the way real readers will experience it.

Check: In your own words, name the three colour formats and the four most important typography properties.

Practical task

Build a small article page with a heading, three paragraphs and a pull-quote. Style it with a colour palette of no more than four colours (use hex for at least two and hsl for at least one), a font stack with a generic fallback, body text at a readable size with a line-height of about 1.5, and a styled heading hierarchy. Then check it on a phone-sized browser window and in a zoomed view, and fix anything that becomes hard to read.

Chapter questions

  1. Explain the difference between rgb and hsl, and say when hsl is the more convenient choice.
  2. A designer gives you a colour as #336699. Write it in short hex and in rgb.
  3. Why is an alpha colour usually better than opacity for text sitting on a tinted background?
  4. Write the CSS to set a page's body font to a sans-serif stack, 18px, with a line-height of 1.6.
  5. A paragraph of light grey text on a white background is hard to read. What two changes would you make, and why?