Progressive Enhancement and Cross-Browser Compatibility Explained
Learn how to build a useful baseline, add browser-aware enhancements, and test the combinations that matter to your users.
Progressive enhancement means starting with essential content and actions that work in a simple baseline, then adding richer presentation and behavior when the browser supports them. Cross-browser compatibility is the practice of making that experience work across the browsers, devices, and input methods that matter to your audience. Standards help browsers interoperate, but feature checks, fallbacks, and real testing are still necessary.
A practical rule: make the core task work first, enhance it when capabilities are available, and verify the result in the contexts your users actually use. A page that loads in several browsers is not automatically accessible, usable, fast, secure, or bug-free.
What progressive enhancement means
Progressive enhancement is a design approach that gives as many people as possible a baseline of essential content and functionality, then provides a better experience to browsers that can run the additional code. The baseline is a valuable version of the page, not a deliberately broken preview. It should preserve the user’s essential task when JavaScript is unavailable, an API is unsupported, or a device lacks a capability.
Think of an experience in layers:
- Content and structure: Use semantic HTML for meaningful content, links, forms, and controls.
- Presentation: Add CSS for hierarchy and responsive layouts without making content inaccessible at smaller viewports.
- Behavior: Add JavaScript where it improves a task, while retaining the baseline action or a clear alternative.
- Optional capabilities: Use advanced APIs, animation, or other features only when available and appropriate; keep a useful fallback.
- Verification: Test important browser and device combinations, plus accessibility, performance, and usability.
This is a way to reason about a page, not a mandatory stack. HTML, CSS, and JavaScript can be organized differently while preserving the same principle.
Progressive enhancement and graceful degradation
These approaches are related and can complement each other. They differ mainly in where planning begins:
| Question | Progressive enhancement | Graceful degradation |
|---|---|---|
| Starting point | Essential content and behavior that work first | A fully featured experience |
| Compatibility plan | Add layers after checking capability | Provide a reduced experience if the richer implementation cannot run |
| Failure question | What is the simplest version that still completes the task? | What essential task remains if this feature fails? |
Neither label guarantees a good result. The practical test is whether users can understand the content and complete essential tasks in the environments you support. MDN explains the relationship and distinction.
Build a baseline, then add enhancements
1. Put the essential action in HTML
A form can submit to a server using ordinary HTML. JavaScript can then add client-side validation and handle submission for browsers where the script runs. This keeps the core path available if the enhancement does not run.
<form action="/subscribe" method="post" id="subscribe-form">
<label for="email">Email address</label>
<input id="email" name="email" type="email" required>
<button type="submit">Subscribe</button>
<p id="form-status" aria-live="polite"></p>
</form>
<script>
const form = document.querySelector('#subscribe-form');
const status = document.querySelector('#form-status');
if (form && 'fetch' in window) {
form.addEventListener('submit', async (event) => {
event.preventDefault();
if (!form.reportValidity()) return;
try {
const response = await fetch(form.action, {
method: form.method,
body: new FormData(form),
headers: { Accept: 'application/json' }
});
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
status.textContent = 'Thank you for subscribing.';
form.reset();
} catch {
// Leave the regular form action available if the enhanced request fails.
form.submit();
}
});
}
</script>
The server endpoint must still validate input and provide the actual subscription behavior. Client-side validation improves feedback; it is not a security boundary. If script execution fails, the form’s standard action remains the intended route.
2. Use CSS feature queries for optional presentation
CSS @supports lets a stylesheet check for a particular declaration and apply an enhancement only when the browser understands it. Keep a baseline style outside the query.
.card {
background: white;
border: 1px solid #bbb;
padding: 1rem;
}
@supports (backdrop-filter: blur(8px)) {
.card {
background: rgb(255 255 255 / 75%);
backdrop-filter: blur(8px);
}
}
A CSS support query checks whether a declaration is recognized; it does not prove that the resulting experience is readable, performant, or appropriate. Test the actual result, including contrast and content visibility.
3. Check JavaScript capabilities, not browser names
Feature detection asks whether the specific capability exists. User-agent detection guesses based on a browser label, which does not reliably establish support for each feature. For example, check for geolocation before offering a location-based enhancement:
async function locateUser() {
if (!('geolocation' in navigator)) {
showStaticMapOrAddressInstructions();
return;
}
navigator.geolocation.getCurrentPosition(
(position) => showNearbyResults(position.coords),
() => showManualLocationEntry()
);
}
The fallback should preserve the user’s goal where possible, such as a static map or manual location entry. A presence check does not prove two implementations behave identically. If a known behavioral difference matters, test that behavior and handle the result.
The W3C Web Platform Design Principles state: “Provide a way for authors to programmatically detect whether your feature is available, so that web content may gracefully handle the feature not being present.” See the W3C section on feature detection and MDN’s feature detection guide.
How to make a website work across browsers
- Choose a support target. Use audience evidence, product requirements, and business constraints to decide which browsers, versions, devices, and assistive technology combinations matter. Document the policy.
- Prefer interoperable platform features. Use semantic HTML and well-supported CSS and JavaScript patterns for essential tasks.
- Check support for optional features. Use capability detection, such as JavaScript property checks or CSS
@supports, rather than branching on browser identity. - Provide fallbacks. Preserve the task with a simpler interaction, server path, static content, or an explanation and alternative route.
- Test the important combinations. Include representative browsers, operating systems, device classes, viewports, orientations, and input methods.
- Check accessibility and quality separately. Test keyboard use, screen readers and other assistive technologies, magnification, readability, loading behavior, and the real user task.
- Retest after meaningful changes. Browser behavior and support data change over time. Revisit the matrix when adding a capability or when audience evidence changes.
Standards aim to support interoperability, so equivalent HTML, CSS, and JavaScript should render consistently. That goal is a foundation, not proof that every browser release, operating system, assistive technology, or real-world implementation behaves identically. See MDN’s web standards learning material.
Use compatibility data carefully
MDN Baseline summarizes support across its named core browser set: Safari on iOS and macOS, Chrome on Android and desktop, Edge desktop, and Firefox on Android and desktop. “Widely available” describes consistent support history for at least 2.5 years across Baseline browsers. “Newly available” means support exists in the latest stable version of each Baseline browser, but older browsers and devices may not support it. “Limited availability” indicates it is not broadly available across that set.
Baseline can help with an initial feature decision, but it is not a substitute for accessibility, usability, performance, security, or other testing. Check the current status of a specific feature when making a support decision; classifications change. A support badge cannot establish that your implementation works for your audience.
Cross-browser testing: choose a useful matrix
Testing every possible combination is rarely a practical project target. Prioritize combinations using audience evidence and the consequences of failure. A matrix can include:
| Axis | Examples to consider | What to verify |
|---|---|---|
| Browser and version | Browsers your audience uses; current and required older versions | Rendering, interaction, feature behavior |
| Operating system and device | Desktop, phone, tablet; relevant operating systems | Native controls, layout, input differences |
| Viewport and orientation | Narrow and wide widths; portrait and landscape | Reflow, clipping, readable content |
| Input method | Keyboard, mouse, touch, stylus | Reachability, focus, target operation |
| Assistive technology | Screen reader, magnification, other relevant tools | Names, structure, status changes, usable alternatives |
| Network and scripting | Slow or interrupted network; constrained or unavailable scripts when relevant | Loading feedback, essential baseline, error recovery |
| User task | Sign in, submit, search, purchase, read | Whether a person can complete the outcome end to end |
Semantic HTML helps because native elements support common input methods by default. For example, use a real <button> for an action and a real link for navigation instead of recreating their behavior with generic elements. MDN recommends testing across browsers, operating systems, devices, and input methods in its PWA testing guidance; these considerations also apply to general web experiences.
Accessibility is part of compatibility
A page can render in multiple browsers and still fail someone using a keyboard, screen reader, magnification, or another assistive technology. Use semantic structure, visible focus, meaningful labels, and status messages that are announced when needed. Test the actual task with relevant assistive technology; browser feature availability by itself does not show that a feature is accessible.
W3C’s WCAG 2.2 understanding material describes “accessibility supported” in terms of interoperability with users’ assistive technologies and accessibility features in mainstream user agents. Whether a use is supported depends on the specific technology use and languages involved. See WCAG 2.2: Accessibility Supported.
Common mistakes and fixes
| Problem | Why it happens | Fix |
|---|---|---|
| Essential content appears only after JavaScript runs | The baseline depends on script execution | Render meaningful content in HTML or from the server; reserve JavaScript for enhancements. |
| A feature check passes but the feature still fails | Presence does not guarantee permission, successful operation, or identical behavior | Handle denial and runtime errors; test behavior and keep a fallback. |
| Code branches on a browser name | User-agent identity is being used as a proxy for capabilities | Check the required feature directly. If behavior differs despite support, test that behavior explicitly. |
| A supported CSS feature makes content unreadable | Recognition was mistaken for suitability | Keep the baseline style, verify contrast and layout, and test the enhancement at relevant viewports. |
| A page works with a mouse but not a keyboard | Custom controls or interaction paths omit keyboard behavior | Use native controls where possible; verify focus order, visible focus, and operation without a pointer. |
| A support label is treated as a compatibility guarantee | Feature support data was mistaken for whole-site quality evidence | Use it to inform an initial decision, then test the implementation, task, accessibility, and performance. |
| A form’s enhanced request fails and leaves no recovery route | The JavaScript path replaced the only working action | Retain a server form action or show a clear retry and alternative route. |
Performance, reliability, and cost
Progressive enhancement is a planning method, not an automatic performance or reliability guarantee. A simple baseline can reduce dependence on optional code, but extra layers still have download, execution, and maintenance costs. Keep enhancements focused on user value, avoid loading optional work before it is needed, and check behavior under the network conditions relevant to your users.
Fallbacks improve recovery only when they are real paths that have been maintained and tested. A fallback that points to an absent endpoint, inaccessible control, or stale instruction does not preserve the task. Include failure cases in the same test plan as successful flows.
There is no universal monetary cost for cross-browser compatibility: it depends on the support policy, complexity, testing approach, and consequences of defects. Narrowing the matrix without audience evidence may exclude users; supporting every combination can consume effort without a clear product benefit. Use product analytics and user needs to prioritize, then revisit that choice.
Or skip the browser setup
If you need screenshots of your site across browser or viewport states for review, ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It can help capture a page for visual inspection; screenshots do not replace keyboard, assistive technology, interaction, or functional testing.
One GET request returns an image or PDF. For example, save a WebP screenshot of a page:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
See the ScreenshotNeo API documentation for request options and response details. Cookie banners are accepted like a visitor and 60+ known consent platforms, newsletter popups, and chat widgets are removed before the shot; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing, and response headers identify the page verdict and whether it was billed. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for AI agents. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Every feature is on every plan.
Sign up free for 1,000 screenshots a month, with no card required.
FAQ
Does progressive enhancement mean avoiding JavaScript?
No. It means essential content and tasks have a useful baseline, with JavaScript adding improvements where it helps and works.
Do web standards make cross-browser testing unnecessary?
No. Standards support interoperability, but implementations and contexts vary. Test the browsers, devices, assistive technologies, and tasks that matter to your audience.
Should a site support every browser?
Set a support target from audience needs and product requirements. There is no universal list that fits every site.
Is feature detection enough to prove a page is compatible?
No. It helps decide whether to use a capability. You still need fallbacks and tests for actual behavior, accessibility, and the user’s task.


