Lesson · Beginner
Progressive enhancement
Start with a working HTML experience, then add CSS and JavaScript enhancements without making basic access depend on them.
- Published
- Last checked
Start from the outcome
A link should navigate before JavaScript adds transitions. A form should describe a real submission before JavaScript adds inline feedback. Native elements should provide their built-in behavior whenever it fits.
<details>
<summary>What does the workshop cost?</summary>
<p>The introductory workshop is free.</p>
</details>
This disclosure works without a script. JavaScript is justified when the required interaction is not already available or when it improves feedback without removing the baseline.
Failure is a design state
Test with scripts blocked, slow, and throwing an error. Critical text and navigation should remain available. Label features that genuinely require JavaScript and provide a useful fallback.
How browsers actually behave
The parser builds the DOM while scripts can read and change it. A classic script
without defer can pause parsing; a deferred script runs after the document is
parsed; a module is deferred by default. Event listeners enhance existing
elements after the script loads, so the original markup determines what is
available before that moment or when loading fails.
Native controls continue to participate in forms, keyboard navigation, focus, and accessibility APIs without application code. JavaScript can add feedback or state, but it can also remove those contracts when it replaces elements with generic containers.
Decisions and trade-offs
Enhance where the result improves a defined task: inline validation, an optimistic state with rollback, or a disclosure unavailable in the required browser set. Do not add a client framework to static content merely to reproduce ordinary navigation. Every dependency adds transfer, execution, update, and failure cost.
Server-rendered or static HTML is the safest baseline for critical content. Some applications genuinely require JavaScript; for those, provide a useful loading state, explicit failure state, retry path, and server enforcement rather than pretending the dependency does not exist.
Accessibility consequences
Dynamic updates need focus and announcement decisions. Do not move focus for minor background changes. Use a restrained status region for relevant results, and move focus only when a new context such as a dialog or error summary requires it.
Controls created after load still need visible labels, predictable keyboard behavior, sufficient target size, and reduced-motion support. Preserve native elements instead of adding roles to generic markup wherever possible.
Common failures and fixes
- Navigation is blank while a bundle loads. Critical links are client-only. Render ordinary anchors in the initial HTML.
- A button works only with a pointer. Click behavior is attached to a
generic element. Use
buttonand its native activation. - A request fails but the UI says success. Optimistic state has no rollback. Tie confirmation to a verified response and expose retry.
- One script exception breaks unrelated tasks. Enhancements share one fragile initialization path. Isolate features and fail locally.
Verify it
Load with scripts blocked, then with a slow connection and a deliberate script error. Confirm content, navigation, and honest form behavior remain. Inspect the initial response HTML, network waterfall, and console. Complete enhanced interactions by keyboard, verify announcements with a screen reader, and test reduced motion, 200% zoom, and 320-pixel reflow. Use direct URLs and browser history so client routing cannot hide broken baseline navigation.
Exercise and next step
Choose a scripted component on a page. Write down the user’s goal, disable the script, and decide whether native HTML can preserve more of that goal. Next, learn how scripts query and update the DOM without replacing its semantic foundation.
Boundary of this lesson
Progressive enhancement does not mean every advanced application can deliver its complete task without JavaScript. It requires an honest baseline and proportionate failure handling. A collaborative editor may need scripts, while its documentation, sign-in explanation, saved-state warning, and recovery route can still render as HTML. Document the hard dependency, minimize its surface, and make loading, timeout, offline, authorization, and retry states explicit instead of presenting an empty shell.
Practice with a failure budget
List every script-backed feature on one page and rank it as essential, beneficial, or decorative. Block each script or request independently. Essential features need an explicit loading, failure, retry, and recovery path; beneficial features should fall back to the HTML task; decorative features should disappear without noise. Record the transferred bytes and runtime work for each category so enhancement cost remains proportional to user value.
Repeat the audit with one request timing out after the server may have completed the action. The page must not invent success, encourage unsafe duplicate work, or erase recoverable input. Give the person a status check or retry path whose wording reflects uncertainty. Progressive enhancement includes honest recovery, not only a script-disabled baseline.
Free template + launch checklist
Build a page you can check before launch
Start with one reviewed semantic HTML page, then work through the one-page checklist against your published URL.