$npx -y skills add mgifford/accessibility-skills --skill progressive-enhancementLoad this skill when building any web feature, reviewing architecture decisions, or evaluating JavaScript dependencies. Under no circumstances build features that break completely when JavaScript is unavailable or fails. Absolutely always start with semantic HTML, layer CSS enhan
| 1 | # Progressive Enhancement Accessibility Skill |
| 2 | |
| 3 | > **Canonical source**: `examples/PROGRESSIVE_ENHANCEMENT_BEST_PRACTICES.md` in `mgifford/ACCESSIBILITY.md` |
| 4 | > This skill is derived from that file. When in doubt, the example is authoritative. |
| 5 | |
| 6 | Apply these rules when building any web feature or reviewing architecture decisions. |
| 7 | |
| 8 | --- |
| 9 | |
| 10 | ## Core Mandate |
| 11 | |
| 12 | Start with a solid foundation that works for every user, then layer enhancements. |
| 13 | Every user — regardless of browser capability, network speed, assistive technology, |
| 14 | or JavaScript availability — must be able to access core content and complete core |
| 15 | tasks. |
| 16 | |
| 17 | Progressive enhancement is also a sustainability practice. Pages that work without |
| 18 | JavaScript are dramatically lighter: fewer bytes transferred, less CPU spent on |
| 19 | parsing and executing scripts, and less energy consumed per page view. See |
| 20 | [SUSTAINABILITY.md](https://github.com/mgifford/SUSTAINABILITY.md) and the |
| 21 | [Web Sustainability Guidelines](https://www.w3.org/TR/web-sustainability-guidelines/). |
| 22 | |
| 23 | --- |
| 24 | |
| 25 | ## Severity Scale (this skill) |
| 26 | |
| 27 | | Level | Meaning | |
| 28 | | --- | --- | |
| 29 | | **Critical** | Core content or task inaccessible without JS/CSS | |
| 30 | | **Serious** | Core content accessible but significantly degraded without JS/CSS | |
| 31 | | **Moderate** | Enhancement degrades gracefully but with friction | |
| 32 | | **Minor** | Best-practice gap; marginal impact | |
| 33 | |
| 34 | --- |
| 35 | |
| 36 | ## The Three Layers |
| 37 | |
| 38 | ### Layer 1 — Semantic HTML (always required) |
| 39 | |
| 40 | **Failure here is Critical.** |
| 41 | |
| 42 | * All core content readable in plain HTML — no CSS or JS required |
| 43 | * Forms submittable with native browser behaviour |
| 44 | * Navigation functions as standard links |
| 45 | * Headings, lists, tables, and landmarks accurately reflect document structure |
| 46 | |
| 47 | ### Layer 2 — CSS (enhance presentation) |
| 48 | |
| 49 | **Failure here is Moderate to Serious.** |
| 50 | |
| 51 | * External stylesheets that can be disabled without losing content |
| 52 | * Respect user preferences: `prefers-reduced-motion`, `prefers-color-scheme`, |
| 53 | `prefers-contrast`, `forced-colors` |
| 54 | * Page remains usable if stylesheets fail to load |
| 55 | |
| 56 | ### Layer 3 — JavaScript (enhance interactivity) |
| 57 | |
| 58 | **JS that gates core content or tasks is Critical.** |
| 59 | |
| 60 | * JS enhances; it does not gate access to core content or tasks |
| 61 | * Apply JS-dependent classes/behaviours from scripts, not static markup |
| 62 | * Handle script failure gracefully — the HTML layer must still work |
| 63 | * Use feature detection, not browser detection: |
| 64 | |
| 65 | ```js |
| 66 | if ('fetch' in window && 'querySelector' in document) { |
| 67 | // apply enhanced experience |
| 68 | } |
| 69 | ``` |
| 70 | |
| 71 | ### Layer 3 extension — Service Workers (offline capability) |
| 72 | |
| 73 | Service Workers are a Layer 3 enhancement: they require JavaScript, are |
| 74 | registered by a script, and must degrade gracefully when unavailable (private |
| 75 | browsing, unsupported browsers, HTTPS not available). |
| 76 | |
| 77 | When implemented, they extend both accessibility and sustainability: |
| 78 | |
| 79 | * **Accessibility**: users on unreliable connections (common in rural areas, |
| 80 | low-income households, and developing regions) can continue to access |
| 81 | previously loaded content offline |
| 82 | * **Sustainability**: cached responses eliminate repeat network requests, |
| 83 | reducing data transfer and server energy per page view |
| 84 | |
| 85 | ```js |
| 86 | // Register Service Worker only if supported — classic PE feature detection |
| 87 | if ('serviceWorker' in navigator) { |
| 88 | navigator.serviceWorker.register('/sw.js').catch((err) => { |
| 89 | // Registration failure: page continues to work normally without it |
| 90 | console.warn('Service Worker registration failed:', err); |
| 91 | }); |
| 92 | } |
| 93 | ``` |
| 94 | |
| 95 | **Offline fallback pattern:** |
| 96 | |
| 97 | ```js |
| 98 | // sw.js — cache-first with network fallback |
| 99 | const CACHE_NAME = 'v1'; |
| 100 | const OFFLINE_URL = '/offline.html'; |
| 101 | |
| 102 | self.addEventListener('install', (event) => { |
| 103 | event.waitUntil( |
| 104 | caches.open(CACHE_NAME).then((cache) => |
| 105 | cache.addAll([OFFLINE_URL, '/', '/styles.css']) |
| 106 | ) |
| 107 | ); |
| 108 | }); |
| 109 | |
| 110 | self.addEventListener('fetch', (event) => { |
| 111 | if (event.request.mode === 'navigate') { |
| 112 | event.respondWith( |
| 113 | fetch(event.request).catch(() => caches.match(OFFLINE_URL)) |
| 114 | ); |
| 115 | } |
| 116 | }); |
| 117 | ``` |
| 118 | |
| 119 | The offline fallback page (`/offline.html`) must itself be a valid Layer 1 |
| 120 | HTML document — semantic, readable without CSS or JS. |
| 121 | |
| 122 | Service Worker caching strategies (cache-first, network-first, stale-while- |
| 123 | revalidate) should be chosen based on content update frequency. Avoid caching |
| 124 | strategies that serve stale content for content that changes frequently without |
| 125 | a cache-invalidation plan. |
| 126 | |
| 127 | --- |
| 128 | |
| 129 | ## Critical: Core Content Must Not Require JavaScript |
| 130 | |
| 131 | Rendering page content exclusively in JavaScript is **Critical** — users with |
| 132 | JS disabled, AT users encou |