HTML5 Semantic Elements Cheat Sheet
Covers HTML5 semantic tags like header, nav, main, article, and section, plus their implicit accessibility roles.
Semantic Page Layout
A typical page skeleton built from semantic elements.
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Page Title</title></head><body> <header> <nav> <a href="/">Home</a> <a href="/about">About</a> </nav> </header> <main> <article> <h1>Article Title</h1> <section> <h2>Section Heading</h2> <p>Content...</p> </section> <aside>Related links</aside> </article> </main> <footer> <p>© 2026 Company</p> </footer></body></html>
Accessible Semantics
Implicit roles and native interactive elements.
<!-- Native elements already expose implicit ARIA roles --><nav aria-label="Breadcrumb">...</nav> <!-- role="navigation" --><main>...</main> <!-- role="main" --><!-- details/summary as a native accordion --><details> <summary>Click to expand</summary> <p>Hidden content revealed on toggle.</p></details><!-- Prefer <button> over a styled <div> for interactive controls --><button type="button" aria-pressed="false">Toggle</button>
Native <dialog> Modal
Build accessible modals with built-in focus trapping and no JavaScript library.
<dialog id="confirm-modal"> <form method="dialog"> <p>Delete this item permanently?</p> <button value="cancel">Cancel</button> <button value="confirm">Delete</button> </form></dialog><script> const dialog = document.getElementById('confirm-modal'); dialog.showModal(); // opens modal, traps focus, adds ::backdrop dialog.addEventListener('close', () => { console.log('closed with:', dialog.returnValue); // 'cancel' or 'confirm' });</script><style> dialog::backdrop { background: rgb(0 0 0 / 0.5); }</style>
<template> & <slot> for Reusable Markup
Define inert, clonable markup fragments and composable component placeholders.
<!-- Content in <template> is parsed but not rendered or executed --><template id="row-template"> <tr><td class="name"></td><td class="value"></td></tr></template><script> const tpl = document.getElementById('row-template'); const clone = tpl.content.cloneNode(true); clone.querySelector('.name').textContent = 'CPU'; document.querySelector('tbody').appendChild(clone);</script><!-- <slot> is used inside a web component's shadow DOM to project light-DOM children --><user-card> <span slot="username">jdoe</span></user-card>
Schema.org Microdata on Semantic Elements
Annotate existing semantic markup with itemscope/itemtype/itemprop so search engines parse structured entities.
<article itemscope itemtype="https://schema.org/Recipe"> <h1 itemprop="name">Weeknight Pasta</h1> <img itemprop="image" src="/pasta.jpg" alt="Bowl of pasta"> <p> Prep time: <time itemprop="prepTime" datetime="PT15M">15 minutes</time> </p> <section itemprop="recipeIngredient"> <ul> <li>200g spaghetti</li> <li>2 cloves garlic</li> </ul> </section></article>
Lesser-Known Semantic Elements
Precise-meaning tags that go beyond the common layout landmarks.
- <address>- contact information for the nearest article or page author; browsers often italicize it by default
- <data value="...">- pairs human-readable text with a machine-readable value attribute
- <output>- represents the result of a calculation, typically inside a <form>
- <meter>- a scalar measurement within a known range, like disk usage; not for progress
- <progress>- completion of a task, either determinate (with value/max) or indeterminate
- <bdi>- isolates text whose directionality (LTR/RTL) is unknown from surrounding text
- <wbr>- suggests an optional line-break opportunity inside a long word or URL
- <ins> / <del>- marks inserted or deleted content, e.g. in a tracked-changes view
- <cite>- the title of a creative work being referenced, not the author's name
Labelling Repeated Landmarks
When a page has more than one <nav> or <section>, give each an accessible name so screen reader users can tell them apart.
<nav aria-label="Primary">...</nav><nav aria-label="Footer links">...</nav><section aria-labelledby="reviews-heading"> <h2 id="reviews-heading">Customer Reviews</h2> ...</section><!-- A <section> without an accessible name exposes no role in the accessibility tree at all -- it's treated as a generic div. -->
Semantic elements come with implicit ARIA roles built in, such as nav mapping to role navigation and main mapping to role main, so reaching for explicit role attributes on a div should be your last resort, not your default.