HTML Elements
HTML elements are the fundamental building blocks of web pages. Each element is defined by a tag name, optional attributes, and optional content. Elements provide structure, semantics, and interactivity to documents.
info
Every HTML element follows a consistent structure composed of an opening tag, optional attributes, content, and a closing tag:
| 1 | <opening-tag attributes>content</closing-tag> |
| 2 | |
| 3 | <!-- Concrete example: --> |
| 4 | <a href="https://example.com" class="link"> |
| 5 | Click here |
| 6 | </a> |
| 7 | |
| 8 | <!-- Void elements have no closing tag or content: --> |
| 9 | <img src="photo.jpg" alt="Photo" /> |
The opening tag contains the element name and any attributes. Attributes provide additional configuration — common ones include class, id, style, and data-*. The content is what appears between the opening and closing tags. Void elements (like <img>, <br>, <hr>) cannot have children.
The HTML specification categorizes elements into content models that define what kind of content an element can contain. Understanding these models is essential for writing valid, semantic markup.
| Content Model | Description | Examples |
|---|---|---|
| Flow | Most elements that appear in the body. The broadest category. | <div>, <p>, <section>, <h1> |
| Phrasing | Inline-level text markup. Can contain only other phrasing elements. | <span>, <strong>, <a>, <code> |
| Sectioning | Creates sections in the document outline. | <section>, <article>, <nav>, <aside> |
| Heading | Defines headings for sections. | <h1>–<h6>, <hgroup> |
| Interactive | Elements designed for user interaction. | <button>, <a>, <details>, <input> |
| Embedded | Elements that embed external resources. | <img>, <video>, <audio>, <iframe> |
| Palpable | Elements that render visible content (non-empty). | Most flow and phrasing elements |
| Metadata | Elements that describe the document itself. | <title>, <meta>, <link>, <style> |
best practice
Content models are hierarchical. Phrasing content is a subset of flow content. Sectioning and heading content are also flow content. An element belongs to one or more categories, which determines where it may be used and what it may contain.
| 1 | <!-- Valid: phrasing inside flow --> |
| 2 | <div> |
| 3 | <p>This is <strong>valid</strong> nesting.</p> |
| 4 | </div> |
| 5 | |
| 6 | <!-- Invalid: block inside phrasing --> |
| 7 | <p> |
| 8 | <div>This is invalid.</div> |
| 9 | </p> |
| 10 | |
| 11 | <!-- Valid: interactive inside phrasing --> |
| 12 | <p> |
| 13 | Click <a href="/">here</a> for more. |
| 14 | </p> |
Sectioning elements define the structure and outline of a document. They give semantic meaning to different parts of a page, improving accessibility and SEO.
| Element | Purpose | Usage |
|---|---|---|
| <header> | Introductory content or navigation aids | Top of page or section |
| <nav> | Navigation links | Primary site navigation |
| <main> | Dominant content of the document | One per page |
| <section> | Generic standalone section | Thematic grouping |
| <article> | Self-contained composition | Blog posts, forum posts |
| <aside> | Tangentially related content | Sidebars, pull quotes |
| <footer> | Footer for nearest sectioning root | Bottom of page or section |
| <address> | Contact information | Author or organization contact |
| 1 | <body> |
| 2 | <header> |
| 3 | <nav> |
| 4 | <a href="/">Home</a> |
| 5 | <a href="/blog">Blog</a> |
| 6 | <a href="/contact">Contact</a> |
| 7 | </nav> |
| 8 | </header> |
| 9 | |
| 10 | <main> |
| 11 | <article> |
| 12 | <header> |
| 13 | <hgroup> |
| 14 | <h1>Article Title</h1> |
| 15 | <p>Published on <time datetime="2026-07-07">July 7, 2026</time></p> |
| 16 | </hgroup> |
| 17 | </header> |
| 18 | |
| 19 | <section> |
| 20 | <h2>Introduction</h2> |
| 21 | <p>This is the first section of the article.</p> |
| 22 | </section> |
| 23 | |
| 24 | <section> |
| 25 | <h2>Details</h2> |
| 26 | <p>More detailed content goes here.</p> |
| 27 | </section> |
| 28 | |
| 29 | <footer> |
| 30 | <address> |
| 31 | Written by <a href="mailto:author@example.com">Jane Doe</a> |
| 32 | </address> |
| 33 | </footer> |
| 34 | </article> |
| 35 | |
| 36 | <aside> |
| 37 | <h3>Related Articles</h3> |
| 38 | <ul> |
| 39 | <li><a href="/article-2">How to Use HTML</a></li> |
| 40 | <li><a href="/article-3">CSS Basics</a></li> |
| 41 | </ul> |
| 42 | </aside> |
| 43 | </main> |
| 44 | |
| 45 | <footer> |
| 46 | <p>© 2026 Example Corp</p> |
| 47 | </footer> |
| 48 | </body> |
pro tip
Live preview of a sectioning layout:
Block-level elements occupy the full width of their parent container and start on a new line. They are used for structural layout and grouping content.
| Element | Purpose |
|---|---|
| <div> | Generic container (no semantic meaning) |
| <p> | Paragraph of text |
| <ul> / <ol> | Unordered and ordered lists |
| <dl> / <dt> / <dd> | Description list (terms and definitions) |
| <pre> | Preformatted text (preserves whitespace) |
| <blockquote> | Block quotation |
| <figure> / <figcaption> | Self-contained content with caption |
| <hr> | Thematic break (horizontal rule) |
Lists are one of the most common block elements. Unordered lists use bullet points, ordered lists use numbers, and description lists pair terms with definitions:
| 1 | <!-- Unordered list --> |
| 2 | <ul> |
| 3 | <li>Apples</li> |
| 4 | <li>Bananas</li> |
| 5 | <li>Cherries</li> |
| 6 | </ul> |
| 7 | |
| 8 | <!-- Ordered list with custom numbering --> |
| 9 | <ol type="A" start="3"> |
| 10 | <li>First step</li> |
| 11 | <li>Second step</li> |
| 12 | <li>Third step</li> |
| 13 | </ol> |
| 14 | |
| 15 | <!-- Description list --> |
| 16 | <dl> |
| 17 | <dt>HTML</dt> |
| 18 | <dd>HyperText Markup Language — the standard language for creating web pages.</dd> |
| 19 | <dt>CSS</dt> |
| 20 | <dd>Cascading Style Sheets — used for styling HTML documents.</dd> |
| 21 | </dl> |
Figures group media with optional captions. Blockquotes represent quoted content from external sources:
| 1 | <figure> |
| 2 | <img src="diagram.png" alt="Architecture diagram showing client-server flow"> |
| 3 | <figcaption>Figure 1: System architecture overview</figcaption> |
| 4 | </figure> |
| 5 | |
| 6 | <blockquote cite="https://example.com/source"> |
| 7 | <p>The best way to predict the future is to invent it.</p> |
| 8 | <footer>— <cite>Alan Kay</cite></footer> |
| 9 | </blockquote> |
| 10 | |
| 11 | <pre><code>function hello() { |
| 12 | console.log("Hello, World!"); |
| 13 | }</code></pre> |
best practice
Inline elements flow within text without breaking the line. They are used to semantically mark up parts of a sentence or phrase.
| Element | Purpose | Example |
|---|---|---|
| <span> | Generic inline container | <span class="icon">★</span> |
| <a> | Hyperlink anchor | <a href="/">Home</a> |
| <strong> | Strong importance (bold) | <strong>Warning!</strong> |
| <em> | Emphasis (italic) | <em>really</em> important |
| <code> | Inline code fragment | <code>npm start</code> |
| <kbd> | Keyboard input | Press <kbd>Ctrl+S</kbd> |
| <samp> | Sample output | <samp>Error 404</samp> |
| <abbr> | Abbreviation / acronym | <abbr title="World Wide Web">WWW</abbr> |
| <cite> | Title of a work | <cite>The Art of War</cite> |
| <q> | Inline quotation | <q>Hello World</q> |
| <sub> / <sup> | Subscript / Superscript | H<sub>2</sub>O, 10<sup>3</sup> |
| <time> | Machine-readable date/time | <time datetime="2026-07-07">Today</time> |
| <mark> | Highlighted text | <mark>important</mark> |
| <bdo> | Bidirectional text override | <bdo dir="rtl">مرحبا</bdo> |
| <small> | Side comments / fine print | <small>Terms apply</small> |
| <dfn> | Defining instance of a term | <dfn>HTML</dfn> is a language |
| 1 | <p> |
| 2 | The <abbr title="HyperText Markup Language">HTML</abbr> |
| 3 | specification defines the <code><strong></code> element |
| 4 | for <strong>strong importance</strong> and the |
| 5 | <code><em></code> element for <em>emphasis</em>. |
| 6 | </p> |
| 7 | |
| 8 | <p> |
| 9 | Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy. |
| 10 | The terminal outputs: <samp>Copied 1 file</samp> |
| 11 | </p> |
| 12 | |
| 13 | <p> |
| 14 | Chemical formula: H<sub>2</sub>O |
| 15 | Mathematical expression: 10<sup>3</sup> = 1000 |
| 16 | </p> |
| 17 | |
| 18 | <p> |
| 19 | The <cite>HTML Living Standard</cite> is maintained by the |
| 20 | <abbr title="World Wide Web Consortium">W3C</abbr>. |
| 21 | Published <time datetime="2026-07-07">today</time>. |
| 22 | </p> |
| 23 | |
| 24 | <p> |
| 25 | Search results for "semantic <mark>HTML</mark>": |
| 26 | <q>Semantic HTML improves accessibility.</q> |
| 27 | </p> |
Embedded elements allow you to integrate external resources — images, video, audio, and other documents — into your web pages.
| Element | Purpose |
|---|---|
| <img> | Embeds an image |
| <picture> | Responsive image with multiple sources |
| <source> | Specifies multiple media sources |
| <video> | Embeds video content |
| <audio> | Embeds audio content |
| <iframe> | Embeds another HTML document |
| <canvas> | Drawing surface for scripts |
| <svg> | Scalable Vector Graphics |
Images are the most common embedded element. Always include descriptive alt text for accessibility. Use <picture> for responsive images that serve different formats based on screen size or capabilities:
| 1 | <!-- Basic image with alt text --> |
| 2 | <img |
| 3 | src="photo.jpg" |
| 4 | alt="A sunset over the ocean with silhouetted palm trees" |
| 5 | width="800" |
| 6 | height="600" |
| 7 | loading="lazy" |
| 8 | decoding="async" |
| 9 | /> |
| 10 | |
| 11 | <!-- Responsive image with picture element --> |
| 12 | <picture> |
| 13 | <source |
| 14 | srcset="photo.avif" |
| 15 | type="image/avif" |
| 16 | media="(min-width: 800px)" |
| 17 | /> |
| 18 | <source |
| 19 | srcset="photo.webp" |
| 20 | type="image/webp" |
| 21 | /> |
| 22 | <img |
| 23 | src="photo.jpg" |
| 24 | alt="Sunset over the ocean" |
| 25 | width="800" |
| 26 | height="600" |
| 27 | loading="lazy" |
| 28 | /> |
| 29 | </picture> |
Video and audio elements provide native playback controls without requiring third-party plugins:
| 1 | <!-- Video with controls --> |
| 2 | <video |
| 3 | controls |
| 4 | width="640" |
| 5 | height="360" |
| 6 | poster="thumbnail.jpg" |
| 7 | preload="metadata" |
| 8 | > |
| 9 | <source src="video.mp4" type="video/mp4" /> |
| 10 | <source src="video.webm" type="video/webm" /> |
| 11 | <track |
| 12 | kind="subtitles" |
| 13 | src="subtitles.vtt" |
| 14 | srclang="en" |
| 15 | label="English" |
| 16 | /> |
| 17 | <p>Your browser does not support video.</p> |
| 18 | </video> |
| 19 | |
| 20 | <!-- Audio with controls --> |
| 21 | <audio controls preload="none"> |
| 22 | <source src="podcast.mp3" type="audio/mpeg" /> |
| 23 | <source src="podcast.ogg" type="audio/ogg" /> |
| 24 | <p>Your browser does not support audio.</p> |
| 25 | </audio> |
| 26 | |
| 27 | <!-- Inline SVG --> |
| 28 | <svg viewBox="0 0 100 100" width="50" height="50"> |
| 29 | <circle cx="50" cy="50" r="40" fill="#00FF41" /> |
| 30 | <text x="50" y="55" text-anchor="middle" fill="#000" font-size="20">SVG</text> |
| 31 | </svg> |
warning
Live preview of an inline SVG and an image placeholder:
Forms are the primary way users interact with web applications. They collect and submit user input to servers for processing.
| Element | Purpose |
|---|---|
| <form> | Container for form controls |
| <input> | Versatile input control (many types) |
| <textarea> | Multi-line text input |
| <select> / <option> | Dropdown selection |
| <button> | Clickable button |
| <label> | Label for a form control |
| <fieldset> / <legend> | Groups related form controls with caption |
| <datalist> | Predefined options for autocomplete |
| <output> | Result of a calculation |
| <progress> | Progress indicator |
| <meter> | Scalar measurement gauge |
Input types define the kind of data expected. HTML5 introduced many specialized input types for better user experience on different devices:
| 1 | <form action="/submit" method="POST" novalidate> |
| 2 | <fieldset> |
| 3 | <legend>Personal Information</legend> |
| 4 | |
| 5 | <label for="name">Full Name</label> |
| 6 | <input |
| 7 | type="text" |
| 8 | id="name" |
| 9 | name="name" |
| 10 | required |
| 11 | minlength="2" |
| 12 | maxlength="100" |
| 13 | placeholder="Jane Doe" |
| 14 | /> |
| 15 | |
| 16 | <label for="email">Email Address</label> |
| 17 | <input |
| 18 | type="email" |
| 19 | id="email" |
| 20 | name="email" |
| 21 | required |
| 22 | placeholder="jane@example.com" |
| 23 | /> |
| 24 | |
| 25 | <label for="age">Age</label> |
| 26 | <input |
| 27 | type="number" |
| 28 | id="age" |
| 29 | name="age" |
| 30 | min="1" |
| 31 | max="150" |
| 32 | step="1" |
| 33 | /> |
| 34 | |
| 35 | <label for="dob">Date of Birth</label> |
| 36 | <input type="date" id="dob" name="dob" /> |
| 37 | |
| 38 | <label for="color">Favorite Color</label> |
| 39 | <input type="color" id="color" name="color" value="#00FF41" /> |
| 40 | |
| 41 | <label for="website">Website</label> |
| 42 | <input |
| 43 | type="url" |
| 44 | id="website" |
| 45 | name="website" |
| 46 | placeholder="https://example.com" |
| 47 | /> |
| 48 | </fieldset> |
| 49 | |
| 50 | <fieldset> |
| 51 | <legend>Additional Details</legend> |
| 52 | |
| 53 | <label for="bio">Biography</label> |
| 54 | <textarea |
| 55 | id="bio" |
| 56 | name="bio" |
| 57 | rows="4" |
| 58 | maxlength="500" |
| 59 | placeholder="Tell us about yourself..." |
| 60 | ></textarea> |
| 61 | |
| 62 | <label for="country">Country</label> |
| 63 | <select id="country" name="country"> |
| 64 | <optgroup label="North America"> |
| 65 | <option value="us">United States</option> |
| 66 | <option value="ca">Canada</option> |
| 67 | </optgroup> |
| 68 | <optgroup label="Europe"> |
| 69 | <option value="uk">United Kingdom</option> |
| 70 | <option value="de">Germany</option> |
| 71 | <option value="fr">France</option> |
| 72 | </optgroup> |
| 73 | </select> |
| 74 | |
| 75 | <label for="browser">Preferred Browser</label> |
| 76 | <input list="browsers" id="browser" name="browser" /> |
| 77 | <datalist id="browsers"> |
| 78 | <option value="Chrome" /> |
| 79 | <option value="Firefox" /> |
| 80 | <option value="Safari" /> |
| 81 | <option value="Edge" /> |
| 82 | </datalist> |
| 83 | </fieldset> |
| 84 | |
| 85 | <button type="submit">Submit Form</button> |
| 86 | <button type="reset">Reset</button> |
| 87 | </form> |
Progress and meter elements provide native visual indicators:
| 1 | <!-- Progress bar --> |
| 2 | <label for="upload">Upload Progress:</label> |
| 3 | <progress id="upload" value="65" max="100">65%</progress> |
| 4 | |
| 5 | <!-- Meter gauge --> |
| 6 | <label for="disk">Disk Usage:</label> |
| 7 | <meter |
| 8 | id="disk" |
| 9 | value="0.72" |
| 10 | min="0" |
| 11 | max="1" |
| 12 | low="0.6" |
| 13 | high="0.85" |
| 14 | optimum="0.3" |
| 15 | >72%</meter> |
| 16 | |
| 17 | <!-- Output for calculations --> |
| 18 | <form oninput="result.value = parseInt(a.value) + parseInt(b.value)"> |
| 19 | <input type="range" id="a" value="50" /> |
| 20 | <input type="range" id="b" value="50" /> |
| 21 | <output name="result" for="a b">100</output> |
| 22 | </form> |
pro tip
Live preview of a compact form:
Interactive elements allow users to show/hide content, open dialogs, and interact with custom controls using native HTML behavior.
| Element | Purpose |
|---|---|
| <details> / <summary> | Disclosure widget (expand/collapse) |
| <dialog> | Modal or non-modal dialog |
| <menu> | Semantic menu container (toolbar, popup) |
The <details> element creates an expandable disclosure widget. The <summary> provides the visible label:
| 1 | <details> |
| 2 | <summary>What is semantic HTML?</summary> |
| 3 | <p> |
| 4 | Semantic HTML means using elements that convey meaning |
| 5 | about their content. For example, <code><nav></code> |
| 6 | for navigation, <code><article></code> for main content, |
| 7 | and <code><footer></code> for footer information. |
| 8 | </p> |
| 9 | </details> |
| 10 | |
| 11 | <details open> |
| 12 | <summary>Why use details?</summary> |
| 13 | <ul> |
| 14 | <li>No JavaScript required for basic toggle behavior</li> |
| 15 | <li>Built-in keyboard accessibility</li> |
| 16 | <li>Screen reader friendly</li> |
| 17 | <li>Can be styled with CSS</li> |
| 18 | </ul> |
| 19 | </details> |
The <dialog> element creates modal or non-modal dialogs. Use the .showModal() method for modals:
| 1 | <!-- Modal dialog --> |
| 2 | <dialog id="myDialog"> |
| 3 | <form method="dialog"> |
| 4 | <h2>Confirm Action</h2> |
| 5 | <p>Are you sure you want to delete this item?</p> |
| 6 | <menu> |
| 7 | <button value="cancel">Cancel</button> |
| 8 | <button value="confirm" autofocus>Delete</button> |
| 9 | </menu> |
| 10 | </form> |
| 11 | </dialog> |
| 12 | |
| 13 | <button id="openDialog">Open Dialog</button> |
| 14 | |
| 15 | <script> |
| 16 | const dialog = document.getElementById('myDialog'); |
| 17 | const openBtn = document.getElementById('openDialog'); |
| 18 | |
| 19 | openBtn.addEventListener('click', () => { |
| 20 | dialog.showModal(); |
| 21 | }); |
| 22 | |
| 23 | dialog.addEventListener('close', () => { |
| 24 | console.log('Dialog closed with:', dialog.returnValue); |
| 25 | }); |
| 26 | </script> |
Live preview of interactive elements:
HTML tables display tabular data in rows and columns. Use the semantic table elements to structure data, not for layout purposes.
| Element | Purpose |
|---|---|
| <table> | Table container |
| <caption> | Table title / description |
| <thead> | Header rows group |
| <tbody> | Body rows group |
| <tfoot> | Footer rows group |
| <tr> | Table row |
| <th> | Header cell (scope attribute for accessibility) |
| <td> | Data cell |
| <colgroup> / <col> | Column-level styling |
| 1 | <table> |
| 2 | <caption> |
| 3 | Monthly savings report for Q1 2026 |
| 4 | </caption> |
| 5 | <colgroup> |
| 6 | <col style="background: #f5f5f5;" /> |
| 7 | <col /> |
| 8 | <col style="background: #f5f5f5;" /> |
| 9 | </colgroup> |
| 10 | <thead> |
| 11 | <tr> |
| 12 | <th scope="col">Month</th> |
| 13 | <th scope="col">Income</th> |
| 14 | <th scope="col">Expenses</th> |
| 15 | <th scope="col">Savings</th> |
| 16 | </tr> |
| 17 | </thead> |
| 18 | <tbody> |
| 19 | <tr> |
| 20 | <th scope="row">January</th> |
| 21 | <td>$5,000</td> |
| 22 | <td>$3,200</td> |
| 23 | <td>$1,800</td> |
| 24 | </tr> |
| 25 | <tr> |
| 26 | <th scope="row">February</th> |
| 27 | <td>$5,200</td> |
| 28 | <td>$3,500</td> |
| 29 | <td>$1,700</td> |
| 30 | </tr> |
| 31 | <tr> |
| 32 | <th scope="row">March</th> |
| 33 | <td>$5,400</td> |
| 34 | <td>$3,100</td> |
| 35 | <td>$2,300</td> |
| 36 | </tr> |
| 37 | </tbody> |
| 38 | <tfoot> |
| 39 | <tr> |
| 40 | <th scope="row">Total</th> |
| 41 | <td>$15,600</td> |
| 42 | <td>$9,800</td> |
| 43 | <td>$5,800</td> |
| 44 | </tr> |
| 45 | </tfoot> |
| 46 | </table> |
best practice
Live preview of a styled table:
Scripting elements add dynamic behavior, content templates, and fallback content for script-disabled environments.
| Element | Purpose |
|---|---|
| <script> | Embeds or references JavaScript code |
| <noscript> | Fallback content for users with JS disabled |
| <canvas> | Drawing surface rendered via JavaScript |
| <template> | Declarative fragment for later use |
| <slot> | Placeholder in Web Components |
Use defer or async attributes on scripts to control loading behavior. The <template> element holds HTML that is not rendered until cloned by JavaScript:
| 1 | <!-- External script with defer --> |
| 2 | <script src="app.js" defer></script> |
| 3 | |
| 4 | <!-- Inline script --> |
| 5 | <script> |
| 6 | document.addEventListener('DOMContentLoaded', () => { |
| 7 | console.log('Page loaded'); |
| 8 | }); |
| 9 | </script> |
| 10 | |
| 11 | <!-- Module script --> |
| 12 | <script type="module"> |
| 13 | import { greet } from './utils.js'; |
| 14 | greet('World'); |
| 15 | </script> |
| 16 | |
| 17 | <!-- Noscript fallback --> |
| 18 | <noscript> |
| 19 | <div class="alert"> |
| 20 | JavaScript is disabled. Please enable it for full functionality. |
| 21 | </div> |
| 22 | </noscript> |
| 23 | |
| 24 | <!-- Template element --> |
| 25 | <template id="card-template"> |
| 26 | <article class="card"> |
| 27 | <img src="" alt="" /> |
| 28 | <h3></h3> |
| 29 | <p></p> |
| 30 | </article> |
| 31 | </template> |
| 32 | |
| 33 | <script> |
| 34 | const template = document.getElementById('card-template'); |
| 35 | const clone = template.content.cloneNode(true); |
| 36 | clone.querySelector('h3').textContent = 'Dynamic Card'; |
| 37 | document.body.appendChild(clone); |
| 38 | </script> |
Canvas provides a bitmap drawing surface controlled by JavaScript:
| 1 | <canvas id="myCanvas" width="300" height="200"> |
| 2 | Your browser does not support the canvas element. |
| 3 | </canvas> |
| 4 | |
| 5 | <script> |
| 6 | const canvas = document.getElementById('myCanvas'); |
| 7 | const ctx = canvas.getContext('2d'); |
| 8 | |
| 9 | // Draw a gradient rectangle |
| 10 | const gradient = ctx.createLinearGradient(0, 0, 300, 200); |
| 11 | gradient.addColorStop(0, '#00FF41'); |
| 12 | gradient.addColorStop(1, '#00FF41'); |
| 13 | |
| 14 | ctx.fillStyle = gradient; |
| 15 | ctx.fillRect(10, 10, 280, 180); |
| 16 | |
| 17 | // Draw text |
| 18 | ctx.fillStyle = '#000'; |
| 19 | ctx.font = 'bold 20px system-ui'; |
| 20 | ctx.textAlign = 'center'; |
| 21 | ctx.fillText('Canvas Drawing', 150, 110); |
| 22 | </script> |
warning
Global attributes can be used on any HTML element. They provide common configuration, identification, and behavior across all elements.
| Attribute | Description | Example |
|---|---|---|
| class | Space-separated list of CSS class names | class="btn primary" |
| id | Unique identifier for the element | id="header" |
| style | Inline CSS declarations | style="color: red;" |
| title | Advisory information (tooltip) | title="More info" |
| lang | Language of the element's content | lang="en" |
| dir | Text direction (ltr, rtl, auto) | dir="rtl" |
| hidden | Element is not rendered (until="…" variant) | hidden |
| tabindex | Tab order of focusable elements | tabindex="0" |
| role | ARIA role for accessibility | role="button" |
| aria-* | ARIA states and properties | aria-label="Close" |
| data-* | Custom data attributes | data-user-id="123" |
| slot | Assigns to a named slot in shadow DOM | slot="header" |
| spellcheck | Enable/disable spell checking | spellcheck="false" |
| contenteditable | Element is editable by the user | contenteditable="true" |
| draggable | Element can be dragged | draggable="true" |
| translate | Whether content should be translated | translate="no" |
| autocapitalize | Automatic capitalization behavior | autocapitalize="words" |
| inputmode | Hints at the type of data input | inputmode="numeric" |
| is | Specifies a custom element name | is="my-button" |
| itemscope / itemtype | Microdata for structured data | itemscope itemtype="…" |
| 1 | <article |
| 2 | class="post featured" |
| 3 | id="post-42" |
| 4 | lang="en" |
| 5 | data-author="jane_doe" |
| 6 | data-published="2026-07-07" |
| 7 | itemscope |
| 8 | itemtype="https://schema.org/Article" |
| 9 | > |
| 10 | <h2 |
| 11 | contenteditable="true" |
| 12 | spellcheck="true" |
| 13 | title="Click to edit the title" |
| 14 | > |
| 15 | Editable Article Title |
| 16 | </h2> |
| 17 | <p |
| 18 | dir="ltr" |
| 19 | tabindex="0" |
| 20 | role="region" |
| 21 | aria-label="Article summary" |
| 22 | translate="no" |
| 23 | > |
| 24 | This section should not be translated. |
| 25 | </p> |
| 26 | <button |
| 27 | aria-label="Delete article" |
| 28 | aria-pressed="false" |
| 29 | draggable="true" |
| 30 | hidden |
| 31 | > |
| 32 | Delete |
| 33 | </button> |
| 34 | </article> |
Void elements (also called self-closing or empty elements) cannot have child content or a closing tag. They represent a single standalone entity in the document.
| Element | Description | Key Attributes |
|---|---|---|
| <area> | Clickable area in an image map | shape, coords, href |
| <base> | Base URL for relative URLs | href, target |
| <br> | Line break within text | — |
| <col> | Defines a column within <colgroup> | span, style |
| <embed> | Embeds external content or plugin | src, type, width, height |
| <hr> | Thematic break / horizontal rule | — |
| <img> | Image embed | src, alt, width, height |
| <input> | Form input control | type, name, value |
| <link> | External resource link (CSS, favicon) | rel, href |
| <meta> | Document metadata | charset, name, content |
| <source> | Media source for <picture>, <video>, <audio> | src, type, srcset |
| <track> | Text track for media (subtitles, captions) | src, kind, srclang |
| <wbr> | Word break opportunity | — |
| 1 | <head> |
| 2 | <meta charset="UTF-8" /> |
| 3 | <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
| 4 | <base href="https://example.com/" /> |
| 5 | <link rel="stylesheet" href="styles.css" /> |
| 6 | <link rel="icon" href="favicon.ico" type="image/x-icon" /> |
| 7 | </head> |
| 8 | <body> |
| 9 | <h1>Welcome</h1> |
| 10 | <p> |
| 11 | First line.<br /> |
| 12 | Second line after a break. |
| 13 | </p> |
| 14 | <hr /> |
| 15 | <img src="photo.jpg" alt="Photo" width="400" height="300" /> |
| 16 | <input type="text" name="search" placeholder="Search..." /> |
| 17 | <picture> |
| 18 | <source srcset="image.avif" type="image/avif" /> |
| 19 | <img src="image.jpg" alt="Fallback" /> |
| 20 | </picture> |
| 21 | </body> |
info
Custom Elements allow you to define your own HTML elements with custom behavior. They are part of the Web Components specification and work across all modern browsers.
Custom element names must contain a hyphen (my-element) to differentiate them from built-in elements. You define behavior by extending the HTMLElement class:
| 1 | <!-- Usage in HTML --> |
| 2 | <my-counter initial="5" min="0" max="10"></my-counter> |
| 3 | <my-counter initial="0" min="0" max="100"></my-counter> |
| 4 | |
| 5 | <my-tooltip text="Save your work"> |
| 6 | <button>Save</button> |
| 7 | </my-tooltip> |
| 8 | |
| 9 | <script> |
| 10 | // Define a custom element |
| 11 | class MyCounter extends HTMLElement { |
| 12 | constructor() { |
| 13 | super(); |
| 14 | this._count = parseInt(this.getAttribute('initial') || '0'); |
| 15 | this._min = parseInt(this.getAttribute('min') || '0'); |
| 16 | this._max = parseInt(this.getAttribute('max') || '100'); |
| 17 | |
| 18 | // Create shadow DOM |
| 19 | this.attachShadow({ mode: 'open' }); |
| 20 | this.shadowRoot.innerHTML = ` |
| 21 | <style> |
| 22 | :host { display: inline-flex; align-items: center; gap: 8px; |
| 23 | font-family: system-ui; } |
| 24 | button { width: 32px; height: 32px; border-radius: 4px; |
| 25 | border: 1px solid #ccc; background: #fff; |
| 26 | cursor: pointer; font-size: 18px; } |
| 27 | span { min-width: 24px; text-align: center; |
| 28 | font-variant-numeric: tabular-nums; } |
| 29 | </style> |
| 30 | <button id="dec">−</button> |
| 31 | <span id="value">${this._count}</span> |
| 32 | <button id="inc">+</button> |
| 33 | `; |
| 34 | } |
| 35 | |
| 36 | connectedCallback() { |
| 37 | this.shadowRoot.getElementById('inc') |
| 38 | .addEventListener('click', () => this.increment()); |
| 39 | this.shadowRoot.getElementById('dec') |
| 40 | .addEventListener('click', () => this.decrement()); |
| 41 | } |
| 42 | |
| 43 | increment() { |
| 44 | if (this._count < this._max) { |
| 45 | this._count++; |
| 46 | this.shadowRoot.getElementById('value').textContent = this._count; |
| 47 | } |
| 48 | } |
| 49 | |
| 50 | decrement() { |
| 51 | if (this._count > this._min) { |
| 52 | this._count--; |
| 53 | this.shadowRoot.getElementById('value').textContent = this._count; |
| 54 | } |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | // Register the custom element |
| 59 | customElements.define('my-counter', MyCounter); |
| 60 | |
| 61 | // Custom element extending built-in (only supported in some browsers) |
| 62 | class FancyButton extends HTMLButtonElement { |
| 63 | constructor() { |
| 64 | super(); |
| 65 | this.style.borderRadius = '8px'; |
| 66 | this.style.padding = '12px 24px'; |
| 67 | } |
| 68 | } |
| 69 | customElements.define('fancy-button', FancyButton, { extends: 'button' }); |
| 70 | </script> |
Custom Elements lifecycle callbacks:
| 1 | class MyElement extends HTMLElement { |
| 2 | // Called when element is created |
| 3 | constructor() { super(); } |
| 4 | |
| 5 | // Called when element is added to the DOM |
| 6 | connectedCallback() { } |
| 7 | |
| 8 | // Called when element is removed from the DOM |
| 9 | disconnectedCallback() { } |
| 10 | |
| 11 | // Called when element is moved to a new document |
| 12 | adoptedCallback() { } |
| 13 | |
| 14 | // Called when observed attributes change |
| 15 | attributeChangedCallback(name, oldVal, newVal) { } |
| 16 | |
| 17 | // Specify which attributes to observe |
| 18 | static get observedAttributes() { |
| 19 | return ['disabled', 'value']; |
| 20 | } |
| 21 | } |
Semantic HTML Checklist
Accessibility (A11Y)
Performance
best practice
Example of semantic vs non-semantic markup:
| 1 | <!-- ❌ Non-semantic --> |
| 2 | <div class="header"> |
| 3 | <div class="nav"> |
| 4 | <span onclick="navigate('/')">Home</span> |
| 5 | <span onclick="navigate('/about')">About</span> |
| 6 | </div> |
| 7 | </div> |
| 8 | <div class="content"> |
| 9 | <div class="section"> |
| 10 | <div class="title">Welcome</div> |
| 11 | <div class="text">Content goes here.</div> |
| 12 | </div> |
| 13 | </div> |
| 14 | |
| 15 | <!-- ✅ Semantic --> |
| 16 | <header> |
| 17 | <nav> |
| 18 | <a href="/">Home</a> |
| 19 | <a href="/about">About</a> |
| 20 | </nav> |
| 21 | </header> |
| 22 | <main> |
| 23 | <section> |
| 24 | <h1>Welcome</h1> |
| 25 | <p>Content goes here.</p> |
| 26 | </section> |
| 27 | </main> |
pro tip