Progressive Enhancement
debt(d5/e7/b7/t5)
Closest to 'specialist tool catches' (d5) — Lighthouse and axe can flag JS-only content, missing semantic HTML, and accessibility issues, but they don't catch every progressive-enhancement violation; some issues only surface when JS is disabled or fails to load.
Closest to 'cross-cutting refactor across the codebase' (e7) — retrofitting PE means rewriting forms to POST natively, restructuring SPA routes to have server-rendered equivalents, and adding HTML fallbacks across many components; the quick_fix describes rebuilding the rendering pipeline (PHP renders semantic HTML first), not a localized change.
Closest to 'strong gravitational pull' (b7) — committing to PE shapes every feature: backend must render meaningful HTML, JS must layer on top, every interaction needs a no-JS path. It applies to all web contexts and influences architecture choices broadly without fully defining the system shape.
Closest to 'notable trap most devs eventually learn' (t5) — the misconception (that PE means supporting no-JS users specifically, rather than starting from an HTML foundation that's then enhanced) is a documented gotcha; developers commonly assume modern SPAs are exempt when SSR-based PE is still viable.
Also Known As
TL;DR
Explanation
Progressive enhancement starts with a functional HTML foundation (forms that POST, links that navigate, semantic structure). CSS adds visual polish. JavaScript adds interactivity, but the page works without it. Contrast with graceful degradation (start rich, degrade). Benefits: works for users with disabled JS (corporate firewalls, accessibility tools), faster initial page load (HTML renders before JS parses), better SEO (crawlers see real content), and resilience to JS errors. PHP apps naturally support progressive enhancement — server-rendered HTML is the baseline.
Common Misconception
Why It Matters
Common Mistakes
- onClick for navigation without href — <button onclick=navigate()> breaks keyboard nav, bookmarking, and no-JS.
- Search forms that only work with JS — POST to /search should work as a native HTML form.
- Critical content rendered only by JavaScript — invisible to search engines and slow-connection users.
- AJAX-only forms with no server-rendered fallback.
Code Examples
<!-- JavaScript-only form — broken without JS:
<form id="search">
<input id="q" type="text">
<button onclick="doSearch()">Search</button>
</form>
<div id="results"></div>
<script>
function doSearch() {
fetch('/api/search?q=' + document.getElementById('q').value)
.then(r => r.json()).then(renderResults);
}
</script>
<!-- Works without JS (POST to server), enhanced with JS:
<form action="/search" method="GET" id="search">
<input name="q" type="search" value="<?= esc($q) ?>">
<button type="submit">Search</button>
</form>
<div id="results">
<!-- Server-rendered results here -->
<?php foreach ($results as $r): ?>
<article>...</article>
<?php endforeach; ?>
</div>
<script>
// Enhancement: AJAX search without page reload
document.getElementById('search').addEventListener('submit', async e => {
e.preventDefault(); // Override default only if JS works
// ... AJAX update
});
</script>