HTML — Getting Started
HTML (HyperText Markup Language) is the standard markup language for creating web pages. It describes the structure of a web page semantically, providing the foundation upon which CSS and JavaScript are applied.
Every web page you visit is built on HTML. This guide covers everything from document anatomy to modern HTML features, parsing internals, and best practices.
Every HTML document follows a standard structure. This is the minimal skeleton of a valid HTML5 page:
| 1 | <!DOCTYPE html> |
| 2 | <html lang="en"> |
| 3 | <head> |
| 4 | <meta charset="UTF-8" /> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 6 | <title>My Page</title> |
| 7 | </head> |
| 8 | <body> |
| 9 | <h1>Hello, World!</h1> |
| 10 | <p>Welcome to HTML.</p> |
| 11 | </body> |
| 12 | </html> |
info
The <!DOCTYPE html> is not an HTML element — it is a document type declaration that instructs the browser to parse the page in standards mode. It must be the very first line of every HTML document, before the <html> element.
| DOCTYPE | Mode | Usage |
|---|---|---|
| <!DOCTYPE html> | Standards Mode | HTML5 — Recommended |
| <!DOCTYPE HTML PUBLIC ...> | Almost Standards | Legacy HTML 4.01 |
| (none) | Quirks Mode | Avoid — inconsistent rendering |
| 1 | <!-- HTML5 (always use this) --> |
| 2 | <!DOCTYPE html> |
| 3 | <html lang="en"> |
| 4 | ... |
| 5 | |
| 6 | <!-- Legacy HTML 4.01 (avoid) --> |
| 7 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
| 8 | "http://www.w3.org/TR/html4/strict.dtd"> |
| 9 | |
| 10 | <!-- XHTML 1.0 (avoid) --> |
| 11 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
| 12 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
best practice
The <html> element is the root element of every HTML document. It wraps all content on the page. The lang attribute is critical for accessibility, search engines, and browser features like translation.
| 1 | <!DOCTYPE html> |
| 2 | <html lang="en"> |
| 3 | <!-- All page content here --> |
| 4 | </html> |
Common lang values:
| Value | Language | BCP 47 |
|---|---|---|
| en | English | ✓ |
| en-US | English (US) | ✓ |
| zh-CN | Chinese (Simplified) | ✓ |
| es | Spanish | ✓ |
| ar | Arabic | ✓ |
warning
The <head> element contains metadata about the document: title, character encoding, viewport settings, stylesheets, scripts, and SEO information. Content inside the head is not displayed in the browser.
| 1 | <head> |
| 2 | <!-- Character encoding — must be UTF-8 --> |
| 3 | <meta charset="UTF-8" /> |
| 4 | |
| 5 | <!-- Viewport for responsive design --> |
| 6 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 7 | |
| 8 | <!-- Title — required, shown in tab and search results --> |
| 9 | <title>My Page | Site Name</title> |
| 10 | |
| 11 | <!-- SEO meta tags --> |
| 12 | <meta name="description" content="A description of the page for search engines." /> |
| 13 | <meta name="keywords" content="html, tutorial, web development" /> |
| 14 | <meta name="author" content="Your Name" /> |
| 15 | |
| 16 | <!-- Open Graph (social sharing) --> |
| 17 | <meta property="og:title" content="My Page" /> |
| 18 | <meta property="og:description" content="What shows up when shared on social media." /> |
| 19 | <meta property="og:image" content="https://example.com/thumbnail.jpg" /> |
| 20 | <meta property="og:url" content="https://example.com/page" /> |
| 21 | <meta property="og:type" content="website" /> |
| 22 | |
| 23 | <!-- Twitter Card --> |
| 24 | <meta name="twitter:card" content="summary_large_image" /> |
| 25 | <meta name="twitter:title" content="My Page" /> |
| 26 | <meta name="twitter:description" content="Twitter-specific description." /> |
| 27 | <meta name="twitter:image" content="https://example.com/thumbnail.jpg" /> |
| 28 | |
| 29 | <!-- Favicon --> |
| 30 | <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> |
| 31 | <link rel="apple-touch-icon" href="/apple-touch-icon.png" /> |
| 32 | |
| 33 | <!-- Canonical URL (prevents duplicate content issues) --> |
| 34 | <link rel="canonical" href="https://example.com/page" /> |
| 35 | |
| 36 | <!-- RSS feed --> |
| 37 | <link rel="alternate" type="application/rss+xml" title="RSS" href="/feed.xml" /> |
| 38 | |
| 39 | <!-- Preconnect to external origins for performance --> |
| 40 | <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| 41 | <link rel="preconnect" href="https://cdn.example.com" crossorigin /> |
| 42 | |
| 43 | <!-- External stylesheets --> |
| 44 | <link rel="stylesheet" href="/styles.css" /> |
| 45 | |
| 46 | <!-- Deferred JavaScript --> |
| 47 | <script src="/app.js" defer></script> |
| 48 | </head> |
pro tip
Here is an interactive example showing how meta tags affect how your page appears:
HTML elements belong to content categories that define where they can be placed and what they can contain. Understanding these categories is essential for writing valid, semantic HTML.
Flow Content
Most elements that go inside the body. This is the broadest category, including text, images, forms, tables, sections, and interactive elements.
| 1 | <p>, <div>, <span>, <ul>, <ol>, <table>, <form>, |
| 2 | <section>, <article>, <header>, <footer>, <main>, |
| 3 | <img>, <video>, <audio>, <canvas>, <figure>, <blockquote> |
Phrasing Content
Inline-level elements that mark up text. These can only contain other phrasing content.
| 1 | <span>, <strong>, <em>, <a>, <code>, <b>, <i>, <u>, |
| 2 | <small>, <sub>, <sup>, <mark>, <q>, <cite>, <time>, |
| 3 | <button>, <input>, <label>, <textarea>, <select>, |
| 4 | <br>, <img>, <svg>, <abbr>, <dfn>, <var>, <samp>, <kbd> |
Interactive Content
Elements specifically designed for user interaction. They are content that the user can click, tap, or otherwise interact with.
| 1 | <a> (with href), <button>, <input>, <select>, <textarea>, |
| 2 | <details>, <summary>, <label>, <audio> (with controls), |
| 3 | <video> (with controls), <dialog> |
Sectioning Content
Elements that create sections in the document outline. They define the scope of headings and the document structure.
| 1 | <article>, <section>, <nav>, <aside>, <h1>-<h6>, |
| 2 | <header>, <footer>, <main>, <address> |
Embedded Content
Elements that import external resources into the document.
| 1 | <img>, <video>, <audio>, <iframe>, <embed>, <object>, |
| 2 | <picture>, <source>, <track>, <canvas>, <svg> |
Metadata Content
Elements that set up the presentation or behavior of the document, or provide information about the document.
| 1 | <base>, <link>, <meta>, <noscript>, <script>, <style>, |
| 2 | <title>, <template> |
best practice
Semantic HTML means using elements that convey meaning about their content, not just how it looks. This improves accessibility, SEO, and code maintainability.
| 1 | <!DOCTYPE html> |
| 2 | <html lang="en"> |
| 3 | <head> |
| 4 | <meta charset="UTF-8" /> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 6 | <title>Semantic HTML Structure</title> |
| 7 | </head> |
| 8 | <body> |
| 9 | <header> |
| 10 | <nav> |
| 11 | <ul> |
| 12 | <li><a href="/">Home</a></li> |
| 13 | <li><a href="/about">About</a></li> |
| 14 | <li><a href="/blog">Blog</a></li> |
| 15 | <li><a href="/contact">Contact</a></li> |
| 16 | </ul> |
| 17 | </nav> |
| 18 | </header> |
| 19 | |
| 20 | <main> |
| 21 | <article> |
| 22 | <header> |
| 23 | <h1>Article Title</h1> |
| 24 | <p>Published on <time datetime="2026-01-15">January 15, 2026</time></p> |
| 25 | </header> |
| 26 | |
| 27 | <section id="introduction"> |
| 28 | <h2>Introduction</h2> |
| 29 | <p>Content introduction here.</p> |
| 30 | </section> |
| 31 | |
| 32 | <section id="body"> |
| 33 | <h2>Main Content</h2> |
| 34 | <p>Article body content goes here.</p> |
| 35 | |
| 36 | <figure> |
| 37 | <img src="diagram.png" alt="Diagram explaining the concept" /> |
| 38 | <figcaption>Figure 1: Concept explanation diagram</figcaption> |
| 39 | </figure> |
| 40 | </section> |
| 41 | </article> |
| 42 | |
| 43 | <aside> |
| 44 | <h2>Related Articles</h2> |
| 45 | <ul> |
| 46 | <li><a href="/related-1">Related Post 1</a></li> |
| 47 | <li><a href="/related-2">Related Post 2</a></li> |
| 48 | </ul> |
| 49 | </aside> |
| 50 | </main> |
| 51 | |
| 52 | <footer> |
| 53 | <p>© 2026 Your Site. All rights reserved.</p> |
| 54 | <address> |
| 55 | Contact: <a href="mailto:info@example.com">info@example.com</a> |
| 56 | </address> |
| 57 | </footer> |
| 58 | </body> |
| 59 | </html> |
Semantic elements and their purposes:
| Element | Purpose | Usage |
|---|---|---|
| <header> | Introductory content or navigation | Top of page or section |
| <nav> | Navigation links | Primary site navigation |
| <main> | Primary content (unique per page) | One per page |
| <article> | Self-contained composition | Blog posts, news, comments |
| <section> | Thematic grouping of content | Chapters, tab panels |
| <aside> | Tangentially related content | Sidebars, pull quotes |
| <footer> | Footer for page or section | Copyright, links, meta |
| <figure> | Self-contained media with caption | Images, diagrams, code |
| <figcaption> | Caption for figure | Must be inside figure |
| <time> | Machine-readable date/time | Dates, event times |
| <address> | Contact information | Author or company contact |
| <mark> | Highlighted/referenced text | Search results, quotes |
warning
HTML provides powerful elements for embedding external resources like images, video, audio, and other web pages.
Images — <img> & <picture>
The <img> element embeds images. Always provide alt text for accessibility. Use <picture> for responsive images.
| 1 | <!-- Basic image with required alt text --> |
| 2 | <img src="photo.jpg" alt="A sunset over the ocean" width="800" height="600" /> |
| 3 | |
| 4 | <!-- Responsive image with srcset --> |
| 5 | <img |
| 6 | src="photo-800.jpg" |
| 7 | srcset="photo-400.jpg 400w, photo-800.jpg 800w, photo-1200.jpg 1200w" |
| 8 | sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px" |
| 9 | alt="A sunset over the ocean" |
| 10 | loading="lazy" |
| 11 | decoding="async" |
| 12 | /> |
| 13 | |
| 14 | <!-- Picture element for art direction --> |
| 15 | <picture> |
| 16 | <source media="(min-width: 1200px)" srcset="hero-wide.jpg" /> |
| 17 | <source media="(min-width: 768px)" srcset="hero-tablet.jpg" /> |
| 18 | <source media="(min-width: 480px)" srcset="hero-mobile.jpg" /> |
| 19 | <img src="hero-fallback.jpg" alt="Hero banner image" /> |
| 20 | </picture> |
| 21 | |
| 22 | <!-- Figure with caption --> |
| 23 | <figure> |
| 24 | <img src="diagram.svg" alt="Architecture diagram" /> |
| 25 | <figcaption>System architecture overview</figcaption> |
| 26 | </figure> |
Video — <video>
The <video> element embeds video content. Provide multiple source formats for cross-browser compatibility and include captions.
| 1 | <video |
| 2 | controls |
| 3 | width="640" |
| 4 | height="360" |
| 5 | poster="thumbnail.jpg" |
| 6 | preload="metadata" |
| 7 | > |
| 8 | <source src="video.mp4" type="video/mp4" /> |
| 9 | <source src="video.webm" type="video/webm" /> |
| 10 | <source src="video.ogv" type="video/ogg" /> |
| 11 | <track |
| 12 | kind="captions" |
| 13 | src="captions-en.vtt" |
| 14 | srclang="en" |
| 15 | label="English" |
| 16 | /> |
| 17 | <p>Your browser does not support video. |
| 18 | <a href="video.mp4">Download the video</a>.</p> |
| 19 | </video> |
Audio — <audio>
The <audio> element embeds audio content. Like video, provide multiple source formats.
| 1 | <audio controls preload="none"> |
| 2 | <source src="podcast.mp3" type="audio/mpeg" /> |
| 3 | <source src="podcast.ogg" type="audio/ogg" /> |
| 4 | <source src="podcast.wav" type="audio/wav" /> |
| 5 | <p>Your browser does not support audio. |
| 6 | <a href="podcast.mp3">Download the podcast</a>.</p> |
| 7 | </audio> |
Iframe — <iframe>
The <iframe> element embeds another HTML page. Always sandbox for security.
| 1 | <!-- Secure iframe with sandbox --> |
| 2 | <iframe |
| 3 | src="https://example.com/widget" |
| 4 | width="400" |
| 5 | height="300" |
| 6 | sandbox="allow-scripts allow-same-origin" |
| 7 | loading="lazy" |
| 8 | title="Example widget" |
| 9 | allow="clipboard-write; payment" |
| 10 | > |
| 11 | <p>Your browser does not support iframes.</p> |
| 12 | </iframe> |
| 13 | |
| 14 | <!-- Lazy-loaded map embed --> |
| 15 | <iframe |
| 16 | src="https://maps.google.com/?q=location" |
| 17 | width="100%" |
| 18 | height="350" |
| 19 | loading="lazy" |
| 20 | referrerpolicy="no-referrer-when-downgrade" |
| 21 | title="Map location" |
| 22 | style="border:0;border-radius:8px" |
| 23 | ></iframe> |
warning
best practice
HTML has evolved significantly. These modern features provide native browser functionality that previously required JavaScript.
<dialog> — Native Modal
The <dialog> element provides a native modal dialog. It supports open/close, backdrop dismissal, and focus trapping automatically.
| 1 | <!-- Modal dialog --> |
| 2 | <dialog id="myModal"> |
| 3 | <form method="dialog"> |
| 4 | <h2>Confirm Action</h2> |
| 5 | <p>Are you sure you want to proceed?</p> |
| 6 | <menu> |
| 7 | <button value="cancel">Cancel</button> |
| 8 | <button value="confirm">Confirm</button> |
| 9 | </menu> |
| 10 | </form> |
| 11 | </dialog> |
| 12 | |
| 13 | <!-- Open with JavaScript --> |
| 14 | <script> |
| 15 | const modal = document.getElementById("myModal"); |
| 16 | modal.showModal(); // Opens as modal |
| 17 | modal.close("confirmed"); // Closes with return value |
| 18 | </script> |
pro tip
<details> & <summary> — Disclosure Widget
Native accordion-style expand/collapse without JavaScript. Perfect for FAQs, collapsible sections, and progressive disclosure.
| 1 | <details> |
| 2 | <summary>What is HTML?</summary> |
| 3 | <p>HTML (HyperText Markup Language) is the standard |
| 4 | markup language for creating web pages.</p> |
| 5 | </details> |
| 6 | |
| 7 | <details open> |
| 8 | <summary>Already expanded section</summary> |
| 9 | <p>Add the open attribute to start expanded.</p> |
| 10 | </details> |
| 11 | |
| 12 | <!-- Nested details for tree navigation --> |
| 13 | <details> |
| 14 | <summary>Chapter 1: Introduction</summary> |
| 15 | <details> |
| 16 | <summary>1.1 What is the Web?</summary> |
| 17 | <p>The web is a system of interlinked hypertext documents.</p> |
| 18 | </details> |
| 19 | <details> |
| 20 | <summary>1.2 History of HTML</summary> |
| 21 | <p>HTML was created by Tim Berners-Lee in 1991.</p> |
| 22 | </details> |
| 23 | </details> |
<datalist> — Autocomplete Suggestions
Provides a list of predefined options for an input field, enabling autocomplete behavior natively.
| 1 | <label for="browser">Choose a browser:</label> |
| 2 | <input |
| 3 | list="browsers" |
| 4 | id="browser" |
| 5 | name="browser" |
| 6 | placeholder="Start typing..." |
| 7 | /> |
| 8 | |
| 9 | <datalist id="browsers"> |
| 10 | <option value="Chrome" /> |
| 11 | <option value="Firefox" /> |
| 12 | <option value="Safari" /> |
| 13 | <option value="Edge" /> |
| 14 | <option value="Opera" /> |
| 15 | <option value="Brave" /> |
| 16 | <option value="Arc" /> |
| 17 | </datalist> |
info
<template> — Declarative Templates
The <template> element holds HTML that is not rendered immediately but can be instantiated by JavaScript. Its contents are parsed but inert.
| 1 | <!-- Define template --> |
| 2 | <template id="card-template"> |
| 3 | <div class="card"> |
| 4 | <img class="card-image" src="" alt="" /> |
| 5 | <div class="card-body"> |
| 6 | <h3 class="card-title"></h3> |
| 7 | <p class="card-description"></p> |
| 8 | </div> |
| 9 | </div> |
| 10 | </template> |
| 11 | |
| 12 | <!-- Clone and populate with JavaScript --> |
| 13 | <script> |
| 14 | const template = document.getElementById("card-template"); |
| 15 | |
| 16 | function createCard(image, alt, title, description) { |
| 17 | const clone = template.content.cloneNode(true); |
| 18 | |
| 19 | clone.querySelector(".card-image").src = image; |
| 20 | clone.querySelector(".card-image").alt = alt; |
| 21 | clone.querySelector(".card-title").textContent = title; |
| 22 | clone.querySelector(".card-description").textContent = description; |
| 23 | |
| 24 | document.body.appendChild(clone); |
| 25 | } |
| 26 | |
| 27 | createCard("photo.jpg", "A photo", "My Card", "Description"); |
| 28 | </script> |
<slot> — Web Component Composition
The <slot> element is part of the Web Components specification. It defines insertion points within a shadow DOM where content can be projected.
| 1 | <!-- Custom element with slots --> |
| 2 | <template id="page-layout"> |
| 3 | <style> |
| 4 | header { background: #1a1a2e; padding: 1rem; } |
| 5 | main { padding: 1rem; } |
| 6 | footer { background: #1a1a2e; padding: 0.5rem; text-align: center; } |
| 7 | </style> |
| 8 | <header> |
| 9 | <slot name="header">Default header</slot> |
| 10 | </header> |
| 11 | <main> |
| 12 | <slot>Default content</slot> |
| 13 | </main> |
| 14 | <footer> |
| 15 | <slot name="footer">© 2026</slot> |
| 16 | </footer> |
| 17 | </template> |
| 18 | |
| 19 | <!-- Usage --> |
| 20 | <page-layout> |
| 21 | <h1 slot="header">My Page</h1> |
| 22 | <p>Main content goes here.</p> |
| 23 | <p slot="footer">Custom footer content</p> |
| 24 | </page-layout> |
| 25 | |
| 26 | <script> |
| 27 | class PageLayout extends HTMLElement { |
| 28 | constructor() { |
| 29 | super(); |
| 30 | const template = document.getElementById("page-layout"); |
| 31 | const shadow = this.attachShadow({ mode: "open" }); |
| 32 | shadow.appendChild(template.content.cloneNode(true)); |
| 33 | } |
| 34 | } |
| 35 | customElements.define("page-layout", PageLayout); |
| 36 | </script> |
<progress> & <meter>
Native elements for displaying progress and measurements.
| 1 | <!-- Progress bar (indeterminate or determinate) --> |
| 2 | <label for="upload">Upload progress:</label> |
| 3 | <progress id="upload" max="100" value="65">65%</progress> |
| 4 | |
| 5 | <!-- Meter for scalar measurements --> |
| 6 | <label for="disk">Disk usage:</label> |
| 7 | <meter id="disk" min="0" max="100" low="25" high="75" optimum="50" value="80"> |
| 8 | 80% used |
| 9 | </meter> |
| 10 | |
| 11 | <!-- Styling with CSS --> |
| 12 | <style> |
| 13 | progress[value] { |
| 14 | appearance: none; |
| 15 | height: 8px; |
| 16 | border-radius: 4px; |
| 17 | overflow: hidden; |
| 18 | } |
| 19 | progress[value]::-webkit-progress-value { |
| 20 | background: #00FF41; |
| 21 | } |
| 22 | progress[value]::-webkit-progress-bar { |
| 23 | background: #222222; |
| 24 | } |
| 25 | </style> |
best practice
Microdata adds machine-readable semantics to HTML, while ARIA (Accessible Rich Internet Applications) provides accessibility semantics for assistive technologies.
Microdata & Schema.org
Microdata allows you to annotate HTML elements with machine-readable labels from Schema.org, enabling rich search results (rich snippets).
| 1 | <article itemscope itemtype="https://schema.org/Article"> |
| 2 | <h1 itemprop="headline">How HTML Works</h1> |
| 3 | <p itemprop="description">A comprehensive guide to HTML.</p> |
| 4 | <meta itemprop="datePublished" content="2026-01-15" /> |
| 5 | <span itemprop="author" itemscope itemtype="https://schema.org/Person"> |
| 6 | <span itemprop="name">Jane Doe</span> |
| 7 | </span> |
| 8 | </article> |
| 9 | |
| 10 | <!-- Product schema for e-commerce --> |
| 11 | <div itemscope itemtype="https://schema.org/Product"> |
| 12 | <img itemprop="image" src="product.jpg" alt="Product photo" /> |
| 13 | <h2 itemprop="name">Wireless Headphones</h2> |
| 14 | <p itemprop="description">Noise-cancelling Bluetooth headphones.</p> |
| 15 | <div itemprop="offers" itemscope itemtype="https://schema.org/Offer"> |
| 16 | <span itemprop="priceCurrency" content="USD">$</span> |
| 17 | <span itemprop="price">79.99</span> |
| 18 | <link itemprop="availability" href="https://schema.org/InStock" /> |
| 19 | </div> |
| 20 | <div itemprop="aggregateRating" itemscope |
| 21 | itemtype="https://schema.org/AggregateRating"> |
| 22 | <span itemprop="ratingValue">4.5</span> stars |
| 23 | (<span itemprop="reviewCount">128</span> reviews) |
| 24 | </div> |
| 25 | </div> |
| 26 | |
| 27 | <!-- Breadcrumb schema --> |
| 28 | <nav aria-label="Breadcrumb" itemscope |
| 29 | itemtype="https://schema.org/BreadcrumbList"> |
| 30 | <ol> |
| 31 | <li itemprop="itemListElement" itemscope |
| 32 | itemtype="https://schema.org/ListItem"> |
| 33 | <a itemprop="item" href="/"> |
| 34 | <span itemprop="name">Home</span> |
| 35 | </a> |
| 36 | <meta itemprop="position" content="1" /> |
| 37 | </li> |
| 38 | <li itemprop="itemListElement" itemscope |
| 39 | itemtype="https://schema.org/ListItem"> |
| 40 | <a itemprop="item" href="/docs"> |
| 41 | <span itemprop="name">Docs</span> |
| 42 | </a> |
| 43 | <meta itemprop="position" content="2" /> |
| 44 | </li> |
| 45 | </ol> |
| 46 | </nav> |
ARIA — Accessible Rich Internet Applications
ARIA attributes bridge gaps where native HTML semantics are insufficient. The golden rule: only use ARIA when native HTML is not enough.
| 1 | <!-- ARIA Landmarks (use semantic HTML first!) --> |
| 2 | <header role="banner"> |
| 3 | <nav role="navigation" aria-label="Main navigation"> |
| 4 | ... |
| 5 | </nav> |
| 6 | </header> |
| 7 | |
| 8 | <main role="main"> |
| 9 | <article role="article"> |
| 10 | ... |
| 11 | </article> |
| 12 | </main> |
| 13 | |
| 14 | <footer role="contentinfo"> |
| 15 | ... |
| 16 | </footer> |
| 17 | |
| 18 | <!-- ARIA for dynamic content --> |
| 19 | <div |
| 20 | role="alert" |
| 21 | aria-live="polite" |
| 22 | aria-atomic="true" |
| 23 | > |
| 24 | Form submitted successfully! |
| 25 | </div> |
| 26 | |
| 27 | <!-- ARIA for custom interactive widgets --> |
| 28 | <button |
| 29 | aria-expanded="false" |
| 30 | aria-controls="menu-list" |
| 31 | aria-haspopup="true" |
| 32 | > |
| 33 | Menu |
| 34 | </button> |
| 35 | <ul id="menu-list" role="menu" aria-hidden="true"> |
| 36 | <li role="menuitem" tabindex="0">Item 1</li> |
| 37 | <li role="menuitem" tabindex="-1">Item 2</li> |
| 38 | </ul> |
| 39 | |
| 40 | <!-- ARIA for status messages --> |
| 41 | <div |
| 42 | role="status" |
| 43 | aria-live="polite" |
| 44 | aria-relevant="additions" |
| 45 | > |
| 46 | <p>3 new messages</p> |
| 47 | </div> |
| 48 | |
| 49 | <!-- ARIA for progress (when not using native progress) --> |
| 50 | <div |
| 51 | role="progressbar" |
| 52 | aria-valuenow="65" |
| 53 | aria-valuemin="0" |
| 54 | aria-valuemax="100" |
| 55 | aria-label="Upload progress" |
| 56 | > |
| 57 | 65% |
| 58 | </div> |
warning
pro tip
HTML attributes play a crucial role in web performance. Correct use of async, defer, preload, and prefetch can dramatically improve loading times.
| Attribute | Resource | Behavior |
|---|---|---|
| async | Script | Download in parallel, execute as soon as downloaded |
| defer | Script | Download in parallel, execute after document parsed (in order) |
| preload | Critical assets | Fetch early, high priority (current page) |
| prefetch | Future navigation | Fetch opportunistically, low priority (next page) |
| preconnect | Origin | Pre-emptively open connection (DNS + TLS) |
| dns-prefetch | Origin | Pre-resolve DNS only |
| modulepreload | Module scripts | Preload ES module and its dependencies |
| prerender | Full page | Render entire page in background (deprecated in some browsers) |
| 1 | <!-- Script loading strategies --> |
| 2 | |
| 3 | <!-- Blocking (default) — pauses HTML parsing --> |
| 4 | <script src="app.js"></script> |
| 5 | |
| 6 | <!-- Async — download in parallel, execute ASAP (out of order) --> |
| 7 | <script src="analytics.js" async></script> |
| 8 | |
| 9 | <!-- Defer — download in parallel, execute after parse (in order) --> |
| 10 | <script src="app.js" defer></script> |
| 11 | <script src="utils.js" defer></script> |
| 12 | |
| 13 | <!-- Resource hints for performance --> |
| 14 | |
| 15 | <!-- Preload critical resources (fonts, hero images, CSS) --> |
| 16 | <link rel="preload" href="/fonts/inter-var.woff2" as="font" type="font/woff2" crossorigin /> |
| 17 | <link rel="preload" href="/hero.jpg" as="image" /> |
| 18 | <link rel="preload" href="/critical.css" as="style" /> |
| 19 | |
| 20 | <!-- Prefetch resources for the next page --> |
| 21 | <link rel="prefetch" href="/about" as="document" /> |
| 22 | <link rel="prefetch" href="/about/style.css" as="style" /> |
| 23 | <link rel="prefetch" href="/about/hero.jpg" as="image" /> |
| 24 | |
| 25 | <!-- Preconnect to third-party origins --> |
| 26 | <link rel="preconnect" href="https://fonts.googleapis.com" /> |
| 27 | <link rel="preconnect" href="https://www.googletagmanager.com" crossorigin /> |
| 28 | |
| 29 | <!-- DNS prefetch (fallback for older browsers) --> |
| 30 | <link rel="dns-prefetch" href="https://cdn.example.com" /> |
| 31 | |
| 32 | <!-- Module preload for ES modules --> |
| 33 | <link rel="modulepreload" href="/app.mjs" /> |
| 34 | |
| 35 | <!-- Lazy loading for images and iframes --> |
| 36 | <img src="photo.jpg" loading="lazy" decoding="async" /> |
| 37 | <iframe src="widget.html" loading="lazy"></iframe> |
info
best practice
Understanding how browsers parse HTML helps you write more efficient code and debug rendering issues. The HTML parser follows a deterministic algorithm defined in the HTML specification.
Tokenization Phase
The tokenizer reads raw HTML byte by byte and produces tokens: start tags, end tags, attributes, comments, and text. It uses a state machine with over 80 states.
| 1 | <!-- Raw HTML input --> |
| 2 | <p class="greeting">Hello, <strong>World</strong>!</p> |
| 3 | |
| 4 | <!-- Tokenization output (conceptual) --> |
| 5 | StartTag: <p> |
| 6 | Attribute: class="greeting" |
| 7 | Text: "Hello, " |
| 8 | StartTag: <strong> |
| 9 | Text: "World" |
| 10 | EndTag: </strong> |
| 11 | Text: "!" |
| 12 | EndTag: </p> |
Tree Construction Phase
Tokens are fed to the tree construction algorithm, which builds the DOM tree. The parser maintains a stack of open elements and an insertion mode that determines how each token is processed.
| 1 | <!-- Parsing algorithm steps (simplified) --> |
| 2 | |
| 3 | 1. Initial state |
| 4 | - DOCTYPE: <!DOCTYPE html> |
| 5 | - Insert document node, set mode to standards |
| 6 | |
| 7 | 2. Before HTML |
| 8 | - Any token before <html> creates implicit <html> |
| 9 | |
| 10 | 3. Before Head |
| 11 | - After <html>, next token creates implicit <head> |
| 12 | |
| 13 | 4. In Head |
| 14 | - <title>, <meta>, <link>, <script> processed |
| 15 | - </head> pops head element |
| 16 | |
| 17 | 5. After Head / In Body |
| 18 | - Most content parsed here |
| 19 | - Implicit <body> created before first body content |
| 20 | |
| 21 | 6. In Table |
| 22 | - Special parsing rules for table elements |
| 23 | - Foster parenting: misnested table content moved before table |
| 24 | |
| 25 | 7. After Body / After After Body |
| 26 | - </body> and </html> tokens processed |
| 27 | |
| 28 | 8. Text / Foreign Content |
| 29 | - CDATA sections (SVG, MathML) use different rules |
| 30 | - Raw text elements (<script>, <style>) use different tokenizer |
warning
Key Parsing Concepts
Foster Parenting
When content that should be inside a table element is incorrectly placed, the parser moves ("fosters") it before the table. This is why misnested HTML rarely breaks pages entirely.
| 1 | <!-- Misnested (foster parenting applies) --> |
| 2 | <table> |
| 3 | <tr> |
| 4 | <td>Cell</td> |
| 5 | </tr> |
| 6 | <p>This text gets fostered before the table</p> |
| 7 | </table> |
| 8 | |
| 9 | <!-- Parse result: --> |
| 10 | <p>This text gets fostered before the table</p> |
| 11 | <table> |
| 12 | <tr> |
| 13 | <td>Cell</td> |
| 14 | </tr> |
| 15 | </table> |
Script Execution Model
When the parser encounters a blocking <script>, it pauses HTML parsing, downloads and executes the script, then resumes. defer and async change this behavior.
| 1 | <!-- Parser blocking — delays rendering --> |
| 2 | <script src="large.js"></script> |
| 3 | |
| 4 | <!-- Parser pauses, waits for download, executes, resumes --> |
| 5 | |
| 6 | <!-- Async — download in parallel, execute when ready --> |
| 7 | <script src="analytics.js" async></script> |
| 8 | |
| 9 | <!-- Defer — download in parallel, execute after parse --> |
| 10 | <script src="app.js" defer></script> |
pro tip
A comprehensive checklist to ensure your HTML is valid, accessible, performant, and maintainable.
Structure & Validity
Accessibility
Performance
SEO & Semantics
Security
Maintainability
best practice
✗ Incorrect
<div onclick="submit()">Submit</div>Non-semantic: use <button> or <a> instead.
✗ Incorrect
<img src="photo.jpg" />Missing alt attribute — accessibility failure.
✗ Incorrect
<h1>Title</h1><h3>Subtitle</h3>Skipping heading levels — use <h2> after <h1>.
✗ Incorrect
<form><button>Submit</button></form>Button defaults to type="submit" in forms — specify type="button" if not submitting.
✗ Incorrect
<a href="#" onclick="navigate()">Link</a>Use real URLs instead of href="#" for progressive enhancement.
✗ Incorrect
<div class="nav">...</div>Use <nav> instead of a generic div for navigation.
pro tip