HTML Reference & Documentation
Exhaustive HTML5 semantic standard reference with accessibility attributes, meta tags, and structured schema implementation.
HTML Reference
& Documentation
Every HTML concept, element, attribute, API, and best practice — structured as an interactive living document for developers and educators.
HTML Foundations
HTML (HyperText Markup Language) is the standard markup language for creating web pages. It describes the structure of content — not its appearance. CSS handles styling; JavaScript handles behaviour.
Markup
Elements wrap content in angle-bracket tags to give it meaning and structure.
DOM Tree
The browser parses HTML into a Document Object Model — a live tree of nodes.
HyperText
Links connect documents across the web, forming the "hyper" in HyperText.
Standards
HTML is maintained by WHATWG as a living standard; no version numbers anymore.
Anatomy of an HTML Element
<!-- Opening tag Content Closing tag --> <p class="intro">Hello, World!</p> <!-- Void element — self-closing, no content --> <img src="photo.jpg" alt="A photo" /> <!-- Attributes: name="value" pairs on the opening tag --> <a href="https://example.com" target="_blank" rel="noopener"> Visit Example </a>
Global Attributes
These attributes can be placed on any HTML element:
| Attribute | Description | Example |
|---|---|---|
id | Unique identifier on the page | id="header" |
class | CSS class name(s) | class="btn primary" |
style | Inline CSS styles | style="color:red" |
data-* | Custom data attributes | data-user-id="42" |
hidden | Hides element visually & from a11y tree | hidden |
tabindex | Controls keyboard focus order | tabindex="0" |
contenteditable | Makes element editable | contenteditable="true" |
draggable | Enables drag-and-drop | draggable="true" |
lang | Language of element's content | lang="fr" |
dir | Text direction (ltr / rtl / auto) | dir="rtl" |
title | Tooltip text on hover | title="More info" |
spellcheck | Browser spell-check on/off | spellcheck="false" |
translate | Whether to translate content | translate="no" |
slot | Web Component slot assignment | slot="icon" |
part | CSS part for Shadow DOM styling | part="button" |
Document Structure
Every HTML document follows a specific skeleton. Understanding this skeleton is essential before writing a single tag.
<!-- DOCTYPE declaration: tells browser this is HTML5 --> <!DOCTYPE html> <!-- Root element with language attribute --> <html lang="en"> <head> <!-- Character encoding — MUST be first in <head> --> <meta charset="UTF-8" /> <!-- Responsive viewport --> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <!-- Page title shown in tab and search results --> <title>My Page Title</title> <!-- Meta description for SEO --> <meta name="description" content="Page description here" /> <!-- External CSS --> <link rel="stylesheet" href="styles.css" /> <!-- Inline CSS --> <style> body { margin: 0; } </style> <!-- Favicon --> <link rel="icon" href="favicon.ico" /> <!-- Preload critical resource --> <link rel="preload" href="font.woff2" as="font" crossorigin /> </head> <body> <!-- All visible content goes here --> <h1>Hello World</h1> <!-- Scripts at bottom for performance --> <script src="app.js" defer></script> </body> </html>
The <head> Element
The <head> contains metadata — information about the page, not displayed content.
📌 All valid <head> children
| Element | Purpose |
|---|---|
<title> | Tab title, bookmark name, search result headline |
<meta> | Metadata (charset, viewport, SEO, social) |
<link> | External resources (CSS, favicon, preload, canonical) |
<style> | Embedded CSS |
<script> | JavaScript (inline or external) |
<noscript> | Fallback content when JS is disabled |
<base> | Base URL for relative links |
<template> | Inert HTML template for Web Components |
Text & Semantics
Semantic HTML means using elements that convey the meaning of content, not just its appearance. This benefits SEO, accessibility, and maintainability.
Headings Hierarchy
<h1>Page Title — only ONE per page</h1> <h2>Major Section</h2> <h3>Sub-section</h3> <h4>Sub-sub-section</h4> <h5>Rarely needed</h5> <h6>Very rarely needed</h6>
Inline Text Elements
| Element | Meaning | Renders as |
|---|---|---|
<strong> | Strong importance | bold |
<em> | Stress emphasis | italic |
<b> | Stylistically bold (no extra importance) | bold |
<i> | Alternative voice / idiom / term | italic |
<mark> | Highlighted / search result | highlighted |
<del> | Deleted text | |
<ins> | Inserted text | inserted |
<sub> | Subscript | H2O |
<sup> | Superscript | x2 |
<code> | Inline code | console.log() |
<pre> | Preformatted (preserves whitespace) | code blocks |
<kbd> | Keyboard input | Ctrl+S |
<samp> | Sample program output | Error 404 |
<var> | Variable in math/programming | x |
<abbr> | Abbreviation with tooltip | HTML |
<cite> | Title of a creative work | MDN Web Docs |
<q> | Inline quotation | The Web is for everyone. |
<time> | Date/time with machine-readable value | |
<small> | Fine print / side comments | © 2025 |
<s> | No longer accurate (strikethrough) | |
<u> | Unarticulated annotation (underline) | proper noun |
<bdi> | Bidirectional isolation | for RTL text mixing |
<bdo> | Bidirectional override | reversed |
<ruby>, <rt>, <rp> | Ruby annotations (East Asian typography) | 漢 |
<wbr> | Optional line-break opportunity | VeryLong |
<span> | Generic inline container (no semantics) | for CSS/JS hooks |
<br> | Line break | ↵ new line |
<hr> | Thematic break |
Block Text Elements
<p>A paragraph of text.</p> <!-- Block quotation with citation --> <blockquote cite="https://source.com"> <p>The best way to predict the future is to invent it.</p> <footer>— <cite>Alan Kay</cite></footer> </blockquote> <!-- Address contact info for author/owner --> <address> Written by <a href="mailto:[email protected]">Author Name</a> </address> <!-- Thematic break --> <hr /> <!-- Generic block container --> <div class="wrapper">...</div>
Lists
<!-- Unordered list --> <ul> <li>Apples</li> <li>Bananas</li> <li>Nested list: <ul><li>Sub-item</li></ul> </li> </ul> <!-- Ordered list with custom start and type --> <ol start="3" type="A" reversed> <li>Step three</li> <li value="10">Jump to item 10</li> </ol> <!-- Description list — terms and definitions --> <dl> <dt>HTML</dt> <dd>HyperText Markup Language — structure of the web.</dd> <dt>CSS</dt> <dd>Cascading Style Sheets — appearance of the web.</dd> </dl> <!-- Menu (list of commands — spec usage) --> <menu> <li><button>Cut</button></li> <li><button>Copy</button></li> </menu>
Links & Navigation
<!-- Standard hyperlink --> <a href="https://example.com">External link</a> <!-- Open in new tab safely --> <a href="https://example.com" target="_blank" rel="noopener noreferrer">New tab</a> <!-- In-page anchor --> <a href="#section-id">Jump to section</a> <!-- Email link --> <a href="mailto:[email protected]?subject=Hi&body=Hello">Send email</a> <!-- Phone link --> <a href="tel:+15551234567">Call us</a> <!-- SMS link --> <a href="sms:+15551234567?body=Hello">Text us</a> <!-- Download link --> <a href="/files/report.pdf" download="Annual-Report.pdf">Download PDF</a> <!-- Navigation landmark --> <nav aria-label="Main navigation"> <ul> <li><a href="/" aria-current="page">Home</a></li> <li><a href="/about">About</a></li> </ul> </nav>
| rel value | Meaning |
|---|---|
noopener | New tab can't access opener's window object |
noreferrer | No Referer header sent; implies noopener |
nofollow | Tell search engines not to follow this link |
sponsored | Marks paid/sponsored links for search engines |
ugc | User-generated content link |
canonical | (on <link>) Preferred URL for this page |
alternate | Alternate version (RSS feed, translated page) |
preload | Fetch resource early (high priority) |
prefetch | Hint: will likely be needed soon |
preconnect | Early connection to origin |
dns-prefetch | DNS resolution ahead of time |
Images & Figures
<!-- Basic image — alt is MANDATORY for accessibility --> <img src="photo.jpg" alt="A golden retriever sitting in a park" width="800" height="600" loading="lazy" decoding="async" /> <!-- Figure with caption --> <figure> <img src="chart.png" alt="Bar chart showing Q3 revenue growth" /> <figcaption>Figure 1: Q3 2024 Revenue Growth by Region</figcaption> </figure> <!-- Decorative image (empty alt = skip by screen reader) --> <img src="divider.svg" alt="" role="presentation" /> <!-- Image map with clickable regions --> <img src="map.jpg" alt="World map" usemap="#worldmap" /> <map name="worldmap"> <area shape="rect" coords="0,0,200,100" href="/north-america" alt="North America" /> <area shape="circle" coords="300,150,50" href="/europe" alt="Europe" /> </map>
alt="". Functional images (buttons/links): describe the action. Complex images (charts): describe the data or link to a text alternative.Tables
Tables are for tabular data — rows and columns of related information. Never use them for page layout.
<table> <!-- Caption: acts as the table's heading --> <caption>Monthly Sales Data — Q1 2025</caption> <!-- Column group styling hooks --> <colgroup> <col span="1" style="background:#f0f"/> <col span="3" /> </colgroup> <!-- Table head --> <thead> <tr> <th scope="col">Month</th> <th scope="col">Revenue</th> <th scope="col">Units</th> </tr> </thead> <!-- Table body --> <tbody> <tr> <th scope="row">January</th> <td>$12,400</td> <td>248</td> </tr> <tr> <!-- colspan / rowspan for merged cells --> <td colspan="2">Merged cell</td> <td rowspan="2">Spans 2 rows</td> </tr> </tbody> <!-- Table foot — totals / summaries --> <tfoot> <tr> <th scope="row">Total</th> <td>$37,200</td> <td>744</td> </tr> </tfoot> </table>
Forms
Forms are the primary way users send data to a server. Every form element needs a <label> for accessibility.
<form action="/submit" method="post" enctype="multipart/form-data" novalidate autocomplete="on" > <!-- Fieldset groups related controls --> <fieldset> <legend>Personal Information</legend> <!-- Label explicitly linked via for/id --> <label for="name">Full Name</label> <input type="text" id="name" name="full_name" required minlength="2" maxlength="80" autocomplete="name" placeholder="Jane Smith" /> <!-- Helper text linked via aria-describedby --> <label for="email">Email</label> <input type="email" id="email" name="email" required aria-describedby="email-hint" /> <small id="email-hint">We'll never share your email.</small> <!-- Select dropdown --> <label for="country">Country</label> <select id="country" name="country"> <optgroup label="Americas"> <option value="us">United States</option> </optgroup> </select> <!-- Textarea --> <label for="bio">Bio</label> <textarea id="bio" name="bio" rows="4" cols="50" maxlength="500"></textarea> <!-- Datalist for autocomplete suggestions --> <input list="browsers" name="browser" /> <datalist id="browsers"> <option value="Chrome"/><option value="Firefox"/> </datalist> </fieldset> <!-- Hidden field --> <input type="hidden" name="csrf_token" value="abc123" /> <!-- Buttons --> <button type="submit">Submit</button> <button type="reset">Clear</button> <button type="button" onclick="saveDraft()">Save Draft</button> </form>
Live Form Demo
Input Types
HTML5 introduced many semantic input types that trigger appropriate keyboards on mobile and enable built-in validation.
<input type="text" /> <!-- default --> <input type="email" /> <!-- validates email format --> <input type="password" /> <!-- masks input --> <input type="number" /> <!-- numeric keyboard on mobile --> <input type="tel" /> <!-- phone keyboard on mobile --> <input type="url" /> <!-- validates URL format --> <input type="search" /> <!-- search field with clear button --> <input type="date" /> <!-- date picker --> <input type="time" /> <!-- time picker --> <input type="datetime-local" /> <input type="month" /> <input type="week" /> <input type="color" /> <!-- colour picker --> <input type="range" min="0" max="100" step="1" /> <input type="checkbox" /> <input type="radio" name="group" /> <input type="file" accept="image/*" multiple /> <input type="hidden" value="token" /> <input type="submit" value="Send" /> <input type="reset" /> <input type="button" value="Click" /> <input type="image" src="btn.png" alt="Submit" />
Form Validation
<input type="text" required <!-- Field must be filled --> minlength="3" <!-- Min char count --> maxlength="60" <!-- Max char count --> pattern="[A-Za-z\s]+" <!-- Regex pattern --> title="Letters only please" <!-- Error tooltip hint --> /> <input type="number" min="1" max="100" step="5" /> <!-- Custom error messages via JavaScript --> <script> const input = document.querySelector('input'); input.addEventListener('invalid', (e) => { e.target.setCustomValidity('Please enter your full name.'); }); input.addEventListener('input', (e) => { e.target.setCustomValidity(''); }); </script>
Pseudo-classes for Styled Validation
input:valid { border-color: green; } input:invalid { border-color: red; } input:required { outline: 2px solid gold; } input:optional { } input:in-range { } input:out-of-range { } input:placeholder-shown { } :user-valid { } /* Only shows after user interaction */ :user-invalid { } /* Better UX than :invalid */
Audio & Video
<video src="movie.mp4" width="800" height="450" controls <!-- Show browser controls --> autoplay <!-- Auto-play (muted required) --> muted <!-- Required for autoplay --> loop <!-- Loop playback --> playsinline <!-- No full-screen on iOS --> preload="metadata" <!-- none | metadata | auto --> poster="thumbnail.jpg" <!-- Shown before play --> crossorigin="anonymous" > <!-- Multiple sources for browser support --> <source src="movie.av1.mp4" type="video/mp4; codecs=av01"/> <source src="movie.h264.mp4" type="video/mp4"/> <source src="movie.webm" type="video/webm"/> <!-- Subtitles / closed captions --> <track kind="subtitles" src="subs.en.vtt" srclang="en" label="English" default/> <track kind="captions" src="cc.en.vtt" srclang="en" label="CC English"/> <track kind="descriptions" src="desc.vtt" srclang="en"/> <track kind="chapters" src="chapters.vtt" srclang="en"/> <track kind="metadata" src="data.vtt"/> Your browser does not support the video element. </video> <!-- Audio --> <audio controls preload="none"> <source src="audio.ogg" type="audio/ogg"/> <source src="audio.mp3" type="audio/mpeg"/> <source src="audio.opus" type="audio/ogg; codecs=opus"/> </audio>
Responsive Images
<!-- Art direction: different crops per breakpoint --> <picture> <!-- WebP format with art direction --> <source media="(max-width: 600px)" srcset="hero-mobile.webp" type="image/webp" /> <source media="(min-width: 601px)" srcset="hero-desktop.webp" type="image/webp" /> <!-- Fallback img --> <img src="hero.jpg" alt="Hero image" /> </picture> <!-- Density / resolution switching --> <img src="logo.png" srcset="logo.png 1x, [email protected] 2x, [email protected] 3x" alt="Company Logo" /> <!-- Width-based srcset with sizes hint --> <img src="photo-800.jpg" srcset=" photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w, photo-1600.jpg 1600w " sizes=" (max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px " alt="Responsive photo" loading="lazy" />
iFrame & Embedded Content
<!-- Basic iframe --> <iframe src="https://example.com" title="Example page" <!-- REQUIRED for accessibility --> width="800" height="500" loading="lazy" sandbox="allow-scripts allow-same-origin" referrerpolicy="no-referrer" allowfullscreen ></iframe> <!-- YouTube embed --> <iframe src="https://www.youtube.com/embed/VIDEO_ID?rel=0" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope" allowfullscreen ></iframe> <!-- sandbox values --> <!-- allow-scripts — run JS --> <!-- allow-same-origin — treat as same origin --> <!-- allow-forms — submit forms --> <!-- allow-popups — open new windows --> <!-- allow-top-navigation — navigate parent --> <!-- allow-downloads — trigger file downloads --> <!-- Object (legacy) --> <object data="file.pdf" type="application/pdf" width="600" height="400"> <p>PDF not supported. <a href="file.pdf">Download it.</a></p> </object>
SVG — Scalable Vector Graphics
SVG is an XML-based vector format that can be embedded directly in HTML. SVGs are resolution-independent, scriptable, and styleable with CSS.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200" role="img" aria-label="Geometric shapes demo" > <title>Geometric shapes demo</title> <!-- Basic shapes --> <circle cx="50" cy="50" r="40" fill="#4fc3f7" /> <rect x="110" y="10" width="80" height="80" fill="#e8c547" rx="8" /> <ellipse cx="50" cy="150" rx="40" ry="25" fill="#f06292" /> <line x1="110" y1="110" x2="190" y2="190" stroke="white" stroke-width="3" /> <polygon points="150,110 190,190 110,190" fill="#4ade80" /> <polyline points="0,200 30,150 60,180 90,120" stroke="orange" fill="none" /> <!-- Path — the most powerful shape --> <path d="M10,80 Q95,10 180,80 T350,80" stroke="white" fill="none" /> <!-- Text --> <text x="100" y="195" text-anchor="middle" fill="white" font-size="12">SVG</text> <!-- Gradient definition + usage --> <defs> <linearGradient id="grad" x1="0%" y1="0%" x2="100%" y2="0%"> <stop offset="0%" stop-color="#e8c547"/> <stop offset="100%" stop-color="#f06292"/> </linearGradient> <filter id="blur"><feGaussianBlur stdDeviation="3"/></filter> <clipPath id="clip"><circle cx="100" cy="100" r="50"/></clipPath> </defs> <!-- SVG Animation --> <circle cx="100" cy="100" r="20" fill="url(#grad)"> <animate attributeName="r" values="20;30;20" dur="2s" repeatCount="indefinite"/> </circle> </svg>
Canvas 2D API
The <canvas> element provides a pixel-based drawing surface. Drawing is done entirely via JavaScript using the Canvas 2D API.
<canvas id="myCanvas" width="600" height="400" role="img" aria-label="Animated wave chart"> Canvas not supported. <!-- Fallback --> </canvas> <script> const canvas = document.getElementById('myCanvas'); const ctx = canvas.getContext('2d'); // Fill & stroke ctx.fillStyle = '#e8c547'; ctx.fillRect(10, 10, 100, 50); // Path drawing ctx.beginPath(); ctx.moveTo(200, 50); ctx.lineTo(300, 150); ctx.arcTo(350, 150, 350, 100, 30); ctx.closePath(); ctx.strokeStyle = '#4fc3f7'; ctx.lineWidth = 3; ctx.stroke(); // Gradient const grad = ctx.createLinearGradient(0,0,600,0); grad.addColorStop(0, '#e8c547'); grad.addColorStop(1, '#f06292'); ctx.fillStyle = grad; ctx.fillRect(0, 200, 600, 20); // Text ctx.font = 'bold 24px Syne'; ctx.fillStyle = '#fff'; ctx.fillText('Canvas 2D', 10, 250); // Image drawing const img = new Image(); img.src = 'photo.jpg'; img.onload = () => ctx.drawImage(img, 400, 0, 200, 150); // Transform ctx.save(); ctx.translate(100, 300); ctx.rotate(Math.PI / 4); ctx.fillRect(-25, -25, 50, 50); ctx.restore(); // Animation loop let frame = 0; function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); frame++; // draw frame... requestAnimationFrame(animate); } animate(); </script>
WebGL
WebGL provides a low-level GPU-accelerated 3D graphics API inside <canvas>. It is based on OpenGL ES and uses GLSL shaders.
<canvas id="glCanvas" width="640" height="480"></canvas> <script> const gl = canvas.getContext('webgl2') // WebGL 2 ?? canvas.getContext('webgl'); // fallback // Vertex Shader (runs per vertex on GPU) const vsSource = ` attribute vec4 aPosition; uniform mat4 uModelViewProjection; void main() { gl_Position = uModelViewProjection * aPosition; } `; // Fragment Shader (runs per pixel on GPU) const fsSource = ` precision mediump float; uniform vec4 uColor; void main() { gl_FragColor = uColor; } `; // Compile shader function compileShader(gl, type, source) { const shader = gl.createShader(type); gl.shaderSource(shader, source); gl.compileShader(shader); return shader; } // Buffer upload, link program, draw call… gl.clearColor(0, 0, 0, 1); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.drawArrays(gl.TRIANGLES, 0, 3); // Key WebGL concepts: // VBO — Vertex Buffer Object (geometry on GPU) // VAO — Vertex Array Object (attribute layout) // EBO — Element Buffer Object (index buffer) // FBO — Framebuffer Object (off-screen render) // UBO — Uniform Buffer Object (WebGL2) // SSBO — Shader Storage Buffer (WebGL2 ext) </script>
HTML5 Semantic Elements
HTML5 introduced landmark elements that describe the purpose of page regions, enabling better accessibility, SEO, and browser features.
<body> <!-- ❶ Site header --> <header> <nav aria-label="Primary">…</nav> </header> <!-- ❷ Skip link for keyboard users --> <a href="#main-content" class="skip-link">Skip to main content</a> <!-- ❸ Main content landmark --> <main id="main-content"> <!-- Article: self-contained composable unit --> <article> <header> <h1>Article Title</h1> <p>By <address><a href="mailto:[email protected]">Author</a></address></p> <time datetime="2025-06-01">June 1, 2025</time> </header> <!-- Section: thematic grouping inside article --> <section aria-labelledby="intro-heading"> <h2 id="intro-heading">Introduction</h2> <p>Content…</p> </section> <footer><p>Published by…</p></footer> </article> <!-- Aside: tangentially related content --> <aside aria-label="Related articles"> <h2>Also Read</h2> … </aside> </main> <!-- ❹ Site footer --> <footer> <nav aria-label="Footer navigation">…</nav> <p>© 2025 My Company</p> </footer> </body>
Full Semantic Element Reference
| Element | ARIA Landmark | Use Case |
|---|---|---|
<header> | banner (if top-level) | Introductory content, navigation branding |
<nav> | navigation | Navigation link groups |
<main> | main | Primary unique content — ONE per page |
<article> | article | Self-contained composable unit (blog post, product card) |
<section> | region (if labelled) | Thematic grouping with a heading |
<aside> | complementary | Related but tangential content (sidebar, callout) |
<footer> | contentinfo (if top-level) | Footer for its nearest sectioning ancestor |
<search> | search | Search form wrapper (HTML 2023) |
<figure> | figure | Referenced media with optional caption |
<figcaption> | — | Caption for <figure> |
<hgroup> | — | Heading + sub-heading group |
ARIA & Accessibility
ARIA (Accessible Rich Internet Applications) attributes extend HTML semantics for assistive technologies. The first rule of ARIA: don't use ARIA if native HTML semantics work.
ARIA Roles
<!-- Live region: announces dynamic content to screen readers --> <div role="status" aria-live="polite" aria-atomic="true"></div> <div role="alert" aria-live="assertive" ></div> <!-- Modal dialog --> <div role="dialog" aria-modal="true" aria-labelledby="dlg-title" aria-describedby="dlg-desc"> <h2 id="dlg-title">Confirm Action</h2> <p id="dlg-desc">Are you sure?</p> </div> <!-- Tab widget --> <div role="tablist" aria-label="Settings"> <button role="tab" aria-selected="true" aria-controls="panel-1">General</button> <button role="tab" aria-selected="false" aria-controls="panel-2">Privacy</button> </div> <div role="tabpanel" id="panel-1" aria-labelledby="tab-1">…</div> <div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>…</div> <!-- Toggle button --> <button aria-pressed="false">Dark Mode</button> <!-- Expandable menu --> <button aria-expanded="false" aria-controls="submenu">Products ▾</button> <ul id="submenu" hidden>…</ul> <!-- Combobox / autocomplete --> <input role="combobox" aria-expanded="true" aria-autocomplete="list" aria-controls="results-list" /> <!-- Visually hidden but accessible text --> <span class="sr-only">Loading, please wait…</span> <!-- CSS: .sr-only { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0,0,0,0); } --> <!-- aria-hidden: hide from AT but keep visible --> <span aria-hidden="true">★★★☆☆</span> <span class="sr-only">3 out of 5 stars</span>
Live ARIA Tab Demo
HTML structure uses semantic elements like <header>, <main>, <article>, and <footer> to describe page regions meaningfully.
HTML forms collect user input via <input>, <select>, <textarea>, and <button> elements. Always pair inputs with <label>.
Media elements <video> and <audio> embed rich content natively. Always provide captions via <track> for accessibility.
ARIA Properties Reference
| Attribute | Values | Purpose |
|---|---|---|
aria-label | string | Name when no visible label exists |
aria-labelledby | id ref | Points to element that is the label |
aria-describedby | id ref | Points to description element |
aria-hidden | true/false | Remove from accessibility tree |
aria-live | off/polite/assertive | Announce dynamic updates |
aria-atomic | true/false | Read whole region on change |
aria-expanded | true/false | Disclosure widget open/closed state |
aria-selected | true/false | Selection state (tabs, listbox) |
aria-pressed | true/false/mixed | Toggle button state |
aria-checked | true/false/mixed | Checkbox / switch state |
aria-disabled | true/false | Semantically disabled |
aria-required | true/false | Field is required |
aria-invalid | true/false/grammar/spelling | Validation failure |
aria-current | page/step/location/date/time/true | Current item in set |
aria-sort | ascending/descending/none/other | Column sort direction |
aria-posinset | integer | Position in set |
aria-setsize | integer | Size of set (-1 = unknown) |
aria-level | integer | Hierarchical level |
aria-valuemin/max/now/text | number/string | Range widget values |
aria-controls | id ref | Element this one controls |
aria-owns | id ref | DOM child relationship override |
aria-flowto | id ref | Next element in reading order |
aria-modal | true/false | Dialog is a modal |
aria-busy | true/false | Content is loading |
aria-keyshortcuts | string | Keyboard shortcut for element |
aria-roledescription | string | Custom role description |
aria-placeholder | string | Hint text for empty field |
aria-errormessage | id ref | Points to error message element |
SEO & Meta Tags
<head> <!-- ── Core ── --> <meta charset="UTF-8"/> <title>Primary Keyword — Brand Name | Max ~60 chars</title> <meta name="description" content="Compelling summary with keywords. Max 155-160 chars for Google."/> <meta name="robots" content="index, follow, max-snippet:-1, max-image-preview:large"/> <meta name="author" content="Author Name"/> <link rel="canonical" href="https://example.com/this-page"/> <!-- ── Open Graph (Facebook, LinkedIn, WhatsApp) ── --> <meta property="og:type" content="article"/> <meta property="og:title" content="Page Title"/> <meta property="og:description" content="Share description"/> <meta property="og:url" content="https://example.com/page"/> <meta property="og:image" content="https://example.com/og.jpg"/> <meta property="og:image:width" content="1200"/> <meta property="og:image:height"content="630"/> <meta property="og:site_name" content="Brand Name"/> <meta property="og:locale" content="en_US"/> <!-- ── Twitter Card ── --> <meta name="twitter:card" content="summary_large_image"/> <meta name="twitter:site" content="@YourHandle"/> <meta name="twitter:title" content="Page Title"/> <meta name="twitter:description" content="Tweet description"/> <meta name="twitter:image" content="https://example.com/card.jpg"/> <!-- ── JSON-LD Structured Data ── --> <script type="application/ld+json"> { "@context": "https://schema.org", "@type": "Article", "headline": "Page Title", "author": { "@type": "Person", "name": "Jane Smith" }, "datePublished": "2025-06-01", "image": "https://example.com/image.jpg" } </script> <!-- ── Hreflang for multilingual sites ── --> <link rel="alternate" hreflang="en" href="https://example.com/en/"/> <link rel="alternate" hreflang="fr" href="https://example.com/fr/"/> <link rel="alternate" hreflang="x-default" href="https://example.com/"/> </head>
Google Search Preview
Modern HTML Elements
<!-- Native Dialog (modal) --> <dialog id="my-dialog"> <h2>Dialog Title</h2> <p>Dialog content with backdrop support.</p> <button onclick="this.closest('dialog').close()">Close</button> </dialog> <button onclick="document.getElementById('my-dialog').showModal()">Open Dialog</button> <!-- Details / Summary — native accordion --> <details open> <summary>Click to toggle</summary> <p>Hidden content revealed on click. No JS needed!</p> </details> <!-- Popover API (2024) — No JS needed for basic use --> <button popovertarget="my-popover">Show Popover</button> <div id="my-popover" popover> <p>Popover content. Click outside to dismiss.</p> </div> <!-- Output — result of a calculation --> <form oninput="result.value = parseInt(a.value) + parseInt(b.value)"> <input type="number" id="a" value="0"/> + <input type="number" id="b" value="0"/> = <output name="result" for="a b">0</output> </form> <!-- Progress and Meter --> <progress value="70" max="100">70%</progress> <meter value="0.6" min="0" max="1" low="0.25" high="0.75" optimum="0.8"> 60% disk used </meter>
Live Demo
Native Accordion (no JS)
<details> element has been in browsers for years!Web Components
Web Components are a suite of standards for building reusable, encapsulated custom HTML elements.
<!-- ① Usage in HTML --> <user-card name="Jane Smith" role="Developer" avatar="jane.jpg" ></user-card> <!-- ② Definition --> <template id="user-card-template"> <style> /* Styles are encapsulated — don't leak out */ :host { display: block; padding: 16px; border-radius: 8px; } .name { font-weight: bold; } </style> <img class="avatar" alt=""/> <div class="name"></div> <div class="role"></div> <!-- Slot: allows parent to inject content --> <slot name="bio"></slot> </template> <script> class UserCard extends HTMLElement { // Attributes to observe for changes static get observedAttributes() { return ['name', 'role', 'avatar']; } constructor() { super(); // Attach Shadow DOM this.attachShadow({ mode: 'open' }); const tmpl = document.getElementById('user-card-template'); this.shadowRoot.appendChild(tmpl.content.cloneNode(true)); } // Lifecycle callbacks connectedCallback() { this.render(); } // Added to DOM disconnectedCallback() { } // Removed from DOM adoptedCallback() { } // Moved to new document attributeChangedCallback(name, old, next) { this.render(); } render() { this.shadowRoot.querySelector('.name').textContent = this.getAttribute('name'); this.shadowRoot.querySelector('.role').textContent = this.getAttribute('role'); this.shadowRoot.querySelector('.avatar').src = this.getAttribute('avatar'); } } // Register the custom element customElements.define('user-card', UserCard); </script>
Performance & Loading
<head> <!-- Preconnect: early TCP+TLS handshake to origin --> <link rel="preconnect" href="https://fonts.googleapis.com"/> <link rel="preconnect" href="https://cdn.example.com" crossorigin/> <!-- DNS Prefetch: cheaper than preconnect --> <link rel="dns-prefetch" href="https://analytics.example.com"/> <!-- Preload: fetch critical resource before parser finds it --> <link rel="preload" href="hero.jpg" as="image"/> <link rel="preload" href="font.woff2" as="font" crossorigin/> <link rel="preload" href="critical.css"as="style"/> <!-- Prefetch: fetch next-page resource in idle time --> <link rel="prefetch" href="/next-page.html"/> <!-- Speculation Rules API (Chrome 2023) --> <script type="speculationrules"> { "prerender": [{ "source": "list", "urls": ["/dashboard"] }] } </script> </head> <body> <!-- LCP image: eager load, priority fetch --> <img src="hero.jpg" alt="Hero" loading="eager" fetchpriority="high" decoding="sync" /> <!-- Below-fold images: lazy load --> <img src="below-fold.jpg" alt="…" loading="lazy" decoding="async" /> <!-- Script loading strategies --> <script src="app.js" defer></script> <!-- parse HTML, execute after --> <script src="widget.js" async></script> <!-- fetch in parallel, execute ASAP --> <script src="module.js" type="module"></script><!-- ESM: deferred by default --> <script src="worker-hint.js" blocking="render"></script> </body>
defer
Script downloads in parallel, executes after HTML is fully parsed. Order preserved. Use for most scripts.
async
Downloads in parallel, executes immediately when ready. No order guarantee. Best for independent analytics.
type="module"
ES Modules. Deferred by default. Enables import/export syntax. Always strict mode. CORS required.
loading="lazy"
Browser-native lazy loading for images and iframes. Loads only when near the viewport.
HTML APIs
Drag & Drop
<!-- Draggable source --> <div draggable="true" ondragstart="e.dataTransfer.setData('text/plain', 'Hello')"> Drag me! </div> <!-- Drop target --> <div ondragover="event.preventDefault()" ondrop="e.target.textContent = e.dataTransfer.getData('text/plain')"> Drop here </div>
Content Editable
<!-- Any element can become editable --> <article contenteditable="true" spellcheck="true" role="textbox" aria-multiline="true" aria-label="Edit article content" > Click to edit this paragraph… </article> <!-- execCommand (legacy but still used) --> <button onclick="document.execCommand('bold')">Bold</button>
Other Notable HTML APIs
| Feature | How to use |
|---|---|
| Web Storage | localStorage.setItem('key', val) / sessionStorage |
| History API | history.pushState({}, '', '/new-url') |
| Geolocation | navigator.geolocation.getCurrentPosition(cb) |
| Clipboard | navigator.clipboard.writeText('text') |
| Notifications | Notification.requestPermission() |
| Vibration | navigator.vibrate([200,100,200]) |
| Battery | navigator.getBattery() |
| Share | navigator.share({ title, text, url }) |
| fullscreen | el.requestFullscreen() / document.exitFullscreen() |
| Picture-in-Picture | video.requestPictureInPicture() |
| Screen Wake Lock | navigator.wakeLock.request('screen') |
| Intersection Observer | new IntersectionObserver(cb, opts) |
| ResizeObserver | new ResizeObserver(cb) |
| MutationObserver | new MutationObserver(cb) |
| Web Workers | new Worker('worker.js') |
| Service Workers | navigator.serviceWorker.register('sw.js') |
| IndexedDB | indexedDB.open('myDB', 1) |
| File API | input.files[0].arrayBuffer() |
| Broadcast Channel | new BroadcastChannel('tab-sync') |
Best Practices & Checklist
Structure First
Write semantic HTML before adding CSS or JS. Your HTML alone should make sense.
Accessibility
Test with a keyboard only. Run axe or WAVE. Add alt text, labels, skip links, and ARIA where needed.
Mobile First
Include viewport meta tag. Use responsive images. Test on real devices.
Performance
Lazy-load below-fold images. Defer non-critical JS. Preload LCP image. Use modern formats (WebP, AVIF).
Security
Sanitize user input before inserting to DOM. Use sandbox on iframes. Set rel="noopener" on external links.
SEO
One H1 per page. Descriptive title + meta desc. Canonical URLs. JSON-LD structured data. Valid HTML.
HTML Validation Checklist
✅ DOCTYPE declaration present ✅ <html lang="…"> set correctly ✅ <meta charset="UTF-8"> is first in <head> ✅ Viewport meta tag present ✅ <title> is descriptive and unique per page ✅ Meta description is present (150-160 chars) ✅ One <h1> per page, headings in order ✅ All images have alt attributes ✅ All inputs have associated <label> elements ✅ External links have rel="noopener noreferrer" ✅ iframes have title attributes ✅ Color contrast ratio ≥ 4.5:1 (WCAG AA) ✅ Interactive elements are keyboard-reachable ✅ Skip link provided for keyboard users ✅ Form errors associated with inputs via aria-describedby ✅ Canonical URL set in <head> ✅ Open Graph tags present ✅ Favicon provided (at minimum 32×32 and 180×180) ✅ Scripts use defer or are at end of <body> ✅ No deprecated elements (center, font, frame, etc.) ✅ HTML validates at validator.w3.org
Deprecated Elements — Never Use
Was this guide helpful?
Your feedback helps us continuously improve our developer docs.
