{"id":"l-a11y-2-4","courseId":"web-accessibility","lang":"en","kind":"lesson","summary":"Decide what alt text each image needs from its purpose in context, and mark up data tables so screen readers announce the right headers for every cell.","html":"<p>Every image on Ticketly makes a promise: “this tells you something” or “this is decoration”. Alt text is how you keep that promise for people who can’t see the image. The hard part isn’t the syntax, <code dir=\"ltr\">alt=&quot;…&quot;</code> is easy. It is the decision about what, if anything, to say.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">Alt text describes purpose, not pixels</h2>\n<p>Start from one question: <strong>what would you lose if this image were removed?</strong> That is what the alt text should give back.</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Image on Ticketly</th>\n<th>Purpose</th>\n<th>Alt</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>Photo of the Nour Quartet on their artist card</td>\n<td>Informative</td>\n<td><code dir=\"ltr\">alt=&quot;Nour Quartet performing on a lit harbour stage&quot;</code></td>\n</tr>\n<tr>\n<td>Logo inside a link to the home page</td>\n<td>Functional</td>\n<td><code dir=\"ltr\">alt=&quot;Ticketly home&quot;</code></td>\n</tr>\n<tr>\n<td>Magnifying-glass icon as the only content of the search button</td>\n<td>Functional</td>\n<td><code dir=\"ltr\">alt=&quot;Search&quot;</code></td>\n</tr>\n<tr>\n<td>Wavy line separating sections</td>\n<td>Decorative</td>\n<td><code dir=\"ltr\">alt=&quot;&quot;</code></td>\n</tr>\n<tr>\n<td>Crowd photo behind a headline that already says it all</td>\n<td>Decorative in context</td>\n<td><code dir=\"ltr\">alt=&quot;&quot;</code></td>\n</tr>\n<tr>\n<td>“Sold out” badge drawn as an image</td>\n<td>Text in an image</td>\n<td><code dir=\"ltr\">alt=&quot;Sold out&quot;</code></td>\n</tr>\n<tr>\n<td>Seat map</td>\n<td>Complex</td>\n<td>Short alt plus a full alternative nearby</td>\n</tr>\n</tbody>\n</table></div><p>Context changes the answer. The same crowd photo is decorative behind a headline, but informative on a page called “What the venue is like”, where it needs a description.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">ticketly/lineup.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\">article</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\">\"artist\"</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\">img</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> src</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"nour-quartet.jpg\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> alt</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Nour Quartet performing on a lit harbour stage\"</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:#1F2328;--shiki-dark:#E6EDF3\">>Nour Quartet&#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\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Jazz and Arabic maqam from Alexandria.&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</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\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>A few habits make alt text better:</p>\n<ul>\n<li><strong>Don’t start with “image of” or “photo of”.</strong> Screen readers already announce “image” (or “graphic”). Say “photo” only when the medium matters, such as “Hand-drawn poster of…”.</li>\n<li><strong>Don’t repeat nearby text.</strong> If the caption says “Nour Quartet”, the alt shouldn’t repeat it word for word; describe what the image adds.</li>\n<li><strong>Keep it short.</strong> One sentence covers most images. Long descriptions belong in the page text.</li>\n</ul>\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>Empty alt on the only content of a link or button</span></p>\n<p><code dir=\"ltr\">&lt;button&gt;&lt;img src=&quot;search.svg&quot; alt=&quot;&quot;&gt;&lt;/button&gt;</code> has no accessible name, so it is announced as “button” and nothing else. This is the “empty button” failure that appears on almost a third of home pages. When an image is the whole content of a control, its alt names the action.</p>\n</aside>\n<h2 id=\"sec-2\" tabindex=\"-1\">Decorative images and icons</h2>\n<p>For decorative images, use an <strong>empty</strong> alt: <code dir=\"ltr\">alt=&quot;&quot;</code>, with nothing between the quotes. A missing attribute is different: many screen readers then announce the file name, such as “hero underscore final underscore v3 dot jpg”. Images added with CSS <code dir=\"ltr\">background-image</code> are invisible to assistive technology, which is fine for decoration but wrong for anything that carries meaning.</p>\n<p>Inline SVG icons follow the same logic with different attributes. Decorative icons get <code dir=\"ltr\">aria-hidden=&quot;true&quot;</code>, as you did in <a href=\"/lesson/web-accessibility/l-a11y-1-2\">How Assistive Technology Reads Your Page</a>. An SVG that carries meaning on its own gets <code dir=\"ltr\">role=&quot;img&quot;</code> and a name:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"1\"><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\">svg</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> role</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"img\"</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\">\"Wheelchair accessible venue\"</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> viewBox</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"0 0 24 24\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>…&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">svg</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<h2 id=\"sec-3\" tabindex=\"-1\">Complex images</h2>\n<p>Charts, maps and diagrams hold more information than a sentence can carry. Give them a short alt that states the takeaway, and put the full information in the page: a data table under a chart, a list of sections and availability next to a seat map, or a <code dir=\"ltr\">&lt;details&gt;</code> element holding a longer description. Ticketly’s seat map gets a list of blocks (“Block A, front left, 12 seats left”) that is useful to everyone comparing options, not only to screen reader users.</p>\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>Use the decision tree</span></p>\n<p>When you are unsure, W3C’s alt decision tree walks you through five questions in under a minute. Most teams find that after a week of using it they don’t need it any more.</p>\n</aside>\n<h2 id=\"sec-4\" tabindex=\"-1\">Data tables</h2>\n<p>Ticketly’s pricing grid was built from divs styled with CSS grid. Sighted users read it at a glance; a screen reader reads “Pass, Early bird, Standard, Weekend, SAR 280, SAR 320” as a flat list with no way to know which price belongs to which pass. A real table fixes it:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">ticketly/pricing.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\">table</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\">caption</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Harbour Lights ticket prices&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">caption</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\">thead</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\">tr</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\">th</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> scope</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"col\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Pass&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</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\">th</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> scope</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"col\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Early bird&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</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\">th</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> scope</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"col\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Standard&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</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\">tr</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\">thead</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\">tbody</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\">tr</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> scope</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"row\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Friday&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>SAR 150&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>SAR 180&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">tr</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\">tr</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> scope</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"row\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Weekend&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>SAR 280&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>SAR 320&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">tr</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\">tbody</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\">table</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 640 210\" role=\"img\" aria-labelledby=\"l-a11y-2-4-t1\"><defs><marker id=\"arrow-l-a11y-2-4-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-4-t1\">A three by three price table. The highlighted cell SAR 320 is linked to its row header Weekend and its column header Standard; the screen reader announces Weekend, Standard, SAR 320.</title>\n  <rect class=\"d-box-accent\" x=\"20\" y=\"20\" width=\"130\" height=\"40\"/>\n  <text class=\"d-label-strong\" x=\"85\" y=\"45\" text-anchor=\"middle\">Pass</text>\n  <rect class=\"d-box-accent\" x=\"150\" y=\"20\" width=\"130\" height=\"40\"/>\n  <text class=\"d-label-strong\" x=\"215\" y=\"45\" text-anchor=\"middle\">Early bird</text>\n  <rect class=\"d-box-accent\" x=\"280\" y=\"20\" width=\"130\" height=\"40\"/>\n  <text class=\"d-label-strong\" x=\"345\" y=\"45\" text-anchor=\"middle\">Standard</text>\n  <rect class=\"d-box-accent\" x=\"20\" y=\"60\" width=\"130\" height=\"40\"/>\n  <text class=\"d-label-strong\" x=\"85\" y=\"85\" text-anchor=\"middle\">Friday</text>\n  <rect class=\"d-box\" x=\"150\" y=\"60\" width=\"130\" height=\"40\"/>\n  <text class=\"d-label\" x=\"215\" y=\"85\" text-anchor=\"middle\">SAR 150</text>\n  <rect class=\"d-box\" x=\"280\" y=\"60\" width=\"130\" height=\"40\"/>\n  <text class=\"d-label\" x=\"345\" y=\"85\" text-anchor=\"middle\">SAR 180</text>\n  <rect class=\"d-box-accent\" x=\"20\" y=\"100\" width=\"130\" height=\"40\"/>\n  <text class=\"d-label-strong\" x=\"85\" y=\"125\" text-anchor=\"middle\">Weekend</text>\n  <rect class=\"d-box\" x=\"150\" y=\"100\" width=\"130\" height=\"40\"/>\n  <text class=\"d-label\" x=\"215\" y=\"125\" text-anchor=\"middle\">SAR 280</text>\n  <rect class=\"d-box-primary\" x=\"280\" y=\"100\" width=\"130\" height=\"40\"/>\n  <text class=\"d-label-strong\" x=\"345\" y=\"125\" text-anchor=\"middle\">SAR 320</text>\n  <path class=\"d-arrow\" d=\"M410 120 L460 120\" marker-end=\"url(#arrow-l-a11y-2-4-1)\"/>\n  <text class=\"d-label\" x=\"470\" y=\"115\">\"Weekend,</text>\n  <text class=\"d-label\" x=\"470\" y=\"137\">Standard, SAR 320\"</text>\n  <path class=\"d-dashed\" d=\"M345 60 L345 100\"/>\n  <path class=\"d-dashed\" d=\"M150 120 L280 120\"/>\n  <text class=\"d-label-muted\" x=\"215\" y=\"185\" text-anchor=\"middle\">th scope=\"row\" and th scope=\"col\" supply the context</text>\n</svg><figcaption>How a screen reader announces a table cell</figcaption></figure>\n<p>The <code dir=\"ltr\">&lt;caption&gt;</code> names the table when the user enters it. <code dir=\"ltr\">scope=&quot;col&quot;</code> and <code dir=\"ltr\">scope=&quot;row&quot;</code> tell the browser which cells each header covers. Screen readers then offer table navigation (Ctrl+Alt+arrow keys in NVDA, VO+arrow keys in VoiceOver) and announce the relevant headers as the user moves. Keep tables simple: if you need merged headers several levels deep, splitting into two tables is usually clearer for everyone.</p>\n<p>Use tables for data, never for layout. And be careful with responsive tricks: setting <code dir=\"ltr\">display: block</code> or <code dir=\"ltr\">display: grid</code> on table elements can strip their table semantics in some browsers, so test with a screen reader if you restyle them.</p>\n<p>That completes Ticketly’s semantic foundation. Section 3 moves from what the page is to how people move through it, starting with the keyboard.</p>\n","toc":[{"id":"sec-1","text":"Alt text describes purpose, not pixels","level":2},{"id":"sec-2","text":"Decorative images and icons","level":2},{"id":"sec-3","text":"Complex images","level":2},{"id":"sec-4","text":"Data tables","level":2}],"takeaways":["Alt text describes an image’s purpose in its context, so the same photo can need different alt text on different pages.","Decorative images get alt=“” so screen readers skip them; a missing alt attribute often makes them read the file name.","An image that is the only content of a link or button needs alt text naming the destination or action.","Data tables need a caption and th cells with scope, so each cell is announced with its row and column headers."],"further":[{"title":"W3C WAI: An alt Decision Tree","url":"https://www.w3.org/WAI/tutorials/images/decision-tree/"},{"title":"W3C WAI: Tables Tutorial","url":"https://www.w3.org/WAI/tutorials/tables/"},{"title":"WebAIM: Alternative Text","url":"https://webaim.org/techniques/alttext/"}],"quiz":[{"q":"<p>Ticketly’s header logo is an image inside <code dir=\"ltr\">&lt;a href=&quot;/&quot;&gt;</code>. What alt text should it have?</p>","options":[{"text":"<code dir=\"ltr\">alt=&quot;&quot;</code>, because logos are decorative","why":"Inside a link the image is the link’s only content. An empty alt leaves the link with no name, so screen readers announce just “link”."},{"text":"<code dir=\"ltr\">alt=&quot;Ticketly home&quot;</code>","why":"Correct. A functional image describes what activating it does. “Ticketly” alone is also acceptable; adding “home” makes the destination explicit."},{"text":"<code dir=\"ltr\">alt=&quot;Ticketly logo, purple ticket icon&quot;</code>","why":"The visual description doesn’t help anyone choose to follow the link. Describe the function, not the artwork."}],"answer":1},{"q":"<p>What happens when an <code dir=\"ltr\">img</code> has no alt attribute at all?</p>","options":[{"text":"Screen readers skip it, the same as alt=“”","why":"Only an explicitly empty alt marks an image as decorative. Without the attribute, screen readers try to say something about it."},{"text":"The browser generates alt text automatically from the image","why":"Some screen readers and browsers can generate AI descriptions if the user opts in, but nothing fills in alt by default, and you can’t rely on users having that feature or on it describing the purpose correctly."},{"text":"Many screen readers announce the file name or URL, such as “IMG underscore 4521 dot jpg”","why":"Correct. That is noise at best and confusing at worst, which is why missing alt is the second most common failure on the web."}],"answer":2},{"q":"<p>In this pricing table, what does a screen reader announce when the user moves to the “SAR 320” cell?</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"html\" data-index=\"0\"><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\">table</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\">caption</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Harbour Lights ticket prices&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">caption</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\">tr</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> scope</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"col\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Pass&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> scope</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"col\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Early bird&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> scope</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"col\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Standard&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">tr</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\">tr</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> scope</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"row\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Weekend&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">th</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>SAR 280&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>SAR 320&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">td</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">tr</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\">table</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n","options":[{"text":"“Weekend, Standard, SAR 320”","why":"Correct. With th and scope, the screen reader announces the row header and column header the user hasn’t heard yet, then the value."},{"text":"“SAR 320” only","why":"That is what happens when headers are plain td cells. Here the th elements give each cell its context."},{"text":"“Harbour Lights ticket prices, SAR 320”","why":"The caption names the whole table when the user enters it; it isn’t repeated for every cell."}],"answer":0},{"q":"<p>A Ticketly line chart shows ticket sales per day. What is the most useful text alternative?</p>","options":[{"text":"<code dir=\"ltr\">alt=&quot;Chart&quot;</code>","why":"That says what the image is but not what it means. A screen reader user still knows nothing about sales."},{"text":"<code dir=\"ltr\">alt=&quot;&quot;</code>, because charts are too complex to describe","why":"The chart carries information, so hiding it removes that information for screen reader users entirely."},{"text":"A short alt with the key insight, plus the full data in a table or text nearby","why":"Correct. For example “Daily ticket sales, peaking at 1,200 on launch day”, with the numbers in a table for anyone who wants detail."}],"answer":2}],"exercise":{"kind":"choice","multiple":true,"options":["logo-link","seat-map","artist-file","hero-ambient","search-empty","sold-badge"],"correct":["logo-link","hero-ambient","sold-badge"],"title":"Review Ticketly's alt text","prompt":"<p>A teammate added alt text to every image on Ticketly and asked you to review it. Select <strong>every decision that is correct</strong> as written. Think about each image’s purpose in its context, not just what it shows.</p>\n","hints":["Ask of each image: informative, functional, decorative, text, or complex?","An image that is the only content of a link or button must name the destination or action.","Three of the six decisions are correct."],"explanation":"<p>The logo link names its destination, the ambient crowd photo is decorative because the headline and paragraph already say everything, and the badge image of the words “Sold out” repeats those words. The other three fail: “Seat map” says what the image is but none of the availability it shows, so it needs a short summary plus a list of blocks nearby; “Image of nour_quartet_final.jpg” is a file name with a redundant prefix; and the search icon with alt=“” leaves its button with no accessible name at all.</p>\n","checkLabels":{},"optionText":{"logo-link":{"text":"Logo image inside <code dir=\"ltr\">&lt;a href=&quot;/&quot;&gt;</code>: <code dir=\"ltr\">alt=&quot;Ticketly home&quot;</code>","why":"Correct. The image is the link’s only content, so its alt names where the link goes."},"seat-map":{"text":"Seat map showing which blocks still have seats: <code dir=\"ltr\">alt=&quot;Seat map&quot;</code>","why":"Not enough. It names the image type but none of its information. Give a short summary and a text list of blocks and availability."},"artist-file":{"text":"Artist photo on the Nour Quartet card: <code dir=\"ltr\">alt=&quot;Image of nour_quartet_final.jpg&quot;</code>","why":"Wrong. Screen readers already say “image”, and a file name tells users nothing. Describe what the photo shows that matters, such as the band on stage."},"hero-ambient":{"text":"Blurred crowd photo behind the headline “Harbour Lights 2026”, whose paragraph already describes the event: <code dir=\"ltr\">alt=&quot;&quot;</code>","why":"Correct. In this context the photo is atmosphere; the text carries the meaning, so an empty alt lets screen readers skip it."},"search-empty":{"text":"Magnifying-glass image as the only content of the search button: <code dir=\"ltr\">alt=&quot;&quot;</code>","why":"Wrong. The button now has no name and is announced as just “button”. Use alt=“Search”."},"sold-badge":{"text":"Image of the words “Sold out” on the Friday card: <code dir=\"ltr\">alt=&quot;Sold out&quot;</code>","why":"Correct. For images of text, the alt repeats the text. Even better would be real text styled with CSS."}}},"words":751,"readMinutes":4}