Skip to content

Skill · 08 of 09

a11y usable by keyboard, screen reader, zoom and reduced motion

Accessibility as craft for websites and web apps, targeting WCAG 2.2 AA with APCA as the quality bar.

skills/a11y/SKILL.md160 lines1 referencesupdated 16 Sept 2026

When Claude Code loads it

Use when building or reviewing any UI, component, landing page, form, modal, dropdown, navigation or interactive widget; when choosing colors or focus styles; when adding animation; when asked about accessibility, a11y, WCAG, ARIA, screen readers or keyboard navigation; or when `void a11y`, axe or Lighthouse report accessibility issues.

The target is WCAG 2.2 AA as the hard gate and a Lighthouse/axe score of 100, with APCA as the perceptual quality bar. Accessible markup also makes pages readable to AI agents and crawlers (see the seo skill). Paste-ready components: references/patterns.md. Visual tokens and focus ring styling: the craft skill.

The 10 rules that matter most

  1. Use the native element. navigates,
  2. Every interactive element is reachable and operable by keyboard in DOM order. No positive tabindex. No keyboard traps except inside modal dialogs.
  3. Visible focus everywhere: :focus-visible ring 2px solid --line-focus, offset 2px, ≥ 3:1 against the surface. Never remove an outline without a replacement.
  4. One
    , one

    , headings in order, with landmarks
    ,

  5. Contrast: body text ≥ 4.5:1 (APCA Lc 75+), large text ≥ 3:1, UI borders, icons and focus rings ≥ 3:1. Hover and focus states increase contrast, never decrease it.
  6. Hit targets ≥ 24×24 CSS px (WCAG 2.5.8), ≥ 44×44 on touch. Grow the hit area with a pseudo-element, not by changing the layout.
  7. Every image has alt. Informative images get a description of their content; decorative ones get alt="". Icon-only buttons have an accessible name (aria-label or sr-only text).
  8. Forms: a visible per field (a placeholder is not a label), correct type/autocomplete, errors as text linked with aria-describedby + aria-invalid, and focus moves to the first invalid field on submit.
  9. Dialogs, menus, comboboxes and tabs use a proven primitive (native , Radix, Base UI, React Aria). Don't hand-roll focus traps.
  10. Respect prefers-reduced-motion. Nothing flashes more than 3×/s, and auto-moving content over 5 s has a pause control. Content never depends on an animation finishing.

Structure

tsx
// app/layout.tsx (inside <body>)
<a href="#main" className="sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-50 focus:rounded-md focus:bg-surface-raised focus:px-4 focus:py-2 focus:text-fg">
  Skip to content
</a>
<header>…<nav aria-label="Primary">…</nav></header>
<main id="main">{children}</main>  {/* modern browsers move the focus start point to the target; no tabIndex needed */}
<footer>…</footer>
  • Exactly one
    . A layout
    plus a page
    gives two landmarks and a broken skip link. Pages render
    /
    inside it.
  • Multiple
  • Headings describe their section. Don't skip levels going down (h2 → h4). Don't pick heading levels for font size; style with tokens instead.
  • matches the content. Mark phrases in another language with lang.
  • </code> names the page and site ("Pricing | Acme"). Client route changes in Next update it automatically.</li> <li>Lists are <code><ul></code>/<code><ol></code>, tables are <code><div class="table-wrap" tabindex="0" role="region" aria-label="Table 1"><table></code> with <code><th scope></code>, times are <code><time dateTime></code>.</li> <li>Links say where they go ("Read the pricing guide"), not "click here". Links that open a new tab say so (visible icon + <code>sr-only</code> "(opens in new tab)").</li> </ul> <h2 id="keyboard-and-focus"><a class="heading-anchor" href="#keyboard-and-focus">Keyboard and focus</a></h2> <figure class="code-block" data-lang="css"><figcaption><span>css</span><button type="button" class="copy-button" data-copy>Copy</button></figcaption><pre tabindex="0" aria-label="css code" class="shiki void" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-comment)">/* app/globals.css: base layer (Tailwind v4) */</span></span> <span class="line"><span style="color:var(--shiki-token-keyword)">@layer</span><span style="color:var(--shiki-foreground)"> base {</span></span> <span class="line"><span style="color:var(--shiki-token-function)"> :where</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">a</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-string-expression)"> button</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-string-expression)"> input</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-string-expression)"> select</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-string-expression)"> textarea</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-string-expression)"> summary</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-foreground)"> [</span><span style="color:var(--shiki-token-function)">tabindex</span><span style="color:var(--shiki-foreground)">])</span><span style="color:var(--shiki-token-function)">:focus-visible</span><span style="color:var(--shiki-foreground)"> {</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> outline</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 2</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-token-constant)"> solid </span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--line-focus)</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-token-constant)"> outline-offset</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 2</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-foreground)">;</span></span> <span class="line"><span style="color:var(--shiki-foreground)"> }</span></span> <span class="line"><span style="color:var(--shiki-token-function)"> :where</span><span style="color:var(--shiki-foreground)">(</span><span style="color:var(--shiki-token-string-expression)">input</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-string-expression)"> textarea</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-string-expression)"> select</span><span style="color:var(--shiki-foreground)">)</span><span style="color:var(--shiki-token-function)">:focus-visible</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-constant)">outline-offset</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> 0</span><span style="color:var(--shiki-foreground)">; }</span></span> <span class="line"><span style="color:var(--shiki-token-string-expression)"> html</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-constant)">scroll-padding-top</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-function)"> calc</span><span style="color:var(--shiki-token-constant)">(</span><span style="color:var(--shiki-token-function)">var</span><span style="color:var(--shiki-token-constant)">(--header-h</span><span style="color:var(--shiki-token-punctuation)">,</span><span style="color:var(--shiki-token-constant)"> 64</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-token-constant)">) </span><span style="color:var(--shiki-token-keyword)">+</span><span style="color:var(--shiki-token-constant)"> 16</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-token-constant)">)</span><span style="color:var(--shiki-foreground)">; } </span><span style="color:var(--shiki-token-comment)">/* sticky header never hides focused elements (2.4.11) */</span></span> <span class="line"><span style="color:var(--shiki-foreground)">}</span></span></code></pre></figure> <ul> <li>Tailwind v4: <code>outline-none</code> now literally sets <code>outline-style: none</code>. Use <code>outline-hidden</code> (which keeps a transparent outline for forced-colors mode) and always pair it with a <code>focus-visible:</code> ring. The utility form is <code>focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-line-focus</code>.</li> <li>On a colored or brand surface, use the double ring: <code>box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--line-focus)</code>.</li> <li>Focus rings appear instantly (no transition), because focus feedback must not lag.</li> <li>Composite widgets (tabs, menus, radio groups, toolbars, listboxes, grids) use <strong>one tab stop + arrow keys</strong> (roving tabindex). Page navigation uses plain links, each a tab stop.</li> <li><code>Escape</code> closes the topmost overlay and returns focus to its trigger.</li> <li>Global shortcuts ignore keypresses inside inputs and editors. Single-key shortcuts must be remappable or disableable (2.1.4).</li> <li>Hide inert content with <code>inert</code> (off-canvas menus, content behind non-dialog overlays), not just visually.</li> <li><code>display:none</code>/<code>hidden</code> removes from both the a11y tree and tab order. <code>aria-hidden="true"</code> removes only from the a11y tree, so never put it on focusable content.</li> </ul> <h2 id="hit-targets-and-pointer"><a class="heading-anchor" href="#hit-targets-and-pointer">Hit targets and pointer</a></h2> <figure class="code-block" data-lang="css"><figcaption><span>css</span><button type="button" class="copy-button" data-copy>Copy</button></figcaption><pre tabindex="0" aria-label="css code" class="shiki void" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0"><code><span class="line"><span style="color:var(--shiki-token-function)">.hit</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-constant)">position</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> relative</span><span style="color:var(--shiki-foreground)">; }</span></span> <span class="line"><span style="color:var(--shiki-token-function)">.hit::before</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-constant)">content</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-string-expression)"> ""</span><span style="color:var(--shiki-foreground)">; </span><span style="color:var(--shiki-token-constant)">position</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> absolute</span><span style="color:var(--shiki-foreground)">; </span><span style="color:var(--shiki-token-constant)">inset</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> -8</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-foreground)">; } </span><span style="color:var(--shiki-token-comment)">/* 24px icon → 40px target */</span></span> <span class="line"><span style="color:var(--shiki-token-keyword)">@media</span><span style="color:var(--shiki-foreground)"> (pointer</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-foreground)"> coarse) { </span><span style="color:var(--shiki-token-function)">.hit::before</span><span style="color:var(--shiki-foreground)"> { </span><span style="color:var(--shiki-token-constant)">inset</span><span style="color:var(--shiki-token-keyword)">:</span><span style="color:var(--shiki-token-constant)"> -12</span><span style="color:var(--shiki-token-keyword)">px</span><span style="color:var(--shiki-foreground)">; } } </span><span style="color:var(--shiki-token-comment)">/* ≥ 44px on touch */</span></span></code></pre></figure> <ul> <li>Checkbox/radio + label are one hit area (the whole <code><label></code> row).</li> <li>Adjacent small targets have ≥ 8 px gaps.</li> <li>Dragging (sliders, reorder, swipe) always has a click/tap alternative (2.5.7).</li> <li>Hover-only UI is forbidden. Anything revealed on hover also appears on focus and is reachable by touch.</li> <li><code>touch-action: manipulation</code> on controls removes the double-tap zoom delay.</li> </ul> <h2 id="color-and-contrast-tie-to-tokens"><a class="heading-anchor" href="#color-and-contrast-tie-to-tokens">Color and contrast (tie to tokens)</a></h2> <div class="table-wrap" tabindex="0" role="region" aria-label="Table 2"><table> <thead> <tr> <th>Content</th> <th>WCAG 2.2 (hard gate)</th> <th>APCA Lc (quality target)</th> <th>Token pairs to check</th> </tr> </thead> <tbody><tr> <td>Body text ≤ 18px</td> <td>≥ 4.5:1</td> <td>≥ 75, <strong>90 preferred</strong></td> <td><code>--fg</code> on <code>--bg</code>, <code>--surface</code>, <code>--surface-raised</code></td> </tr> <tr> <td>Secondary text, UI labels</td> <td>≥ 4.5:1</td> <td>≥ 60</td> <td><code>--fg-muted</code> on every surface</td> </tr> <tr> <td>Large text (≥ 24px, or ≥ 18.66px bold)</td> <td>≥ 3:1</td> <td>≥ 45</td> <td>display sizes on <code>--bg</code></td> </tr> <tr> <td>Meta, captions, placeholders</td> <td>≥ 4.5:1</td> <td>≥ 60</td> <td><code>--fg-subtle</code> (AA on <code>--bg</code>, <code>--bg-subtle</code>, <code>--surface</code>, <code>--surface-raised</code>)</td> </tr> <tr> <td>Disabled controls, <code>aria-hidden</code> decoration</td> <td>3:1 (disabled is exempt from 1.4.3)</td> <td>≥ 30</td> <td><code>--fg-faint</code> — never text someone must read</td> </tr> <tr> <td>Input borders, focus ring, meaningful icons, chart marks</td> <td>≥ 3:1 (1.4.11)</td> <td>≥ 45–60 for icons</td> <td><code>--line-strong</code>, <code>--line-focus</code> vs adjacent surface</td> </tr> <tr> <td>Text on brand</td> <td>≥ 4.5:1</td> <td>≥ 60</td> <td><code>--fg-on-brand</code> on <code>--brand</code> and <code>--brand-hover</code>; <code>--brand-text</code> on <code>--bg</code></td> </tr> <tr> <td>Status text</td> <td>≥ 4.5:1</td> <td>≥ 60</td> <td><code>--danger</code>/<code>--success</code>/<code>--warning</code> text on <code>--bg</code> and their <code>-subtle</code> fills</td> </tr> </tbody></table></div> <ul> <li>Check both <strong>light and dark</strong> themes. WCAG 2 overstates light-on-dark contrast, so APCA is stricter there.</li> <li>Never convey meaning by color alone. Errors get an icon + text, status gets a label, links in body text get an underline.</li> <li>Don't put <code>--fg-muted</code> gray text on colored fills. Use the fill's own text token.</li> <li>Support <code>forced-colors: active</code> (Windows High Contrast): don't rely on <code>background-image</code> or <code>box-shadow</code> alone for borders or focus. Outlines survive; shadows don't.</li> </ul> <h2 id="forms"><a class="heading-anchor" href="#forms">Forms</a></h2> <ul> <li><code><label for></code> or a wrapping <code><label></code>, visible and above the field. Required fields: <code>required</code> + visible "(required)" text or a legend note.</li> <li><code>type="email|tel|url|number"</code>, <code>inputMode</code>, <code>autoComplete="email|name|current-password|one-time-code"</code> (1.3.5). <code>spellCheck={false}</code> for codes and emails.</li> <li>Validate on blur and on submit, not on every keystroke. Once an error shows, re-validate on input.</li> <li>Error: text below the field with an icon; <code>aria-invalid="true"</code>; <code>aria-describedby</code> → error id; focus the first invalid field on submit. For long forms, add an error summary with links.</li> <li>Don't disable the submit button before the user tries. Let submit reveal what's missing. While submitting, keep the label ("Saving…") and set <code>aria-busy</code>/<code>disabled</code> on the button.</li> <li>Group radios and checkboxes in <code><fieldset></code> + <code><legend></code>.</li> <li>Never block paste. Allow password managers (3.3.8): no CAPTCHA-only logins, no copy-blocking on OTP fields.</li> <li>Field height ≥ 44 px on touch. Font size ≥ 16 px on inputs so iOS doesn't zoom.</li> </ul> <h2 id="motion"><a class="heading-anchor" href="#motion">Motion</a></h2> <ul> <li>Every animation is wrapped by <code>@media (prefers-reduced-motion: no-preference)</code>, or neutralized under <code>reduce</code>. See <a class="code-link" href="/docs/speed/rendering-smoothness"><code>speed/references/rendering-smoothness.md</code></a> §2 and the <a class="code-link" href="/docs/motion"><code>motion</code></a> skill.</li> <li>Under reduced motion, replace movement with ≤ 150 ms opacity changes or nothing. Keep essential feedback (progress, state change).</li> <li>No parallax, zoom-on-scroll, large translations or auto-advancing carousels under <code>reduce</code>.</li> <li>Auto-playing video, marquees and carousels > 5 s get a visible pause button (2.2.2).</li> <li>Nothing flashes more than 3 times per second (2.3.1).</li> <li>Decorative animated text (scramble, typewriter) keeps the real text in <code>sr-only</code> and marks the animated copy <code>aria-hidden</code>.</li> </ul> <h2 id="overlays-and-widgets-choose-dont-hand-roll"><a class="heading-anchor" href="#overlays-and-widgets-choose-dont-hand-roll">Overlays and widgets: choose, don't hand-roll</a></h2> <div class="table-wrap" tabindex="0" role="region" aria-label="Table 3"><table> <thead> <tr> <th>Need</th> <th>First choice</th> <th>Weight (gzip)</th> <th>Notes</th> </tr> </thead> <tbody><tr> <td>Disclosure / accordion</td> <td><code><details></code>/<code><summary></code> (use <code>name</code> for exclusive groups)</td> <td>0 KB</td> <td>Style <code>summary</code>; don't hide key SEO content in closed panels</td> </tr> <tr> <td>Modal dialog</td> <td>native <code><dialog></code> + <code>showModal()</code></td> <td>0 KB (tiny client leaf)</td> <td>Background becomes inert, Esc closes, focus returns to the opener. <code>closedby="any"</code> is Chromium/Firefox only, so add a backdrop click handler</td> </tr> <tr> <td>Non-modal popover / simple menu of links</td> <td><code>popover</code> attribute + <code>popovertarget</code></td> <td>0 KB</td> <td>Light-dismiss and Esc built in; Baseline 2025</td> </tr> <tr> <td>Button opens dialog/popover without JS</td> <td><code>command</code>/<code>commandfor</code> invokers</td> <td>0 KB</td> <td>Baseline newly available (Safari 26.2+); keep a JS fallback for older browsers</td> </tr> <tr> <td>Dropdown menu, context menu, select, combobox, date picker</td> <td>Radix (<code>radix-ui</code>), Base UI (<code>@base-ui/react</code>), or React Aria Components</td> <td>Radix Dialog ~12.6 KB, Tabs ~8.4 KB, Popover ~22.6 KB (includes Floating UI)</td> <td>Import per component. The full packages are large if not tree-shaken (Base UI ~147 KB, React Aria Components ~274 KB). Measure with <code>void perf</code></td> </tr> <tr> <td>Toasts</td> <td><code>sonner</code></td> <td>~9.4 KB</td> <td>Announces via a live region; toasts only for mutations</td> </tr> <tr> <td>Tabs</td> <td>Radix Tabs or the pattern in <code>patterns.md</code></td> <td>0–8 KB</td> <td>Arrow keys, <code>aria-selected</code>, <code>aria-controls</code></td> </tr> </tbody></table></div> <p>Use one primitive library per app. Vendored registry components (shadcn blocks, Magic UI, Aceternity, React Bits) often ship with no ARIA and no reduced-motion handling, so audit them before use.</p> <h2 id="images-media-icons"><a class="heading-anchor" href="#images-media-icons">Images, media, icons</a></h2> <ul> <li><code>alt</code> describes purpose and content in ≤ 125 chars. Don't start with "image of". Logos: the company name. Linked images: the link destination.</li> <li>Charts: a text summary or data table next to the chart. Complex SVG: <code>role="img"</code> + <code><title></code>/<code>aria-label</code>.</li> <li>Decorative SVG icons: <code>aria-hidden="true"</code> (lucide does this by default). Icon-only buttons: <code><button aria-label="Close"></code>. <code>title</code> alone isn't enough.</li> <li>Video: captions (<code><track kind="captions"></code>), no autoplay with sound, controls available. Audio-only content gets a transcript.</li> <li>Canvas that conveys status: <code>role="img"</code> + <code>aria-label</code>. Decorative canvas: <code>aria-hidden="true"</code>.</li> </ul> <h2 id="live-regions-and-status"><a class="heading-anchor" href="#live-regions-and-status">Live regions and status</a></h2> <ul> <li>Toast and async results: <code>role="status"</code> (polite) for success and info; <code>role="alert"</code> only for errors that need immediate attention.</li> <li>The live region must <strong>exist in the DOM before</strong> its text changes. Mount it empty at startup and update its text later.</li> <li>Loading: <code>aria-busy="true"</code> on the region being updated. Spinners get <code>sr-only</code> "Loading…" text.</li> <li>Don't announce on every keystroke. Debounce search result counts ("12 results").</li> <li>Status is never color-only: pair the dot with a text label (<code>role="status"</code> where it updates).</li> </ul> <h2 id="zoom-reflow-text-spacing"><a class="heading-anchor" href="#zoom-reflow-text-spacing">Zoom, reflow, text spacing</a></h2> <ul> <li>Never set <code>maximum-scale=1</code> or <code>user-scalable=no</code>.</li> <li><strong>Reflow at 320 CSS px width</strong> (≈ 400% zoom) with no horizontal scroll except for tables, code and maps (1.4.10). Test at 320px and at 200% browser zoom.</li> <li>Sizes in <code>rem</code> for type. Display type uses <code>clamp()</code> so the h1 fits at 320px.</li> <li>Tolerate user text spacing (1.4.12): no fixed heights on text containers. Use <code>min-height</code>, and <code>line-clamp</code> only with a way to read the rest.</li> <li><code>overflow-x: clip</code> (not <code>hidden</code>) on <code>html</code>/<code>body</code>, so sticky elements and focus scrolling keep working.</li> <li><code>100dvh</code>/<code>svh</code> instead of <code>100vh</code> for full-height layouts on mobile.</li> </ul> <h2 id="verify"><a class="heading-anchor" href="#verify">Verify</a></h2> <ol> <li><code>void lint</code> (static): <a class="code-link" href="/rules/lint/div-button"><code>lint/div-button</code></a>, <a class="code-link" href="/rules/lint/missing-alt"><code>lint/missing-alt</code></a>, <a class="code-link" href="/rules/lint/outline-none-no-replacement"><code>lint/outline-none-no-replacement</code></a>, <a class="code-link" href="/rules/lint/no-reduced-motion"><code>lint/no-reduced-motion</code></a>, <a class="code-link" href="/rules/lint/multiple-h1"><code>lint/multiple-h1</code></a>, <a class="code-link" href="/rules/lint/missing-lang"><code>lint/missing-lang</code></a>.</li> <li><code>void a11y --start "next start -p 3000" --port 3000 --format md</code> on a production build: axe-core (WCAG 2.2 AA + best practices) on mobile and desktop, plus void's own checks <a class="code-link" href="/rules/a11y/focus-not-visible"><code>a11y/focus-not-visible</code></a> (keyboard focus walk), <a class="code-link" href="/rules/a11y/target-size"><code>a11y/target-size</code></a> (mobile), <a class="code-link" href="/rules/a11y/reflow-overflow"><code>a11y/reflow-overflow</code></a> (320px). Common axe ids: <a class="code-link" href="/rules/a11y/color-contrast"><code>a11y/color-contrast</code></a>, <a class="code-link" href="/rules/a11y/button-name"><code>a11y/button-name</code></a>, <a class="code-link" href="/rules/a11y/link-name"><code>a11y/link-name</code></a>, <a class="code-link" href="/rules/a11y/label"><code>a11y/label</code></a>, <a class="code-link" href="/rules/a11y/image-alt"><code>a11y/image-alt</code></a>, <a class="code-link" href="/rules/a11y/heading-order"><code>a11y/heading-order</code></a>, <a class="code-link" href="/rules/a11y/landmark-one-main"><code>a11y/landmark-one-main</code></a>, <a class="code-link" href="/rules/a11y/bypass"><code>a11y/bypass</code></a>, <a class="code-link" href="/rules/a11y/meta-viewport"><code>a11y/meta-viewport</code></a>, <a class="code-link" href="/rules/a11y/aria-hidden-focus"><code>a11y/aria-hidden-focus</code></a>, <a class="code-link" href="/rules/a11y/nested-interactive"><code>a11y/nested-interactive</code></a>, <a class="code-link" href="/rules/a11y/link-in-text-block"><code>a11y/link-in-text-block</code></a>. Unmapped axe rules arrive as <a class="code-link" href="/rules/a11y/axe-other"><code>a11y/axe-other</code></a> with the axe id in the message.</li> <li><strong>Stop when:</strong> zero axe violations, the focus walk passes, a11y score 100. Then do a 2-minute manual pass: Tab through the page, open and close every overlay with the keyboard, and use VoiceOver (macOS: Cmd+F5) or NVDA on the main flow.</li> <li>Automated tools catch only part of real issues. The manual pass is not optional for dialogs, menus and forms.</li> </ol> </div><footer class="mt-16 border-t border-line-subtle pt-8 pb-20"><div class="flex flex-wrap items-center justify-between gap-3 font-mono text-xs text-fg-muted"><span>Updated <time dateTime="2026-09-16">16 Sept 2026</time></span><span class="flex gap-4"><a href="/docs/a11y.md" class="hover:text-fg">View as Markdown</a><a href="https://github.com/ParthKapoor-dev/design/blob/main/skills/a11y/SKILL.md" rel="noopener noreferrer" class="hover:text-fg">Edit on GitHub</a></span></div><div class="mt-8 grid gap-3 sm:grid-cols-2"><a class="press group flex flex-col gap-1 rounded-lg border border-line-subtle p-4 transition-colors duration-(--duration-fast) hover:border-line-strong" href="/docs/seo/structured-data"><span class="label-mono flex items-center gap-1.5 text-fg-muted"><svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" class="size-3"><path d="M13 8H3.5M7.5 4l-4 4 4 4" stroke-linecap="round" stroke-linejoin="round"></path></svg> Previous</span><span class="text-sm text-fg">seo: structured data</span></a><a class="press group flex flex-col items-end gap-1 rounded-lg border border-line-subtle p-4 text-right transition-colors duration-(--duration-fast) hover:border-line-strong" href="/docs/a11y/patterns"><span class="label-mono flex items-center gap-1.5 text-fg-muted">Next <svg aria-hidden="true" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" class="size-3"><path d="M3 8h9.5M8.5 4l4 4-4 4" stroke-linecap="round" stroke-linejoin="round"></path></svg></span><span class="text-sm text-fg">a11y: patterns</span></a></div></footer></article><aside aria-label="Page contents" class="hidden xl:block"><div data-lenis-prevent="true" class="sticky top-(--header-h) max-h-[calc(100dvh-var(--header-h))] overflow-y-auto pt-4 pb-10 [scrollbar-width:thin]"><nav aria-label="On this page" class="text-sm"><p class="label-mono mb-3 text-fg-muted">On this page</p><ul class="grid gap-1.5 border-l border-line-subtle"><li><a href="#the-10-rules-that-matter-most" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">The 10 rules that matter most</a></li><li><a href="#structure" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Structure</a></li><li><a href="#keyboard-and-focus" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Keyboard and focus</a></li><li><a href="#hit-targets-and-pointer" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Hit targets and pointer</a></li><li><a href="#color-and-contrast-tie-to-tokens" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Color and contrast (tie to tokens)</a></li><li><a href="#forms" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Forms</a></li><li><a href="#motion" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Motion</a></li><li><a href="#overlays-and-widgets-choose-dont-hand-roll" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Overlays and widgets: choose, don't hand-roll</a></li><li><a href="#images-media-icons" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Images, media, icons</a></li><li><a href="#live-regions-and-status" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Live regions and status</a></li><li><a href="#zoom-reflow-text-spacing" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Zoom, reflow, text spacing</a></li><li><a href="#verify" class="block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4">Verify</a></li></ul></nav></div></aside></div></div><!--$--><!--/$--></main><footer class="relative mt-24 overflow-hidden border-t border-line px-(--gutter)"><div aria-hidden="true" class="pointer-events-none absolute -bottom-[40vw] left-1/2 size-[80vw] -translate-x-1/2 rounded-full bg-[radial-gradient(closest-side,color-mix(in_oklab,var(--flare)_12%,transparent),transparent)]"></div><div class="relative mx-auto grid max-w-7xl gap-12 py-16 md:grid-cols-12 md:gap-8"><div class="md:col-span-5"><a class="inline-flex items-center gap-2.5 text-ink" aria-label="void home" href="/"><svg viewBox="0 0 32 32" class="size-6" aria-hidden="true"><defs><radialGradient id="mk" cx="50%" cy="50%" r="50%"><stop offset="0.52" stop-color="#000"></stop><stop offset="0.6" stop-color="var(--flare-2)"></stop><stop offset="0.8" stop-color="var(--flare)" stop-opacity="0.6"></stop><stop offset="1" stop-color="var(--flare)" stop-opacity="0"></stop></radialGradient></defs><ellipse cx="16" cy="16" rx="15" ry="5" fill="none" stroke="var(--flare)" stroke-opacity="0.8" stroke-width="1.4" transform="rotate(-18 16 16)"></ellipse><circle cx="16" cy="16" r="10" fill="url(#mk)"></circle></svg><span class="text-lg font-medium tracking-tight">void</span></a><p class="mt-4 max-w-[44ch] text-mute">A design system for AI coding agents. This site is built with it and audited by it on every change.</p><p class="mt-6 inline-flex flex-wrap items-center gap-x-4 gap-y-1 rounded-full border border-line-2 bg-black/40 px-4 py-2 font-mono text-xs text-mute"><span class="text-flare-2">void audit</span><span>perf<!-- --> <span class="text-ink tabular-nums">100</span></span><span>a11y<!-- --> <span class="text-ink tabular-nums">100</span></span><span>seo<!-- --> <span class="text-ink tabular-nums">99</span></span><span>geo<!-- --> <span class="text-ink tabular-nums">100</span></span></p></div><nav aria-label="Product" class="md:col-span-2 md:first-of-type:col-start-7"><p class="kicker">Product</p><ul class="mt-4 grid gap-2"><li><a class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink" href="/docs">Docs</a></li><li><a class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink" href="/cli">CLI reference</a></li><li><a class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink" href="/rules">Rules</a></li><li><a class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink" href="/directions">Directions</a></li><li><a class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink" href="/evals">Evals</a></li></ul></nav><nav aria-label="For agents" class="md:col-span-2 md:first-of-type:col-start-7"><p class="kicker">For agents</p><ul class="mt-4 grid gap-2"><li><a href="/llms.txt" class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink">llms.txt</a></li><li><a href="/llms-full.txt" class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink">llms-full.txt</a></li><li><a href="/index.md" class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink">index.md</a></li><li><a href="/sitemap.xml" class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink">sitemap.xml</a></li></ul></nav><nav aria-label="Source" class="md:col-span-2 md:first-of-type:col-start-7"><p class="kicker">Source</p><ul class="mt-4 grid gap-2"><li><a href="https://github.com/ParthKapoor-dev/design" rel="noopener noreferrer" class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink">GitHub</a></li><li><a href="https://github.com/ParthKapoor-dev/design/blob/main/LICENSE" rel="noopener noreferrer" class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink">MIT licence</a></li><li><a href="https://parthkapoor.me" rel="noopener noreferrer" class="inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink">Parth Kapoor</a></li></ul></nav></div><p aria-hidden="true" class="wordmark relative mx-auto max-w-7xl select-none text-center font-semibold leading-[0.72] tracking-[-0.06em] text-[clamp(6rem,24vw,22rem)]">void</p><div class="relative mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-4 border-t border-line py-6 font-mono text-xs text-mute"><span>© 2026 <!-- -->Parth Kapoor<!-- --> · <!-- -->MIT</span><span>void-design.vercel.app</span></div></footer><div class="grain" aria-hidden="true"></div><script type="application/ld+json">{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://void-design.vercel.app/#organization","name":"void","url":"https://void-design.vercel.app/","logo":"https://void-design.vercel.app/icon-512.png","sameAs":["https://github.com/ParthKapoor-dev/design"]},{"@type":"WebSite","@id":"https://void-design.vercel.app/#website","name":"void","url":"https://void-design.vercel.app/","description":"void is an open-source design system for AI coding agents: Claude Code skills, Tailwind v4 tokens and a CLI that audits speed, accessibility, SEO and design.","inLanguage":"en","publisher":{"@id":"https://void-design.vercel.app/#organization"}}]}</script><script src="/_next/static/immutable/chunks/39mze7sr9bsct.js" id="_R_" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0])</script><script>self.__next_f.push([1,"1:\"$Sreact.fragment\"\n2:I[46810,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\"],\"SmoothScroll\"]\n3:I[45110,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\"],\"Cursor\"]\n4:I[75514,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\"],\"Magnetic\"]\n5:I[88287,[\"/_next/static/immutable/chunks/455sb5e0fgpzb.js\"],\"default\"]\n6:I[74895,[\"/_next/static/immutable/chunks/455sb5e0fgpzb.js\"],\"default\"]\n7:I[821,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\",\"/_next/static/immutable/chunks/1635pnmauuvxi.js\"],\"Nav\"]\n8:I[23408,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\",\"/_next/static/immutable/chunks/1635pnmauuvxi.js\"],\"CanvasRoot\"]\n9:I[57715,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\",\"/_next/static/immutable/chunks/1635pnmauuvxi.js\",\"/_next/static/immutable/chunks/10-jx0r2jtr5s.js\",\"/_next/static/immutable/chunks/2-9e3j82g4al3.js\",\"/_next/static/immutable/chunks/15spxoq7fze2b.js\"],\"PageHero\"]\na:I[4561,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\",\"/_next/static/immutable/chunks/1635pnmauuvxi.js\",\"/_next/static/immutable/chunks/10-jx0r2jtr5s.js\",\"/_next/static/immutable/chunks/2-9e3j82g4al3.js\",\"/_next/static/immutable/chunks/15spxoq7fze2b.js\"],\"\"]\n16:I[52576,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\"],\"default\",1]\n:HL[\"/_next/static/immutable/chunks/0qj2_6xwr7iux.css\",\"style\"]\n:HL[\"/_next/static/immutable/media/797e433ab948586e-s.p.1v5bejj26fx9h.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/immutable/media/7ebf22b5a21034f8-s.p.3strvg7o-g5v2.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n:HL[\"/_next/static/immutable/media/caa3a2e1cccd8315-s.p.0zr6hhvz-h9nw.woff2\",\"font\",{\"crossOrigin\":\"\",\"type\":\"font/woff2\"}]\n14:X\n0:{\"P\":null,\"c\":[\"\",\"docs\",\"a11y\"],\"q\":\"\",\"i\":false,\"f\":[[[\"\",{\"children\":[\"(pages)\",{\"children\":[\"docs\",{\"children\":[[\"skill\",\"a11y\",\"d\",[]],{\"children\":[\"__PAGE__\",{},\"$undefined\",\"$undefined\",4608]},\"$undefined\",\"$undefined\",4608]},\"$undefined\",\"$undefined\",4608]},\"$undefined\",\"$undefined\",4608]},\"$undefined\",\"$undefined\",4624],[[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/0qj2_6xwr7iux.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"html\",null,{\"lang\":\"en\",\"className\":\"geist_e799d87a-module__u8MhCG__variable geist_mono_8f04fc5a-module__ZGwrmq__variable instrument_serif_d5a02a32-module__PnFala__variable\",\"suppressHydrationWarning\":true,\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"script\",null,{\"dangerouslySetInnerHTML\":{\"__html\":\"document.documentElement.classList.add(\\\"js\\\")\"}}],[\"$\",\"link\",null,{\"rel\":\"describedby\",\"type\":\"text/markdown\",\"href\":\"/llms.txt\"}]]}],[\"$\",\"body\",null,{\"children\":[[\"$\",\"$L2\",null,{}],[\"$\",\"$L3\",null,{}],[\"$\",\"$L4\",null,{}],[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"$L7\",null,{}],[\"$\",\"div\",null,{\"aria-hidden\":\"true\",\"children\":[\"$\",\"$L8\",null,{}]}],[\"$\",\"main\",null,{\"id\":\"main\",\"className\":\"page-shell relative px-(--gutter)\",\"children\":[\"$\",\"$L9\",null,{\"scene\":\"snow\",\"crumbs\":[{\"name\":\"void\",\"path\":\"/\"},{\"name\":\"404\"}],\"kicker\":\"HTTP 404 · noindex\",\"title\":[\"Nothing measured \",[\"$\",\"span\",null,{\"className\":\"serif hot\",\"children\":\"at this address.\"}]],\"lead\":\"The link may be old, or a rule id was renamed. Everything that exists is one of these.\",\"children\":[\"$\",\"div\",null,{\"className\":\"enter mt-10 max-w-xl overflow-hidden rounded-2xl border border-line-2 bg-black/60 font-mono text-sm backdrop-blur-md\",\"style\":{\"--d\":\"0.45s\"},\"children\":[[\"$\",\"div\",null,{\"className\":\"flex h-10 items-center justify-between border-b border-line px-4\",\"children\":[[\"$\",\"span\",null,{\"className\":\"label-mono text-fg-subtle\",\"children\":\"~/void\"}],[\"$\",\"span\",null,{\"className\":\"label-mono text-danger\",\"children\":\"exit 3\"}]]}],[\"$\",\"div\",null,{\"className\":\"px-4 py-4 leading-7\",\"children\":[[\"$\",\"p\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",[\"$\",\"span\",null,{\"className\":\"text-fg\",\"children\":\"curl -sI this-page\"}]]}],[\"$\",\"p\",null,{\"className\":\"text-fg-muted\",\"children\":[\"HTTP/2 \",[\"$\",\"span\",null,{\"className\":\"text-danger\",\"children\":\"404\"}]]}],[\"$\",\"p\",null,{\"className\":\"mt-3 text-fg-subtle\",\"children\":\"# try one of these\"}],[\"$\",\"ul\",null,{\"children\":[[\"$\",\"li\",\"/docs\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/docs\",\"className\":\"group flex min-h-7 flex-wrap items-center gap-x-3 text-fg transition-colors hover:text-brand-text\",\"children\":[[\"$\",\"span\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",\"cd /docs\"]}],[\"$\",\"span\",null,{\"className\":\"text-fg-subtle group-hover:text-fg-muted\",\"children\":[\"# \",\"install, the starter, the skills\"]}]]}]}],[\"$\",\"li\",\"/rules\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/rules\",\"className\":\"group flex min-h-7 flex-wrap items-center gap-x-3 text-fg transition-colors hover:text-brand-text\",\"children\":[[\"$\",\"span\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",\"void rules\"]}],[\"$\",\"span\",null,{\"className\":\"text-fg-subtle group-hover:text-fg-muted\",\"children\":[\"# \",\"every rule id the CLI can print\"]}]]}]}],[\"$\",\"li\",\"/cli\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/cli\",\"className\":\"group flex min-h-7 flex-wrap items-center gap-x-3 text-fg transition-colors hover:text-brand-text\",\"children\":[[\"$\",\"span\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",\"void --help\"]}],[\"$\",\"span\",null,{\"className\":\"text-fg-subtle group-hover:text-fg-muted\",\"children\":[\"# \",\"commands, flags and exit codes\"]}]]}]}],[\"$\",\"li\",\"/\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/\",\"className\":\"group flex min-h-7 flex-wrap items-center gap-x-3 text-fg transition-colors hover:text-brand-text\",\"children\":[[\"$\",\"span\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",\"cd ~\"]}],[\"$\",\"span\",null,{\"className\":\"text-fg-subtle group-hover:text-fg-muted\",\"children\":[\"# \",\"home\"]}]]}]}]]}]]}]]}]}]}],[\"$\",\"footer\",null,{\"className\":\"relative mt-24 overflow-hidden border-t border-line px-(--gutter)\",\"children\":[[\"$\",\"div\",null,{\"aria-hidden\":\"true\",\"className\":\"pointer-events-none absolute -bottom-[40vw] left-1/2 size-[80vw] -translate-x-1/2 rounded-full bg-[radial-gradient(closest-side,color-mix(in_oklab,var(--flare)_12%,transparent),transparent)]\"}],\"$Lb\",\"$Lc\",\"$Ld\"]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}],\"$Le\",\"$Lf\"]}]]}]]}],{\"children\":[\"$L10\",{\"children\":[\"$L11\",{\"children\":[\"$L12\",{\"children\":[\"$L13\",{},null,false,null]},null,false,\"$14\"]},null,false,\"$14\"]},null,false,null]},null,false,null],\"$L15\",false]],\"m\":\"$undefined\",\"G\":[\"$16\",[\"$L17\"]],\"S\":true,\"h\":null,\"r\":\"$undefined\",\"s\":\"$undefined\",\"a\":\"$undefined\",\"l\":\"$undefined\",\"p\":\"$undefined\",\"d\":\"$undefined\",\"b\":\"kQQT0nsN5xJVQW80oWLeY\"}\n18:I[821,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\",\"/_next/static/immutable/chunks/1635pnmauuvxi.js\"],\"Mark\"]\n26:I[51858,[\"/_next/static/immutable/chunks/455sb5e0fgpzb.js\"],\"OutletBoundary\"]\n27:\"$Sreact.suspense\"\n29:I[51858,[\"/_next/static/immutable/chunks/455sb5e0fgpzb.js\"],\"ViewportBoundary\"]\n2b:I[51858,[\"/_next/static/immutable/chunks/455sb5e0fgpzb.js\"],\"MetadataBoundary\"]\nb:[\"$\",\"div\",null,{\"className\":\"relative mx-auto grid max-w-7xl gap-12 py-16 md:grid-cols-12 md:gap-8\",\"children\":[[\"$\",\"div\",null,{\"className\":\"md:col-span-5\",\"children\":[[\"$\",\"$La\",null,{\"href\":\"/\",\"className\":\"inline-flex items-center gap-2.5 text-ink\",\"aria-label\":\"void home\",\"children\":[[\"$\",\"$L18\",null,{\"className\":\"size-6\"}],[\"$\",\"span\",null,{\"className\":\"text-lg font-medium tracking-tight\",\"children\":\"void\"}]]}],[\"$\",\"p\",null,{\"className\":\"mt-4 max-w-[44ch] text-mute\",\"children\":\"A design system for AI coding agents. This site is built with it and audited by it on every change.\"}],[\"$\",\"p\",null,{\"className\":\"mt-6 inline-flex flex-wrap items-center gap-x-4 gap-y-1 rounded-full border border-line-2 bg-black/40 px-4 py-2 font-mono text-xs text-mute\",\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare-2\",\"children\":\"void audit\"}],[[\"$\",\"span\",\"perf\",{\"children\":[\"perf\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"100\"}]]}],[\"$\",\"span\",\"a11y\",{\"children\":[\"a11y\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"100\"}]]}],[\"$\",\"span\",\"seo\",{\"children\":[\"seo\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"99\"}]]}],[\"$\",\"span\",\"geo\",{\"children\":[\"geo\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"100\"}]]}]]]}]]}],[[\"$\",\"nav\",\"Product\",{\"aria-label\":\"Product\",\"className\":\"md:col-span-2 md:first-of-type:col-start-7\",\"children\":[[\"$\",\"p\",null,{\"className\":\"kicker\",\"children\":\"Product\"}],[\"$\",\"ul\",null,{\"className\":\"mt-4 grid gap-2\",\"children\":[[\"$\",\"li\",\"/docs\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/docs\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Docs\"}]}],[\"$\",\"li\",\"/cli\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/cli\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"CLI reference\"}]}],[\"$\",\"li\",\"/rules\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/rules\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Rules\"}]}],[\"$\",\"li\",\"/directions\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/directions\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Directions\"}]}],[\"$\",\"li\",\"/evals\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/evals\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Evals\"}]}]]}]]}],[\"$\",\"nav\",\"For agents\",{\"aria-label\":\"For agents\",\"className\":\"md:col-span-2 md:first-of-type:col-start-7\",\"children\":[[\"$\",\"p\",null,{\"className\":\"kicker\",\"children\":\"For agents\"}],[\"$\",\"ul\",null,{\"className\":\"mt-4 grid gap-2\",\"children\":[[\"$\",\"li\",\"/llms.txt\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/llms.txt\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"llms.txt\"}]}],[\"$\",\"li\",\"/llms-full.txt\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/llms-full.txt\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"llms-full.txt\"}]}],[\"$\",\"li\",\"/index.md\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/index.md\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"index.md\"}]}],[\"$\",\"li\",\"/sitemap.xml\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/sitemap.xml\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"sitemap.xml\"}]}]]}]]}],[\"$\",\"nav\",\"Source\",{\"aria-label\":\"Source\",\"className\":\"md:col-span-2 md:first-of-type:col-start-7\",\"children\":[[\"$\",\"p\",null,{\"className\":\"kicker\",\"children\":\"Source\"}],[\"$\",\"ul\",null,{\"className\":\"mt-4 grid gap-2\",\"children\":[[\"$\",\"li\",\"https://github.com/ParthKapoor-dev/design\",{\"children\":[\"$\",\"a\",null,{\"href\":\"https://github.com/ParthKapoor-dev/design\",\"rel\":\"noopener noreferrer\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"GitHub\"}]}],[\"$\",\"li\",\"https://github.com/ParthKapoor-dev/design/blob/main/LICENSE\",{\"children\":[\"$\",\"a\",null,{\"href\":\"https://github.com/ParthKapoor-dev/design/blob/main/LICENSE\",\"rel\":\"noopener noreferrer\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"MIT licence\"}]}],\"$L19\"]}]]}]]]}]\nc:[\"$\",\"p\",null,{\"aria-hidden\":\"true\",\"className\":\"wordmark relative mx-auto max-w-7xl select-none text-center font-semibold leading-[0.72] tracking-[-0.06em] text-[clamp(6rem,24vw,22rem)]\",\"children\":\"void\"}]\nd:[\"$\",\"div\",null,{\"className\":\"relative mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-4 border-t border-line py-6 font-mono text-xs text-mute\",\"children\":[[\"$\",\"span\",null,{\"children\":[\"© 2026 \",\"Parth Kapoor\",\" · \",\"MIT\"]}],[\"$\",\"span\",null,{\"children\":\"void-design.vercel.app\"}]]}]\ne:[\"$\",\"div\",null,{\"className\":\"grain\",\"aria-hidden\":\"true\"}]\nf:[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"{\\\"@context\\\":\\\"https://schema.org\\\",\\\"@graph\\\":[{\\\"@type\\\":\\\"Organization\\\",\\\"@id\\\":\\\"https://void-design.vercel.app/#organization\\\",\\\"name\\\":\\\"void\\\",\\\"url\\\":\\\"https://void-design.vercel.app/\\\",\\\"logo\\\":\\\"https://void-design.vercel.app/icon-512.png\\\",\\\"sameAs\\\":[\\\"https://github.com/ParthKapoor-dev/design\\\"]},{\\\"@type\\\":\\\"WebSite\\\",\\\"@id\\\":\\\"https://void-design.vercel.app/#website\\\",\\\"name\\\":\\\"void\\\",\\\"url\\\":\\\"https://void-design.vercel.app/\\\",\\\"description\\\":\\\"void is an open-source design system for AI coding agents: Claude Code skills, Tailwind v4 tokens and a CLI that audits speed, accessibility, SEO and design.\\\",\\\"inLanguage\\\":\\\"en\\\",\\\"publisher\\\":{\\\"@id\\\":\\\"https://void-design.vercel.app/#organization\\\"}}]}\"}}]\n10:[\"$\",\"$1\",\"c\",{\"children\":[[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/immutable/chunks/1635pnmauuvxi.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[[\"$\",\"a\",null,{\"href\":\"#main\",\"className\":\"sr-only z-[60] rounded-full bg-flare px-4 py-2 text-sm font-medium text-black focus:not-sr-only focus:fixed focus:left-4 focus:top-4\",\"children\":\"Skip to content\"}],[\"$\",\"$L7\",null,{}],[\"$\",\"div\",null,{\"aria-hidden\":\"true\",\"children\":[\"$\",\"$L8\",null,{}]}],[\"$\",\"main\",null,{\"id\":\"main\",\"className\":\"page-shell relative\",\"children\":[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[[[\"$\",\"$L7\",null,{}],[\"$\",\"div\",null,{\"aria-hidden\":\"true\",\"children\":[\"$\",\"$L8\",null,{}]}],[\"$\",\"main\",null,{\"id\":\"main\",\"className\":\"page-shell relative px-(--gutter)\",\"children\":[\"$\",\"$L9\",null,{\"scene\":\"snow\",\"crumbs\":[{\"name\":\"void\",\"path\":\"/\"},{\"name\":\"404\"}],\"kicker\":\"HTTP 404 · noindex\",\"title\":[\"Nothing measured \",[\"$\",\"span\",null,{\"className\":\"serif hot\",\"children\":\"at this address.\"}]],\"lead\":\"The link may be old, or a rule id was renamed. Everything that exists is one of these.\",\"children\":[\"$\",\"div\",null,{\"className\":\"enter mt-10 max-w-xl overflow-hidden rounded-2xl border border-line-2 bg-black/60 font-mono text-sm backdrop-blur-md\",\"style\":{\"--d\":\"0.45s\"},\"children\":[[\"$\",\"div\",null,{\"className\":\"flex h-10 items-center justify-between border-b border-line px-4\",\"children\":[[\"$\",\"span\",null,{\"className\":\"label-mono text-fg-subtle\",\"children\":\"~/void\"}],[\"$\",\"span\",null,{\"className\":\"label-mono text-danger\",\"children\":\"exit 3\"}]]}],[\"$\",\"div\",null,{\"className\":\"px-4 py-4 leading-7\",\"children\":[[\"$\",\"p\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",[\"$\",\"span\",null,{\"className\":\"text-fg\",\"children\":\"curl -sI this-page\"}]]}],[\"$\",\"p\",null,{\"className\":\"text-fg-muted\",\"children\":[\"HTTP/2 \",[\"$\",\"span\",null,{\"className\":\"text-danger\",\"children\":\"404\"}]]}],[\"$\",\"p\",null,{\"className\":\"mt-3 text-fg-subtle\",\"children\":\"# try one of these\"}],[\"$\",\"ul\",null,{\"children\":[[\"$\",\"li\",\"/docs\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/docs\",\"className\":\"group flex min-h-7 flex-wrap items-center gap-x-3 text-fg transition-colors hover:text-brand-text\",\"children\":[[\"$\",\"span\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",\"cd /docs\"]}],[\"$\",\"span\",null,{\"className\":\"text-fg-subtle group-hover:text-fg-muted\",\"children\":[\"# \",\"install, the starter, the skills\"]}]]}]}],[\"$\",\"li\",\"/rules\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/rules\",\"className\":\"group flex min-h-7 flex-wrap items-center gap-x-3 text-fg transition-colors hover:text-brand-text\",\"children\":[[\"$\",\"span\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",\"void rules\"]}],[\"$\",\"span\",null,{\"className\":\"text-fg-subtle group-hover:text-fg-muted\",\"children\":[\"# \",\"every rule id the CLI can print\"]}]]}]}],[\"$\",\"li\",\"/cli\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/cli\",\"className\":\"group flex min-h-7 flex-wrap items-center gap-x-3 text-fg transition-colors hover:text-brand-text\",\"children\":[[\"$\",\"span\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",\"void --help\"]}],[\"$\",\"span\",null,{\"className\":\"text-fg-subtle group-hover:text-fg-muted\",\"children\":[\"# \",\"commands, flags and exit codes\"]}]]}]}],[\"$\",\"li\",\"/\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/\",\"className\":\"group flex min-h-7 flex-wrap items-center gap-x-3 text-fg transition-colors hover:text-brand-text\",\"children\":[[\"$\",\"span\",null,{\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare\",\"children\":\"$$\"}],\" \",\"cd ~\"]}],[\"$\",\"span\",null,{\"className\":\"text-fg-subtle group-hover:text-fg-muted\",\"children\":[\"# \",\"home\"]}]]}]}]]}]]}]]}]}]}],[\"$\",\"footer\",null,{\"className\":\"relative mt-24 overflow-hidden border-t border-line px-(--gutter)\",\"children\":[[\"$\",\"div\",null,{\"aria-hidden\":\"true\",\"className\":\"pointer-events-none absolute -bottom-[40vw] left-1/2 size-[80vw] -translate-x-1/2 rounded-full bg-[radial-gradient(closest-side,color-mix(in_oklab,var(--flare)_12%,transparent),transparent)]\"}],[\"$\",\"div\",null,{\"className\":\"relative mx-auto grid max-w-7xl gap-12 py-16 md:grid-cols-12 md:gap-8\",\"children\":[[\"$\",\"div\",null,{\"className\":\"md:col-span-5\",\"children\":[[\"$\",\"$La\",null,{\"href\":\"/\",\"className\":\"inline-flex items-center gap-2.5 text-ink\",\"aria-label\":\"void home\",\"children\":[\"$L1a\",\"$L1b\"]}],\"$L1c\",\"$L1d\"]}],[\"$L1e\",\"$L1f\",\"$L20\"]]}],\"$L21\",\"$L22\"]}]],[]],\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]}],\"$L23\",\"$L24\"]]}]\n11:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\n12:[\"$\",\"$1\",\"c\",{\"children\":[null,[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"forbidden\":\"$undefined\",\"unauthorized\":\"$undefined\"}]]}]\n13:[\"$\",\"$1\",\"c\",{\"children\":[\"$L25\",[[\"$\",\"script\",\"script-0\",{\"src\":\"/_next/static/immutable/chunks/10-jx0r2jtr5s.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-1\",{\"src\":\"/_next/static/immutable/chunks/2-9e3j82g4al3.js\",\"async\":true,\"nonce\":\"$undefined\"}],[\"$\",\"script\",\"script-2\",{\"src\":\"/_next/static/immutable/chunks/15spxoq7fze2b.js\",\"async\":true,\"nonce\":\"$undefined\"}]],[\"$\",\"$L26\",null,{\"children\":[\"$\",\"$27\",null,{\"name\":\"Next.MetadataOutlet\",\"children\":\"$@28\"}]}]]}]\n15:[\"$\",\"$1\",\"h\",{\"children\":[null,[\"$\",\"$L29\",null,{\"children\":\"$L2a\"}],[\"$\",\"div\",null,{\"hidden\":true,\"children\":[\"$\",\"$L2b\",null,{\"children\":[\"$\",\"$27\",null,{\"name\":\"Next.Metadata\",\"children\":\"$L2c\"}]}]}],[\"$\",\"meta\",null,{\"name\":\"next-size-adjust\",\"content\":\"\"}]]}]\n17:[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/immutable/chunks/0qj2_6xwr7iux.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\",\"nonce\":\"$undefined\"}]\n31:I[5246,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\",\"/_next/static/immutable/chunks/1635pnmauuvxi.js\"],\"CopyDelegate\"]\n14:C\n19:[\"$\",\"li\",\"https://parthkapoor.me\",{\"children\":[\"$\",\"a\",null,{\"href\":\"https://parthkapoor.me\",\"rel\":\"noopener noreferrer\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Parth Kapoor\"}]}]\n1a:[\"$\",\"$L18\",null,{\"className\":\"size-6\"}]\n1b:[\"$\",\"span\",null,{\"className\":\"text-lg font-medium tracking-tight\",\"children\":\"void\"}]\n1c:[\"$\",\"p\",null,{\"className\":\"mt-4 max-w-[44ch] text-mute\",\"children\":\"A design system for AI coding agents. This site is built with it and audited by it on every change.\"}]\n1d:[\"$\",\"p\",null,{\"className\":\"mt-6 inline-flex flex-wrap items-center gap-x-4 gap-y-1 rounded-full border border-line-2 bg-black/40 px-4 py-2 font-mono text-xs text-mute\",\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare-2\",\"children\":\"void audit\"}],[[\"$\",\"span\",\"perf\",{\"children\":[\"perf\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"100\"}]]}],[\"$\",\"span\",\"a11y\",{\"children\":[\"a11y\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"100\"}]]}],[\"$\",\"span\",\"seo\",{\"children\":[\"seo\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"99\"}]]}],[\"$\",\"span\",\"geo\",{\"children\":[\"geo\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"100\"}]]}]]]}]\n1e:[\"$\",\"nav\",\"Product\",{\"aria-label\":\"Product\",\"className\":\"md:col-span-2 md:first-of-type:col-start-7\",\"children\":[[\"$\",\"p\",null,{\"className\":\"kicker\",\"children\":\"Product\"}],[\"$\",\"ul\",null,{\"className\":\"mt-4 grid gap-2\",\"children\":[[\"$\",\"li\",\"/docs\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/docs\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Docs\"}]}],[\"$\",\"li\",\"/cli\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/cli\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"CLI reference\"}]}],[\"$\",\"li\",\"/rules\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/rules\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Rules\"}]}],[\"$\",\"li\",\"/directions\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/directions\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Directions\"}]}],[\"$\",\"li\",\"/evals\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/evals\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Evals\"}]}]]}]]}]\n1f:[\"$\",\"nav\",\"For agents\",{\"aria-label\":\"For agents\",\"className\":\"md:col-span-2 md:first-of-type:col-start-7\",\"children\":[[\"$\",\"p\",null,{\"className\":\"kicker\",\"children\":\"For agents\"}],[\"$\",\"ul\",null,{\"className\":\"mt-4 grid gap-2\",\"children\":[[\"$\",\"li\",\"/llms.txt\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/llms.txt\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"llms.txt\"}]}],[\"$\",\"li\",\"/llms-full.txt\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/llms-full.txt\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"llms-full.txt\"}]}],[\"$\",\"li\",\"/index.md\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/index.md\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"index.md\"}]}],[\"$\",\"li\",\"/sitemap.xml\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/sitemap.xml\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"sitemap.xml\"}]}]]}]]}]\n20:[\"$\",\"nav\",\"Source\",{\"aria-label\":\"Source\",\"className\":\"md:col-span-2 md:first-of-type:col-start-7\",\"children\":[[\"$\",\"p\",null,{\"className\":\"kicker\",\"children\":\"Source\"}],[\"$\",\"ul\",null,{\"className\":\"mt-4 grid gap-2\",\"children\":[[\"$\",\"li\",\"https://github.com/ParthKapoor-dev/design\",{\"children\":[\"$\",\"a\",null,{\"href\":\"https://github.com/ParthKapoor-dev/design\",\"rel\":\"noopener noreferrer\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"GitHub\"}]}],[\"$\",\"li\",\"https://github.com/ParthKapoor-dev/design/blob/main/LICENSE\",{\"children\":[\"$\",\"a\",null,{\"href\":\"https://github.com/ParthKapoor-dev/design/blob/main/LICENSE\",\"rel\":\"noopener noreferrer\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"MIT licence\"}]}],[\"$\",\"li\",\"https://parthkapoor.me\",{\"children\":[\"$\",\"a\",null,{\"href\":\"https://parthkapoor.me\",\"rel\":\"noopener noreferrer\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Parth Kapoor\"}]}]]}]]}]\n21:[\"$\",\"p\",null,{\"aria-hidden\":\"true\",\"className\":\"wordmark relative mx-auto max-w-7xl select-none text-center font-semibold leading-[0.72] tracking-[-0.06em] text-[clamp(6rem,24vw,22rem)]\",\"children\":\"void\"}]\n22:[\"$\",\"div\",null,{\"className\":\"relative mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-4 border-t border-line py-6 font-mono text-xs text-mute\",\"children\":[[\"$\",\"span\",null,{\"children\":[\"© 2026 \",\"Parth Kapoor\",\" · \",\"MIT\"]}],[\"$\",\"span\",null,{\"children\":\"void-design.vercel.app\"}]]}]\n23:[\"$\",\"footer\",null,{\"className\":\"relative mt-24 overflow-hidden border-t border-line px-(--gutter)\",\"children\":[[\"$\",\"div\",null,{\"aria-hidden\":\"true\",\"className\":\"pointer-events-none absolute -bottom-[40vw] left-1/2 size-[80vw] -translate-x-1/2 rounded-full bg-[radial-gradient(closest-side,color-mix(in_oklab,var(--flare)_12%,transparent),transparent)]\"}],[\"$\",\"div\",null,{\"className\":\"relative mx-auto grid max-w-7xl gap-12 py-16 md:grid-cols-12 md:gap-8\",\"children\":[[\"$\",\"div\",null,{\"className\":\"md:col-span-5\",\"children\":[[\"$\",\"$La\",null,{\"href\":\"/\",\"className\":\"inline-flex items-center gap-2.5 text-ink\",\"aria-label\":\"void home\",\"children\":[[\"$\",\"$L18\",null,{\"className\":\"size-6\"}],[\"$\",\"span\",null,{\"className\":\"text-lg font-medium tracking-tight\",\"children\":\"void\"}]]}],[\"$\",\"p\",null,{\"className\":\"mt-4 max-w-[44ch] text-mute\",\"children\":\"A design system for AI coding agents. This site is built with it and audited by it on every change.\"}],[\"$\",\"p\",null,{\"className\":\"mt-6 inline-flex flex-wrap items-center gap-x-4 gap-y-1 rounded-full border border-line-2 bg-black/40 px-4 py-2 font-mono text-xs text-mute\",\"children\":[[\"$\",\"span\",null,{\"className\":\"text-flare-2\",\"children\":\"void audit\"}],[[\"$\",\"span\",\"perf\",{\"children\":[\"perf\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"100\"}]]}],[\"$\",\"span\",\"a11y\",{\"children\":[\"a11y\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"100\"}]]}],[\"$\",\"span\",\"seo\",{\"children\":[\"seo\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"99\"}]]}],[\"$\",\"span\",\"geo\",{\"children\":[\"geo\",\" \",[\"$\",\"span\",null,{\"className\":\"text-ink tabular-nums\",\"children\":\"100\"}]]}]]]}]]}],[[\"$\",\"nav\",\"Product\",{\"aria-label\":\"Product\",\"className\":\"md:col-span-2 md:first-of-type:col-start-7\",\"children\":[[\"$\",\"p\",null,{\"className\":\"kicker\",\"children\":\"Product\"}],[\"$\",\"ul\",null,{\"className\":\"mt-4 grid gap-2\",\"children\":[[\"$\",\"li\",\"/docs\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/docs\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Docs\"}]}],[\"$\",\"li\",\"/cli\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/cli\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"CLI reference\"}]}],[\"$\",\"li\",\"/rules\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/rules\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Rules\"}]}],[\"$\",\"li\",\"/directions\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/directions\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Directions\"}]}],[\"$\",\"li\",\"/evals\",{\"children\":[\"$\",\"$La\",null,{\"href\":\"/evals\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Evals\"}]}]]}]]}],[\"$\",\"nav\",\"For agents\",{\"aria-label\":\"For agents\",\"className\":\"md:col-span-2 md:first-of-type:col-start-7\",\"children\":[[\"$\",\"p\",null,{\"className\":\"kicker\",\"children\":\"For agents\"}],[\"$\",\"ul\",null,{\"className\":\"mt-4 grid gap-2\",\"children\":[[\"$\",\"li\",\"/llms.txt\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/llms.txt\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"llms.txt\"}]}],[\"$\",\"li\",\"/llms-full.txt\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/llms-full.txt\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"llms-full.txt\"}]}],[\"$\",\"li\",\"/index.md\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/index.md\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"index.md\"}]}],[\"$\",\"li\",\"/sitemap.xml\",{\"children\":[\"$\",\"a\",null,{\"href\":\"/sitemap.xml\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"sitemap.xml\"}]}]]}]]}],[\"$\",\"nav\",\"Source\",{\"aria-label\":\"Source\",\"className\":\"md:col-span-2 md:first-of-type:col-start-7\",\"children\":[[\"$\",\"p\",null,{\"className\":\"kicker\",\"children\":\"Source\"}],[\"$\",\"ul\",null,{\"className\":\"mt-4 grid gap-2\",\"children\":[[\"$\",\"li\",\"https://github.com/ParthKapoor-dev/design\",{\"children\":[\"$\",\"a\",null,{\"href\":\"https://github.com/ParthKapoor-dev/design\",\"rel\":\"noopener noreferrer\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"GitHub\"}]}],\"$L2d\",\"$L2e\"]}]]}]]]}],\"$L2f\",\"$L30\"]}]\n24:[\"$\",\"$L31\",null,{}]\n2d:[\"$\",\"li\",\"https://github.com/ParthKapoor-dev/design/blob/main/LICENSE\",{\"children\":[\"$\",\"a\",null,{\"href\":\"https://github.com/ParthKapoor-dev/design/blob/main/LICENSE\",\"rel\":\"noopener noreferrer\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"MIT licence\"}]}]\n2e:[\"$\",\"li\",\"https://parthkapoor.me\",{\"children\":[\"$\",\"a\",null,{\"href\":\"https://parthkapoor.me\",\"rel\":\"noopener noreferrer\",\"className\":\"inline-flex min-h-6 items-center text-sm text-mute transition-colors hover:text-ink\",\"children\":\"Parth Kapoor\"}]}]\n2f:[\"$\",\"p\",null,{\"aria-hidden\":\"true\",\"className\":\"wordmark relative mx-auto max-w-7xl select-none text-center font-semibold leading-[0.72] tracking-[-0.06em] text-[clamp(6rem,24vw,22rem)]\",\"children\":\"void\"}]\n30:[\"$\",\"div\",null,{\"className\":\"relative mx-auto flex max-w-7xl flex-wrap items-center justify-between gap-4 border-t border-line py-6 font-mono text-xs text-mute\",\"children\":[[\"$\",\"span\",null,{\"children\":[\"© 2026 \",\"Parth Kapoor\",\" · \",\"MIT\"]}],[\"$\",\"span\",null,{\"children\":\"void-design.vercel.app\"}]]}]\n33:I[50835,[\"/_next/static/immutable/chunks/3kyyc8_ntqnp4.js\",\"/_next/static/immutable/chunks/1635pnmauuvxi.js\",\"/_next/static/immutable/chunks/10-jx0r2jtr5s.js\",\"/_next/static/immutable/chunks/2-9e3j82g4al3.js\",\"/_next/static/immutable/chunks/15spxoq7fze2b.js\"],\"DocsNav\"]\n32:T58c,{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https://void-design.vercel.app/docs/a11y#webpage\",\"url\":\"https://void-design.vercel.app/docs/a11y\",\"name\":\"a11y skill: usable by keyboard, screen reader, zoom and reduced motion\",\"description\":\"Accessibility as craft for websites and web apps, targeting WCAG 2.2 AA with APCA as the quality bar.\",\"isPartOf\":{\"@id\":\"https://void-design.vercel.app/#website\"},\"inLanguage\":\"en\",\"dateModified\":\"2026-09-16\"},{\"@type\":\"TechArticle\",\"@id\":\"https://void-design.vercel.app/docs/a11y#article\",\"headline\":\"a11y skill: usable by keyboard, screen reader, zoom and reduced motion\",\"description\":\"Accessibility as craft for websites and web apps, targeting WCAG 2.2 AA with APCA as the quality bar.\",\"datePublished\":\"2026-09-16\",\"dateModified\":\"2026-09-16\",\"mainEntityOfPage\":{\"@id\":\"https://void-design.vercel.app/docs/a11y#webpage\"},\"author\":{\"@id\":\"https://void-design.vercel.app/#organization\"},\"publisher\":{\"@id\":\"https://void-design.vercel.app/#organization\"},\"image\":[\"https://void-design.vercel.app/docs/a11y/opengraph-image\"]},{\"@type\":\"BreadcrumbList\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"void\",\"item\":\"https://void-design.vercel.app/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"docs\",\"item\":\"https://void-design.vercel.app/docs\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"a11y\",\"item\":\"https://void-design.vercel.app/docs/a11y\"}]}]}25:[\"$\",\"div\",null,{\"className\":\"px-(--gutter)\",\"children\":[[\"$\",\"script\",null,{\"type\":\"application/ld+json\",\"dangerouslySetInnerHTML\":{\"__html\":\"$32\"}}],[\"$\",\"$L9\",null,{\"crumbs\":[{\"name\":\"void\",\"path\":\"/\"},{\"name\":\"docs\",\"path\":\"/docs\"},{\"name\":\"a11y\",\"path\":\"/docs/a11y\"}],\"kicker\":\"Skill · 08 of 09\",\"title\":[\"$\",\"span\",null,{\"className\":\"block text-[clamp(2.2rem,4.4vw,4.4rem)] leading-[0.98]\",\"children\":[\"a11y\",[\"$\",\"span\",null,{\"className\":\"serif text-mute\",\"children\":[\" \",\"usable by keyboard, screen reader, zoom and reduced motion\"]}]]}],\"lead\":\"Accessibility as craft for websites and web apps, targeting WCAG 2.2 AA with APCA as the quality bar.\",\"meta\":[[\"$\",\"span\",null,{\"children\":[\"skills/\",\"a11y\",\"/SKILL.md\"]}],[\"$\",\"span\",null,{\"children\":[160,\" lines\"]}],[\"$\",\"span\",null,{\"children\":[1,\" references\"]}],[\"$\",\"span\",null,{\"children\":[\"updated \",\"16 Sept 2026\"]}]],\"scene\":\"ether\"}],[\"$\",\"div\",null,{\"className\":\"mx-auto grid max-w-7xl gap-x-12 lg:grid-cols-[13rem_minmax(0,1fr)] xl:grid-cols-[13rem_minmax(0,1fr)_12rem]\",\"children\":[[\"$\",\"div\",null,{\"className\":\"lg:hidden\",\"children\":[\"$\",\"button\",null,{\"type\":\"button\",\"popoverTarget\":\"docs-nav\",\"className\":\"flex h-11 w-full items-center gap-2.5 rounded-xl border border-line bg-bg-subtle px-4 text-sm text-fg transition-colors hover:border-line-strong\",\"children\":[[\"$\",\"svg\",null,{\"aria-hidden\":true,\"viewBox\":\"0 0 16 16\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":1.5,\"className\":\"size-4 text-fg-muted\",\"children\":[\"$\",\"path\",null,{\"d\":\"M2.5 5.5h11M2.5 10.5h11\",\"strokeLinecap\":\"round\"}]}],\"Browse docs\"]}]}],[\"$\",\"aside\",null,{\"id\":\"docs-nav\",\"aria-label\":\"Documentation sections\",\"popover\":\"auto\",\"data-lenis-prevent\":true,\"className\":\"docs-aside m-0 max-h-[80dvh] w-[min(20rem,calc(100vw-2rem))] overflow-y-auto overscroll-contain rounded-2xl border border-line bg-surface-raised p-5 text-fg shadow-3 [scrollbar-color:var(--line-strong)_transparent] [scrollbar-width:thin]\",\"children\":[\"$\",\"$L33\",null,{\"groups\":[{\"title\":\"Start\",\"items\":[{\"href\":\"/docs\",\"label\":\"Getting started\"},{\"href\":\"/cli\",\"label\":\"CLI reference\"},{\"href\":\"/rules\",\"label\":\"Rules\"},{\"href\":\"/directions\",\"label\":\"Directions\"}]},{\"title\":\"Skills\",\"items\":[{\"href\":\"/docs/website\",\"label\":\"website\"},{\"href\":\"/docs/website/copy-and-content\",\"label\":\"copy and content\",\"parent\":\"/docs/website\"},{\"href\":\"/docs/website/page-types\",\"label\":\"page types\",\"parent\":\"/docs/website\"},{\"href\":\"/docs/website/section-archetypes\",\"label\":\"section archetypes\",\"parent\":\"/docs/website\"},{\"href\":\"/docs/craft\",\"label\":\"craft\"},{\"href\":\"/docs/craft/anti-slop\",\"label\":\"anti slop\",\"parent\":\"/docs/craft\"},{\"href\":\"/docs/craft/color\",\"label\":\"color\",\"parent\":\"/docs/craft\"},{\"href\":\"/docs/craft/depth-and-detail\",\"label\":\"depth and detail\",\"parent\":\"/docs/craft\"},{\"href\":\"/docs/craft/directions\",\"label\":\"directions\",\"parent\":\"/docs/craft\"},{\"href\":\"/docs/craft/layout\",\"label\":\"layout\",\"parent\":\"/docs/craft\"},{\"href\":\"/docs/craft/tailwind-v4\",\"label\":\"tailwind v4\",\"parent\":\"/docs/craft\"},{\"href\":\"/docs/craft/typography\",\"label\":\"typography\",\"parent\":\"/docs/craft\"},{\"href\":\"/docs/motion\",\"label\":\"motion\"},{\"href\":\"/docs/motion/3d-scenes\",\"label\":\"3d scenes\",\"parent\":\"/docs/motion\"},{\"href\":\"/docs/motion/CREDITS\",\"label\":\"CREDITS\",\"parent\":\"/docs/motion\"},{\"href\":\"/docs/motion/backgrounds\",\"label\":\"backgrounds\",\"parent\":\"/docs/motion\"},{\"href\":\"/docs/motion/fallbacks\",\"label\":\"fallbacks\",\"parent\":\"/docs/motion\"},{\"href\":\"/docs/motion/recipes\",\"label\":\"recipes\",\"parent\":\"/docs/motion\"},{\"href\":\"/docs/motion/timing\",\"label\":\"timing\",\"parent\":\"/docs/motion\"},{\"href\":\"/docs/motion/webgl-canvas\",\"label\":\"webgl canvas\",\"parent\":\"/docs/motion\"},{\"href\":\"/docs/components\",\"label\":\"components\"},{\"href\":\"/docs/components/audit-checklist\",\"label\":\"audit checklist\",\"parent\":\"/docs/components\"},{\"href\":\"/docs/components/catalogue\",\"label\":\"catalogue\",\"parent\":\"/docs/components\"},{\"href\":\"/docs/components/recipes\",\"label\":\"recipes\",\"parent\":\"/docs/components\"},{\"href\":\"/docs/components/upstream-bugs\",\"label\":\"upstream bugs\",\"parent\":\"/docs/components\"},{\"href\":\"/docs/inspire\",\"label\":\"inspire\"},{\"href\":\"/docs/inspire/effect-downgrades\",\"label\":\"effect downgrades\",\"parent\":\"/docs/inspire\"},{\"href\":\"/docs/inspire/extraction-guide\",\"label\":\"extraction guide\",\"parent\":\"/docs/inspire\"},{\"href\":\"/docs/inspire/font-alternatives\",\"label\":\"font alternatives\",\"parent\":\"/docs/inspire\"},{\"href\":\"/docs/speed\",\"label\":\"speed\"},{\"href\":\"/docs/speed/images-fonts\",\"label\":\"images fonts\",\"parent\":\"/docs/speed\"},{\"href\":\"/docs/speed/next-performance\",\"label\":\"next performance\",\"parent\":\"/docs/speed\"},{\"href\":\"/docs/speed/react-pitfalls\",\"label\":\"react pitfalls\",\"parent\":\"/docs/speed\"},{\"href\":\"/docs/speed/rendering-smoothness\",\"label\":\"rendering smoothness\",\"parent\":\"/docs/speed\"},{\"href\":\"/docs/seo\",\"label\":\"seo\"},{\"href\":\"/docs/seo/ai-crawlers\",\"label\":\"ai crawlers\",\"parent\":\"/docs/seo\"},{\"href\":\"/docs/seo/content-for-citation\",\"label\":\"content for citation\",\"parent\":\"/docs/seo\"},{\"href\":\"/docs/seo/llms-txt-markdown\",\"label\":\"llms txt markdown\",\"parent\":\"/docs/seo\"},{\"href\":\"/docs/seo/next-metadata\",\"label\":\"next metadata\",\"parent\":\"/docs/seo\"},{\"href\":\"/docs/seo/structured-data\",\"label\":\"structured data\",\"parent\":\"/docs/seo\"},{\"href\":\"/docs/a11y\",\"label\":\"a11y\"},{\"href\":\"/docs/a11y/patterns\",\"label\":\"patterns\",\"parent\":\"/docs/a11y\"},{\"href\":\"/docs/audit\",\"label\":\"audit\"},{\"href\":\"/docs/audit/ci\",\"label\":\"ci\",\"parent\":\"/docs/audit\"},{\"href\":\"/docs/audit/commands\",\"label\":\"commands\",\"parent\":\"/docs/audit\"}]}]}]}],\"$L34\",\"$L35\"]}]]}]\n36:T734a,\u003cp\u003eThe target is WCAG 2.2 AA as the hard gate and a Lighthouse/axe score of 100, with APCA as the perceptual quality bar. Accessible markup also makes pages readable to AI agents and crawlers (see the \u003ca class=\"code-link\" href=\"/docs/seo\"\u003e\u003ccode\u003eseo\u003c/code\u003e\u003c/a\u003e skill).\nPaste-ready components: \u003ca class=\"code-link\" href=\"/docs/a11y/patterns\"\u003e\u003ccode\u003ereferences/patterns.md\u003c/code\u003e\u003c/a\u003e. Visual tokens and focus ring styling: the \u003ca class=\"code-link\" href=\"/docs/craft\"\u003e\u003ccode\u003ecraft\u003c/code\u003e\u003c/a\u003e skill.\u003c/p\u003e\n\u003ch2 id=\"the-10-rules-that-matter-most\"\u003e\u003ca class=\"heading-anchor\" href=\"#the-10-rules-that-matter-most\"\u003eThe 10 rules that matter most\u003c/a\u003e\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003eUse the native element.\u003c/strong\u003e \u003ccode\u003e\u003ca href\u003e\u003c/code\u003e navigates, \u003ccode\u003e\u003cbutton type=\"button\"\u003e\u003c/code\u003e acts, \u003ccode\u003e\u003clabel\u003e\u003c/code\u003e labels. Never put \u003ccode\u003eonClick\u003c/code\u003e on a \u003ccode\u003ediv\u003c/code\u003e or \u003ccode\u003espan\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eEvery interactive element is reachable and operable by keyboard\u003c/strong\u003e in DOM order. No positive \u003ccode\u003etabindex\u003c/code\u003e. No keyboard traps except inside modal dialogs.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eVisible focus everywhere:\u003c/strong\u003e \u003ccode\u003e:focus-visible\u003c/code\u003e ring 2px solid \u003ccode\u003e--line-focus\u003c/code\u003e, offset 2px, ≥ 3:1 against the surface. Never remove an outline without a replacement.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eOne \u003ccode\u003e\u003cmain\u003e\u003c/code\u003e, one \u003ccode\u003e\u003ch1\u003e\u003c/code\u003e, headings in order\u003c/strong\u003e, with landmarks \u003ccode\u003e\u003cheader\u003e\u003c/code\u003e, \u003ccode\u003e\u003cnav aria-label\u003e\u003c/code\u003e, \u003ccode\u003e\u003cfooter\u003e\u003c/code\u003e. A skip link to \u003ccode\u003e#main\u003c/code\u003e comes first in \u003ccode\u003e\u003cbody\u003e\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eContrast:\u003c/strong\u003e body text ≥ 4.5:1 (APCA Lc 75+), large text ≥ 3:1, UI borders, icons and focus rings ≥ 3:1. Hover and focus states increase contrast, never decrease it.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eHit targets ≥ 24×24 CSS px\u003c/strong\u003e (WCAG 2.5.8), \u003cstrong\u003e≥ 44×44 on touch.\u003c/strong\u003e Grow the hit area with a pseudo-element, not by changing the layout.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eEvery image has \u003ccode\u003ealt\u003c/code\u003e.\u003c/strong\u003e Informative images get a description of their content; decorative ones get \u003ccode\u003ealt=\"\"\u003c/code\u003e. Icon-only buttons have an accessible name (\u003ccode\u003earia-label\u003c/code\u003e or \u003ccode\u003esr-only\u003c/code\u003e text).\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eForms:\u003c/strong\u003e a visible \u003ccode\u003e\u003clabel\u003e\u003c/code\u003e per field (a placeholder is not a label), correct \u003ccode\u003etype\u003c/code\u003e/\u003ccode\u003eautocomplete\u003c/code\u003e, errors as text linked with \u003ccode\u003earia-describedby\u003c/code\u003e + \u003ccode\u003earia-invalid\u003c/code\u003e, and focus moves to the first invalid field on submit.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eDialogs, menus, comboboxes and tabs use a proven primitive\u003c/strong\u003e (native \u003ccode\u003e\u003cdialog\u003e\u003c/code\u003e, Radix, Base UI, React Aria). Don\u0026#39;t hand-roll focus traps.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eRespect \u003ccode\u003eprefers-reduced-motion\u003c/code\u003e.\u003c/strong\u003e Nothing flashes more than 3×/s, and auto-moving content over 5 s has a pause control. Content never depends on an animation finishing.\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch2 id=\"structure\"\u003e\u003ca class=\"heading-anchor\" href=\"#structure\"\u003eStructure\u003c/a\u003e\u003c/h2\u003e\n\u003cfigure class=\"code-block\" data-lang=\"tsx\"\u003e\u003cfigcaption\u003e\u003cspan\u003etsx\u003c/span\u003e\u003cbutton type=\"button\" class=\"copy-button\" data-copy\u003eCopy\u003c/button\u003e\u003c/figcaption\u003e\u003cpre tabindex=\"0\" aria-label=\"tsx code\" class=\"shiki void\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-comment)\"\u003e// app/layout.tsx (inside \u0026#x3C;body\u003e)\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u0026#x3C;\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003ea\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e href\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e=\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e\"#main\"\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e className\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e=\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e\"sr-only focus:not-sr-only focus:fixed focus:left-4 focus:top-4 focus:z-50 focus:rounded-md focus:bg-surface-raised focus:px-4 focus:py-2 focus:text-fg\"\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e Skip to content\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u0026#x3C;/\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003ea\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u0026#x3C;\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003eheader\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e…\u0026#x3C;\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003enav\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e aria-label\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e=\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e\"Primary\"\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e…\u0026#x3C;/\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003enav\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e\u0026#x3C;/\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003eheader\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u0026#x3C;\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003emain\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e id\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e=\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e\"main\"\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e{children}\u0026#x3C;/\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003emain\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e {\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-comment)\"\u003e/* modern browsers move the focus start point to the target; no tabIndex needed */\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e}\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u0026#x3C;\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003efooter\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e…\u0026#x3C;/\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003efooter\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e\u003e\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/figure\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003eExactly one \u003ccode\u003e\u003cmain\u003e\u003c/code\u003e.\u003c/strong\u003e A layout \u003ccode\u003e\u003cmain\u003e\u003c/code\u003e plus a page \u003ccode\u003e\u003cmain\u003e\u003c/code\u003e gives two landmarks and a broken skip link. Pages render \u003ccode\u003e\u003carticle\u003e\u003c/code\u003e/\u003ccode\u003e\u003csection\u003e\u003c/code\u003e inside it.\u003c/li\u003e\n\u003cli\u003eMultiple \u003ccode\u003e\u003cnav\u003e\u003c/code\u003es each get a distinct \u003ccode\u003earia-label\u003c/code\u003e (\u0026quot;Primary\u0026quot;, \u0026quot;Footer\u0026quot;, \u0026quot;Breadcrumb\u0026quot;).\u003c/li\u003e\n\u003cli\u003eHeadings describe their section. Don\u0026#39;t skip levels going down (h2 → h4). Don\u0026#39;t pick heading levels for font size; style with tokens instead.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e\u003chtml lang=\"en\"\u003e\u003c/code\u003e matches the content. Mark phrases in another language with \u003ccode\u003elang\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e\u003ctitle\u003e\u003c/code\u003e names the page and site (\u0026quot;Pricing | Acme\u0026quot;). Client route changes in Next update it automatically.\u003c/li\u003e\n\u003cli\u003eLists are \u003ccode\u003e\u003cul\u003e\u003c/code\u003e/\u003ccode\u003e\u003col\u003e\u003c/code\u003e, tables are \u003ccode\u003e\u003cdiv class=\"table-wrap\" tabindex=\"0\" role=\"region\" aria-label=\"Table 1\"\u003e\u003ctable\u003e\u003c/code\u003e with \u003ccode\u003e\u003cth scope\u003e\u003c/code\u003e, times are \u003ccode\u003e\u003ctime dateTime\u003e\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eLinks say where they go (\u0026quot;Read the pricing guide\u0026quot;), not \u0026quot;click here\u0026quot;. Links that open a new tab say so (visible icon + \u003ccode\u003esr-only\u003c/code\u003e \u0026quot;(opens in new tab)\u0026quot;).\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"keyboard-and-focus\"\u003e\u003ca class=\"heading-anchor\" href=\"#keyboard-and-focus\"\u003eKeyboard and focus\u003c/a\u003e\u003c/h2\u003e\n\u003cfigure class=\"code-block\" data-lang=\"css\"\u003e\u003cfigcaption\u003e\u003cspan\u003ecss\u003c/span\u003e\u003cbutton type=\"button\" class=\"copy-button\" data-copy\u003eCopy\u003c/button\u003e\u003c/figcaption\u003e\u003cpre tabindex=\"0\" aria-label=\"css code\" class=\"shiki void\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-comment)\"\u003e/* app/globals.css: base layer (Tailwind v4) */\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e@layer\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e base {\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e :where\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e(\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003ea\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-punctuation)\"\u003e,\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e button\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-punctuation)\"\u003e,\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e input\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-punctuation)\"\u003e,\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e select\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-punctuation)\"\u003e,\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e textarea\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-punctuation)\"\u003e,\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e summary\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-punctuation)\"\u003e,\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e [\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003etabindex\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e])\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e:focus-visible\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e {\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e outline\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e 2\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003epx\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e solid \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003evar\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e(--line-focus)\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e;\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e outline-offset\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e 2\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003epx\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e;\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e }\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e :where\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e(\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003einput\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-punctuation)\"\u003e,\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e textarea\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-punctuation)\"\u003e,\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e select\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e)\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e:focus-visible\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e { \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003eoutline-offset\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e 0\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e; }\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e html\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e { \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003escroll-padding-top\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e calc\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e(\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003evar\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e(--header-h\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-punctuation)\"\u003e,\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e 64\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003epx\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e) \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e+\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e 16\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003epx\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e)\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e; } \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-comment)\"\u003e/* sticky header never hides focused elements (2.4.11) */\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e}\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/figure\u003e\n\u003cul\u003e\n\u003cli\u003eTailwind v4: \u003ccode\u003eoutline-none\u003c/code\u003e now literally sets \u003ccode\u003eoutline-style: none\u003c/code\u003e. Use \u003ccode\u003eoutline-hidden\u003c/code\u003e (which keeps a transparent outline for forced-colors mode) and always pair it with a \u003ccode\u003efocus-visible:\u003c/code\u003e ring. The utility form is \u003ccode\u003efocus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-line-focus\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eOn a colored or brand surface, use the double ring: \u003ccode\u003ebox-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--line-focus)\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eFocus rings appear instantly (no transition), because focus feedback must not lag.\u003c/li\u003e\n\u003cli\u003eComposite widgets (tabs, menus, radio groups, toolbars, listboxes, grids) use \u003cstrong\u003eone tab stop + arrow keys\u003c/strong\u003e (roving tabindex). Page navigation uses plain links, each a tab stop.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eEscape\u003c/code\u003e closes the topmost overlay and returns focus to its trigger.\u003c/li\u003e\n\u003cli\u003eGlobal shortcuts ignore keypresses inside inputs and editors. Single-key shortcuts must be remappable or disableable (2.1.4).\u003c/li\u003e\n\u003cli\u003eHide inert content with \u003ccode\u003einert\u003c/code\u003e (off-canvas menus, content behind non-dialog overlays), not just visually.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003edisplay:none\u003c/code\u003e/\u003ccode\u003ehidden\u003c/code\u003e removes from both the a11y tree and tab order. \u003ccode\u003earia-hidden=\"true\"\u003c/code\u003e removes only from the a11y tree, so never put it on focusable content.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"hit-targets-and-pointer\"\u003e\u003ca class=\"heading-anchor\" href=\"#hit-targets-and-pointer\"\u003eHit targets and pointer\u003c/a\u003e\u003c/h2\u003e\n\u003cfigure class=\"code-block\" data-lang=\"css\"\u003e\u003cfigcaption\u003e\u003cspan\u003ecss\u003c/span\u003e\u003cbutton type=\"button\" class=\"copy-button\" data-copy\u003eCopy\u003c/button\u003e\u003c/figcaption\u003e\u003cpre tabindex=\"0\" aria-label=\"css code\" class=\"shiki void\" style=\"background-color:var(--shiki-background);color:var(--shiki-foreground)\" tabindex=\"0\"\u003e\u003ccode\u003e\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e.hit\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e { \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003eposition\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e relative\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e; }\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e.hit::before\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e { \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003econtent\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-string-expression)\"\u003e \"\"\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e; \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003eposition\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e absolute\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e; \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003einset\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e -8\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003epx\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e; } \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-comment)\"\u003e/* 24px icon → 40px target */\u003c/span\u003e\u003c/span\u003e\n\u003cspan class=\"line\"\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e@media\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e (pointer\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e coarse) { \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-function)\"\u003e.hit::before\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e { \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003einset\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003e:\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-constant)\"\u003e -12\u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-keyword)\"\u003epx\u003c/span\u003e\u003cspan style=\"color:var(--shiki-foreground)\"\u003e; } } \u003c/span\u003e\u003cspan style=\"color:var(--shiki-token-comment)\"\u003e/* ≥ 44px on touch */\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/figure\u003e\n\u003cul\u003e\n\u003cli\u003eCheckbox/radio + label are one hit area (the whole \u003ccode\u003e\u003clabel\u003e\u003c/code\u003e row).\u003c/li\u003e\n\u003cli\u003eAdjacent small targets have ≥ 8 px gaps.\u003c/li\u003e\n\u003cli\u003eDragging (sliders, reorder, swipe) always has a click/tap alternative (2.5.7).\u003c/li\u003e\n\u003cli\u003eHover-only UI is forbidden. Anything revealed on hover also appears on focus and is reachable by touch.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003etouch-action: manipulation\u003c/code\u003e on controls removes the double-tap zoom delay.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"color-and-contrast-tie-to-tokens\"\u003e\u003ca class=\"heading-anchor\" href=\"#color-and-contrast-tie-to-tokens\"\u003eColor and contrast (tie to tokens)\u003c/a\u003e\u003c/h2\u003e\n\u003cdiv class=\"table-wrap\" tabindex=\"0\" role=\"region\" aria-label=\"Table 2\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eContent\u003c/th\u003e\n\u003cth\u003eWCAG 2.2 (hard gate)\u003c/th\u003e\n\u003cth\u003eAPCA Lc (quality target)\u003c/th\u003e\n\u003cth\u003eToken pairs to check\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003eBody text ≤ 18px\u003c/td\u003e\n\u003ctd\u003e≥ 4.5:1\u003c/td\u003e\n\u003ctd\u003e≥ 75, \u003cstrong\u003e90 preferred\u003c/strong\u003e\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003e--fg\u003c/code\u003e on \u003ccode\u003e--bg\u003c/code\u003e, \u003ccode\u003e--surface\u003c/code\u003e, \u003ccode\u003e--surface-raised\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSecondary text, UI labels\u003c/td\u003e\n\u003ctd\u003e≥ 4.5:1\u003c/td\u003e\n\u003ctd\u003e≥ 60\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003e--fg-muted\u003c/code\u003e on every surface\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eLarge text (≥ 24px, or ≥ 18.66px bold)\u003c/td\u003e\n\u003ctd\u003e≥ 3:1\u003c/td\u003e\n\u003ctd\u003e≥ 45\u003c/td\u003e\n\u003ctd\u003edisplay sizes on \u003ccode\u003e--bg\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eMeta, captions, placeholders\u003c/td\u003e\n\u003ctd\u003e≥ 4.5:1\u003c/td\u003e\n\u003ctd\u003e≥ 60\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003e--fg-subtle\u003c/code\u003e (AA on \u003ccode\u003e--bg\u003c/code\u003e, \u003ccode\u003e--bg-subtle\u003c/code\u003e, \u003ccode\u003e--surface\u003c/code\u003e, \u003ccode\u003e--surface-raised\u003c/code\u003e)\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDisabled controls, \u003ccode\u003earia-hidden\u003c/code\u003e decoration\u003c/td\u003e\n\u003ctd\u003e3:1 (disabled is exempt from 1.4.3)\u003c/td\u003e\n\u003ctd\u003e≥ 30\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003e--fg-faint\u003c/code\u003e — never text someone must read\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eInput borders, focus ring, meaningful icons, chart marks\u003c/td\u003e\n\u003ctd\u003e≥ 3:1 (1.4.11)\u003c/td\u003e\n\u003ctd\u003e≥ 45–60 for icons\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003e--line-strong\u003c/code\u003e, \u003ccode\u003e--line-focus\u003c/code\u003e vs adjacent surface\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eText on brand\u003c/td\u003e\n\u003ctd\u003e≥ 4.5:1\u003c/td\u003e\n\u003ctd\u003e≥ 60\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003e--fg-on-brand\u003c/code\u003e on \u003ccode\u003e--brand\u003c/code\u003e and \u003ccode\u003e--brand-hover\u003c/code\u003e; \u003ccode\u003e--brand-text\u003c/code\u003e on \u003ccode\u003e--bg\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eStatus text\u003c/td\u003e\n\u003ctd\u003e≥ 4.5:1\u003c/td\u003e\n\u003ctd\u003e≥ 60\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003e--danger\u003c/code\u003e/\u003ccode\u003e--success\u003c/code\u003e/\u003ccode\u003e--warning\u003c/code\u003e text on \u003ccode\u003e--bg\u003c/code\u003e and their \u003ccode\u003e-subtle\u003c/code\u003e fills\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/div\u003e\n\u003cul\u003e\n\u003cli\u003eCheck both \u003cstrong\u003elight and dark\u003c/strong\u003e themes. WCAG 2 overstates light-on-dark contrast, so APCA is stricter there.\u003c/li\u003e\n\u003cli\u003eNever convey meaning by color alone. Errors get an icon + text, status gets a label, links in body text get an underline.\u003c/li\u003e\n\u003cli\u003eDon\u0026#39;t put \u003ccode\u003e--fg-muted\u003c/code\u003e gray text on colored fills. Use the fill\u0026#39;s own text token.\u003c/li\u003e\n\u003cli\u003eSupport \u003ccode\u003eforced-colors: active\u003c/code\u003e (Windows High Contrast): don\u0026#39;t rely on \u003ccode\u003ebackground-image\u003c/code\u003e or \u003ccode\u003ebox-shadow\u003c/code\u003e alone for borders or focus. Outlines survive; shadows don\u0026#39;t.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"forms\"\u003e\u003ca class=\"heading-anchor\" href=\"#forms\"\u003eForms\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e\u003clabel for\u003e\u003c/code\u003e or a wrapping \u003ccode\u003e\u003clabel\u003e\u003c/code\u003e, visible and above the field. Required fields: \u003ccode\u003erequired\u003c/code\u003e + visible \u0026quot;(required)\u0026quot; text or a legend note.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003etype=\"email|tel|url|number\"\u003c/code\u003e, \u003ccode\u003einputMode\u003c/code\u003e, \u003ccode\u003eautoComplete=\"email|name|current-password|one-time-code\"\u003c/code\u003e (1.3.5). \u003ccode\u003espellCheck={false}\u003c/code\u003e for codes and emails.\u003c/li\u003e\n\u003cli\u003eValidate on blur and on submit, not on every keystroke. Once an error shows, re-validate on input.\u003c/li\u003e\n\u003cli\u003eError: text below the field with an icon; \u003ccode\u003earia-invalid=\"true\"\u003c/code\u003e; \u003ccode\u003earia-describedby\u003c/code\u003e → error id; focus the first invalid field on submit. For long forms, add an error summary with links.\u003c/li\u003e\n\u003cli\u003eDon\u0026#39;t disable the submit button before the user tries. Let submit reveal what\u0026#39;s missing. While submitting, keep the label (\u0026quot;Saving…\u0026quot;) and set \u003ccode\u003earia-busy\u003c/code\u003e/\u003ccode\u003edisabled\u003c/code\u003e on the button.\u003c/li\u003e\n\u003cli\u003eGroup radios and checkboxes in \u003ccode\u003e\u003cfieldset\u003e\u003c/code\u003e + \u003ccode\u003e\u003clegend\u003e\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eNever block paste. Allow password managers (3.3.8): no CAPTCHA-only logins, no copy-blocking on OTP fields.\u003c/li\u003e\n\u003cli\u003eField height ≥ 44 px on touch. Font size ≥ 16 px on inputs so iOS doesn\u0026#39;t zoom.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"motion\"\u003e\u003ca class=\"heading-anchor\" href=\"#motion\"\u003eMotion\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eEvery animation is wrapped by \u003ccode\u003e@media (prefers-reduced-motion: no-preference)\u003c/code\u003e, or neutralized under \u003ccode\u003ereduce\u003c/code\u003e. See \u003ca class=\"code-link\" href=\"/docs/speed/rendering-smoothness\"\u003e\u003ccode\u003espeed/references/rendering-smoothness.md\u003c/code\u003e\u003c/a\u003e §2 and the \u003ca class=\"code-link\" href=\"/docs/motion\"\u003e\u003ccode\u003emotion\u003c/code\u003e\u003c/a\u003e skill.\u003c/li\u003e\n\u003cli\u003eUnder reduced motion, replace movement with ≤ 150 ms opacity changes or nothing. Keep essential feedback (progress, state change).\u003c/li\u003e\n\u003cli\u003eNo parallax, zoom-on-scroll, large translations or auto-advancing carousels under \u003ccode\u003ereduce\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eAuto-playing video, marquees and carousels \u0026gt; 5 s get a visible pause button (2.2.2).\u003c/li\u003e\n\u003cli\u003eNothing flashes more than 3 times per second (2.3.1).\u003c/li\u003e\n\u003cli\u003eDecorative animated text (scramble, typewriter) keeps the real text in \u003ccode\u003esr-only\u003c/code\u003e and marks the animated copy \u003ccode\u003earia-hidden\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"overlays-and-widgets-choose-dont-hand-roll\"\u003e\u003ca class=\"heading-anchor\" href=\"#overlays-and-widgets-choose-dont-hand-roll\"\u003eOverlays and widgets: choose, don\u0026#39;t hand-roll\u003c/a\u003e\u003c/h2\u003e\n\u003cdiv class=\"table-wrap\" tabindex=\"0\" role=\"region\" aria-label=\"Table 3\"\u003e\u003ctable\u003e\n\u003cthead\u003e\n\u003ctr\u003e\n\u003cth\u003eNeed\u003c/th\u003e\n\u003cth\u003eFirst choice\u003c/th\u003e\n\u003cth\u003eWeight (gzip)\u003c/th\u003e\n\u003cth\u003eNotes\u003c/th\u003e\n\u003c/tr\u003e\n\u003c/thead\u003e\n\u003ctbody\u003e\u003ctr\u003e\n\u003ctd\u003eDisclosure / accordion\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003e\u003cdetails\u003e\u003c/code\u003e/\u003ccode\u003e\u003csummary\u003e\u003c/code\u003e (use \u003ccode\u003ename\u003c/code\u003e for exclusive groups)\u003c/td\u003e\n\u003ctd\u003e0 KB\u003c/td\u003e\n\u003ctd\u003eStyle \u003ccode\u003esummary\u003c/code\u003e; don\u0026#39;t hide key SEO content in closed panels\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eModal dialog\u003c/td\u003e\n\u003ctd\u003enative \u003ccode\u003e\u003cdialog\u003e\u003c/code\u003e + \u003ccode\u003eshowModal()\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e0 KB (tiny client leaf)\u003c/td\u003e\n\u003ctd\u003eBackground becomes inert, Esc closes, focus returns to the opener. \u003ccode\u003eclosedby=\"any\"\u003c/code\u003e is Chromium/Firefox only, so add a backdrop click handler\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eNon-modal popover / simple menu of links\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003epopover\u003c/code\u003e attribute + \u003ccode\u003epopovertarget\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e0 KB\u003c/td\u003e\n\u003ctd\u003eLight-dismiss and Esc built in; Baseline 2025\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eButton opens dialog/popover without JS\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003ecommand\u003c/code\u003e/\u003ccode\u003ecommandfor\u003c/code\u003e invokers\u003c/td\u003e\n\u003ctd\u003e0 KB\u003c/td\u003e\n\u003ctd\u003eBaseline newly available (Safari 26.2+); keep a JS fallback for older browsers\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eDropdown menu, context menu, select, combobox, date picker\u003c/td\u003e\n\u003ctd\u003eRadix (\u003ccode\u003eradix-ui\u003c/code\u003e), Base UI (\u003ccode\u003e@base-ui/react\u003c/code\u003e), or React Aria Components\u003c/td\u003e\n\u003ctd\u003eRadix Dialog ~12.6 KB, Tabs ~8.4 KB, Popover ~22.6 KB (includes Floating UI)\u003c/td\u003e\n\u003ctd\u003eImport per component. The full packages are large if not tree-shaken (Base UI ~147 KB, React Aria Components ~274 KB). Measure with \u003ccode\u003evoid perf\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eToasts\u003c/td\u003e\n\u003ctd\u003e\u003ccode\u003esonner\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e~9.4 KB\u003c/td\u003e\n\u003ctd\u003eAnnounces via a live region; toasts only for mutations\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eTabs\u003c/td\u003e\n\u003ctd\u003eRadix Tabs or the pattern in \u003ccode\u003epatterns.md\u003c/code\u003e\u003c/td\u003e\n\u003ctd\u003e0–8 KB\u003c/td\u003e\n\u003ctd\u003eArrow keys, \u003ccode\u003earia-selected\u003c/code\u003e, \u003ccode\u003earia-controls\u003c/code\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/tbody\u003e\u003c/table\u003e\u003c/div\u003e\n\u003cp\u003eUse one primitive library per app. Vendored registry components (shadcn blocks, Magic UI, Aceternity, React Bits) often ship with no ARIA and no reduced-motion handling, so audit them before use.\u003c/p\u003e\n\u003ch2 id=\"images-media-icons\"\u003e\u003ca class=\"heading-anchor\" href=\"#images-media-icons\"\u003eImages, media, icons\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003ealt\u003c/code\u003e describes purpose and content in ≤ 125 chars. Don\u0026#39;t start with \u0026quot;image of\u0026quot;. Logos: the company name. Linked images: the link destination.\u003c/li\u003e\n\u003cli\u003eCharts: a text summary or data table next to the chart. Complex SVG: \u003ccode\u003erole=\"img\"\u003c/code\u003e + \u003ccode\u003e\u003ctitle\u003e\u003c/code\u003e/\u003ccode\u003earia-label\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003eDecorative SVG icons: \u003ccode\u003earia-hidden=\"true\"\u003c/code\u003e (lucide does this by default). Icon-only buttons: \u003ccode\u003e\u003cbutton aria-label=\"Close\"\u003e\u003c/code\u003e. \u003ccode\u003etitle\u003c/code\u003e alone isn\u0026#39;t enough.\u003c/li\u003e\n\u003cli\u003eVideo: captions (\u003ccode\u003e\u003ctrack kind=\"captions\"\u003e\u003c/code\u003e), no autoplay with sound, controls available. Audio-only content gets a transcript.\u003c/li\u003e\n\u003cli\u003eCanvas that conveys status: \u003ccode\u003erole=\"img\"\u003c/code\u003e + \u003ccode\u003earia-label\u003c/code\u003e. Decorative canvas: \u003ccode\u003earia-hidden=\"true\"\u003c/code\u003e.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"live-regions-and-status\"\u003e\u003ca class=\"heading-anchor\" href=\"#live-regions-and-status\"\u003eLive regions and status\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eToast and async results: \u003ccode\u003erole=\"status\"\u003c/code\u003e (polite) for success and info; \u003ccode\u003erole=\"alert\"\u003c/code\u003e only for errors that need immediate attention.\u003c/li\u003e\n\u003cli\u003eThe live region must \u003cstrong\u003eexist in the DOM before\u003c/strong\u003e its text changes. Mount it empty at startup and update its text later.\u003c/li\u003e\n\u003cli\u003eLoading: \u003ccode\u003earia-busy=\"true\"\u003c/code\u003e on the region being updated. Spinners get \u003ccode\u003esr-only\u003c/code\u003e \u0026quot;Loading…\u0026quot; text.\u003c/li\u003e\n\u003cli\u003eDon\u0026#39;t announce on every keystroke. Debounce search result counts (\u0026quot;12 results\u0026quot;).\u003c/li\u003e\n\u003cli\u003eStatus is never color-only: pair the dot with a text label (\u003ccode\u003erole=\"status\"\u003c/code\u003e where it updates).\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"zoom-reflow-text-spacing\"\u003e\u003ca class=\"heading-anchor\" href=\"#zoom-reflow-text-spacing\"\u003eZoom, reflow, text spacing\u003c/a\u003e\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eNever set \u003ccode\u003emaximum-scale=1\u003c/code\u003e or \u003ccode\u003euser-scalable=no\u003c/code\u003e.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eReflow at 320 CSS px width\u003c/strong\u003e (≈ 400% zoom) with no horizontal scroll except for tables, code and maps (1.4.10). Test at 320px and at 200% browser zoom.\u003c/li\u003e\n\u003cli\u003eSizes in \u003ccode\u003erem\u003c/code\u003e for type. Display type uses \u003ccode\u003eclamp()\u003c/code\u003e so the h1 fits at 320px.\u003c/li\u003e\n\u003cli\u003eTolerate user text spacing (1.4.12): no fixed heights on text containers. Use \u003ccode\u003emin-height\u003c/code\u003e, and \u003ccode\u003eline-clamp\u003c/code\u003e only with a way to read the rest.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003eoverflow-x: clip\u003c/code\u003e (not \u003ccode\u003ehidden\u003c/code\u003e) on \u003ccode\u003ehtml\u003c/code\u003e/\u003ccode\u003ebody\u003c/code\u003e, so sticky elements and focus scrolling keep working.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e100dvh\u003c/code\u003e/\u003ccode\u003esvh\u003c/code\u003e instead of \u003ccode\u003e100vh\u003c/code\u003e for full-height layouts on mobile.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch2 id=\"verify\"\u003e\u003ca class=\"heading-anchor\" href=\"#verify\"\u003eVerify\u003c/a\u003e\u003c/h2\u003e\n\u003col\u003e\n\u003cli\u003e\u003ccode\u003evoid lint\u003c/code\u003e (static): \u003ca class=\"code-link\" href=\"/rules/lint/div-button\"\u003e\u003ccode\u003elint/div-button\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/lint/missing-alt\"\u003e\u003ccode\u003elint/missing-alt\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/lint/outline-none-no-replacement\"\u003e\u003ccode\u003elint/outline-none-no-replacement\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/lint/no-reduced-motion\"\u003e\u003ccode\u003elint/no-reduced-motion\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/lint/multiple-h1\"\u003e\u003ccode\u003elint/multiple-h1\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/lint/missing-lang\"\u003e\u003ccode\u003elint/missing-lang\u003c/code\u003e\u003c/a\u003e.\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003evoid a11y --start \"next start -p 3000\" --port 3000 --format md\u003c/code\u003e on a production build: axe-core (WCAG 2.2 AA + best practices) on mobile and desktop, plus void\u0026#39;s own checks \u003ca class=\"code-link\" href=\"/rules/a11y/focus-not-visible\"\u003e\u003ccode\u003ea11y/focus-not-visible\u003c/code\u003e\u003c/a\u003e (keyboard focus walk), \u003ca class=\"code-link\" href=\"/rules/a11y/target-size\"\u003e\u003ccode\u003ea11y/target-size\u003c/code\u003e\u003c/a\u003e (mobile), \u003ca class=\"code-link\" href=\"/rules/a11y/reflow-overflow\"\u003e\u003ccode\u003ea11y/reflow-overflow\u003c/code\u003e\u003c/a\u003e (320px). Common axe ids: \u003ca class=\"code-link\" href=\"/rules/a11y/color-contrast\"\u003e\u003ccode\u003ea11y/color-contrast\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/button-name\"\u003e\u003ccode\u003ea11y/button-name\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/link-name\"\u003e\u003ccode\u003ea11y/link-name\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/label\"\u003e\u003ccode\u003ea11y/label\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/image-alt\"\u003e\u003ccode\u003ea11y/image-alt\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/heading-order\"\u003e\u003ccode\u003ea11y/heading-order\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/landmark-one-main\"\u003e\u003ccode\u003ea11y/landmark-one-main\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/bypass\"\u003e\u003ccode\u003ea11y/bypass\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/meta-viewport\"\u003e\u003ccode\u003ea11y/meta-viewport\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/aria-hidden-focus\"\u003e\u003ccode\u003ea11y/aria-hidden-focus\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/nested-interactive\"\u003e\u003ccode\u003ea11y/nested-interactive\u003c/code\u003e\u003c/a\u003e, \u003ca class=\"code-link\" href=\"/rules/a11y/link-in-text-block\"\u003e\u003ccode\u003ea11y/link-in-text-block\u003c/code\u003e\u003c/a\u003e. Unmapped axe rules arrive as \u003ca class=\"code-link\" href=\"/rules/a11y/axe-other\"\u003e\u003ccode\u003ea11y/axe-other\u003c/code\u003e\u003c/a\u003e with the axe id in the message.\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003eStop when:\u003c/strong\u003e zero axe violations, the focus walk passes, a11y score 100. Then do a 2-minute manual pass: Tab through the page, open and close every overlay with the keyboard, and use VoiceOver (macOS: Cmd+F5) or NVDA on the main flow.\u003c/li\u003e\n\u003cli\u003eAutomated tools catch only part of real issues. The manual pass is not optional for dialogs, menus and forms.\u003c/li\u003e\n\u003c/ol\u003e\n34:[\"$\",\"article\",null,{\"className\":\"min-w-0 max-w-[68ch] pt-8 lg:pt-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"relative overflow-hidden rounded-2xl border border-line bg-bg-subtle p-5\",\"children\":[[\"$\",\"span\",null,{\"aria-hidden\":\"true\",\"className\":\"absolute inset-y-0 left-0 w-px bg-gradient-to-b from-flare-2 via-flare to-transparent\"}],[\"$\",\"p\",null,{\"className\":\"label-mono text-brand-text\",\"children\":\"When Claude Code loads it\"}],[\"$\",\"p\",null,{\"className\":\"mt-2 text-fg-muted\",\"children\":\"Use when building or reviewing any UI, component, landing page, form, modal, dropdown, navigation or interactive widget; when choosing colors or focus styles; when adding animation; when asked about accessibility, a11y, WCAG, ARIA, screen readers or keyboard navigation; or when `void a11y`, axe or Lighthouse report accessibility issues.\"}]]}],[\"$\",\"div\",null,{\"className\":\"prose mt-10\",\"dangerouslySetInnerHTML\":{\"__html\":\"$36\"}}],\"$L37\"]}]\n35:[\"$\",\"aside\",null,{\"aria-label\":\"Page contents\",\"className\":\"hidden xl:block\",\"children\":[\"$\",\"div\",null,{\"data-lenis-prevent\":true,\"className\":\"sticky top-(--header-h) max-h-[calc(100dvh-var(--header-h))] overflow-y-auto pt-4 pb-10 [scrollbar-width:thin]\",\"children\":[\"$\",\"nav\",null,{\"aria-label\":\"On this page\",\"className\":\"text-sm\",\"children\":[[\"$\",\"p\",null,{\"className\":\"label-mono mb-3 text-fg-muted\",\"children\":\"On this page\"}],[\"$\",\"ul\",null,{\"className\":\"grid gap-1.5 border-l border-line-subtle\",\"children\":[[\"$\",\"li\",\"the-10-rules-that-matter-most\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#the-10-rules-that-matter-most\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"The 10 rules that matter most\"}]}],[\"$\",\"li\",\"structure\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#structure\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Structure\"}]}],[\"$\",\"li\",\"keyboard-and-focus\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#keyboard-and-focus\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Keyboard and focus\"}]}],[\"$\",\"li\",\"hit-targets-and-pointer\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#hit-targets-and-pointer\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Hit targets and pointer\"}]}],[\"$\",\"li\",\"color-and-contrast-tie-to-tokens\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#color-and-contrast-tie-to-tokens\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Color and contrast (tie to tokens)\"}]}],[\"$\",\"li\",\"forms\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#forms\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Forms\"}]}],[\"$\",\"li\",\"motion\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#motion\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Motion\"}]}],[\"$\",\"li\",\"overlays-and-widgets-choose-dont-hand-roll\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#overlays-and-widgets-choose-dont-hand-roll\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Overlays and widgets: choose, don't hand-roll\"}]}],[\"$\",\"li\",\"images-media-icons\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#images-media-icons\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Images, media, icons\"}]}],[\"$\",\"li\",\"live-regions-and-status\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#live-regions-and-status\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Live regions and status\"}]}],[\"$\",\"li\",\"zoom-reflow-text-spacing\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#zoom-reflow-text-spacing\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Zoom, reflow, text spacing\"}]}],[\"$\",\"li\",\"verify\",{\"children\":[\"$\",\"a\",null,{\"href\":\"#verify\",\"className\":\"block py-0.5 pr-2 text-fg-muted transition-colors duration-(--duration-fast) hover:text-fg pl-4\",\"children\":\"Verify\"}]}]]}]]}]}]}]\n37:[\"$\",\"footer\",null,{\"className\":\"mt-16 border-t border-line-subtle pt-8 pb-20\",\"children\":[[\"$\",\"div\",null,{\"className\":\"flex flex-wrap items-center justify-between gap-3 font-mono text-xs text-fg-muted\",\"children\":[[\"$\",\"span\",null,{\"children\":[\"Updated \",[\"$\",\"time\",null,{\"dateTime\":\"2026-09-16\",\"children\":\"16 Sept 2026\"}]]}],[\"$\",\"span\",null,{\"className\":\"flex gap-4\",\"children\":[[\"$\",\"a\",null,{\"href\":\"/docs/a11y.md\",\"className\":\"hover:text-fg\",\"children\":\"View as Markdown\"}],[\"$\",\"a\",null,{\"href\":\"https://github.com/ParthKapoor-dev/design/blob/main/skills/a11y/SKILL.md\",\"rel\":\"noopener noreferrer\",\"className\":\"hover:text-fg\",\"children\":\"Edit on GitHub\"}]]}]]}],[\"$\",\"div\",null,{\"className\":\"mt-8 grid gap-3 sm:grid-cols-2\",\"children\":[[\"$\",\"$La\",null,{\"href\":\"/docs/seo/structured-data\",\"className\":\"press group flex flex-col gap-1 rounded-lg border border-line-subtle p-4 transition-colors duration-(--duration-fast) hover:border-line-strong\",\"children\":[[\"$\",\"span\",null,{\"className\":\"label-mono flex items-center gap-1.5 text-fg-muted\",\"children\":[[\"$\",\"svg\",null,{\"aria-hidden\":true,\"viewBox\":\"0 0 16 16\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":1.5,\"className\":\"size-3\",\"children\":[\"$\",\"path\",null,{\"d\":\"M13 8H3.5M7.5 4l-4 4 4 4\",\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\"}]}],\" Previous\"]}],[\"$\",\"span\",null,{\"className\":\"text-sm text-fg\",\"children\":\"seo: structured data\"}]]}],[\"$\",\"$La\",null,{\"href\":\"/docs/a11y/patterns\",\"className\":\"press group flex flex-col items-end gap-1 rounded-lg border border-line-subtle p-4 text-right transition-colors duration-(--duration-fast) hover:border-line-strong\",\"children\":[[\"$\",\"span\",null,{\"className\":\"label-mono flex items-center gap-1.5 text-fg-muted\",\"children\":[\"Next \",[\"$\",\"svg\",null,{\"aria-hidden\":true,\"viewBox\":\"0 0 16 16\",\"fill\":\"none\",\"stroke\":\"currentColor\",\"strokeWidth\":1.5,\"className\":\"size-3\",\"children\":[\"$\",\"path\",null,{\"d\":\"M3 8h9.5M8.5 4l4 4-4 4\",\"strokeLinecap\":\"round\",\"strokeLinejoin\":\"round\"}]}]]}],[\"$\",\"span\",null,{\"className\":\"text-sm text-fg\",\"children\":\"a11y: patterns\"}]]}]]}]]}]\n2a:[[\"$\",\"meta\",\"0\",{\"charSet\":\"utf-8\"}],[\"$\",\"meta\",\"1\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"2\",{\"name\":\"theme-color\",\"content\":\"#020203\"}],[\"$\",\"meta\",\"3\",{\"name\":\"color-scheme\",\"content\":\"dark\"}]]\n38:I[49310,[\"/_next/static/immutable/chunks/455sb5e0fgpzb.js\"],\"IconMark\"]\n28:null\n2c:[[\"$\",\"title\",\"0\",{\"children\":\"a11y skill: usable by keyboard, screen reader, zoom and reduced motion | void\"}],[\"$\",\"meta\",\"1\",{\"name\":\"description\",\"content\":\"Accessibility as craft for websites and web apps, targeting WCAG 2.2 AA with APCA as the quality bar.\"}],[\"$\",\"meta\",\"2\",{\"name\":\"application-name\",\"content\":\"void\"}],[\"$\",\"link\",\"3\",{\"rel\":\"author\",\"href\":\"https://parthkapoor.me\"}],[\"$\",\"meta\",\"4\",{\"name\":\"author\",\"content\":\"Parth Kapoor\"}],[\"$\",\"link\",\"5\",{\"rel\":\"manifest\",\"href\":\"/manifest.webmanifest\",\"crossOrigin\":\"$undefined\"}],[\"$\",\"meta\",\"6\",{\"name\":\"keywords\",\"content\":\"design system,AI coding agents,Claude Code,WebGL,shaders,web performance,accessibility,SEO,Next.js,Tailwind\"}],[\"$\",\"meta\",\"7\",{\"name\":\"creator\",\"content\":\"Parth Kapoor\"}],[\"$\",\"meta\",\"8\",{\"name\":\"publisher\",\"content\":\"Parth Kapoor\"}],[\"$\",\"meta\",\"9\",{\"name\":\"robots\",\"content\":\"index, follow\"}],[\"$\",\"meta\",\"10\",{\"name\":\"googlebot\",\"content\":\"index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1\"}],[\"$\",\"meta\",\"11\",{\"name\":\"category\",\"content\":\"technology\"}],[\"$\",\"link\",\"12\",{\"rel\":\"canonical\",\"href\":\"https://void-design.vercel.app/docs/a11y\"}],[\"$\",\"link\",\"13\",{\"rel\":\"alternate\",\"type\":\"text/markdown\",\"href\":\"https://void-design.vercel.app/docs/a11y.md\"}],[\"$\",\"meta\",\"14\",{\"name\":\"format-detection\",\"content\":\"telephone=no, address=no, email=no\"}],[\"$\",\"meta\",\"15\",{\"name\":\"mobile-web-app-capable\",\"content\":\"yes\"}],[\"$\",\"meta\",\"16\",{\"name\":\"apple-mobile-web-app-title\",\"content\":\"void\"}],[\"$\",\"meta\",\"17\",{\"name\":\"apple-mobile-web-app-status-bar-style\",\"content\":\"black-translucent\"}],[\"$\",\"meta\",\"18\",{\"property\":\"og:title\",\"content\":\"a11y skill: usable by keyboard, screen reader, zoom and reduced motion\"}],[\"$\",\"meta\",\"19\",{\"property\":\"og:description\",\"content\":\"Accessibility as craft for websites and web apps, targeting WCAG 2.2 AA with APCA as the quality bar.\"}],[\"$\",\"meta\",\"20\",{\"property\":\"og:url\",\"content\":\"https://void-design.vercel.app/docs/a11y\"}],[\"$\",\"meta\",\"21\",{\"property\":\"og:site_name\",\"content\":\"void\"}],[\"$\",\"meta\",\"22\",{\"property\":\"og:locale\",\"content\":\"en_US\"}],[\"$\",\"meta\",\"23\",{\"property\":\"og:image\",\"content\":\"https://void-design.vercel.app/docs/a11y/opengraph-image-1dv662?d56abb6face50325\"}],[\"$\",\"meta\",\"24\",{\"property\":\"og:image:type\",\"content\":\"image/png\"}],[\"$\",\"meta\",\"25\",{\"property\":\"og:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"26\",{\"property\":\"og:image:height\",\"content\":\"630\"}],[\"$\",\"meta\",\"27\",{\"property\":\"og:image:alt\",\"content\":\"void skill\"}],[\"$\",\"meta\",\"28\",{\"property\":\"og:type\",\"content\":\"article\"}],[\"$\",\"meta\",\"29\",{\"property\":\"article:modified_time\",\"content\":\"2026-09-16\"}],[\"$\",\"meta\",\"30\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"31\",{\"name\":\"twitter:title\",\"content\":\"a11y skill: usable by keyboard, screen reader, zoom and reduced motion\"}],[\"$\",\"meta\",\"32\",{\"name\":\"twitter:description\",\"content\":\"Accessibility as craft for websites and web apps, targeting WCAG 2.2 AA with APCA as the quality bar.\"}],[\"$\",\"meta\",\"33\",{\"name\":\"twitter:image\",\"content\":\"https://void-design.vercel.app/docs/a11y/opengraph-image-1dv662?d56abb6face50325\"}],[\"$\",\"meta\",\"34\",{\"name\":\"twitter:image:alt\",\"content\":\"void skill\"}],[\"$\",\"meta\",\"35\",{\"name\":\"twitter:image:type\",\"content\":\"image/png\"}],[\"$\",\"meta\",\"36\",{\"name\":\"twitter:image:width\",\"content\":\"1200\"}],[\"$\",\"meta\",\"37\",{\"name\":\"twitter:image:height\",\"content\":\"630\"}],[\"$\",\"link\",\"38\",{\"rel\":\"icon\",\"href\":\"/favicon.ico?favicon.07_7ic0_1d0u-.ico\",\"sizes\":\"48x48\",\"type\":\"image/x-icon\"}],[\"$\",\"link\",\"39\",{\"rel\":\"icon\",\"href\":\"/icon.svg?icon.0uaj260ufluee.svg\",\"sizes\":\"any\",\"type\":\"image/svg+xml\"}],[\"$\",\"link\",\"40\",{\"rel\":\"apple-touch-icon\",\"href\":\"/apple-icon.png?apple-icon.0wjwpgjlk50hj.png\",\"sizes\":\"180x180\",\"type\":\"image/png\"}],[\"$\",\"$L38\",\"41\",{}]]\n"])</script></body></html>