|
| 1 | +/** |
| 2 | + * Skip Link Accessibility Enhancement |
| 3 | + * |
| 4 | + * Starlight renders the main content inside a `<main>` element but does not |
| 5 | + * expose a stable `id` on it. The page title heading receives `id="_top"` by |
| 6 | + * default, which the custom SkipLink component previously targeted. The name |
| 7 | + * `_top` implies "top of page" rather than "main content", creating ambiguity |
| 8 | + * for assistive technology users (WCAG 2.4.1). |
| 9 | + * |
| 10 | + * This script adds `id="main-content"` and `tabindex="-1"` to the `<main>` |
| 11 | + * element so that the skip link (`href="#main-content"`) lands on the correct |
| 12 | + * landmark and keyboard focus is reliably placed there. The `tabindex="-1"` |
| 13 | + * attribute makes the element programmatically focusable without including it |
| 14 | + * in the natural tab order. |
| 15 | + * |
| 16 | + * The enhancement is applied on every page load and re-applied on Astro |
| 17 | + * client-side navigation so it works across all pages and navigations. |
| 18 | + */ |
| 19 | + |
| 20 | +function enhanceMainLandmark(): void { |
| 21 | + const main = document.querySelector<HTMLElement>('main'); |
| 22 | + if (!main) { |
| 23 | + return; |
| 24 | + } |
| 25 | + if (!main.id) { |
| 26 | + main.id = 'main-content'; |
| 27 | + } |
| 28 | + if (!main.hasAttribute('tabindex')) { |
| 29 | + main.setAttribute('tabindex', '-1'); |
| 30 | + } |
| 31 | +} |
| 32 | + |
| 33 | +// Run on initial page load |
| 34 | +if (document.readyState === 'loading') { |
| 35 | + document.addEventListener('DOMContentLoaded', enhanceMainLandmark); |
| 36 | +} else { |
| 37 | + enhanceMainLandmark(); |
| 38 | +} |
| 39 | + |
| 40 | +// Re-run on Astro client-side navigation |
| 41 | +document.addEventListener('astro:page-load', enhanceMainLandmark); |
0 commit comments