$npx -y skills add mgifford/accessibility-skills --skill svgLoad this skill whenever the project contains SVG graphics — inline SVGs, external SVG files, SVG icons, SVG illustrations, or SVG-based data visualizations. Under no circumstances use SVG without proper accessible titles, descriptions, and ARIA roles where required. Absolutely a
| 1 | # SVG Accessibility Skill |
| 2 | |
| 3 | > **Canonical source**: `examples/SVG_ACCESSIBILITY_BEST_PRACTICES.md` in `mgifford/ACCESSIBILITY.md` |
| 4 | > This skill is derived from that file. When in doubt, the example is authoritative. |
| 5 | |
| 6 | Apply these rules when creating, optimising, or reviewing SVG graphics. |
| 7 | **Only load this skill if the project contains SVGs.** |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## Core Mandate |
| 12 | |
| 13 | SVG accessibility depends on the graphic's purpose and how it is embedded. A |
| 14 | decorative icon, an informative image, an icon inside a button, and an |
| 15 | interactive diagram require different patterns. Start by deciding what users |
| 16 | need from the graphic, then provide the simplest semantic implementation that |
| 17 | delivers the same information or function. The presence of `<title>`, |
| 18 | `<desc>`, `role="img"`, or `aria-label` does not by itself make an SVG |
| 19 | accessible — test the result in its final context. |
| 20 | |
| 21 | SVG is also an active XML format that can contain scripts, event handlers, |
| 22 | and external resource references. **Untrusted SVG (uploads, pasted markup, |
| 23 | AI-generated output) must be sanitized before rendering** — see the Security |
| 24 | section below. |
| 25 | |
| 26 | --- |
| 27 | |
| 28 | ## Severity Scale (this skill) |
| 29 | |
| 30 | | Level | Meaning | |
| 31 | | --- | --- | |
| 32 | | **Critical** | SVG conveys essential information with no accessible alternative; untrusted SVG rendered without sanitization | |
| 33 | | **Serious** | SVG is interactive but unreachable or unlabelled for AT users | |
| 34 | | **Moderate** | AT experience degraded but content still partially accessible | |
| 35 | | **Minor** | Best-practice gap; marginal impact | |
| 36 | |
| 37 | --- |
| 38 | |
| 39 | ## Choose the Pattern by Purpose and Context |
| 40 | |
| 41 | | Context | Preferred pattern | |
| 42 | |:---|:---| |
| 43 | | Decorative SVG loaded with `<img>` | `<img src="…" alt="">` | |
| 44 | | Meaningful SVG loaded with `<img>` | `<img src="…" alt="Useful description">` | |
| 45 | | Decorative inline SVG | `<svg aria-hidden="true">…</svg>` | |
| 46 | | Simple meaningful inline SVG | `<svg role="img" aria-labelledby="…"><title>…</title></svg>` | |
| 47 | | Icon inside a named button or link | Name the HTML control and hide the SVG | |
| 48 | | Complex graphic | Short name plus visible summary, data, or detailed description | |
| 49 | | Reused sprite icon | Label or hide each outer `<svg>` instance, not the shared symbol | |
| 50 | | Interactive graphic | Prefer native HTML controls; provide an equivalent structured alternative | |
| 51 | |
| 52 | Use native HTML semantics before ARIA. Do not add `role="img"` to an `<img>` |
| 53 | merely because its source is an SVG file — that's redundant. |
| 54 | |
| 55 | --- |
| 56 | |
| 57 | ## Critical: Decorative SVGs Must Be Hidden Completely from AT |
| 58 | |
| 59 | ```html |
| 60 | <svg aria-hidden="true" focusable="false"> |
| 61 | <!-- decorative icon — no title, no desc, no role needed --> |
| 62 | </svg> |
| 63 | ``` |
| 64 | |
| 65 | `aria-hidden="true"` removes the SVG and its descendants from the |
| 66 | accessibility tree — do not place focusable or interactive content inside an |
| 67 | element hidden this way. `focusable="false"` addresses legacy IE/older Edge |
| 68 | behavior; it can remain for projects still supporting those browsers but |
| 69 | isn't a universal modern requirement. Do not add `<title>`/`<desc>` to an |
| 70 | intentionally-hidden SVG. **Never use `role="presentation"` as a substitute** |
| 71 | for `aria-hidden="true"` — it does not reliably suppress announcement across |
| 72 | browser/screen-reader combinations. |
| 73 | |
| 74 | **When is an SVG decorative?** If the same meaning is conveyed by adjacent |
| 75 | visible text, or the image is purely ornamental, it's decorative. If removing |
| 76 | it would leave a user without needed information, it's meaningful. |
| 77 | |
| 78 | --- |
| 79 | |
| 80 | ## External SVG Used with `<img>` |
| 81 | |
| 82 | Follows the same text-alternative rules as other image formats. |
| 83 | |
| 84 | ```html |
| 85 | <!-- Informative --> |
| 86 | <img src="/assets/images/service-area.svg" alt="Map showing the service area extending from Kingston to Ottawa" width="640" height="360"> |
| 87 | |
| 88 | <!-- Decorative --> |
| 89 | <img src="/assets/images/flourish.svg" alt="" width="120" height="24"> |
| 90 | |
| 91 | <!-- Functional (destination-describing) --> |
| 92 | <a href="/reports/annual-report.pdf"> |
| 93 | <img src="/assets/icons/download.svg" alt="Download annual report"> |
| 94 | </a> |
| 95 | ``` |
| 96 | |
| 97 | `role="img"` is redundant on `<img>`; `aria-label` unnecessary when `alt` can |
| 98 | provide the name. **Internal SVG `<title>`/`<desc>` does not replace HTML |
| 99 | `alt`** when loaded through `<img>` — the HTML alt is what's read. |
| 100 | |
| 101 | --- |
| 102 | |
| 103 | ## Serious: Simple Meaningful Inline SVG |
| 104 | |
| 105 | ```html |
| 106 | <svg role="img" aria-labelledby="download-icon-title" viewBox="0 0 24 24"> |
| 107 | <title id="download-icon-title">Download</title> |
| 108 | <path d="M12 3v12m0 0 5-5m-5 5-5-5M5 21h14"></path> |
| 109 | </svg> |
| 110 | ``` |
| 111 | |
| 112 | The `<title>` must be a direct child |