HyperText Markup Language (HTML) is the foundation for every
web document. This modern guide covers HTML 5.3‑plus features (supported by Evergreen
browsers as of May 2025) along with <tag>
customisations, accessibility add‑ons,
and performance‑oriented attributes such as loading="lazy"
.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body></body>
</html>
<meta>
tags<meta name="theme-color">
lets you accent the browser UI on mobile.og:
(Open Graph) and twitter:
meta pairs for richer sharing cards.Content-Security-Policy
as a HTTP header but you can also add a <meta http-equiv>
fallback for static hosts.<div id="card" data-user-id="42"
data-theme="dark">
...
</div>
<script>
const card = document.querySelector('#card');
// Access with the dataset API
console.log(card.dataset.userId); // "42"
</script>
Any data‑*
attribute is valid HTML and can be read via the
dataset accessor, enabling declarative‑to‑imperative hand‑offs.
<h1>
through <h6>
provide a nested outline that screen readers
and search engines follow. Use only one <h1>
per page for primary topic context.
<details>
/<summary>
.<mark>
for highlights, <strong>
for importance,
and <em>
for nuance. These tags have default styling that can be themed with CSS variables.<code>
for snippets; pair with <kbd>
when documenting key‑strokes.<a href="https://example.com"
target="_blank"
rel="noopener external"
title="External resource">Example</a>
rel="noopener"
prevents window.opener
hijacking — a 2025 must‑have.
Add descriptive title
when link text alone is vague. For hash‑based page anchors
pair role="button"
when JS transforms the link into an interactive control.
Wrap primary menus in <nav aria‑label="Main">
.
Use landmark roles (<header>
, <footer>
, <main>
) to define
predictable regions for assistive tech.
<img>
<img src="hero.webp"
alt="Person playing synthesizer"
width="1280" height="720"
loading="lazy"
decoding="async">
loading="lazy"
defers non‑critical images.decoding="async"
off‑loads decoding work.width/height
to avoid CLS (layout shift).<picture>
art‑direction<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Fallback JPEG">
</picture>
The browser picks the first matching <source>
, enabling next‑gen format roll‑outs.
<figure>
<img src="diagram.svg" alt="Block diagram of AV pipeline">
<figcaption>Figure 1 — AV processing pipeline</figcaption>
</figure>
<table>
<caption>Supplement intake schedule</caption>
<thead>
<tr><th scope="col">Time</th>
<th scope="col">Supplement</th>
<th scope="col">Dosage</th></tr>
</thead>
<tbody>
<tr><th scope="row">08:00</th><td>USN Hardcore Whey</td><td>30 g</td></tr>
<tr><th scope="row">12:00</th><td>Chia Seeds</td><td>16 g</td></tr>
</tbody>
</table>
scope ties headers to cells for screen readers. Use <caption>
for context.
<form action="/subscribe" method="post">
<label>
Email
<input type="email"
name="email"
autocomplete="email"
required
placeholder="you@example.com">
</label>
<label>
Choose a plan
<select name="plan" required>
<option>Free</option>
<option>Pro</option>
</select>
</label>
<button>Subscribe</button>
</form>
pattern
adds declarative regex enforcement:
<input type="text"
pattern="[A-Za-z]{3}-\d{4}"
title="Format ABC-1234">
Leverage :focus-visible
, :user-invalid
, and :placeholder-shown
to
build accessible, animated states without JavaScript.
HTML’s native landmarks (<main>
, <nav>
, <aside>
) provide structure;
custom components rely on WAI‑ARIA:
<div role="dialog" aria-modal="true"
aria-labelledby="dlgTitle" aria-describedby="dlgDesc">
<h2 id="dlgTitle">Confirm delete</h2>
<p id="dlgDesc">Are you sure?</p>
</div>
<div aria-live="polite"
aria-atomic="true" id="status"></div>
<script>
document.querySelector('#status').textContent =
'Form saved successfully.';
</script>
Use live regions to announce dynamic content without a focus shift.
<video>
customisation<video controls
src="sample.mp4"
poster="thumb.jpg"
preload="metadata"
width="640" height="360">
<track default
kind="captions"
srclang="en"
src="captions.vtt"
label="English">
</video>
Augment with the requestPictureInPicture() method or Media Session API to customise background controls in Chrome & Safari 17+.
<svg width="100" height="100"
role="img"
aria-labelledby="title desc">
<title id="title">Warning Icon</title>
<desc id="desc">An exclamation mark in a triangle</desc>
<path d="…" fill="currentColor"></path>
</svg>
Use currentColor
for inherited theming; expose text equivalents.
<canvas id="stage" width="400" height="200">
Fallback text
</canvas>
<script>
const ctx = stage.getContext('2d');
ctx.fillStyle = 'orange';
ctx.fillRect(10,10,380,180);
</script>
<ul class="toolbar">
<li>Cut</li> <li>Copy</li> <li>Paste</li>
</ul>
.toolbar{
display:flex;
gap:.5rem;
justify-content:center;
}
<div class="dashboard">
<header>…</header>
<nav>…</nav>
<main>…</main>
<footer>…</footer>
</div>
.dashboard{
display:grid;
grid-template-areas:
"hd hd"
"nv mn"
"ft ft";
grid-template-columns:200px 1fr;
}
header{grid-area:hd;}
nav{grid-area:nv;}
main{grid-area:mn;}
footer{grid-area:ft;}
.card{
container-type:inline-size;
border:1px solid #ccc;
}
@container (min-width:400px){
.card{display:flex;}
}
Unlike media queries, container queries respond to the element’s own size — perfect for reusable widgets.
class ColorSwatch extends HTMLElement{
constructor(){
super();
const root = this.attachShadow({mode:'open'});
root.innerHTML = `
<style>
:host{display:inline-block;width:1.5rem;height:1.5rem;
border-radius:50%;border:2px solid #000;}
</style>`;
}
static get observedAttributes(){ return ['value']; }
attributeChangedCallback(name, oldVal, val){
if(name==='value') this.style.backgroundColor = val;
}
}
customElements.define('color-swatch', ColorSwatch);
<color-swatch value="#d6336c"></color-swatch>
Shadow DOM encapsulates styles; events bubble as usual. Tie into forms with the ElementInternals API.
<script>
with type="module"
or defer
.<link rel="preload">
key assets; <link rel="prefetch">
anticipatory nav.Combine semantic headings, descriptive alt texts, a valid <title>
and <meta description>
.
Use JSON‑LD <script type="application/ld+json">
for rich snippets.
Always serve via TLS. Harden forms with autocomplete="off"
for sensitive fields,
referrerpolicy
on links, and Subresource Integrity checksums when
embedding third‑party scripts.
// Detect element entering viewport
const io = new IntersectionObserver(entries => {
entries.forEach(e => {
if(e.isIntersecting){
e.target.classList.add('in-view');
io.unobserve(e.target);
}
});
});
io.observe(document.querySelector('.card'));
Linters (eslint‑plugin‑html
), Bundlers (Vite
, Parcel 3
), Accessibility scanners
(Lighthouse
, axe‑core
), and design systems (Shoelace, Web MDC) accelerate
standards‑based development.