This episode breaks down progressive enhancement: the three web layers, fallbacks for images, scripts, and modern elements, and feature detection so content always reaches users.

Not every visitor uses the newest browser on the fastest network. Episode 22 covers progressive enhancement and fallback content: building up from the most basic layer, strategies when JavaScript fails or image formats aren't supported, and honest feature detection.
The principle is simple: content must still get through. Styles and behavior are enhancements that may partially fail — content is the foundation that must never be lost.
A page is made of three layers. The first layer is HTML for content and structure, the second is CSS for presentation, and the third is JavaScript for behavior:
<article>
<h2>Berita Pagi</h2>
<p>Hujan lebat diperkirakan melanda beberapa wilayah hari ini.</p>
</article>
<script src="js/berita.js" defer></script>Without CSS, the page is still readable. Without JavaScript, content still shows. Progressive enhancement ensures each layer can fail without destroying the layer beneath it.
Build complete, functional markup first, then add enhancements. A dialog with form method="dialog" already works without scripts; showModal is only an enhancement for a better experience.
noscript displays content when JavaScript isn't running:
<noscript>
<p>JavaScript dinonaktifkan. Gunakan tautan ini untuk versi ringkas:
<a href="/versi-ringkas">Versi ringkas</a>.</p>
</noscript>Don't duplicate the entire page in noscript — provide an alternative path, like a link or a basic form that still works without scripts.
Browsers that don't recognize a new format jump to the next <source>, then to img:
<picture>
<source srcset="foto.avif" type="image/avif">
<source srcset="foto.webp" type="image/webp">
<img src="foto.jpg" alt="Dokumentasi acara tahunan">
</picture>The img with src and alt is the last safety net. alt ensures content still reaches screen reader users and when the image fails to load.
Rather than guessing browser support, check directly whether a feature exists:
const elemenDialog = document.createElement("dialog");
const dialogDidukung = typeof elemenDialog.showModal === "function";
if (!dialogDidukung) {
document.body.classList.add("tanpa-dialog");
}The pattern typeof fitur === "function" assesses the browser's real capability. The tanpa-dialog class is then used by CSS to adjust the layout when dialogs aren't available.
Built-in browser validation still blocks invalid submissions without scripts. Add the correct action and method so a form works even if fetch fails:
<form action="/daftar" method="post">
<label for="email">Email</label>
<input type="email" id="email" name="email" required>
<button type="submit">Daftar</button>
</form>If JavaScript runs, the script can intercept the submit and use fetch. If it doesn't, the browser submits the form traditionally to action — both paths work.
details and summary open via the open attribute without JavaScript. Elements that demand scripts, like a swipe gallery, should carry static content as the base and only hide it once the script is ready.
Content hidden by default then revealed by a script risks being lost permanently if the script fails. Show the content first, then hide it via the script once ready.
Using avif as the only source means users on older browsers see no image at all. Always provide a jpeg or png fallback through picture.
Episode 22 completes your understanding of resilience: three web layers that can fail without breaking each other, fallbacks for images and scripts, and honest feature detection instead of guessing browsers.
Key takeaways:
noscript provides an alternative path when scripts are off.picture degrades to a supported format, then to img.action and method still work without fetch.Your Learning HTML series is almost complete. The final episode will close the series: production-ready markup for modern designs — summarizing everything from episodes 0 through 22, checking the final checklist, and making sure your pages are fit to launch.