{"id":"l-a11y-2-1","courseId":"web-accessibility","lang":"en","kind":"lesson","summary":"Turn a page of anonymous divs into landmarks and a logical heading outline that screen reader users can scan and jump through in seconds.","html":"<p>Samir once described a badly built page to me as “a phone book with the names torn off”. Everything is there, but nothing tells him where one part ends and the next begins. Ticketly’s home page is exactly that: every block is a <code dir=\"ltr\">div</code>, and the biggest text is a <code dir=\"ltr\">div</code> with a large font. A sighted user sees a header, a menu, a festival title, a line-up and a footer. A screen reader sees one long stream of text.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Landmarks: the regions of a page</h2>\n<p>HTML has elements that describe the role of a whole region. The browser maps each to an ARIA <strong>landmark</strong>, and screen readers let users list landmarks or jump between them (the D key in NVDA, the rotor in VoiceOver).</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Element</th>\n<th>Landmark role</th>\n<th>Notes</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code dir=\"ltr\">&lt;header&gt;</code></td>\n<td>banner</td>\n<td>Only when it is a direct part of the page, not inside article, aside, main, nav or section</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;nav&gt;</code></td>\n<td>navigation</td>\n<td>Name it when there are several</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;main&gt;</code></td>\n<td>main</td>\n<td>Exactly one visible per page</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;aside&gt;</code></td>\n<td>complementary</td>\n<td>Related content such as a sidebar</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;footer&gt;</code></td>\n<td>contentinfo</td>\n<td>Same scoping rule as header</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;form&gt;</code>, <code dir=\"ltr\">&lt;section&gt;</code></td>\n<td>form, region</td>\n<td>Only when they have an accessible name</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">&lt;search&gt;</code></td>\n<td>search</td>\n<td>Wraps a site search form</td>\n</tr>\n</tbody>\n</table></div><p>Here is Ticketly’s skeleton before and after:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">before.html</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"top\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"logo\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Ticketly&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"links\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/events\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Events&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/faq\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>FAQ&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"content\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"title\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Harbour Lights 2026&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  …</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"bottom\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>© 2026 Ticketly · &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/terms\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Terms&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">div</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">after.html</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">header</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"logo\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Ticketly&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">nav</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-label</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Site\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/events\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Events&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/faq\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>FAQ&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">nav</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">header</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">main</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Harbour Lights 2026&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h1</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  …</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">main</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">footer</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>© 2026 Ticketly · &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> href</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"/terms\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Terms&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">footer</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>The visual design doesn’t change at all; these elements have no special styling beyond <code dir=\"ltr\">display: block</code>. What changes is the accessibility tree: Samir can now press D to land on “main landmark” and start reading the festival content, skipping the header entirely.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 300\" role=\"img\" aria-labelledby=\"l-a11y-2-1-t1\"><defs><marker id=\"arrow-l-a11y-2-1-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-a11y-2-1-t1\">The page is divided into a banner containing a site navigation, a main region holding the h1 Harbour Lights 2026 with two h2 sections, Line-up and Tickets, where Tickets contains two h3 items, and a contentinfo footer.</title>\n  <rect class=\"d-box-accent\" x=\"20\" y=\"15\" width=\"300\" height=\"50\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"35\" y=\"45\">header (banner)</text>\n  <rect class=\"d-box\" x=\"200\" y=\"23\" width=\"110\" height=\"34\" rx=\"6\"/>\n  <text class=\"d-label-muted\" x=\"255\" y=\"45\" text-anchor=\"middle\">nav \"Site\"</text>\n  <rect class=\"d-box-primary\" x=\"20\" y=\"80\" width=\"300\" height=\"150\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"35\" y=\"105\">main</text>\n  <rect class=\"d-box-accent\" x=\"20\" y=\"245\" width=\"300\" height=\"40\" rx=\"8\"/>\n  <text class=\"d-label\" x=\"35\" y=\"270\">footer (contentinfo)</text>\n  <text class=\"d-code\" x=\"380\" y=\"105\">h1 Harbour Lights 2026</text>\n  <text class=\"d-code\" x=\"410\" y=\"140\">h2 Line-up</text>\n  <text class=\"d-code\" x=\"410\" y=\"175\">h2 Tickets</text>\n  <text class=\"d-code\" x=\"440\" y=\"205\">h3 Friday pass</text>\n  <text class=\"d-code\" x=\"440\" y=\"232\">h3 Weekend pass</text>\n  <path class=\"d-arrow\" d=\"M320 110 L370 105\" marker-end=\"url(#arrow-l-a11y-2-1-1)\"/>\n  <path class=\"d-line\" d=\"M395 112 L395 175 L405 175\"/>\n  <path class=\"d-line\" d=\"M395 140 L405 140\"/>\n  <path class=\"d-line\" d=\"M425 182 L425 232 L435 232\"/>\n  <path class=\"d-line\" d=\"M425 205 L435 205\"/>\n</svg><figcaption>Ticketly’s landmarks and heading outline</figcaption></figure>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Wrapping everything in ARIA roles</span></p>\n<p>You may see <code dir=\"ltr\">&lt;div role=&quot;main&quot;&gt;</code> or <code dir=\"ltr\">&lt;nav role=&quot;navigation&quot;&gt;</code> in older code. The first works but is a longer way to write <code dir=\"ltr\">&lt;main&gt;</code>. The second is redundant noise. Use the HTML element; reach for a role only when you truly can’t change the element.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">Naming regions, and how many to use</h2>\n<p>A <code dir=\"ltr\">&lt;section&gt;</code> on its own is just a generic container. Give it a name and it becomes a <strong>region</strong> landmark that appears in the landmarks list. The neatest way is to point at its own heading, so the name and the visible text can never drift apart:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">HTML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">section</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> aria-labelledby</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"tickets-heading\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> id</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"tickets-heading\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Tickets&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  …</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">section</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>Don’t turn every section into a region, though. Landmarks work like the chapter list of a book: five to eight entries are useful, thirty are not. The rule of thumb is that every piece of content lives inside some landmark (automated checkers flag text that doesn’t), and that only the big, distinct areas a user might want to jump to get one of their own.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Headings: the outline people navigate</h2>\n<p>Landmarks are the rooms of the page; headings are the signs on the doors. Most screen reader users find content by jumping from heading to heading (H in NVDA and JAWS, or 1 to 6 for a specific level), or by opening a list of all headings and typing to filter it. That list is only useful if the headings form a real outline.</p>\n<p>Three rules cover almost every case:</p>\n<ol>\n<li><strong>One <code dir=\"ltr\">h1</code> that says what the page is.</strong> On Ticketly, “Harbour Lights 2026”, not the logo.</li>\n<li><strong>Levels describe nesting.</strong> A section’s subsections are one level deeper. Going back up is fine (h3 to h2); skipping down (h2 to h4) suggests a missing level.</li>\n<li><strong>Every heading has content under it</strong>, and every chunk of content worth finding has a heading.</li>\n</ol>\n<p>Ticketly’s tickets area uses <code dir=\"ltr\">h4</code> and <code dir=\"ltr\">h5</code> because a designer wanted smaller text. Fix the structure first, then style:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">HTML</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Tickets&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h3</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"pass-title\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Friday pass&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h3</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> class</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"pass-title\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Weekend pass&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h3</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"css\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">CSS</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">.pass-title</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  font-size</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">rem</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">  font-weight</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">600</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Check the outline in seconds</span></p>\n<p>Turn on a screen reader and press H repeatedly, or use a browser extension that lists headings. If the list reads like a table of contents for the page, you are done. If it reads “Ticketly, Menu, Newsletter, Follow us”, the headings describe decoration rather than content.</p>\n</aside>\n<p>Don’t forget the page’s <code dir=\"ltr\">&lt;title&gt;</code> either. It is the first thing a screen reader announces when the page loads and what appears in tabs and history (2.4.2 Page Titled). Put the specific part first: <code dir=\"ltr\">&lt;title&gt;Harbour Lights 2026 tickets · Ticketly&lt;/title&gt;</code>.</p>\n<p>With the skeleton in place, the next lesson tackles the parts users actually click: buttons, links, and the clickable divs pretending to be both.</p>\n","toc":[{"id":"sec-1","text":"Landmarks: the regions of a page","level":2},{"id":"sec-2","text":"Naming regions, and how many to use","level":2},{"id":"sec-3","text":"Headings: the outline people navigate","level":2}],"takeaways":["The elements header, nav, main, aside and footer create landmarks that screen reader users can list and jump between.","A page has one main landmark; when there are several nav or aside regions, give each a distinct accessible name.","Headings form the page’s outline; choose the level for structure, never for font size, and style the size with CSS.","Start with one h1 that names the page, and don’t skip levels on the way down."],"further":[{"title":"W3C WAI: Page Structure Tutorial","url":"https://www.w3.org/WAI/tutorials/page-structure/"},{"title":"MDN: The HTML section heading elements","url":"https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/Heading_Elements"},{"title":"W3C APG: Landmark Regions","url":"https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/"}],"quiz":[{"q":"<p>Ticketly has two navigation menus: the site menu at the top and a “Festival days” menu in the sidebar. A screen reader lists the landmarks as “navigation, navigation”. What is the best fix?</p>\n","options":[{"text":"Give each nav an accessible name, for example <code dir=\"ltr\">aria-label=&quot;Site&quot;</code> and <code dir=\"ltr\">aria-label=&quot;Festival days&quot;</code>","why":"Correct. Screen readers then announce “Site navigation” and “Festival days navigation”, so users can tell them apart."},{"text":"Change the sidebar menu to a div so only one navigation remains","why":"That removes a useful landmark instead of naming it. Users lose a fast way to reach the day menu."},{"text":"Add <code dir=\"ltr\">role=&quot;navigation&quot;</code> to both nav elements","why":"nav already has the navigation role; repeating it changes nothing about the duplicate announcement."}],"answer":0},{"q":"<p>A designer wants the “Friday pass” title to look small, so the developer used an h5 right after an h2. What is wrong, and what is the fix?</p>","options":[{"text":"Nothing; any heading level is fine as long as the text is descriptive","why":"Descriptive text matters, but levels convey hierarchy. Jumping from h2 to h5 suggests missing sections and confuses outline navigation."},{"text":"Use a bold paragraph instead, because headings shouldn’t be small","why":"A bold paragraph isn’t a heading at all, so it disappears from heading navigation. Headings can be any visual size."},{"text":"Use an h3 for the structure and make it small with CSS","why":"Correct. The level describes where the content sits in the outline; font size is a styling decision that belongs in CSS."}],"answer":2},{"q":"<p>Which element creates a landmark only when it has an accessible name?</p>","options":[{"text":"<code dir=\"ltr\">&lt;main&gt;</code>","why":"main is always the main landmark; it doesn’t need a name."},{"text":"<code dir=\"ltr\">&lt;nav&gt;</code>","why":"nav is always a navigation landmark. A name only helps tell several navs apart."},{"text":"<code dir=\"ltr\">&lt;footer&gt;</code> directly inside body","why":"A footer that isn’t nested inside article, aside, main, nav or section is always the contentinfo landmark."},{"text":"<code dir=\"ltr\">&lt;section&gt;</code>","why":"Correct. A section becomes a region landmark only when it is labelled, for example with aria-labelledby pointing to its heading."}],"answer":3},{"q":"<p>On a long Ticketly page, what does a screen reader user most often do first to find the “Refund policy” part?</p>","options":[{"text":"Listen to the whole page from the top","why":"That is the slowest strategy, and few experienced users do it on long pages."},{"text":"Press Tab repeatedly until they reach it","why":"Tab only visits interactive elements such as links and fields, so headings and paragraphs are skipped entirely."},{"text":"Jump between headings, for example with the H key in NVDA","why":"Correct. In WebAIM’s 2026 survey, 67.8% of screen reader users navigate long pages by headings first."}],"answer":2}],"exercise":{"kind":"web","mode":"dom","starter":{"html":"<div class=\"top\">\n  <div class=\"logo\">Ticketly</div>\n  <div class=\"links\">\n    <a href=\"#events\">Events</a>\n    <a href=\"#faq\">FAQ</a>\n    <a href=\"#account\">Account</a>\n  </div>\n</div>\n<div class=\"content\">\n  <div class=\"title\">Harbour Lights 2026</div>\n  <p>Two nights of music on the old harbour wall, 12 and 13 June.</p>\n  <h4>Line-up</h4>\n  <ul>\n    <li>Nour Quartet</li>\n    <li>The Saltmarsh Band</li>\n  </ul>\n  <h4>Tickets</h4>\n  <h5>Friday pass</h5>\n  <p>SAR 180, standing.</p>\n  <h5>Weekend pass</h5>\n  <p>SAR 320, both nights.</p>\n</div>\n<div class=\"bottom\">© 2026 Ticketly · <a href=\"#terms\">Terms</a></div>","css":".top { display: flex; justify-content: space-between; align-items: center; padding-block-end: 8px; border-block-end: 1px solid #ddd; }\n.logo { font-weight: 700; }\n.links { display: flex; gap: 12px; }\n.title { font-size: 1.75rem; font-weight: 700; margin: 16px 0 8px; }\nh2, h4 { font-size: 1.2rem; margin: 16px 0 4px; }\nh3, h5 { font-size: 1rem; margin: 8px 0 2px; }\n.bottom { margin-block-start: 24px; font-size: 0.875rem; color: #555; }\n","js":""},"solution":{"html":"<header class=\"top\">\n  <div class=\"logo\">Ticketly</div>\n  <nav class=\"links\" aria-label=\"Site\">\n    <a href=\"#events\">Events</a>\n    <a href=\"#faq\">FAQ</a>\n    <a href=\"#account\">Account</a>\n  </nav>\n</header>\n<main class=\"content\">\n  <h1 class=\"title\">Harbour Lights 2026</h1>\n  <p>Two nights of music on the old harbour wall, 12 and 13 June.</p>\n  <h2>Line-up</h2>\n  <ul>\n    <li>Nour Quartet</li>\n    <li>The Saltmarsh Band</li>\n  </ul>\n  <h2>Tickets</h2>\n  <h3>Friday pass</h3>\n  <p>SAR 180, standing.</p>\n  <h3>Weekend pass</h3>\n  <p>SAR 320, both nights.</p>\n</main>\n<footer class=\"bottom\">© 2026 Ticketly · <a href=\"#terms\">Terms</a></footer>","css":".top { display: flex; justify-content: space-between; align-items: center; padding-block-end: 8px; border-block-end: 1px solid #ddd; }\n.logo { font-weight: 700; }\n.links { display: flex; gap: 12px; }\n.title { font-size: 1.75rem; font-weight: 700; margin: 16px 0 8px; }\nh2, h4 { font-size: 1.2rem; margin: 16px 0 4px; }\nh3, h5 { font-size: 1rem; margin: 8px 0 2px; }\n.bottom { margin-block-start: 24px; font-size: 0.875rem; color: #555; }\n","js":""},"checks":[{"id":"one-main","test":"const m = $$('main, [role=\"main\"]'); return m.length === 1 && m[0].textContent.includes('Harbour Lights 2026') && m[0].textContent.includes('Weekend pass');"},{"id":"banner-nav","test":"const h = $('header'); if (!h || h.closest('main, article, aside, nav, section')) return false; const nav = $('nav'); return !!nav && h.contains(nav) && nav.textContent.includes('Events') && nav.textContent.includes('Account');"},{"id":"footer","test":"const f = $('footer'); return !!f && !f.closest('main, article, aside, nav, section') && f.textContent.includes('Terms');"},{"id":"one-h1","test":"const h1 = $$('h1'); return h1.length === 1 && h1[0].textContent.trim() === 'Harbour Lights 2026';"},{"id":"heading-order","test":"const hs = $$('h1, h2, h3, h4, h5, h6'); const names = hs.map((h) => h.textContent.trim()); if (!['Line-up', 'Tickets', 'Friday pass', 'Weekend pass'].every((n) => names.includes(n))) return false; const lv = hs.map((h) => Number(h.tagName[1])); if (lv[0] !== 1) return false; for (let i = 1; i < lv.length; i++) if (lv[i] - lv[i - 1] > 1) return false; const lvl = (n) => lv[names.indexOf(n)]; return lvl('Friday pass') === lvl('Tickets') + 1 && lvl('Line-up') === lvl('Tickets');"}],"title":"Give Ticketly a skeleton","prompt":"<p>Ticketly’s home page is built from anonymous divs, and its headings were chosen for their font size. Restructure the markup without changing the text:</p>\n<ul>\n<li>Wrap the logo and the menu in a <code dir=\"ltr\">&lt;header&gt;</code>, and make the menu a <code dir=\"ltr\">&lt;nav&gt;</code> (give it a name such as <code dir=\"ltr\">aria-label=&quot;Site&quot;</code>).</li>\n<li>Put the festival content in a single <code dir=\"ltr\">&lt;main&gt;</code>.</li>\n<li>Make the copyright line a <code dir=\"ltr\">&lt;footer&gt;</code> outside <code dir=\"ltr\">main</code>.</li>\n<li>Turn “Harbour Lights 2026” into the only <code dir=\"ltr\">&lt;h1&gt;</code>, and fix the other headings so the outline has no skipped levels: Line-up and Tickets are sections; the two passes belong under Tickets.</li>\n</ul>\n","hints":["You can keep the existing class names on the new elements, so the CSS still applies.","Line-up and Tickets sit directly under the h1, so they are h2. The passes sit under Tickets.","header &gt; nav, then main &gt; h1, h2, h2, h3, h3, then footer."],"explanation":"<p>Landmark elements cost nothing visually but give screen reader users a map: header (banner), nav (navigation), main and footer (contentinfo). The heading levels now describe nesting rather than size: one h1 for the page, h2 for its two sections, and h3 for the passes inside Tickets. The CSS keeps the passes small, because size is a styling decision. Navigating by headings now reads like a table of contents: Harbour Lights 2026, Line-up, Tickets, Friday pass, Weekend pass.</p>\n","checkLabels":{"one-main":"There is exactly one main landmark holding the festival content","banner-nav":"A top-level header contains a nav with the menu links","footer":"A top-level footer contains the Terms link","one-h1":"“Harbour Lights 2026” is the page’s only h1","heading-order":"Headings start at h1 and never skip a level; the passes sit one level under Tickets"}},"words":703,"readMinutes":3}