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

Chapter 4 — Text, Links and Images

Section 1 — Paragraphs are the unit of text

Most of the body text on a web page lives in paragraph elements, and that is not an accident of habit — it reflects how writing actually works. A paragraph is one idea, or one step in an argument, expressed in a few sentences that belong together. When a reader meets a paragraph break, they understand, without thinking about it, that one thought has ended and another is beginning. That rhythm — idea, break, idea, break — is what makes long text readable, and it is why the paragraph is the basic unit of web writing, just as it is the basic unit of writing in books, articles, and letters. A page that respects paragraphs respects the reader's attention, because it gives that attention natural resting places, moments where the reader can pause, absorb what they have read, and decide to continue. A page that ignores paragraphs, cramming everything into one long block, denies the reader those resting places, and the result is not merely ugly — it is tiring to read, however good the words themselves may be.

Browsers help the paragraph do its job by adding space around it by default. That space is not a decoration; it is the visual equivalent of the pause between thoughts, and it is controlled by CSS, which means it can be adjusted to suit the design without changing the structure. The author's job is to decide where the breaks fall, and that decision is a writing decision, not a technical one. A new paragraph is warranted when the topic shifts, when the speaker changes, when the argument takes a new step, or when a paragraph has simply grown so long that it is hard to hold in mind at once. A paragraph of two sentences can be perfectly complete, and a paragraph of ten sentences can be justified if the idea genuinely needs that room — but a paragraph that contains three unrelated ideas is three paragraphs waiting to be separated, and separating them will improve the writing as well as the page. Good paragraphing is therefore a skill that belongs to writing as much as to coding, and learning HTML will make you a better writer in this specific way, because it forces you to decide, for every block of text, where one idea ends and the next begins.

There is a related point about what a paragraph is not. A paragraph is not a generic container for any block of content, and it should not be used to wrap headings, lists, images, or other paragraphs. Those elements have their own meanings, and wrapping them in a paragraph confuses the structure: a browser meeting a heading inside a paragraph will close the paragraph early, producing a structure different from what the author wrote. A paragraph contains text and inline elements — links, emphasis, code, and the other phrase-level marks covered in this chapter — and nothing else. That restriction is a strength, not a limitation, because it keeps the paragraph's meaning pure: a paragraph is a unit of text, and nothing else.

<p>Bread needs time more than it needs effort.</p>
<p>Give the dough a slow rise and it will reward you.</p>

Check: Why split a long block of text into several paragraphs?

Section 2 — Headings organise text, not just decorate it

Headings are often treated as a way to make text look important — bigger, bolder, more eye-catching — but their real job is organisational. A heading names a section of content, and a set of headings, read together, forms an outline of the whole page, a map that a reader can use to understand what the page covers and to find the part they need. That map works for sighted readers, who scan a page by its headings before deciding whether to read it in full, and it works even more powerfully for screen reader users, who can ask their software to list all the headings and jump directly to the one they want, without listening to everything in between. A page whose headings are chosen for their appearance rather than their meaning has no useful map: its outline is a random list of phrases at random levels, and a reader trying to navigate by it will be misled rather than helped. A page whose headings are chosen for their meaning has a map that works for everyone, regardless of how they read.

The levels of headings — h1 through h6 — express the hierarchy of that map. The h1 names the page as a whole, and there should normally be exactly one of them. The h2 elements name the page's major parts, the h3 elements name sub-parts within those parts, and so on down to h6, which is rarely needed outside very long, deeply structured documents. The levels must be used in order, without skipping, because a skipped level is a hole in the map: a reader jumping from an h1 to an h4 is left wondering what happened to the levels in between, and whether content is missing. That order is a structural commitment, and it must be kept separate from appearance. An h2 can be styled to look small, and an h4 can be styled to look large, and the outline remains correct either way, because CSS controls appearance and HTML controls meaning. Choosing a heading level because you like its size is one of the most common beginner mistakes, and it is also one of the most consequential, because it corrupts the map that many readers depend on.

Writing good headings is itself a skill, distinct from writing good paragraphs. A good heading is short, specific, and informative on its own, without the paragraphs beneath it. "Ingredients" is a good heading for a recipe section, because a reader scanning headings knows instantly what that section contains. "More information" is a poor heading, because it could describe any section on any page, and it tells the scanning reader nothing. "How to choose flour" is better than "Flour", because it promises a specific kind of content — guidance, not just a mention. The test is to read your headings alone, as a list, and ask whether a stranger could tell from that list alone what the page covers and in what order. If they can, your headings are doing their organisational job. If they cannot, the headings need rewriting, even if the paragraphs beneath them are excellent.

<h1>Bread Guide</h1>
<h2>Ingredients</h2>
<p>Flour, water, salt, yeast.</p>

Check: What should a reader learn from headings alone?

Section 3 — Strong means important

The strong element marks text as important, serious, or urgent. Browsers usually render it in bold, and that rendering is so familiar that many authors think of strong as simply "the bold tag". That thinking misses the point, and it leads to misuse. Bold is an appearance; importance is a meaning. When you mark text as strong, you are telling every reader — sighted, listening, or searching — that these words carry extra weight in the sentence, that they deserve more attention than the words around them. A screen reader may convey that weight by stressing the words, or by announcing them differently, so that a listener hears the emphasis a sighted reader sees. A search engine may treat strongly marked words as more significant to the page's topic. The bold rendering is just one way of expressing that meaning, for one kind of reader, and it is not the meaning itself. If you want text to look bold without claiming it is important, that is what the b element is for, and the distinction between strong and b is precisely the distinction between meaning and appearance.

The practical question is when text genuinely deserves strong. A warning deserves it: "Warning: the oven is very hot" is a sentence in which the warning itself is the most important part, and marking it strong tells the reader to attend to it first. A crucial instruction in a sequence deserves it: "Do not open the oven door during the first twenty minutes" contains a prohibition whose violation would ruin the result, and strong marks that prohibition as non-negotiable. A key term being defined for the first time may deserve it, because the reader needs to notice and remember that term. What does not deserve strong is everything the author happens to want to stand out: a product name, a keyword, a phrase that looks nice in bold. Overusing strong is worse than not using it at all, because it teaches the reader that strong means nothing — if every other sentence contains strongly marked words, then no word stands out, and the signal is lost in noise. Strong is a scarce resource. Spend it where the meaning genuinely demands it, and the reader will trust it when they meet it.

There is a useful test for strong, and it works for the other meaning elements in this chapter too: read the sentence aloud, and ask whether you would naturally stress those words when speaking. If you would — if your voice would rise or harden on them, because they carry the sentence's point — then strong is appropriate, because strong is the written equivalent of vocal stress for importance. If you would not stress them, and you merely want them to catch the eye, then you are looking for a visual effect, and you should achieve it with CSS or with the b element, not by claiming an importance the words do not have. That test connects the written element to the spoken experience, which is exactly the connection that matters for accessibility: a screen reader user hears the stress you marked, and they deserve stress that is honest.

<p><strong>Warning:</strong> the oven is very hot.</p>

Check: What does strong mean, beyond looking bold?

Section 4 — Em means emphasis

The em element marks emphasis: the word or phrase that a speaker would stress when reading a sentence aloud, in order to change or sharpen its meaning. That sounds similar to strong, and beginners often confuse the two, but the distinction is real and worth learning precisely. Strong marks importance — the words matter more, they are serious or urgent. Em marks emphasis — the words are stressed, and the stress changes what the sentence means. Consider the sentence "I said tomorrow, not today." With emphasis on "tomorrow", the sentence corrects a misunderstanding about when something will happen. With emphasis on "I", it corrects a misunderstanding about who said it. The words are identical; the meaning changes entirely depending on which word is stressed. Em is how you write that stress into HTML, so that a reader — including a screen reader user, whose software may convey the emphasis vocally — experiences the same shift of meaning that a listener would hear from a speaker.

Browsers usually render em in italics, and as with strong, that rendering is a familiar appearance that tempts authors to treat em as "the italic tag". That is the same mistake, in the opposite direction. Italic is an appearance; emphasis is a meaning, and the two coincide only sometimes. A foreign phrase is often rendered italic, but it is not emphasised — it is simply in another language, and the i element, covered later in this chapter, is the honest mark for it. A book title is often rendered italic, but it is a title, not an emphasis, and the cite element is the honest mark for it. Using em for those purposes claims an emphasis that does not exist, and a screen reader user will hear stress where the author intended none, which changes the sentence's meaning in exactly the way the author did not want. The rule is the same as for strong: choose the element by what the words mean, not by how you want them to look, and let CSS handle the appearance if the default rendering does not suit your design.

Emphasis can also be nested, and nesting changes the degree of emphasis, which is a subtlety worth knowing. An em inside another em represents a higher level of stress — a word emphasised within an already emphasised phrase — and screen readers and browsers may convey that escalation. In practice, nested emphasis is rare in ordinary writing, and it should be used only when the sentence genuinely requires it, because over-emphasis, like over-importance, dilutes the signal. The broader lesson of em is about the relationship between writing and speaking. Written language evolved from spoken language, and it carries many of speech's devices — stress, pause, tone — in typographic form. HTML's phrase elements are an attempt to make those devices explicit and machine-readable, so that software can convey them to readers who do not see the typography. Em is one of the clearest examples of that attempt, and using it well means thinking, for each sentence, not just about what the words say, but about how they would sound if spoken — and marking the stress that makes the meaning clear.

<p>I said <em>tomorrow</em>, not today.</p>

Check: How can moving emphasis change a sentence's meaning?

Section 5 — Bold without importance

The b element marks text that is stylistically bold but not more important than the surrounding text. That definition sounds like a contradiction until you meet the cases it was designed for: a product name in a paragraph, a keyword in a summary, the first words of an article, or a name in a list of credits. In each of those cases, the text should stand out visually — the reader's eye should catch it as they scan — but it does not carry extra importance in the sentence's meaning. A product name is not more important than the sentence that contains it; it is simply a name, and drawing the eye to it helps the reader navigate, without claiming that the name is the sentence's point. The b element expresses exactly that: "make this stand out, but do not change its meaning". Browsers render it bold by default, which is the visual effect the author wants, but the element's meaning is deliberately neutral — it is a typographic instruction, not a semantic claim.

The distinction between b and strong is therefore a test of the author's honesty about their own intent. When you reach for bold, ask yourself why. If the answer is "because these words are important, serious, or urgent, and the reader must attend to them first", then strong is the honest element, and its bold rendering is a bonus. If the answer is "because I want these words to catch the eye as the reader scans, but they are not more important than the rest", then b is the honest element, and its bold rendering is the whole point. That question is worth asking every time, because the two elements look identical on screen, and the difference is invisible to a sighted reader — which is exactly why it matters. The difference is visible, or rather audible, to a screen reader user, who may hear strong words stressed and b words unstressed, and who deserves to hear the stress only where the author genuinely meant it. A document that uses b and strong interchangeably is a document whose emphasis is unreliable, and unreliable emphasis is worse than none, because it teaches the reader to ignore it.

There is a practical consequence for styling, too. Because b is a typographic element, it is entirely appropriate to style it with CSS, to change its weight, its colour, or its size, without changing its meaning — because it has no meaning beyond "stand out". Strong, by contrast, should be styled with care, because its styling should reflect its importance, not contradict it. In practice, many designs style b and strong identically, in bold, and that is fine, because their difference is semantic, not visual. The difference lives in the document's meaning, in the accessibility tree, and in the author's intent, and it is preserved even when the rendering is the same. That is a recurring pattern in HTML: elements that look alike but mean different things, and the author's job is to choose by meaning.

<p>The <b>Kelick Loaf</b> is our most popular bread.</p>

Check: When should you use b instead of strong?

Section 6 — Italic without emphasis

The i element marks text in an alternate voice or mood: a foreign phrase, a technical term being introduced, a ship's name, a thought, or a phrase that is set apart from the surrounding text for some reason other than emphasis. Browsers usually render it in italics, and as with b, that rendering tempts authors to treat i as "the italic tag". The same warning applies: italic is an appearance, and i is a meaning — specifically, the meaning "this text is in a different voice". A French phrase in an English sentence is in a different voice, literally: it belongs to another language, with another set of sounds and conventions, and marking it with i tells the reader — and the software — that a shift has occurred. A technical term like levain, borrowed from French baking, is in a different voice in a subtler sense: it is a specialist's word, set apart from ordinary vocabulary, and i marks that apartness without claiming that the word is emphasised or important.

The distinction between i and em is the mirror image of the distinction between b and strong, and it is worth learning the two pairs together. Strong and em both add meaning — importance and emphasis respectively — and both are conveyed to screen reader users in ways that affect how the text sounds. B and i add no such meaning; they are typographic, marking text that should stand out or sit apart visually, without changing how it should be understood or heard. That makes b and i safe choices when the only goal is appearance, and dangerous choices when the goal is meaning, because they will not convey that meaning to anyone who cannot see the styling. Conversely, strong and em are dangerous choices when the only goal is appearance, because they will convey a meaning the author did not intend to readers who rely on it. The four elements form a tidy square — bold with and without importance, italic with and without emphasis — and a developer who can place any piece of text correctly in that square has understood one of HTML's core ideas: that appearance and meaning are separate, and that HTML's job is meaning.

In practice, i is used less often than em, because most italic-looking text in ordinary writing is either emphasis, which belongs to em, or a title, which belongs to cite, or a foreign phrase or technical term, which belongs to i. That rarity is fine. An element does not need to be used constantly to be valuable; it needs to be available when the case arises, and used correctly when it does. When you introduce a technical term, or quote a phrase in another language, or name a ship, reach for i, and consider also adding a lang attribute to a foreign phrase, so that a screen reader can switch pronunciation as well as voice. The combination — i for the alternate voice, lang for the language — is a small example of HTML elements working together to convey a rich meaning, and it is the kind of detail that distinguishes a document written with care from one written in a hurry.

<p>The word <i>levain</i> comes from French baking.</p>

Check: Give an example of text that suits i rather than em.

Section 7 — Small print

The small element marks side comments and small print: copyright notices, legal disclaimers, attribution lines, and other content that is secondary to the main point of the page. Browsers usually render it in a smaller font, and that rendering is so familiar that small is often treated as a way to make text smaller. As with the other phrase elements, that treatment confuses appearance with meaning. Small does not mean "render this smaller" — CSS does that. Small means "this is a side comment", content that is part of the document but not part of its main argument, and that the reader can treat as secondary. A copyright notice at the bottom of a page is small print in exactly that sense: it is legally necessary, it must be present, but it is not what the visitor came to read, and marking it as small tells every reader, sighted or listening, to treat it accordingly. A disclaimer that a price includes tax is small print in the same sense: it qualifies the main content without being part of it.

The distinction matters most when small is misused to shrink important text. A common abuse, particularly on commercial pages, is to put essential terms — a price, a condition, a limitation — in small text, on the grounds that it is "details" or "fine print", when in fact the reader needs that information to make a decision. Marking essential information as small does not make it less essential; it makes it harder to find, which is a failure of honesty as well as of design. If information matters to the reader's decision, it should be presented at a readable size, in the main flow of the content, regardless of whether it is legally convenient to hide it. Small is for content that is genuinely secondary — a copyright line, a credit, a note about a source — not for content that the author wishes the reader would overlook. That ethical dimension is not an extra; it is part of what the element means. To mark something as small is to say "you may skip this", and saying that about something the reader should not skip is misleading.

There is also a practical accessibility point. Small text, rendered smaller, is harder to read for everyone, and particularly hard for readers with low vision. The default rendering of small is only slightly smaller than body text, which is usually acceptable for genuine side comments, but authors who compound small with CSS that shrinks it further can quickly produce text that is illegible. The guideline is simple: use small for side comments, keep its rendering close to body size, and never use it, alone or with CSS, to make essential text hard to read. A document that respects its readers does not hide its important content in small print, and does not shrink its side comments into illegibility either. Small is a modest element, for modest content, and used modestly, it helps a reader distinguish the main argument from its qualifications — which is a service to the reader, not a trick played on them.

<p><small>Prices include tax where applicable.</small></p>

Check: What kind of content belongs in a small element?

Section 8 — Highlighting with mark

The mark element highlights text as relevant to the reader's current purpose. The classic example is a search result: when a user searches for a word, and the result shows a passage containing that word, the word is marked, so the reader can see at a glance why this result matched their search. The highlight does not mean the word is important in itself, or emphasised by the author — it means it is relevant to this reader, at this moment, for this purpose. Another example is a quoted passage in which a particular phrase is the reason for the quotation: marking that phrase tells the reader "this is the part that matters for the discussion at hand". In both cases, the relevance is contextual and temporary. The same passage, shown to a different reader or for a different purpose, would highlight different words, or none at all. Mark is therefore different from strong and em in an important way: strong and em describe properties of the text itself, while mark describes a relationship between the text and the reader's task.

Browsers usually render mark with a yellow background, like a highlighter pen on paper, and that rendering reinforces the metaphor: highlighting is something a reader does to a text, to mark what matters to them, and mark is HTML's way of representing that act. But the metaphor also warns against misuse. Highlighting everything is the same as highlighting nothing, and a page full of marked text teaches the reader to ignore the highlights. Mark should be used sparingly, for the words that genuinely explain a match, a reference, or a point — and, crucially, it should not be used merely to add colour. If you want coloured text for decoration, CSS is the tool, and using mark for decoration claims a relevance that does not exist, which misleads readers who rely on the element's meaning, including screen reader users, who may be told that marked text is highlighted for a reason when it is not.

There is a subtlety in how mark interacts with quotation. When you quote a passage and mark a phrase within it, you are adding your own emphasis to someone else's words, and that addition should be honest: the reader should be able to tell that the highlight is yours, not the original author's. In print, that is often done with a note like "emphasis added". On the web, the mark element itself carries that implication — it is understood to be the quoter's highlight, not the author's — but for longer or more sensitive quotations, an explicit note is still good practice. The broader lesson is that mark is a reader-oriented element: it exists to serve the person reading, by pointing at what is relevant to them, not to serve the author's desire for decoration. Used in that spirit, for search terms, for key phrases in quotations, and for other genuine relevance, it is a small, precise tool that makes scanning faster and understanding easier. Used for colour, it is a broken promise to the reader.

<p>Your search for <mark>rye</mark> found two recipes.</p>

Check: What is mark for, if not decoration?

Section 9 — Subscripts and superscripts

The sub element lowers text below the baseline, and the sup element raises it above the baseline, and both do so for reasons of meaning, not merely of appearance. A subscript is part of a chemical formula, a mathematical notation, or a footnote reference in which the lowered position is itself significant: in H₂O, the 2 is a subscript because it says "two atoms of hydrogen", and writing it at normal size and position would change the formula's meaning, or at least make it ambiguous. A superscript is part of a power, an ordinal, or a reference in which the raised position is significant: in 2³, the 3 is a superscript because it says "two raised to the power of three", and in "1st", the "st" is a superscript by typographic convention for ordinals. In both cases, the position is the meaning, and an element that merely made the text smaller, without lowering or raising it, would fail to convey that meaning. That is why sub and sup exist as semantic elements, rather than as styling shortcuts.

The practical use of sub and sup is straightforward, but it is worth noting what they are not for. They are not a general way to make text smaller or to position text decoratively. A price with a small cents figure, a footnote marker that is really just a link, or a decorative raised letter — these may look like subscripts or superscripts, but if the position does not carry meaning, then using sub or sup claims a meaning that does not exist, and CSS positioning is the honest tool. Conversely, when the position does carry meaning, CSS positioning alone is not enough, because it conveys the appearance without the meaning: a screen reader that encounters a CSS-raised "3" will read it as an ordinary "3", while a screen reader that encounters a sup element can convey that it is a superscript, preserving the mathematical meaning for a listener. That preservation is the point of semantic markup generally, and sub and sup are a compact example of it: two elements whose entire job is to preserve a distinction that sighted readers take for granted, and that listeners would otherwise lose.

There is also a typographic detail worth knowing. Subscripts and superscripts are rendered smaller than surrounding text by default, and they do not normally affect the line height — the line does not grow taller to accommodate them, which can cause them to overlap neighbouring lines in tight leading. That is a rendering detail, and it is usually handled well by browsers, but it is worth checking in your own designs, particularly for formulas with both subscripts and superscripts, or for text with many references. The content, however, is what matters most: write the formula correctly, with sub and sup marking the parts that belong below and above the baseline, and let the browser handle the rendering. A formula written with correct subscripts and superscripts is a formula that can be read, searched, and spoken correctly, by every reader, in every medium.

<p>Water is H<sub>2</sub>O, and 2<sup>3</sup> is 8.</p>

Check: Write the HTML for "H₂O" using the correct element.

Section 10 — Abbreviations

The abbr element marks an abbreviation or acronym, and its title attribute can give the full, expanded form. That combination is one of the few places where the title attribute earns its keep, because the expanded form is genuinely useful information about the abbreviation itself, and it is the kind of supplementary information that a tooltip can appropriately carry — though, as with all title attributes, the expansion should also be available in visible text on first use, for readers who cannot see tooltips. The pattern for introducing an abbreviation is to write it out in full the first time it appears, with the abbreviation in parentheses, or to mark the abbreviation with abbr and give its expansion in the title, or both. After that first introduction, the abbreviation can be used alone, because the reader now knows what it stands for. That pattern serves both new readers, who need the expansion, and returning readers, who do not want to be told it again on every page.

The abbr element's value goes beyond the tooltip. It tells software that a particular string of letters is an abbreviation, not an ordinary word, which matters for pronunciation, for searching, and for translation. A screen reader that encounters "HTML" marked as an abbreviation may spell it out letter by letter, rather than trying to pronounce it as a word, which is the correct treatment for an initialism. A translation tool that encounters an abbreviation can handle it differently from ordinary text, preserving it or expanding it according to its rules. And a reader who is unfamiliar with an abbreviation can look for its expansion, knowing that the document itself provides it, rather than having to search elsewhere. None of those benefits require the reader to see the tooltip; they come from the element's meaning, which is present in the document whether or not it is rendered visibly.

There are a few practical guidelines for abbreviations. Use abbr for genuine abbreviations — initialisms like HTML and CSS, shortened forms like "etc.", and acronyms that are pronounced as words, like "laser" — but do not mark every short word as an abbreviation, and do not mark an abbreviation that is better known than its expansion. Most readers know "HTML" better than "HyperText Markup Language", and marking it on every occurrence adds noise without adding understanding. Mark an abbreviation on its first use in a document, or where it might genuinely confuse, and leave subsequent uses unmarked. And choose the expansion carefully: it should be the full form the abbreviation stands for, not a definition or an explanation. "HyperText Markup Language" is the expansion of HTML; "the language used to structure web pages" is a definition, and it belongs in the surrounding text, not in a title attribute. Abbr is a small courtesy to the reader — a way of saying "here is what this shorthand means, in case you need it" — and like all courtesies, it works best when offered at the right moment and not repeated to excess.

<p><abbr title="HyperText Markup Language">HTML</abbr> structures a page.</p>

Check: What does the title attribute on abbr provide?

Section 11 — Block quotations

A blockquote element marks a longer quotation taken from another source, set apart from the surrounding text as its own block. The distinction from the inline q element, covered next, is length and presentation: a blockquote is a quotation that deserves its own space, typically a paragraph or more, while a q is a quotation inside a sentence. Browsers usually render a blockquote indented, which visually separates it from the author's own words, and that separation is the point — a reader should be able to tell at a glance where the author's voice ends and the quoted voice begins. That clarity matters ethically as well as typographically. Quoting without clear attribution, or in a way that blurs the boundary between your words and someone else's, is misleading, even when it is unintentional. Blockquote draws that boundary explicitly, in the document's structure, so that every reader — sighted, listening, or searching — can see where the quotation starts and ends.

The cite attribute on blockquote can record the source of the quotation, as a web address. Browsers do not display that address, and readers will not see it unless they view the source, which makes cite a machine-readable annotation rather than a visible credit. That limitation means cite is not a substitute for a visible attribution. If you quote someone, you should also say, in visible text, who said it and where, either in the paragraph introducing the quotation or in a citation beneath it. The cite attribute is an extra, for tools that harvest quotations and want to know their source programmatically, and it is worth adding when the source is a web address you know — but it is not the attribution itself. A quotation with only a cite attribute and no visible credit is a quotation that looks unattributed to every human reader, and that is a failure, however correct the markup may be.

Quoting well is a writing skill as much as a markup skill, and the blockquote element supports it in a specific way: by encouraging you to quote at block length only when the quotation deserves it. A short phrase quoted inside a sentence belongs in q, not in blockquote, because promoting it to a block gives it a weight it does not deserve and interrupts the flow of your argument. A long passage quoted in full, when only a sentence of it matters, burdens the reader with irrelevant words and dilutes the point. The discipline is to quote what you need, no more, and to mark it clearly as a quotation, with a visible attribution, and with a cite attribute where the source is known. Done that way, a blockquote is a sign of intellectual honesty: it says "these are not my words, they are someone else's, and here is where they came from". That honesty is the reason the element exists, and it is more important than its indentation, which CSS can change at will.

<blockquote cite="https://example.com/bread">
  <p>Good bread is a matter of patience.</p>
</blockquote>

Check: What is the cite attribute on blockquote for?

Section 12 — Short inline quotations

The q element marks a short quotation inside a sentence — a phrase or a sentence quoted from another source, without breaking the flow of your own text. Its most distinctive behaviour is that browsers add quotation marks around it automatically, according to the conventions of the page's language. In English, that means double quotes; in a French passage, it means guillemets; in a nested quotation, it means single quotes inside double quotes. That automatic behaviour is the reason you must not type your own quotation marks inside a q element: if you do, the reader will see two sets of marks — yours and the browser's — which looks like an error and reads as one. The q element exists precisely so that you do not have to manage quotation marks yourself, and so that the marks adapt to the language and nesting of the quotation without your intervention. Trust it to do that job, and write only the quoted words themselves.

The q element, like blockquote, can carry a cite attribute recording its source as a web address, and the same limitation applies: browsers do not display it, so it is not a visible attribution, and a quotation that needs credit should also receive it in visible text. For short quotations, that visible credit is often in the surrounding sentence — "As the baker said, ..." — which introduces the speaker before the quotation begins, and that introduction is usually sufficient. The q element's job is narrower than blockquote's: it marks the exact words that are quoted, within the flow of a sentence, so that a reader can tell which words are yours and which are someone else's, even in the middle of a sentence where there is no paragraph break to signal the change. That precision matters, because misattribution inside a sentence is easy: a reader who cannot tell where your paraphrase ends and the quotation begins may credit you with someone else's words, or credit someone else with yours.

There is a practical question about when to use q and when to simply type quotation marks yourself. For a single, isolated quotation in a document, typing the marks is not wrong, and many authors do it out of habit. But using q has advantages that accumulate across a document: the marks adapt to language, nested quotations are handled correctly, and the quotation is marked in the structure, so that tools — search engines, reading modes, translation software — can recognise it as a quotation rather than as ordinary text with punctuation in it. Those advantages are worth the small effort of using the element, particularly in documents that quote frequently, or that will be translated, or that will be read by software that cares about structure. Q is a small element, but it embodies a large idea: that punctuation is not just decoration, but meaning, and that meaning is better expressed in markup than in typed characters, because markup can adapt and be understood, while typed characters can only be displayed.

<p>As the baker said, <q>patience is the ingredient</q>.</p>

Check: Why should you not type your own quotation marks inside a q element?

Section 13 — Citing a work

The cite element marks the title of a creative work: a book, a film, a song, an article, a painting, a play, or any other work that has a title and an author. It does not mark the name of the person who made the work, and that distinction is the most common source of confusion about cite. A sentence like "She quoted The Bread Baker's Apprentice from memory" contains a work title — the book — and an author, who is not named in that sentence at all. Marking the book's title with cite tells software that this phrase is a title, which matters for styling, for searching, and for cataloguing. Marking an author's name with cite would tell software that a person's name is a work title, which is false, and which would mislead any tool that tries to build a list of works mentioned on the page. The element's name is a trap: "cite" sounds like it should mark a citation, in the sense of a person cited, but in HTML it marks a work cited, in the sense of a title referenced.

Browsers usually render cite in italics, which is the typographic convention for titles in English, and that rendering reinforces the confusion, because italics are also used for emphasis and for foreign phrases. But the rendering is not the meaning, and the meaning is the title. That meaning has practical consequences. A reading tool that encounters a cite element can offer to look up the work, or can list all the works mentioned on a page, or can style titles consistently even if the author's CSS changes. A search engine that encounters a cite element can understand that the page is discussing a particular work, which is different from merely mentioning a phrase that happens to match a title. And a human reader of the source can tell, without ambiguity, which phrases are titles and which are ordinary words — a distinction that matters in writing about books, films, and music, where titles are often made of ordinary words and would otherwise be indistinguishable from the surrounding text.

The practical guideline is simple: when you mention a work by its title, mark the title with cite, and leave the author's name unmarked, or mark it with no element at all, unless another element genuinely applies. Do not mark a website's name as a cite unless you are referring to it as a work — a site's name in a sentence like "I read it on Example News" is arguably a work, and reasonable authors disagree about that case, but a company's name, a person's name, and a product's name are not works, and should not be marked as cite. The element is narrow by design, and its narrowness is its strength: it says one precise thing, "this is the title of a work", and it says it reliably, because it is not used for anything else.

<p>She quoted <cite>The Bread Baker's Apprentice</cite> from memory.</p>

Check: Does cite mark a work's title or its author's name?

Section 14 — Code in text

The code element marks a fragment of computer code inside ordinary text: a tag name, an element name, a command, a function name, a variable, or a short snippet that the reader should recognise as code rather than prose. Browsers usually render it in a monospace font, where every character takes the same width, which is the typographic convention for code, and which helps the reader spot at a glance that these words are not ordinary English. That visual distinction is useful, but as with the other phrase elements, it is not the meaning. The meaning is "this is code", and that meaning matters for tools: a search engine can treat code fragments differently from prose, a translation tool can leave code untranslated, and a screen reader can announce code in a way that distinguishes it from surrounding text, or can spell it out, rather than trying to pronounce it as a word. A tag name like "p" is a good example: as prose, it is a letter; as code, it is an element name, and pronouncing it as the letter "pee" would be misleading, while announcing it as code gives the listener the right context.

The code element is for fragments within text, not for whole blocks of code. A multi-line program, a configuration file, or a long snippet belongs in a pre element, often combined with code inside it — pre preserves the line breaks and spacing, and code marks the content as code, so the combination gives you both the layout and the meaning. That combination, <pre><code>...</code></pre>, is the standard pattern for code blocks on the web, and you will see it everywhere, from documentation to tutorials to this book's examples. For a fragment inside a sentence, code alone is sufficient: "Use the p element for paragraphs" becomes, in markup, "Use the code element containing p for paragraphs", and the reader sees the element name in monospace, set apart from the prose, without the sentence breaking. The distinction between fragment and block is worth maintaining, because a block of code inside a paragraph would break the paragraph's flow, and a fragment marked as a block would lose its place in the sentence.

There is a detail about code content that beginners often miss: code fragments frequently contain characters that are special in HTML, particularly angle brackets and ampersands, and those characters must be escaped as entities, as covered in the previous chapter. Writing <p> inside a code element without escaping it would be read by the browser as an actual paragraph tag, not as the text of a tag name, and the fragment would vanish from the display, replaced by a real paragraph element. The correct form is to write the entities — &lt;p&gt; — inside the code element, so that the browser displays the characters rather than interpreting them. That escaping is not a quirk of the code element; it applies to any HTML text that contains special characters, but it is most often encountered with code, because code is full of angle brackets.

<p>Use the <code>&lt;p&gt;</code> element for paragraphs.</p>

Check: Why is code rendered in a monospace font?

Section 15 — Preformatted text

The pre element preserves spaces and line breaks exactly as written in the source, unlike ordinary HTML, which collapses runs of whitespace into a single space. That preservation makes pre the right home for text where the layout itself is part of the content: code samples, where indentation shows structure; poetry, where line breaks are part of the poem; ASCII diagrams, where spaces position characters relative to each other; and any other text whose meaning would change if its spacing were normalised. A poem with its line breaks collapsed into a paragraph is not the same poem, because the breaks are where the poet placed pauses and emphasis. A code sample with its indentation collapsed is not the same code, because the indentation shows which blocks belong inside which. Pre respects that the author's spacing is content, not formatting, and it renders it faithfully, in a monospace font by default, so that the spacing aligns predictably.

That faithfulness comes with a responsibility. Because pre preserves whitespace, it also preserves the author's mistakes: extra spaces, inconsistent indentation, and trailing blank lines all appear on the page, exactly as written, where ordinary HTML would have hidden them. A pre block must therefore be written carefully, with the indentation you actually want displayed, not the indentation that happens to suit the surrounding source code. A common mistake is to indent a pre block to match the surrounding HTML, and then discover that the displayed code is indented by that same amount, because the browser preserved it. The remedy is to write pre content starting at the left margin of the source, or to accept the indentation as part of the display, and to check the rendered result rather than assuming it matches the source's appearance. Pre is one of the few elements where the source and the display are directly coupled, and that coupling requires care.

Pre is also not a general-purpose layout tool. It should not be used to create columns, to align text, or to force spacing, because those are CSS jobs, and using pre for them produces content that is fragile — it breaks when the font changes, when the screen narrows, or when the text is read by a screen reader, which may announce the preserved spaces in a confusing way. Pre is for content whose layout is meaningful, and for that content, it is irreplaceable: no CSS technique reproduces the exact preservation of author spacing as simply or as reliably. Combined with code, as described in the previous section, it becomes the standard way to present code on the web, and that combination is worth learning as a unit. Pre says "preserve this layout". Code says "this is code". Together, they say "here is a code sample, exactly as written", which is a promise to the reader that the sample can be copied and used as shown — a promise that matters, because a code sample that cannot be trusted is worse than no sample at all.

<pre>
Line one
  Line two, indented
</pre>

Check: What does pre preserve that ordinary paragraphs do not?

Section 16 — Keyboard input

The kbd element marks text that a user should type or press, such as a key name, a keyboard shortcut, or a sequence of keys. Its purpose is to make instructions clearer by distinguishing, visually and structurally, the actions the reader must take from the explanation surrounding them. An instruction like "Press Ctrl + S to save" contains two kinds of content: the action, which is pressing two keys, and the purpose, which is saving. Without markup, both are plain text, and the reader must parse the sentence to find the action. With kbd marking the keys, the action stands out — browsers usually render kbd in a monospace font, often with a border that suggests a physical key — and the reader can scan the instruction and find what to press without reading every word. That scannability matters in instructions, which are often read under pressure, by users who want to act quickly and correctly.

The kbd element also carries meaning for tools. A screen reader that encounters kbd can announce it as keyboard input, distinguishing it from ordinary text, so that a listener hears "press Control plus S" as an instruction, not as a phrase in a sentence. A documentation tool that encounters kbd can index shortcuts, or can style them consistently across a whole site, without relying on the author remembering to format each one the same way. And a translation tool can leave key names untranslated, or translate them according to its rules for keyboard input, rather than treating them as ordinary words. Those benefits depend on the element being used for genuine keyboard input, not for any text that happens to look technical. A command typed at a prompt is not keyboard input in the kbd sense — it is input to a program, and the appropriate element depends on context — and a key name mentioned in passing, without an instruction to press it, is arguably not kbd either, though reasonable authors differ on that edge case. The core case is clear: when you tell a reader to press a key, mark the key with kbd.

There is a pattern for complex shortcuts that is worth learning. A shortcut involving multiple keys pressed together, like Ctrl + S, is marked with each key in its own kbd element, joined by a plus sign in ordinary text. A shortcut involving keys pressed in sequence, like pressing G and then I in some applications, is sometimes marked with nested kbd elements, where the outer kbd represents the sequence and the inner ones represent the individual keys — though that nesting is less widely supported, and the simpler form, with separate kbd elements joined by words like "then", is often clearer. Whichever form you choose, the principle is the same: mark the keys, not the surrounding words, and let the reader's eye — and their screen reader — find the action instantly. Instructions are a genre where clarity is kindness, and kbd is one of the small tools that makes instructions clearer without adding a single word to them.

<p>Press <kbd>Ctrl</kbd> + <kbd>S</kbd> to save.</p>

Check: What does kbd mark in an instruction?

Section 17 — Sample output and variables

Two small elements cover two specific kinds of technical text that are often confused with each other, and with ordinary code. The samp element marks sample output from a program: the text a program prints, displays, or returns, as opposed to the code that produces it. The var element marks a variable: a name that stands for a value, or a placeholder in a formula or expression, as opposed to a literal value itself. The distinction is worth learning precisely, because it mirrors a distinction in programming itself. Code is what you write. Output is what the program produces when it runs. A variable is a name that holds a value that may change. Those three things — code, output, and variable — are different kinds of technical text, and HTML provides a different element for each: code, samp, and var. Using the right one tells the reader, and the software, which kind of text they are looking at, without any extra explanation.

In practice, samp appears in documentation and tutorials, where an author shows what a program does: "The program printed Hello, world!" becomes, in markup, a sentence in which "Hello, world!" is marked as samp, because it is the program's output, not the author's words and not the program's code. Var appears in explanations of formulas, algorithms, and code: "stored it in message" becomes a sentence in which "message" is marked as var, because it is the name of a variable, a placeholder for whatever value the program happens to hold at that moment. Browsers render var in italics by default, and samp in a monospace font, like code, but again, the rendering is not the meaning. The meaning is the kind of technical text, and that meaning helps tools treat the text appropriately: a translation tool should not translate a variable name, a search tool can distinguish output from code, and a screen reader can convey the distinction to a listener who cannot see the font change.

The broader lesson of samp and var is that HTML's vocabulary is finer than beginners expect. There is an element for almost every kind of text a technical document contains, and learning to use the fine vocabulary, rather than marking everything as code or as plain text, produces documents that are more precise and more useful. You do not need to use samp and var on every page — many pages contain no sample output and no variables, and forcing them in would be artificial — but when you write documentation, tutorials, or explanations of programs, they are available, and using them correctly signals that you understand the material at a level deeper than its appearance. Precision in markup is precision in thinking, and the habit of asking "what kind of text is this, exactly?" will serve you in every kind of writing, technical or not, because it forces you to understand your own content before you present it to others.

<p>The program printed <samp>Hello, world!</samp> and stored it in <var>message</var>.</p>

Check: What is the difference between samp and var?

Section 18 — Line breaks, used rarely

The br element forces a line break inside a paragraph, ending the current line and starting a new one, without starting a new paragraph. Its legitimate uses are narrow, and they share a common feature: the line divisions are part of the content itself, not merely a way to make the page look a certain way. An address is the classic example: the lines of an address — street, city, country — are divisions that belong to the address as a piece of information, and breaking them onto separate lines is how addresses are conventionally written. A poem is another: its line breaks are part of the poem's structure, and a poem rendered as a single paragraph would be a different poem. A signature block, a verse, or any text where the author has deliberately chosen where lines end — these are br's territory, because the breaks carry meaning that would be lost if the text reflowed as a paragraph.

The misuse of br is equally clear, and it is one of the most common beginner errors: using br to create space between blocks of content. A beginner who wants a gap between two paragraphs may add two br elements at the end of the first paragraph, instead of using separate paragraphs and letting CSS control the spacing. That misuse fails in several ways. It is fragile, because the gap it creates is fixed in lines, not in the flexible spacing CSS provides, and it does not adapt to different font sizes or screen widths. It is semantically wrong, because a line break inside a paragraph is not a paragraph break, and a screen reader will not treat it as one — it will read the two blocks as a single paragraph with a pause, rather than as two separate thoughts. And it is hard to maintain, because changing the spacing requires editing the HTML in multiple places, rather than changing one CSS rule. The rule is simple: if you want a new paragraph, write a new paragraph. If you want space, use CSS. Reserve br for the cases where the break itself is content.

There is a related element, wbr, covered in the next section, which suggests a place where a line may break if needed, as opposed to br, which forces a break whether needed or not. The contrast is instructive: br is imperative, wbr is permissive, and most text needs neither, because browsers break lines automatically at word boundaries, which is the correct behaviour for ordinary prose. That automatic breaking is one of the web's great strengths: text reflows to fit any screen, from a phone to a television, without the author having to decide where lines end. Br overrides that strength, and it should be used only where the author knows better than the browser — which, for addresses and poems, they do, and for ordinary paragraphs, they do not. Use br rarely, use it deliberately, and use it only where the line break is part of what the text means.

<p>12 Baker Street<br>Lagos, Nigeria</p>

Check: When is br the right tool, and when is it not?

Section 19 — Word breaks for long words

The wbr element suggests a place where a long word may break if it needs to, without forcing a break. It is the permissive counterpart to br's imperative: br says "break here, always", while wbr says "you may break here, if you must". That distinction matters for long, unbreakable strings — web addresses, file paths, long identifiers, and compound words — which browsers normally treat as a single unit that cannot be broken. A long address in a narrow column can overflow its container, pushing layout apart or disappearing off the edge of the screen, simply because the browser has no safe place to break it. Inserting wbr elements at sensible points inside the address — after slashes, after dots, between meaningful parts — gives the browser those safe places, and it will use them only when the line actually needs to break. When there is room, the wbr does nothing at all, and the address displays whole, as it should.

The key to using wbr well is choosing break points that do not mislead the reader. A break after a slash in an address is safe, because the reader understands that the address continues on the next line, and the slash signals the division. A break in the middle of a word, at an arbitrary point, is riskier, because the reader may not realise the word continues, or may misread the two parts as separate words. The safest break points are at natural boundaries: after a slash, after a hyphen, after a dot in a domain name, or between the parts of a compound identifier. In many cases, CSS offers an alternative — properties that allow breaking anywhere, or that break long words automatically — and for general text, CSS is often the better tool, because it applies uniformly without requiring the author to mark every possible break point by hand. Wbr earns its place when the author knows the specific string and its natural break points, and wants to offer those points without forcing them, which is a level of control CSS cannot provide for a single string.

Wbr is a small, rarely needed element, and that rarity is a sign of its proper place. Most text does not need it, because browsers break ordinary words at spaces, which is the correct behaviour for prose. Long, unbreakable strings are the exception, and they usually appear in technical writing, in addresses, and in identifiers — contexts where the author is already thinking carefully about how the string will display. In those contexts, wbr is a precise, low-cost solution: it adds nothing visible when it is not needed, and it prevents overflow when it is. It is also a good example of HTML's philosophy of suggestion over command. The web is read on screens of unpredictable widths, and an author cannot know in advance whether a line will need to break. Elements like wbr acknowledge that unpredictability, and give the browser permission to adapt, rather than demanding a fixed layout that may not fit. That adaptability is the web's strength, and wbr is a small part of it, used rarely, but used well when it is used.

<p>Visit example<wbr>.com/very<wbr>long<wbr>address for details.</p>

Check: What does wbr do when there is plenty of room?

Section 20 — Unordered lists

An unordered list, marked with ul, is a list in which the order of the items does not matter. Browsers render it with bullets by default — small discs, though CSS can change them to squares, circles, images, or nothing at all — and each item is marked with an li element, for list item. The bullet is not the meaning; the meaning is "this is a set of items, with no ranking or sequence among them". A shopping list is an unordered list, because flour, water, and salt are all needed, and none comes before the others in importance. A list of features is an unordered list, because the features are a set, not a sequence. A list of ingredients is an unordered list, because the ingredients are gathered together before cooking begins, and the order in which they are listed does not affect the recipe. Whenever the content is genuinely a set, ul is the honest element, and using it tells every reader — including screen reader users, who will hear the list announced as a list, with its number of items — that they are looking at a collection, not a sequence.

The structure of a list is strict in a way that surprises beginners: a ul element may contain only li elements as direct children, and nothing else. Text directly inside a ul, without an li wrapper, is invalid, and a heading directly inside a ul is invalid, because the list's children are its items, and everything in a list must be an item. That strictness is a strength, because it makes the list's structure unambiguous: a tool that reads a list knows that every direct child is an item, and can count the items, announce them, and navigate them, without guessing. An li element, in turn, can contain almost anything — paragraphs, headings, links, images, even another list — because an item in a list can itself be complex. A product card in a list of products, for example, might contain a heading, an image, and a paragraph, all inside a single li, and that nesting is correct, because the card is one item, however complex its contents may be. The rule is about the list's direct children, not about the depth of its items' contents.

Choosing between ul and ol, covered next, is one of the most frequent decisions in HTML writing, and the test is simple: would reordering the items change the meaning? If you shuffled a shopping list, it would still be the same shopping list, so it is unordered. If you shuffled the steps of a recipe, it would become a different, possibly disastrous, recipe, so it is ordered. That test applies even when the list looks similar on screen, because the difference is in the meaning, not in the bullets or numbers, which CSS can change at will. A list of items that happens to be numbered for reference — a list of rules, for example, where the numbers are labels rather than a sequence — is a borderline case, and reasonable authors differ, but the default should be to ask about order, not about appearance. If order matters, use ol. If it does not, use ul.

<ul>
  <li>Flour</li>
  <li>Water</li>
  <li>Salt</li>
</ul>

Check: When should you choose ul over ol?

Section 21 — Ordered lists

An ordered list, marked with ol, is a list in which the order of the items matters. Browsers render it with numbers by default — 1, 2, 3, and so on — and each item is an li element, just as in an unordered list. The number is not merely decoration; it is the list's meaning, made visible. A recipe's steps are an ordered list, because mixing must come before rising, and rising must come before baking, and performing them in another order would produce a different result or no result at all. A ranking is an ordered list, because first place is different from second place, and the order is the information. A sequence of instructions is an ordered list, because step two assumes step one has been completed, and step three assumes step two. In each case, the order is not incidental to the content — it is the content, or a large part of it — and marking the list as ordered tells every reader, including screen reader users, who will hear the numbers announced, that the sequence matters and should be followed in order.

The great practical advantage of ol over typing numbers by hand is that the browser does the numbering for you, and it does it correctly even when the list changes. If you type "1. Mix. 2. Rise. 3. Bake." and then realise a step is missing between mixing and rising, you must renumber every subsequent step by hand, and it is easy to make a mistake — to skip a number, to repeat one, or to leave the list out of order. If you use ol, you simply insert a new li element in the right place, and the browser renumbers everything automatically, correctly, every time. That automatic numbering also adapts to styling: CSS can change the numbering style to letters, Roman numerals, or other formats, without the author having to rewrite the numbers, because the numbers are not in the content at all — they are generated from the structure. That separation, between the list's items as content and their numbers as presentation, is the same separation that runs through all of HTML, and ol is a clear example of it working well.

There is a related point about accessibility. A screen reader announcing an ordered list will tell the user how many items it contains, and will announce each item's number as it reads it, which helps the user keep their place in a long sequence — "step 4 of 7" is useful orientation that a hand-typed number does not provide as reliably, because a hand-typed number is just text, and the screen reader cannot know it is a step number rather than part of the sentence. An ordered list also allows the user to navigate by items, jumping from step to step, which is valuable in instructions that a user follows while doing something else — cooking, assembling, configuring — where they need to return to their place after each step. Those benefits are free, provided the list is marked as ordered, and they are lost if the author types numbers instead.

<ol>
  <li>Mix the dough.</li>
  <li>Let it rise.</li>
  <li>Bake it.</li>
</ol>

Check: Why is ol better than typing numbers by hand?

Section 22 — Starting and reversing a numbered list

An ordered list does not always start at one, and it does not always count upwards, and HTML provides two attributes for those cases: start, which sets the number of the first item, and reversed, which makes the list count downwards. The start attribute is useful when a list continues from an earlier list, or when it represents a portion of a longer sequence — a recipe continued from a previous page, for example, where the steps on this page begin at step 5, or a legal document where a list resumes after an interruption. Without start, the author would have to either restart the numbering, which would be wrong, or type the numbers by hand, which would lose all the benefits of ol described in the previous section. With start, the list remains a real ordered list, with automatic numbering, but its numbering begins where the sequence genuinely begins. The value of start is the number of the first item, so start="5" means the first li is numbered 5, the second 6, and so on, automatically.

The reversed attribute is useful for countdowns and rankings presented in reverse order. A "top 10" list presented from tenth place up to first place, building suspense toward the winner, is naturally a reversed list: it starts at 10 and counts down to 1, and the reversed attribute expresses that counting direction in the structure, so that a screen reader and other tools understand the order correctly, rather than merely seeing a list whose numbers happen to descend. A countdown — ten, nine, eight — is the same pattern. Reversed can be combined with start: a reversed list with start="10" counts down from 10, while a reversed list without start counts down from the number of items in the list, which is the natural behaviour for a ranking whose length is known. That combination covers most cases, and it does so without the author having to calculate or type any numbers, which is the point of using ol in the first place.

Both attributes are examples of HTML's approach to numbering: the numbers are derived from the structure, not typed as content, and attributes adjust the derivation without breaking it. That approach has a limit worth noting. The start and reversed attributes affect the numbering, but they do not affect the order of the items themselves — the items are still read in the order they appear in the source, from first li to last li, and a screen reader will read them in that order, announcing the numbers as they descend or as they begin from a higher value. A reversed list is therefore read in its source order, with descending numbers, which matches the author's intent for a ranking presented tenth-to-first. If the author actually wanted the items read first-to-tenth, they should write them in that order and use a normal list, not write them tenth-to-first and reverse the numbering. The attributes control the numbers; the source order controls the reading order; and the two should agree, because a list whose numbers and whose reading order disagree is a list that confuses every reader, however it looks on screen.

<ol start="5" reversed>
  <li>Fifth place</li>
  <li>Fourth place</li>
</ol>

Check: How would you make a list count down from 10?

Section 23 — Nested lists

A list can contain another list, nested inside one of its items, and that nesting is how you represent sub-points, sub-steps, and hierarchies in HTML. A recipe's ingredients might be grouped by category — dry ingredients, wet ingredients — with each category as an item in an outer list, and the ingredients themselves as items in an inner list inside that category's item. A table of contents might list chapters, with sections nested inside each chapter. An outline might list topics, with sub-topics nested beneath them. In each case, the nesting mirrors the hierarchy of the content: the inner list belongs to its parent item, and it is read as part of that item, not as a separate list at the same level. That belonging is expressed structurally, by placing the inner list inside the parent's li element, and it is the only correct placement. A nested list placed directly inside the outer list, as a sibling of the li elements rather than a child of one of them, is invalid, because a list's direct children must all be list items, and a list is not a list item.

That rule — nested lists go inside li, not directly inside ul or ol — is the most common nesting error in lists, and it is worth understanding why it exists. A list is a collection of items, and everything in the list must be an item, because otherwise the list's structure becomes ambiguous: is the nested list a separate list, or part of the previous item, or part of the next item? Placing it inside an li answers that question unambiguously: it is part of that item, and it will be read as part of that item, by every tool that reads lists. A screen reader announcing a nested list will announce the parent item, then announce the nested list and its items, then return to the next parent item, which matches the reader's mental model of a hierarchy. A nested list placed incorrectly may still look right on screen, because browsers will repair the structure by moving it, but the repair may place it in a different item from the one the author intended, and the resulting hierarchy will be wrong in ways that are hard to spot visually but obvious to a listener.

Nesting can go deeper than two levels, but it should be used with restraint. A list nested three or four levels deep is hard to read, hard to navigate, and often a sign that the content's hierarchy is too complex for a list, or that the list is trying to do the job of a table, a set of sections, or another structure. As a guideline, two levels are common and readable, three levels are acceptable for genuinely hierarchical content, and beyond that, the author should consider whether the content would be clearer as headings and sections, or as a different structure altogether. The depth of nesting is also a styling consideration: each level is typically indented further, and deeply nested lists quickly run out of horizontal room, particularly on narrow screens.

<ul>
  <li>Bread
    <ul>
      <li>White</li>
      <li>Rye</li>
    </ul>
  </li>
</ul>

Check: Where must a nested list sit — inside an li or directly inside the outer list?

Section 24 — Description lists

A description list, marked with dl, pairs terms with their descriptions, and it is the right structure for content that is naturally a set of name-value pairs. Each term is marked with dt, for description term, and each description is marked with dd, for description details. A glossary is the classic example: each word is a term, and its definition is its description. A frequently asked questions section can be a description list, with each question as a term and each answer as its description, though headings and sections are also common for FAQs, and either structure can be appropriate depending on the content. A recipe's details — preparation time, cooking time, servings — can be a description list, with each label as a term and each value as its description. In each case, the pairing is the meaning: the term and its description belong together, and the list structure expresses that belonging, so that a reader — or a tool — can find a term and immediately find its description, without guessing which paragraph belongs to which heading.

The structure of a description list is more flexible than that of an unordered or ordered list, in a way that reflects its purpose. A dt can be followed by one or more dd elements, because a term can have multiple descriptions — a word with two senses, for example, or a question with a short answer and a longer elaboration. Multiple dt elements can also share a single dd, because several terms can have the same description — synonyms, for example, or different names for the same thing. And a dt or dd can contain rich content: paragraphs, links, lists, and other elements, because a term or a description can itself be complex. What a description list may not contain, as direct children, is anything other than dt, dd, and — in modern HTML — a div wrapping a dt-dd group, which is allowed to help with styling. Text directly inside a dl, or a heading directly inside it, is invalid, for the same reason as in other lists: the list's children are its entries, and everything in it must be an entry.

Choosing between a description list and other structures is a matter of recognising the pattern. If the content is a set of terms and their descriptions, dl is the honest element, and using headings for the terms and paragraphs for the descriptions, while visually similar, loses the pairing: a tool reading headings and paragraphs cannot know which paragraph describes which heading, except by proximity, which is a guess. A description list makes the pairing explicit, and that explicitness serves readers who navigate by terms — a screen reader user can jump from term to term, hearing each description as they go, without losing track of which description belongs to which term. If the content is not a set of pairs, dl is the wrong choice, however tempting its default styling may be. A list of features is not a description list, because its items have no terms. A set of sections is not a description list, because its headings are not terms awaiting descriptions.

<dl>
  <dt>Levain</dt>
  <dd>A portion of fermented dough used to leaven bread.</dd>
</dl>

Check: What do dt and dd stand for?

Section 25 — Links are anchors

A link in HTML is an anchor element, written as a, and its name comes from the early web, when an anchor was a point in a document that another document could link to, as well as a point from which a document could link out. Today, the anchor element is overwhelmingly used for links out: its href attribute gives the destination, and the text between its opening and closing tags is what the visitor sees and clicks. That text is the link's content, and it is as much a part of the link as its destination, because it is what the visitor uses to decide whether to follow it. An anchor without an href is not a link at all; it is a placeholder, an anchor in the older sense, and browsers render it as ordinary text, not as a clickable link, and screen readers do not announce it as a link. That behaviour is a useful diagnostic: if a link on your page does not look or behave like a link, the first thing to check is whether its href is present and correct, because without href, there is no link, however link-like the surrounding markup may appear.

The href attribute's value is a web address, and it can take several forms, which are covered in detail in the following sections: a full address to another site, a relative path to another page on the same site, a fragment pointing to a place on the same page, an email address, or a phone number. In each case, the href is the link's destination, and the anchor's content is its label. That separation — destination in an attribute, label in the content — is what makes links flexible: the same destination can be labelled differently in different contexts, for different readers, without changing where it goes, and the same label can point to different destinations in different contexts, though that last practice should be used carefully, because it can confuse readers who meet the same label twice and expect it to go to the same place. The anchor element can contain more than text, too: it can contain an image, making the image clickable, or a combination of text and image, or even a block of content, in modern HTML, where an anchor may wrap a whole card. In each case, the principle is the same: everything inside the anchor is clickable, and everything inside it should describe the destination, because the anchor's content is the link, for every reader, including those who cannot see it.

Links are the web's defining feature. Without them, pages would be isolated documents, like pages in a book that cannot be turned to, and the web would be a library without a catalogue. With them, pages become a network, and a reader can move from one document to another by following a connection the author has made. That power carries a responsibility: every link is a promise that the destination exists, that it is relevant, and that it is what the label says it is.

<a href="https://example.com">Visit Example</a>

Check: What happens to an anchor element with no href?

Section 26 — Link text must make sense alone

Good link text describes its destination, in words that make sense even when the link is read in isolation, without the surrounding sentence. That standard sounds strict, and it is, because it reflects how many readers actually encounter links. A sighted reader scanning a page sees a link in context — in a sentence, in a paragraph, under a heading — and can use that context to understand where the link goes. A screen reader user often encounters links differently: they may ask their software to list all the links on a page, and listen to that list alone, without the surrounding text, in order to find the link they want quickly. In that list, a link that says "Read the bread guide" is immediately useful. A link that says "click here" is useless, because it describes the action of clicking, not the destination, and a list of ten "click here" links is ten identical, indistinguishable entries, none of which tells the user where it goes. The same problem affects search engines, which use link text to understand what the destination is about, and it affects any reader who skims a page by its links, which is a common way to navigate a long document.

The phrase "click here" is the classic failure, but it has many relatives, all of which fail the same test: "read more", "more", "link", "this", and "here" on their own. Each of those phrases describes an action or a pointer, not a destination, and each becomes meaningless when lifted out of its sentence. The remedy is to write link text that names the destination or the action's object: instead of "Click here to read the bread guide", write "Read the bread guide", with the whole phrase as the link, or write a sentence whose link is the guide's name. Instead of "Click here for more information", write "More information about baking bread", or make the link text the topic itself. The goal is not to make link text longer for its own sake — a link that says "Read our comprehensive, beginner-friendly, step-by-step guide to baking bread at home" is longer than necessary, and its length makes it harder to scan — but to make it specific. Specificity is the virtue: a link text should contain the words a reader would use to describe the destination, and no words that do not help.

There is a related point about link text and surrounding text. A link's text does not have to carry the entire burden alone, if the surrounding text is part of the link's context in a way that assistive technology preserves — for example, a link inside a heading, where the heading provides context, or a link in a list item, where the item provides it. But relying on that context is risky, because different tools preserve different amounts of it, and the safest practice is to make the link text itself sufficient. When you review a page, a useful exercise is to extract all its links and read them as a list, aloud if possible.

<a href="bread-guide.html">Read the bread guide</a>

Check: Why is "click here" poor link text?

Section 27 — Absolute and relative links

Links come in two forms, absolute and relative, and the difference is in how the destination is specified. An absolute link gives the complete address, including the protocol and the domain: https://example.com/recipes/bread.html. It works from anywhere, on any page, on any site, because it contains all the information needed to find the destination, without reference to where the link itself lives. A relative link gives a path relative to the current page: recipes.html for a page in the same folder, guides/bread.html for a page in a subfolder, or ../index.html for a page in the parent folder. It works because the browser combines the current page's address with the relative path to produce a complete address, and it keeps working if the whole site moves to a new domain, because the relationship between the pages — same folder, subfolder, parent folder — has not changed, even though the domain has. That portability is the relative link's great advantage, and it is why relative links are the standard for links within a site.

The choice between them is therefore mostly a choice about scope. Inside your own site, use relative links, because your site's internal structure is what you control, and relative links respect that structure: they survive a domain change, a move from a staging server to a live server, and a move from one hosting provider to another, without any editing. They also make a site easier to develop locally, because a site built with relative links works identically when opened from a file on your computer and when served from a web server, whereas a site built with absolute links pointing to its live domain will break when opened locally, because the live domain is not where the local files are. Outside your site, to other people's sites, use absolute links, because you have no relationship to their structure to be relative to — the only way to point to another site is to give its full address. That rule — relative inside, absolute outside — covers the overwhelming majority of links, and following it consistently will save you from a whole class of broken-link bugs.

Relative links have a syntax worth learning precisely, because it is easy to get wrong. A link with no leading slash, like recipes.html, is relative to the current page's folder. A link with a leading slash, like /recipes.html, is relative to the site's root — it is sometimes called root-relative — and it points to the recipes page at the top level of the site, regardless of how deep the current page is. A link starting with ../ goes up one folder level, and ../../ goes up two, and so on. Those distinctions matter when a site has nested folders, because a link that works from the home page may break from a page two folders deep, if it was written as if it were always at the top level. The remedy is to think, for each link, "relative to what?" — to the current page, to the site root, or to another site entirely — and to write the link accordingly.

<a href="recipes.html">Our recipes</a>
<a href="https://example.com">Another site</a>

Check: Which kind of link survives a site moving to a new domain?

Section 28 — Links to a place on the same page

A link whose href begins with a hash sign, like #ingredients, does not point to another page at all. It points to an element on the same page, identified by its id attribute, and when the visitor follows it, the browser scrolls to that element, without loading a new page. That behaviour makes same-page links — also called fragment links, or anchor links — the foundation of several common patterns: a table of contents at the top of a long article, where each entry jumps to its section; a "back to top" link at the bottom of a page, which jumps to an element at the top; a "skip to main content" link, which jumps over navigation to the main element; and a link from one part of a page to a related part, such as a footnote reference jumping to its footnote, and back again. In each case, the link saves the reader from scrolling, and it does so instantly, without a network request, because the destination is already on the page.

For a fragment link to work, the destination must exist: there must be an element on the page whose id matches the fragment, exactly, including case. A link to #ingredients will scroll to an element with id="ingredients", but not to one with id="Ingredients", and not to a section merely titled "Ingredients" without an id. If no such element exists, the browser does nothing — or, more precisely, it changes the address bar to include the fragment, but does not scroll anywhere, leaving the visitor confused about why the link appeared to do nothing. That failure mode is silent, which makes it easy to miss in testing, particularly when the link and its destination are on different parts of a long page and the tester does not scroll to check. The discipline is to create the id at the same moment as the link, and to test the link by clicking it, not merely by reading it. A fragment link is a small contract between two parts of a page, and like all contracts, it must be kept on both sides.

Fragment links also have a second life, as a way to share a specific part of a page. A full address with a fragment — https://example.com/guide.html#ingredients — points not just to the page, but to a section within it, and when someone follows that address, the browser loads the page and then scrolls to the section. That makes fragments useful for documentation, for FAQs, and for any page where a reader might want to direct someone else to a particular part, rather than to the page as a whole. It also means that ids are part of a page's public interface, in a sense: changing an id breaks any links, bookmarks, and shared addresses that point to it, just as changing a page's filename breaks links to the page. Ids that are likely to be linked to — section headings in a long guide, for example — should therefore be chosen carefully, named meaningfully, and changed reluctantly.

<a href="#ingredients">Jump to ingredients</a>
<h2 id="ingredients">Ingredients</h2>

Check: What must exist on the page for a #ingredients link to work?

Section 29 — Email and phone links

Two special kinds of link do not point to a web page at all, but to an action the visitor's device can take. A link whose href begins with mailto: opens the visitor's email program, or webmail, with a new message addressed to the email address that follows, ready for the visitor to write. A link whose href begins with tel: tells the device to dial the phone number that follows — on a phone, that means opening the dialler with the number entered, and on a desktop computer, it means opening whatever application is configured to handle phone numbers, or doing nothing if none is configured. Both are conveniences: they save the visitor from copying an address or a number and pasting it into another application, and they turn contact information on a page into an action, rather than merely a piece of text to be transcribed. For a business or a service, that reduction in friction can be the difference between a visitor making contact and a visitor giving up.

Both kinds of link have limitations that should be understood before using them. A mailto link exposes the email address to anyone who views the page's source, including automated programs that harvest addresses for spam. That exposure is not a reason to avoid mailto links — a contact address on a public page is public by definition — but it is a reason to use a dedicated contact address, rather than a personal one, and to expect that an address published on the web will receive spam, however it is published. A mailto link can also include a pre-filled subject, and even a pre-filled body, by adding a query string to the address, but that should be used sparingly, because a pre-filled message that the visitor did not write can feel presumptuous, and a long pre-filled body may exceed limits in some email programs. A tel link, meanwhile, should use an international format, beginning with a plus sign and the country code, so that it works for visitors calling from other countries, and it should be used only for numbers that are genuinely meant to be called — a phone number that is actually a fax number, or a number that is no longer in service, should not be presented as a clickable call link.

The most important guideline for both is that the link text should make the action clear. A mailto link whose text is the email address itself is self-explanatory, and a tel link whose text is the phone number itself is equally clear. A link whose text is "Contact us" and whose href is a mailto address is also clear, provided the surrounding context makes it obvious that contact means email. What should be avoided is a link whose text promises one action and whose href performs another — a link labelled "Visit our office" that opens an email program, for example — because that breaks the promise that link text makes about its destination. Email and phone links are small features, but they are features that cross from the web into the visitor's other applications, and that crossing should be predictable.

<a href="mailto:hello@example.com">Email us</a>
<a href="tel:+2348000000000">Call us</a>

Check: What does a tel: link do on a desktop computer without a phone app?

Section 30 — Opening links in a new tab

The target attribute on a link controls where the destination opens. By default, a link opens in the same tab, replacing the current page, which is the behaviour most visitors expect and most sites should preserve. The value _blank changes that: the destination opens in a new tab, leaving the current page open behind it. That behaviour is sometimes appropriate — for a link to an external site that the visitor may want to refer back from, for a document like a PDF that the visitor may want to keep open alongside the page, or for a link in a multi-step process where losing the current page would lose the visitor's progress. But it should be used sparingly, and deliberately, because it takes control away from the visitor. A visitor who wants a new tab can open one themselves, with a middle-click or a keyboard shortcut, and a site that forces new tabs on every external link is a site that litters the visitor's browser with tabs they did not ask for, and that makes it harder for them to use the back button, which is one of the web's most fundamental navigation tools.

When you do use target="_blank", you must also add rel="noopener". The reason is security, and it is worth understanding, because it illustrates how a seemingly innocent feature can create a vulnerability. When a page opens another page in a new tab, the new page receives a reference to the page that opened it, through a property called window.opener, unless noopener prevents it. A malicious or compromised destination page could use that reference to change the original page — for example, replacing it with a convincing imitation designed to steal the visitor's credentials, while the visitor is looking at the new tab and not noticing that the original tab has changed. That attack is called tabnabbing, and it is prevented by noopener, which severs the connection between the two pages, so that the new page cannot reach back to its opener. The older value noreferrer also severs the connection, and additionally prevents the browser from sending the referring page's address to the destination, but noopener alone is sufficient for the security issue, and it is the value modern guidance recommends. Some older browsers did not support noopener, which is why you will still see rel="noopener noreferrer" in code — the combination covers both old and new browsers, and it is harmless to include both.

The broader lesson is that opening a new tab is a decision about the visitor's experience, not just a technical setting, and it should be made with the visitor in mind. Ask whether the visitor would benefit from keeping the current page open, or whether they would prefer to move on and come back with the back button. If the answer is not clearly "keep it open", do not force a new tab. And when you do force one, always include rel="noopener", without exception, because the security cost of omitting it is real, and the cost of including it is zero. A link that opens safely in a new tab, for a good reason, is a helpful feature.

<a href="https://example.com" target="_blank" rel="noopener">External site</a>

Check: Why pair target="_blank" with rel="noopener"?

Section 31 — Download links

The download attribute on a link tells the browser that the link's destination is a file to save, rather than a page to display. When a visitor clicks a download link, the browser downloads the file to their downloads folder, or asks them where to save it, depending on their settings, instead of navigating away from the current page. That behaviour is appropriate for files the visitor is meant to keep: a PDF guide, a spreadsheet, a template, an audio file, or any other document that is more useful saved than viewed. The download attribute can also carry a value, which suggests a filename for the saved file — useful when the file's name on the server is technical or unhelpful, and the author wants the visitor's copy to have a clear, human-readable name. That suggested filename is only a suggestion, and the visitor's browser and settings may override it, but it is a courtesy that makes the downloaded file easier to find later, which is part of the link's job.

The download attribute has an important limitation, rooted in security. It works reliably for files on the same site as the linking page — same origin, in technical terms — because the browser trusts the linking page to say what should happen to a file from its own site. For files on other sites, browsers may ignore the download attribute and simply navigate to the file, displaying it if they can, because allowing any site to force a download of another site's file would be a security risk: a malicious site could trick visitors into downloading files they did not intend to save, from sources they did not choose. That limitation means download links to external files are unreliable, and authors should not promise a download for an external file when the browser may instead display it. For external files, the honest approach is to link normally, and let the visitor decide whether to save the file themselves, using their browser's own save command, rather than claiming a download behaviour the browser may not honour.

There is also a user-experience point worth considering. A download is an action with consequences outside the browser: it writes a file to the visitor's device, it consumes storage, and it may trigger security warnings. A link that downloads a file should therefore make that clear in its text, so that the visitor is not surprised. "Download the guide (PDF)" is clear: it says what will happen, what the file is, and what format it is in, including its size if that is known and helpful. A link that merely says "Guide", but downloads a file when clicked, surprises the visitor, and surprise in this context is not delightful — it is a file appearing on their device that they did not expect. The same principle applies to file size: a large file should be labelled as large, so that a visitor on a slow connection or a limited data plan can decide whether to download it now or later.

<a href="bread-guide.pdf" download>Download the guide (PDF)</a>

Check: When might a browser ignore the download attribute?

Section 32 — The link title trap

The title attribute on a link adds a tooltip that appears when a visitor hovers over the link with a mouse and waits. It is tempting to use that tooltip to explain where a link goes, particularly when the link text itself is short or ambiguous — to add "Opens the full bread guide, with photographs" to a link that merely says "Guide", for example. That temptation should be resisted, for the same reasons the title attribute is weak generally, which were covered in the previous chapter and which apply with particular force to links. Touch-screen users cannot hover, so they will never see the tooltip. Keyboard users will not see it. Many screen readers ignore it or announce it inconsistently. And even for mouse users, the tooltip appears after a delay, disappears quickly, and cannot be kept on screen while the user decides. Information that exists only in a title attribute is information that most visitors will never receive, and a link whose destination is explained only in its tooltip is a link whose destination is unexplained for most of its audience.

The remedy is straightforward: put the explanation in the link text itself, or in visible text near the link. If a link needs explanation, that explanation is part of the link's job, and it belongs where every reader can find it, not in a secondary channel that only some readers can reach. A link that says "Guide" and needs a tooltip to explain that it is the full bread guide should instead say "Full bread guide", or "Read the full bread guide", with the explanation in the text that everyone sees. A link that says "PDF" and needs a tooltip to explain that it is a 20-page guide should instead say "Download the 20-page guide (PDF)", with the format and the substance in the text. That approach may make link text slightly longer, but length in the service of clarity is not a flaw — it is the point of link text, which exists to tell the reader where the link goes, and a link text that does that job well is worth its words.

There is a narrow case where a title attribute on a link is harmless: when it adds supplementary information to a link whose text is already sufficient on its own. A link that says "Read the bread guide", with a title that adds "Updated January 2026", is not hiding essential information in the tooltip, because the link text already tells the reader where the link goes; the title merely adds a detail that is nice to know but not necessary to decide. Even in that case, the detail would be better placed in visible text if it matters to the decision — a reader deciding whether to open a guide might want to know it was recently updated, and that information should not be hidden from touch users. The safest rule is to treat the title attribute on links as unnecessary, and to write link text that stands alone.

Check: Why is link text better than a title attribute for explaining a link?

Section 33 — Images with img

The img element displays an image on a page. It is a void element, which means it has no closing tag and no content of its own — it does not contain the image, it points to it. Two attributes are essential: src, which gives the image file's address, and alt, which gives a text alternative for readers who cannot see the image. The src can be a relative path to an image file on the same site, or an absolute address to an image on another site, though hosting images on your own site is generally preferable, because it keeps the page's appearance under your control and avoids depending on another server's availability. The alt attribute, covered in detail in the next sections, is not optional for meaningful images: it is the image's text equivalent, and a page that omits it is a page that excludes readers who cannot see the image, for no reason other than the author's oversight.

Understanding img as a pointer, rather than a container, clarifies several of its behaviours. Because the image file is separate from the page, it is fetched separately, and it may arrive after the page's text, or fail to arrive at all if the file is missing or the network fails. That separation is why width and height attributes matter, covered later in this chapter: they let the browser reserve space for an image before it arrives, so the page does not jump when it does. It is also why an image's alt text is shown when the image cannot be loaded: the alt text is the fallback content, the text that stands in for the image when the image itself is unavailable, for any reason. And it is why images should be optimised — compressed, sized appropriately, and served in efficient formats — because every image is an additional file the visitor must download, and a page with many large images is a page that is slow to load, particularly on a mobile connection. The img element itself is simple; the discipline around it — alt text, dimensions, optimisation — is where the craft lies.

There is a common beginner confusion between img and other ways of showing images, particularly CSS background images. The rule is about meaning: if the image is content — a photograph that illustrates an article, a diagram that explains a concept, a product photo that shows what is for sale — it belongs in HTML, as an img element, with alt text, because it is part of the document's information. If the image is decoration — a pattern behind a heading, a texture on a background, a visual flourish that adds no information — it belongs in CSS, as a background, because it is part of the page's appearance, not its content. That distinction matters for accessibility: a content image in CSS has no alt text and is invisible to screen readers, while a decorative image in HTML, even with empty alt text, adds noise to the document's structure.

<img src="bread.jpg" alt="A golden loaf of bread on a wooden board">

Check: Which two attributes does every meaningful image need?

Section 34 — Writing good alt text

Good alt text is a text equivalent of an image: it says, in words, what the image shows and why it matters, so that a reader who cannot see the image receives the same information as a reader who can. That standard — same information, not same description — is the key to writing alt text well. A photograph of a loaf of bread, sliced open to show its soft interior, is not usefully described as "image of bread" or "a picture", because those phrases tell the reader nothing they did not already know from the context, and they omit the information the image actually conveys: that the loaf is crusty, that it has been sliced, that its crumb is soft and open. A good alt text for that image might be "A crusty loaf, sliced open to show a soft crumb" — a sentence that conveys what a sighted reader would take from the image, in the space of a few words. The test is to imagine the image removed, and ask whether the alt text alone gives the reader what they need to understand the surrounding content. If it does, the alt text is good. If it does not, it needs rewriting, however accurate its description of the image's appearance may be.

Alt text should be concise, but conciseness is not the same as brevity for its own sake. A sentence or two is usually enough, and a paragraph is usually too much, because alt text is read aloud by screen readers in the flow of the page, and a long alt text interrupts that flow, just as a long image caption would interrupt a sighted reader's flow. The length should match the image's informational content: a simple icon needs a few words, a complex photograph needs a sentence, and a chart needs a summary plus a fuller description elsewhere, as covered later in this chapter. Alt text should also avoid phrases like "image of" or "picture of", because the screen reader already announces that the element is an image, and repeating that information is redundant — like introducing a paragraph by saying "paragraph:". And alt text should not repeat information that is already in the surrounding text or in a caption, because duplication bores the listener and wastes their time. If the caption already says "The first loaf from the new oven", the alt text should say what the loaf looks like, not repeat the caption.

Writing good alt text is a skill that improves with practice, and the practice is worth doing, because alt text is one of the most direct ways a developer serves readers they cannot see. A useful exercise is to write alt text for images you meet in daily life — in articles, in advertisements, in social media — and to ask, for each one, what information the image conveys that the text around it does not, and how you would convey that information in a sentence. Another exercise is to view a page with images disabled, and to read it through the alt text alone, checking whether it still makes sense.

<img src="bread.jpg" alt="A crusty loaf, sliced open to show a soft crumb">

Check: What is wrong with alt text that just says "image"?

Section 35 — Empty alt for decoration

Not every image needs descriptive alt text. Some images are purely decorative: a border pattern, a flourish, a spacer, a background texture that has been placed in HTML rather than CSS for some reason, or an illustration that repeats information already given in the surrounding text. For those images, the correct alt attribute is an empty one: alt="". That empty value is not the same as omitting the attribute, and the distinction is crucial. An omitted alt attribute tells a screen reader that the author did not provide a text alternative, and the screen reader may then try to compensate — by announcing the image's filename, for example, which is usually meaningless, like "flourish-2-final-v3.png", or by announcing that an image is present without describing it, which interrupts the reader for no benefit. An empty alt attribute tells the screen reader explicitly to skip the image entirely, to treat it as if it were not there, which is the correct treatment for a decorative image, because the image contributes nothing that the reader needs.

The decision between descriptive alt text and empty alt text is therefore a decision about whether the image carries information. If removing the image would remove information from the page, the image needs descriptive alt text. If removing it would change nothing about the page's meaning — if the page would read exactly the same without it — then the image is decorative, and empty alt is correct. That test is usually easy to apply, but there are borderline cases. An image that illustrates a point already made in text, for example, might seem decorative, because the text already says it — but if the image adds atmosphere, or emotion, or a concrete example that the text only describes abstractly, then it carries information beyond the text, and it deserves a description, even a brief one. Conversely, an image that is visually prominent — a large hero image at the top of a page — is not automatically informative: if its only job is to make the page look attractive, and the page's actual information is in the text below it, then empty alt may still be correct, however large the image is. Prominence is not information, and size is not meaning.

A practical point: decorative images should, where possible, be in CSS rather than in HTML, as discussed in the previous section, because CSS backgrounds are ignored by screen readers automatically, without needing an empty alt attribute at all. Empty alt is for the cases where a decorative image must be in HTML — because it is part of a component, or because it is generated by a system that outputs img elements, or because it was placed there before the distinction was understood. In those cases, empty alt is the correct, explicit signal, and it should be used deliberately, not as a way to avoid writing alt text for informative images. An author who gives every image empty alt text, to save time, has not solved the alt text problem; they have hidden it, by telling screen readers that no image on the page carries information, which is false for most pages, and which excludes readers from content they need.

<img src="flourish.png" alt="">

Check: What does an empty alt attribute tell a screen reader?

Section 36 — Width and height prevent jumping

Every img element should carry width and height attributes giving the image's intrinsic dimensions in pixels. Those attributes serve a purpose that is invisible when they are present and painfully visible when they are absent: they let the browser reserve the correct amount of space for the image before the image file itself has arrived. Without them, the browser does not know how tall the image will be until it downloads it, so it initially lays out the page as if the image had no height at all, and then, when the image arrives, it pushes all the content below it downwards to make room. That sudden shift — called layout shift — is jarring for the reader, who may be in the middle of reading a paragraph that jumps down the screen, and it can cause real errors, because a reader who is about to click a link or a button may find that the target has moved, and click something else instead. On a slow connection, where images arrive one by one over several seconds, a page without dimensions can jump repeatedly, each jump more irritating than the last, until the page finally settles.

With width and height attributes, the browser knows the image's aspect ratio — the relationship between its width and its height — from the moment it reads the HTML, before any image file has downloaded. It can therefore reserve a box of the correct proportions, and when the image arrives, it simply fills the box, without moving anything else. That reservation works even when CSS resizes the image responsively: modern browsers use the width and height attributes to calculate the aspect ratio, and then apply CSS sizing on top of that ratio, so that an image displayed at half its intrinsic width is also displayed at half its intrinsic height, preserving its proportions and its reserved space. That is why the attributes should give the image's true dimensions, not the dimensions at which it will be displayed — the browser needs the ratio, and the ratio comes from the true dimensions, whatever the display size may be. Giving false dimensions, or dimensions that do not match the image's actual ratio, defeats the purpose, because the reserved box will be the wrong shape, and the page will still shift when the image arrives and corrects it.

The width and height attributes are a small example of a larger principle: giving the browser information early, so that it does not have to guess and then correct itself. Browsers are good at guessing, but every guess that turns out wrong costs the reader a visible jump, a moment of confusion, or a misclick. Providing dimensions is cheap — the numbers are known when the image is created, and most image tools report them — and it prevents a whole class of layout instability that is otherwise hard to fix after the fact. It is also a habit that scales: a page with one image benefits, and a page with fifty images benefits fifty times over, because each image that arrives without shifting is one less interruption to the reader's experience.

<img src="bread.jpg" alt="A loaf of bread" width="600" height="400">

Check: What goes wrong on a page when images have no width and height?

Section 37 — Figures and captions

A figure element groups a piece of self-contained content — most often an image, but also a diagram, a code sample, a table, or an audio clip — with its caption, marked by figcaption. The figure is a unit: the content and its caption belong together, and they can be moved together, referenced together, and understood together, without depending on the surrounding text for their meaning. A photograph with a caption explaining when and where it was taken is a figure, because the caption is part of the photograph's presentation, not a separate paragraph that happens to follow it. A diagram with a caption naming what it shows is a figure, for the same reason. The figure element makes that belonging explicit in the structure, so that tools can treat the content and its caption as a pair, and so that a reader can tell that the caption describes this figure, and not the paragraph before or after it.

The caption, figcaption, is visible to everyone, and it is not a replacement for alt text. That distinction is essential, and it is worth stating in full, because the two are often confused. Alt text is for readers who cannot see the image: it replaces the image, conveying in words what the image conveys in pixels, and it is read by screen readers in place of the image. A caption is for all readers: it is visible text, displayed beneath or beside the figure, that adds context the image alone does not give — when the photograph was taken, who is in it, what the diagram represents, or why this figure matters to the argument. A sighted reader sees both the image and its caption, and gets information from both. A screen reader user hears the alt text in place of the image, and then hears the caption as well, and gets information from both, without duplication, provided the author has written them to complement each other rather than repeat each other. If the caption says "The first loaf from the new oven", the alt text should say what the loaf looks like, not repeat the caption, because repeating it wastes the listener's time and adds nothing.

A figure's caption must be the first or last child of the figure element — it cannot sit in the middle, with content on both sides of it — and a figure should normally contain exactly one figcaption, because a figure has one caption, not several. The figure itself can appear anywhere in the flow of a document, and it is often floated or positioned by CSS to sit beside text, but its structural position — where it appears in the document's order — should reflect its relationship to the surrounding content. A figure that illustrates a point in a paragraph should appear near that paragraph, not pages away, because a reader who must hunt for the figure it illustrates will lose the connection between them.

<figure>
  <img src="bread.jpg" alt="A loaf of bread">
  <figcaption>The first loaf from the new oven.</figcaption>
</figure>

Check: What is the difference between alt text and a figcaption?

Section 38 — Responsive images with srcset

A single image file rarely suits every screen. A phone with a narrow screen and a slow connection needs a small file, because a large file wastes bandwidth and takes time to download for a display that cannot show its detail. A large desktop monitor, or a phone with a high-resolution display, needs a larger file, because a small file stretched to fill a large display looks blurry and unprofessional. Serving one file to all devices therefore forces a compromise: either the phone downloads more than it needs, or the desktop displays less than it deserves, and either way, some visitors get a worse experience than they should. The srcset attribute solves that compromise by letting the author offer several versions of the same image, at different widths, and letting the browser choose the most suitable one for the device it is running on. The browser knows the screen's size, its resolution, and the connection's characteristics in ways the author cannot predict, so delegating the choice to the browser produces a better result than any single choice the author could make in advance.

The syntax of srcset, in its width-based form, is a list of image addresses, each followed by its width in pixels, with a "w" descriptor: "bread-400.jpg 400w, bread-800.jpg 800w". The src attribute remains, as a fallback for browsers that do not understand srcset, and as the default image, and it should point to a sensible middle version, not to the largest or smallest. The browser reads the list, considers how wide the image will be displayed — which is where the sizes attribute, covered next, comes in — and chooses the smallest version that will look good at that display size, on that screen's resolution. A phone displaying the image at 400 pixels wide will choose the 400w version, or a larger one if its screen is high-resolution and needs more pixels to look sharp. A desktop displaying the image at 800 pixels wide will choose the 800w version. Neither device downloads a file larger than it needs, and neither displays a file smaller than it deserves, because the choice is made per device, at load time, from the options the author provided.

Providing srcset versions requires preparation: the same image must be exported at several sizes, and those files must be kept alongside the page, with consistent naming. That preparation is real work, and it is worth asking whether it is justified for every image. For a small icon, or an image that is always displayed at the same size, a single file is sufficient, and srcset adds complexity without benefit. For a large, prominent image — a hero image, a product photograph, a gallery image that fills a variable width — srcset is one of the highest-value optimisations available, because it directly reduces the data a visitor must download, which is the single largest factor in how fast a page feels on a mobile connection. The work of exporting several sizes is repaid every time a visitor loads the page on a phone and receives a small file instead of a large one.

<img src="bread-600.jpg" srcset="bread-400.jpg 400w, bread-800.jpg 800w"
     alt="A loaf of bread" width="600" height="400">

Check: Who chooses which srcset version to download — you or the browser?

Section 39 — The sizes attribute

When srcset offers several versions of an image at different widths, the browser must decide which version to download, and that decision depends on how wide the image will be displayed — but at the moment the browser is choosing, it has not yet laid out the page, so it does not yet know the display width. The sizes attribute solves that chicken-and-egg problem by telling the browser, in advance, how wide the image will be at different screen sizes, so that it can choose sensibly before layout happens. Sizes is a list of media conditions and widths: "(max-width: 600px) 100vw, 600px" means "on a screen up to 600 pixels wide, the image will fill the full viewport width; otherwise, it will be 600 pixels wide". The browser evaluates those conditions against its actual screen, finds the first one that matches, and uses the corresponding width to choose from srcset. Without sizes, the browser assumes the image will fill the full viewport width, which is correct for a full-width hero image but wrong for an image that will actually display at 600 pixels, or in a sidebar, or in a grid — and a wrong assumption leads to downloading a larger file than needed, because the browser prepares for a display width that never happens.

The units in sizes deserve attention, because they are not all pixels. The vw unit means a percentage of the viewport width: 100vw is the full viewport, 50vw is half of it, and so on. That unit is appropriate when the image's display width is genuinely relative to the viewport — a full-width image, or an image in a column that takes half the screen. A pixel value, like 600px, is appropriate when the image's display width is fixed, regardless of the viewport — an image in a container that is always 600 pixels wide, for example. Mixing the two, as in the example above, covers the common responsive pattern: full width on small screens, fixed width on larger ones. The last value in sizes, without a media condition, is the default, applied when none of the conditions match, and it must always be present, because otherwise the browser has no width to use for screens that match no condition. That default is also the value used by browsers that do not understand sizes at all, which is why it should be a sensible, representative width, not an extreme.

Sizes is often omitted by beginners, because srcset appears to work without it — and it does work, in the sense that the browser will choose a version, but it will choose based on a wrong assumption, and the result will be wasteful, though not broken. That waste is invisible to the author, who sees a correct-looking image, and invisible to the visitor, who sees a correct-looking image that took longer to load than it should have. The only way to notice it is to inspect the network requests and see which file was actually downloaded, which is a useful debugging habit for responsive images generally.

<img src="bread-600.jpg" srcset="bread-400.jpg 400w, bread-800.jpg 800w"
     sizes="(max-width: 600px) 100vw, 600px" alt="A loaf of bread">

Check: What does sizes tell the browser?

Section 40 — Art direction with picture

Srcset solves the problem of serving the same image at different sizes, but sometimes a small screen needs a different image, not just a smaller copy of the same one. A wide landscape photograph, for example, contains a scene spread across a broad frame: a loaf on a table, a window behind it, a kitchen stretching to either side. On a desktop monitor, that breadth is the photograph's strength. On a phone, the same photograph, shrunk to fit a narrow screen, becomes a thin strip in which the loaf is tiny and the details are illegible — the image has been preserved, but its impact has been lost. What the phone really needs is a differently cropped version: a square crop centred on the loaf, or a portrait crop that emphasises the subject, rather than the scene. That need — for a different composition at different sizes, not merely a different resolution — is called art direction, and it is what the picture element provides.

The picture element wraps several source elements, each with a media condition and a srcset, plus an img element as the fallback. The browser evaluates the sources in order, and uses the first one whose media condition matches the current screen, falling back to the img if none match, or if the browser does not understand picture at all. In the example, a screen up to 600 pixels wide receives the square crop, and any other screen receives the wide version from the img element. The img inside picture is not optional: it is the fallback, and it must carry the alt text, the width and height, and any other attributes the image needs, because it is the element that actually displays the image in all cases — the sources merely suggest which file it should display. That structure makes picture more powerful than srcset alone, but also more complex, and the complexity should be justified by a real art-direction need. If the image's composition works at all sizes, srcset is sufficient, and picture adds nothing but extra markup. If the composition fails at some sizes, picture is the correct tool, because no amount of resizing can fix a composition that does not suit the screen.

Art direction also applies to formats, not just crops. A picture element can offer a modern format, like WebP, to browsers that support it, and fall back to JPEG for browsers that do not, by using source elements with a type attribute instead of, or in addition to, a media attribute. That use is increasingly common, because modern formats offer significantly smaller files for the same quality, and picture provides a clean way to offer them without breaking older browsers. The principle is the same in both uses: picture lets the author offer choices, and lets the browser choose based on its capabilities and its screen, rather than forcing a single file on every visitor. That delegation — author offers, browser chooses — is the pattern that runs through responsive images generally, and picture is its most flexible expression.

<picture>
  <source media="(max-width: 600px)" srcset="bread-square.jpg">
  <img src="bread-wide.jpg" alt="A loaf of bread">
</picture>

Check: When is picture better than srcset alone?

Section 41 — Lazy loading

Lazy loading is the practice of delaying the loading of an image until it is needed — specifically, until it is near the viewport, the visible part of the page — rather than loading every image on the page the moment the page loads. The loading attribute on img makes that practice trivial: loading="lazy" tells the browser to wait, and to fetch the image only when the visitor scrolls near it, or when it is about to become visible. The benefit is immediate and measurable, particularly on pages with many images, such as galleries, product listings, and long articles. A visitor who opens such a page and reads only the top of it should not have to download every image on the page, including ones they will never scroll to, because those downloads consume bandwidth, slow the initial load, and delay the content the visitor actually came for. Lazy loading ensures that the visitor downloads only what they are likely to see, and that the page's first view arrives as quickly as possible, unburdened by images below the fold.

The loading attribute has a counterpart, loading="eager", which is the default behaviour: load the image immediately, as soon as the page loads. Eager is correct for images that are visible on first view — the hero image at the top of a page, the logo, the main product photograph — because delaying those images would delay the very content the visitor is looking at, producing a page that appears with holes where its most important images should be. The rule is therefore simple: lazy-load images below the fold, and eager-load, or simply leave as default, images above it. That rule is easy to state and easy to get wrong in one specific way: lazy-loading an image that is actually above the fold, because the author misjudged where the fold falls, or because the fold moves on different screen sizes. An image that is above the fold on a desktop may be below it on a phone, or vice versa, and an image lazy-loaded on a screen where it is visible will appear late, popping into view after the text around it has already been read, which is jarring. When in doubt about whether an image is above or below the fold, err on the side of eager, because a slightly slower initial load is less disruptive than a missing hero image.

Lazy loading is built into browsers, which means it requires no JavaScript, no libraries, and no complex configuration — just an attribute. That simplicity is a recent development: for years, lazy loading required scripts that watched the scroll position and swapped image sources, with all the complexity and fragility that entailed. The native attribute replaces that machinery with a single word, and it does so more reliably, because the browser itself knows when an image is near the viewport, better than any script can guess. It is worth noting, however, that lazy loading is not a substitute for optimisation. A lazy-loaded image that is enormous will still be enormous when it is finally loaded, and it will still take time to download when the visitor scrolls to it.

<img src="gallery-3.jpg" alt="A tray of rolls" loading="lazy">

Check: Why should the top image on a page not be lazy-loaded?

Section 42 — Choosing an image format

Image formats are not interchangeable, and choosing the right one for each image is often the single largest saving in a page's weight — larger, in many cases, than any other optimisation the author can make. The choice depends on what the image contains. Photographs, with their continuous tones, gradients, and complex detail, suit JPEG, which compresses photographic data efficiently by discarding information the human eye is least likely to notice, or WebP, a newer format that achieves the same quality at a smaller file size, and that also supports transparency, which JPEG does not. Graphics with flat colours, sharp edges, and text — logos, diagrams, screenshots, icons — suit PNG, which compresses without discarding any information, preserving sharp edges exactly, at the cost of larger files for photographic content, where its lossless compression is inefficient. Simple icons and logos, made of shapes and lines rather than pixels, suit SVG, which describes the image as code rather than as a grid of pixels, so that it scales to any size without losing sharpness, and is usually a very small file for simple shapes. Choosing among those formats is a matter of matching the format's strengths to the image's nature, and the wrong choice can easily double or triple a file's size for no benefit.

The practical process for choosing a format starts with asking what the image is. If it is a photograph, start with WebP if your audience's browsers support it, and JPEG as a fallback, and compare the file sizes at a quality setting that looks good — not at maximum quality, because maximum quality in JPEG preserves detail that no visitor will notice, at a large cost in file size. If it is a graphic with flat colours, start with PNG, and check whether SVG would be smaller and sharper, particularly for logos and icons that are drawn from shapes. If it is an image that needs transparency — a logo that must sit on different backgrounds, for example — JPEG is ruled out, because it does not support transparency, and the choice is between PNG and WebP, both of which do. If it is an animated image, the choice is between GIF, the traditional format, which is limited in colours and often large, and modern video formats or WebP animation, which are more efficient but less universally supported. Each of those decisions is a trade-off between quality, size, and compatibility, and the right answer depends on the image, the audience, and the context — which is why format choice is a skill, not a rule.

There is a broader point about formats and the future. New formats appear regularly — WebP is now widely supported, AVIF is newer and offers even smaller files for photographs, and other formats will follow — and the picture element, covered earlier, provides a way to offer a new format to browsers that support it while falling back to an older format for browsers that do not. That pattern means an author does not have to choose between adopting a new format and supporting older browsers; they can do both, by offering both, and letting the browser choose.

Check: Which format would you pick for a logo, and why?

Section 43 — SVG as an image file

An SVG file is an image described in code rather than in pixels. Where a JPEG or PNG file stores a grid of coloured dots — millions of them, for a large photograph — an SVG file stores instructions: draw a circle here, draw a rectangle there, fill this shape with that colour, place this text in that font. Those instructions are written in a text-based format, which means an SVG file can be opened in a text editor, read by a human, and edited by hand, unlike a pixel-based file, which requires an image editor to change. The practical consequence of that difference is sharpness: because an SVG is drawn from instructions each time it is displayed, it can be drawn at any size — sixteen pixels or sixteen thousand — without losing sharpness, because the instructions scale perfectly, whereas a pixel image, enlarged beyond its native size, becomes blurry, as its fixed grid of dots is stretched. That makes SVG the natural format for logos, icons, diagrams, and any graphic made of shapes, lines, and text, where sharpness at every size matters and where the file's content is simple enough that instructions are smaller than pixels would be.

Using an SVG file is no different from using any other image file: you point an img element at it with src, you give it alt text, and you give it width and height, exactly as you would for a JPEG or PNG. The browser fetches the file, draws it from its instructions, and displays the result, and a screen reader treats it exactly like any other image, reading its alt text in place of it. That interchangeability is a strength, because it means adopting SVG requires no new technique, only a new format choice, and it can be introduced gradually, one image at a time, without restructuring a page. It also means SVG files benefit from the same practices as other images: they should be optimised, by removing unnecessary detail and metadata from their code, because an SVG exported from a drawing program often contains editor-specific information, unused definitions, and excessive precision, all of which inflate the file without improving the image. An optimised SVG is often remarkably small — a simple logo in a few hundred bytes — which is part of its appeal for icons and interface graphics, where dozens of images may appear on a single page.

SVG's limitations are worth understanding as well. It is not suitable for photographs, because describing a photograph as instructions — as millions of tiny shapes — would produce a file far larger than the pixel-based original, and the drawing would be slower, not faster. It is also not suitable for complex illustrations with thousands of shapes, where the instruction list becomes long and the rendering becomes expensive, particularly on low-powered devices. For those images, pixel formats remain correct. The rule is to match the format to the image's nature: shapes and lines suit SVG, photographs suit JPEG or WebP, flat graphics suit PNG or SVG depending on their complexity.

<img src="logo.svg" alt="Kelick Kitchen logo">

Check: Why does an SVG logo stay sharp on a large screen?

Section 44 — Inline SVG

SVG code can also be written directly into an HTML page, rather than loaded as a separate file, and that technique is called inline SVG. The svg element sits in the document like any other element, containing shapes — circles, rectangles, paths — as its children, and the browser draws those shapes in place, as part of the page. The advantage of inline SVG over an SVG file is control: because the SVG is part of the document, CSS can style its shapes — changing their colours, their stroke widths, their opacity — and JavaScript can interact with them — responding to clicks, animating them, changing them in response to user actions — in ways that are impossible, or much harder, with an SVG loaded as an image file, which is treated as an opaque picture by the page that contains it. An icon whose colour should change when it is hovered, or when its state changes, is a natural candidate for inline SVG, because the colour change can be achieved with a CSS rule targeting the SVG's shapes, without needing a second image file for the hover state.

That control comes with costs that must be weighed. Inline SVG adds bulk to the HTML document itself, because the SVG code is part of the page, downloaded with it, on every page that contains it, rather than cached as a separate file that the browser can reuse across pages. An SVG file used on fifty pages is downloaded once and cached; the same SVG inlined on fifty pages is downloaded fifty times, as part of each page's HTML. Inline SVG also makes the HTML harder to read, because a complex SVG can contain dozens or hundreds of lines of shape code, dwarfing the surrounding content and obscuring the document's structure. Those costs mean inline SVG suits small graphics — icons, simple illustrations, decorative shapes — where the code is short and the interactivity or styling benefit is real, and it does not suit large illustrations, where the code is long and the benefit is minimal. A large illustration is better served as a file, where its bulk does not pollute the document, and where it can be cached.

Accessibility for inline SVG requires attention, because an inline SVG is part of the document, not an image with an alt attribute. A purely decorative inline SVG — an icon that accompanies text which already says what it means — should be hidden from screen readers, with aria-hidden="true", so that it does not add noise to the page's spoken version. An inline SVG that carries meaning on its own — a chart, a diagram, a standalone icon that is the only indicator of something — needs a text alternative within the SVG itself, using a title element inside the svg, or an aria-label on the svg, so that screen readers can convey its meaning. That distinction, between decorative and meaningful, is the same distinction that governs alt text for img elements, applied in a different form, and it is worth applying with the same care.

<svg width="24" height="24" viewBox="0 0 24 24" aria-hidden="true">
  <circle cx="12" cy="12" r="10"></circle>
</svg>

Check: When is inline SVG worth its extra page weight?

Section 45 — When an image fails to load

Images fail to load for many reasons: the file has been moved or deleted, the filename has been mistyped, the server is down, the network has failed, or the visitor's connection has blocked the image for some reason. When that happens, the browser does not leave a blank space; it shows a broken-image indicator — typically a small icon, sometimes with a border — and, crucially, it shows the image's alt text, in place of the image, inside that frame. That behaviour makes alt text a fallback in the literal sense: it is the content that stands in when the image itself cannot stand. A visitor who meets a broken image with good alt text still learns what the image was meant to show, and can continue reading the page with that information, even though the picture itself is missing. A visitor who meets a broken image with missing, empty, or meaningless alt text learns nothing, and is left with a broken frame and a gap in the page's meaning, which is a worse experience than a missing image alone, because it signals that something is wrong without explaining what was lost.

That fallback role is a strong argument for writing alt text even for images that seem self-evident, or that are accompanied by captions, because the fallback is needed precisely when the image is unavailable, which is also when the caption alone may not be sufficient. A caption that says "The first loaf from the new oven" tells the reader which loaf is meant, but not what it looked like; the alt text tells them what it looked like, and together, caption and alt text survive the image's failure better than either alone. It is also an argument for testing pages with images disabled, or with a simulated network failure, to see what the page looks like without its images. That test reveals, quickly, whether the page's meaning depends too heavily on images, and whether its alt text is sufficient to carry that meaning when the images are gone. A page that still makes sense with images disabled is a page whose text structure is sound; a page that becomes incomprehensible without images is a page whose information lives only in pictures, which is a fragile place for information to live.

There is a preventive side to this as well. Broken images are often caused by simple errors — a mistyped filename, a wrong folder, a case mismatch, because web servers are often case-sensitive while local development machines are not, so an image that loads on the developer's computer fails on the live server. Those errors are preventable by testing on a real server, or on a case-sensitive system, before publishing, and by keeping image filenames simple, lowercase, and free of spaces and special characters. But no prevention is perfect, and files will occasionally fail for reasons outside the author's control — a server outage, a network interruption, a content delivery problem. Alt text is the safety net for those occasions, and it costs nothing to provide, beyond the effort of writing it well in the first place. A broken image with good alt text is a minor inconvenience.

Check: What does a visitor see when an image file cannot be loaded?

Section 46 — Images inside links

An image can be the content of a link, making the image itself clickable, and that pattern is common: a product photograph that links to its product page, a thumbnail that links to a larger version, a logo that links to the home page, or a banner that links to a promotion. When an image is inside a link, the image's alt text takes on an additional job, beyond describing the image: it becomes the link's text, for readers who cannot see the image. A screen reader announcing a linked image does not announce it as an image and then as a link, separately; it announces it as a link, whose text is the image's alt text. That means the alt text must describe where the link goes, not merely what the picture shows, because the alt text is now the only information the listener has about the link's destination. An image of a loaf of bread, linking to its recipe, should not have alt text that merely says "A loaf of bread" — though that describes the picture accurately — but alt text that says where the link leads, such as "See the bread recipe", because that is what the link does, and that is what the listener needs to decide whether to follow it.

That rule has a corollary for images that are accompanied by text inside the same link. If a link contains both an image and text — a product photo and a product name, for example — then the image's alt text should be empty, because the text already describes the link's destination, and a descriptive alt text would duplicate it, causing the screen reader to announce the destination twice, once from the image and once from the text. Empty alt in that case is not a statement that the image is decorative in general; it is a statement that, in this link, the text already carries the link's meaning, and the image adds nothing to it for a listener. The general principle is that a link's accessible name — the text by which it is known to assistive technology — should be clear, unique, and non-duplicative, whether it comes from link text, from an image's alt text, or from a combination of both, and the author must ensure that, however it is composed, it describes the destination. That principle applies to all links, but it is most easily violated with linked images, because the author is thinking about the picture, not about the link, when they write the alt text.

There is also a visual consideration. A linked image should make it clear that it is clickable, because a visitor who does not realise an image is a link will not click it, and a visitor who clicks an image expecting it to enlarge, only to be taken to another page, will be surprised. The usual cues are a cursor change on hover, a border or underline on hover, or a caption that makes the link's purpose explicit, and those cues are a matter of design, covered in later chapters.

<a href="bread.html">
  <img src="bread.jpg" alt="See the bread recipe">
</a>

Check: When an image is a link, what should its alt text describe?

Section 47 — Text in images is a trap

Text baked into an image file — words rendered as pixels, as part of a photograph, a banner, or a graphic — is a trap, because it looks like text to a sighted reader, but it is not text to anything else. It cannot be selected, so a reader cannot copy a phrase from it to quote or to search. It cannot be searched, so a search engine cannot index the words, and a reader using find-in-page cannot locate them. It cannot be translated, so a translation tool cannot render it in another language. It cannot be read by a screen reader, so a blind reader receives no information from it at all, unless the author has duplicated the words in alt text, which is a workaround, not a solution, because the alt text is not the text itself, and it cannot be selected or searched either. And it cannot be resized cleanly: when an image containing text is scaled up, its text blurs, because pixels do not scale the way vector text does, and when it is scaled down, its text becomes illegible long before the image itself becomes too small to recognise. Real text, in HTML, suffers from none of those limitations, because it is text: selectable, searchable, translatable, readable by screen readers, and scalable without loss of sharpness.

The trap is tempting because image-based text can look exactly as the designer intended, in exactly the font, size, and arrangement they chose, without worrying about how different browsers render fonts, or whether a particular font is available on the visitor's device. That control is real, but it is bought at the cost of everything text is for, and the cost is paid by readers who need text to be text. The correct approach, in almost all cases, is to put real text in HTML, and to use images for pictures. If a design requires text over an image — a heading on top of a photograph, for example — the text should be HTML, positioned over the image with CSS, so that it remains real text, with all of text's advantages, while the image remains a picture beneath it. If a design requires a specific font that may not be available, web fonts provide a way to deliver that font as text, without baking it into an image. If a design requires a complex arrangement of words as a graphic — a logo, for example, where the letterforms are part of the artwork — then the words are arguably part of the image, not text, and they should be given alt text that conveys them, because they are the image's content, not its caption.

There are narrow exceptions. A logo's wordmark, a photograph of a street sign, a screenshot of a historical document — these contain text as part of their subject matter, and that text cannot be separated from the image without destroying the image. In those cases, the text-in-image is the content, and the author's job is to provide a text alternative that conveys the important words, in alt text or in surrounding text, so that readers who cannot see the image still receive its information.

Check: Name three things real HTML text can do that text inside an image cannot.

Section 48 — Long descriptions for complex images

Some images need more explanation than a short alt attribute can reasonably hold. A chart showing sales over a year, a diagram of a complex process, a map with many labelled regions, or a detailed infographic — these are images whose information cannot be compressed into a sentence or two without losing something essential. The alt attribute is designed for short equivalents, not for essays, and stuffing a paragraph into alt text produces a poor experience for screen reader users, who must listen to a long, unbroken passage in place of an image, without the ability to skim it, to return to a part of it, or to navigate within it, as they could with ordinary text. The correct approach for a complex image is to provide a short alt summary, which gives the image's overall message in a sentence, and to provide the full description elsewhere, in ordinary text that the reader can navigate, skim, and return to.

That full description can take several forms, and the best form depends on the image and the page. For a chart, the full description is often a data table, giving the numbers the chart visualises, in a form that can be read, searched, and copied, and that is useful to sighted readers as well as to screen reader users, who may prefer precise numbers to a visual impression. For a diagram, the full description may be a structured explanation, in paragraphs or lists, walking through the diagram's parts in order. For a map, it may be a list of regions and their values. In each case, the description should be visible on the page, or linked from near the image, not hidden in a way that only some readers can find it. A link immediately before or after the image, saying "Full data table for the chart below" or "Text description of the diagram", makes the description discoverable to everyone, and it signals to sighted readers, too, that a text version exists, which they may prefer for their own reasons — to copy numbers, to search for a value, or to read on a device where the image is hard to see.

The short alt summary and the full description work together, and neither replaces the other. The alt summary tells a screen reader user, quickly, what the image is about, so that they can decide whether to seek the full description, without having to listen to the full description first. The full description gives them the detail, in a navigable form, when they want it. A chart whose alt text says "Sales rising from January to June" gives the overall trend in a sentence, and a data table beneath it gives the monthly figures, for a reader who wants them. That pairing respects the reader's time and autonomy: it does not force a long description on someone who only needs the trend, and it does not withhold detail from someone who needs the numbers.

<p><a href="chart-data.html">Full data table for the chart below</a></p>
<img src="chart.png" alt="Sales rising from January to June">

Check: What should you do when alt text alone cannot explain a chart?

Section 49 — Keeping text readable over images

Text placed over a photograph is a common design pattern — a heading on a hero image, a caption on a gallery image, a label on a banner — and it is also a common source of unreadability, because photographs are rarely uniform. A heading in white text over a photograph may be perfectly readable where the photograph is dark, and invisible where it is light, and a heading that crosses from a dark region to a light region will be readable in one part and illegible in another, which is worse than being uniformly hard to read, because the reader cannot even tell where the text goes. The problem is contrast: text is readable when it contrasts strongly with its background, and a photograph, by its nature, provides a background whose contrast varies across its surface, unpredictably, and differently for every image. Designing text over an image without addressing that variation is designing for the best case — the part of the image where the contrast happens to work — and ignoring the rest.

The fixes are straightforward, and they all work by making the background behind the text predictable. The most common fix is a dark overlay: a semi-transparent dark layer between the image and the text, which darkens the whole image uniformly behind the text, so that white text contrasts with it everywhere, regardless of the photograph's original variation. Another fix is a solid background panel behind the text, which removes the photograph from behind the text entirely, in that region, giving the text a plain, high-contrast background. A third fix is to choose a quieter part of the image for the text — a sky, a wall, a blurred background — where the variation is minimal, though that fix depends on the image and cannot be relied on for every image. A fourth fix, often combined with the others, is to add a subtle shadow or outline to the text itself, which helps it stand out from a busy background, though a shadow alone is rarely sufficient for a truly busy image. What does not fix the problem is choosing a thinner font, a smaller size, or a more decorative typeface, and hoping the reader will manage — those choices make the problem worse, by reducing the text's own legibility on top of its poor contrast.

Readability over images is ultimately decided by contrast, not by taste, and contrast can be measured. Tools exist that calculate the contrast ratio between text and background, and guidelines exist for what ratios are sufficient for normal text and for large text, and those guidelines should be applied to text over images as rigorously as to text on plain backgrounds — with the added difficulty that the background is not a single colour, but a range, and the ratio should be sufficient for the worst part of that range, not the best. Testing is essential: view the text over the image on different screens, at different sizes, and in different lighting, and ask whether it is readable everywhere, not just in the designer's ideal view. A heading that is readable only on a large, bright screen, in a dark room, is not readable enough.

Check: What is the simplest fix for text that is hard to read over a photo?

Section 50 — Text, links, and images working together

A good page is not a collection of separately perfect elements; it is a mix, in which text, links, and images each contribute what the others cannot, and in which a weakness in any one of them weakens the whole. Text contributes meaning in words: it explains, argues, describes, and instructs, in a form that can be searched, translated, copied, and read aloud. It is the page's substance, and a page without good text is a page without substance, however attractive its images or numerous its links may be. Links contribute connection: they turn a single page into a node in a network, allowing a reader to move to related content, to sources, to definitions, and to actions, without leaving the flow of reading. A page without links is an island, and a page whose links are broken or misleading is an island with false bridges. Images contribute information that words convey poorly: appearance, atmosphere, spatial relationships, and concrete examples that a reader can grasp at a glance, in a way that a paragraph of description cannot match. A page without images may be complete, but it is often harder to grasp, less engaging, and less memorable than a page whose images show what its words describe.

The three work together in specific, practical ways. A heading organises text, and a link in that heading, or beneath it, connects the organised section to related content. An image illustrates a paragraph, and its alt text ensures that the illustration's information reaches readers who cannot see it, while its caption adds context that the paragraph alone does not give. A list organises a set of items, and links within the list turn those items into destinations, so that a list of products becomes a catalogue, and a list of articles becomes an index. A quotation adds another voice to the text, and a link from the quotation to its source lets the reader verify it, or read further. In each case, the combination is more than the sum of its parts, because each element supplies what the others lack: text supplies precision, links supply movement, images supply immediacy, and together they supply a reading experience that is precise, connected, and immediate at once.

That teamwork is also why a page must be judged as a whole, not element by element. A page with excellent text, but with link text that says "click here" throughout, and images without alt text, is not an excellent page; it is a page whose text is excellent and whose navigation and images fail the readers who depend on them. A page with beautiful images, but with text that is a single unbroken wall, and links that go nowhere, is not a beautiful page; it is a gallery without a guide. The standard for a finished page is that its text reads well, its links navigate well, and its images inform well, for every reader, in every medium, and that no one of the three is left to carry a weakness that the others could have compensated for. That standard is demanding, but it is also the standard that separates a page that merely displays content from a page that genuinely communicates it.

Check: In your own words, what does each of text, links, and images contribute to a page?

Practical task

Build a one-page article, in HTML only, about a topic you enjoy. It must have: an h1, at least three h2 sections, paragraphs using strong, em, and abbr at least once each, a blockquote, an ordered list of steps and an unordered list of items, a description list, three links (one to another page on your site, one to another site that opens in a new tab with rel="noopener", and one that jumps to a section on the same page), and two images — one meaningful image with good alt text and width and height, and one figure with a caption. Open the page with images disabled in your browser settings, or with a screen reader if you have one, and check that the page still makes sense.

Chapter questions

  1. Why is "click here" poor link text, and what would you write instead for a link to a bread recipe?
  2. Write the HTML for an image that is also a link, with alt text that makes sense as link text.
  3. When should an image have empty alt text, and when must it have descriptive alt text?
  4. Explain, in plain words, the difference between srcset and the picture element.
  5. A chart on your page cannot be explained in one sentence. What two things should you provide?