Document Structure
Basic Page Template
Every HTML5 document starts with <!DOCTYPE html>, followed by <html> with a lang attribute for accessibility and SEO. The <head> contains metadata (charset, viewport, title), and <body> holds visible content. The viewport meta tag ensures proper rendering on mobile devices.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Web Page</title>
</head>
<body>
<h1>Hello, World!</h1>
<p>Welcome to my page.</p>
</body>
</html>Head Section & Metadata
The <head> element contains machine-readable metadata. charset declares character encoding (UTF-8 covers nearly all characters). The description meta tag is crucial for SEO — search engines display it in results. Use defer on scripts to load them after HTML parsing.
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Page Title - Site Name</title>
<meta name="description" content="A brief description for SEO">
<meta name="author" content="John Doe">
<link rel="stylesheet" href="style.css">
<script src="app.js" defer></script>
</head>Comments & Conditional Comments
HTML comments start with <!-- and end with -->. They are not displayed in the browser but are visible in the page source. Comments are useful for leaving notes, marking sections (TODO, FIXME), and documentation. Conditional comments (IE-only) are obsolete but may appear in legacy code.
<!-- This is a single-line comment -->
<!--
This is a
multi-line comment
-->
<!-- TODO: Add form validation -->
<!-- FIXME: Fix layout on mobile -->
<!--[if lt IE 9]>
<script src="html5shiv.js"></script>
<![endif]-->Headings Hierarchy
HTML provides six heading levels, h1 (highest) to h6 (lowest). Use them in hierarchical order without skipping levels. Search engines use headings to understand page structure. Best practice: one h1 per page, followed by h2 for major sections, h3 for subsections, etc.
<h1>Main Page Title</h1>
<h2>Major Section</h2>
<h3>Subsection</h3>
<h4>Sub-subsection</h4>
<h5>Minor Heading</h5>
<h6>Lowest Level Heading</h6>
<!-- Use only one h1 per page for SEO -->
<h1>Blog Post Title</h1>
<h2>Introduction</h2>
<h3>Background</h3>Div & Span Containers
<div> is a block-level container used to group elements and apply styles. <span> is an inline container for styling small portions of text. Both are generic elements with no semantic meaning — prefer semantic tags (header, nav, main, article) when appropriate for better accessibility and SEO.
<div class="container">
<div id="header" class="header">
<span class="logo">MySite</span>
<span class="tagline">Best Content</span>
</div>
<div class="content">
<p>Main <span class="highlight">content</span> here.</p>
</div>
</div>Text Formatting
Bold, Italic & Emphasis
<strong> indicates important text (screen readers emphasize it), while <b> is purely visual bold. Similarly, <em> indicates stress emphasis (semantic), while <i> is visual italic. Prefer <strong> and <em> for accessibility. <b> and <i> are acceptable for stylistic purposes without semantic importance.
<p>This is <strong>important</strong> text.</p>
<p>This is <b>bold</b> text.</p>
<p>This is <em>emphasized</em> text.</p>
<p>This is <i>italic</i> text.</p>
<p><strong>Warning:</strong> Do not touch!</p>Mark, Delete & Insert
<mark> highlights text as relevant (like a search highlight). <del> marks deleted text, <ins> marks inserted text — useful for showing edits. <s> strikes through outdated info. <small> is for side comments or copyright. <u> should be used carefully as it resembles links.
<p>Please <mark>highlight this</mark> word.</p>
<p>The price is <del>$99</del> <ins>$79</ins> now.</p>
<p><s>Old information</s> is struck through.</p>
<p>Use <u>underline</u> sparingly.</p>
<p>Small <small>print</small> for side comments.</p>Superscript & Subscript
<sub> creates subscript text (below baseline) for chemical formulas (H2O) and mathematical variables. <sup> creates superscript text (above baseline) for exponents (x²), ordinal numbers (1st), and footnotes. Both are inline elements that adjust vertical position and font size.
<p>H<sub>2</sub>O is water.</p>
<p>E = mc<sup>2</sup> is Einstein's equation.</p>
<p>1st<sup>st</sup> January 2024</p>
<p>CO<sub>2</sub> emissions are rising.</p>
<p>x<sup>2</sup> + y<sup>2</sup> = r<sup>2</sup></p>Quotations & Citations
<blockquote> is for block-level quotations (indented, multi-line). <q> is for inline short quotations (adds quotation marks automatically). The cite attribute provides the source URL. <cite> tags the title of a work or author name. These elements improve semantic markup for quoted content.
<blockquote cite="https://example.com/source">
<p>The best way to predict the future is to invent it.</p>
<footer>— <cite>Alan Kay</cite></footer>
</blockquote>
<p>As <q cite="https://example.com">someone once said</q>,
knowledge is power.</p>Code & Preformatted Text
<code> marks inline code snippets (monospace font). <pre> preserves whitespace and line breaks for preformatted text. Combining <pre><code> displays code blocks with proper indentation. <kbd> represents keyboard input. <samp> is for program output, and <var> for variables in mathematical expressions.
<p>Use the <code>console.log()</code> function.</p>
<p>Press <kbd>Ctrl</kbd> + <kbd>C</kbd> to copy.</p>
<pre><code>function hello() {
console.log("Hello, World!");
return true;
}</code></pre>Line Breaks & Horizontal Rules
<br> creates a line break within text (use sparingly, prefer CSS). <hr> creates a horizontal rule representing a thematic break between sections. <wbr> suggests a line break opportunity for long words or URLs, allowing the browser to break at optimal points. Avoid using <br> for spacing — use CSS margins instead.
<p>First line<br>Second line<br>Third line</p>
<p>Paragraph one.</p>
<hr>
<p>Paragraph two after a thematic break.</p>
<wbr><!-- word break opportunity for long URLs -->
https://example.com/<wbr>very<wbr>long<wbr>urlLinks & Navigation
Basic Links
The <a> element creates hyperlinks. href specifies the destination URL. External links use full URLs (https://...), internal links use absolute paths (/about), and relative links use file paths (../index.html). Anchor links (#section) jump to elements with matching id attributes on the same page.
<!-- External link -->
<a href="https://www.example.com">Visit Example</a>
<!-- Internal link (same site) -->
<a href="/about">About Us</a>
<!-- Relative link -->
<a href="../index.html">Home</a>
<!-- Anchor link (same page) -->
<a href="#section1">Jump to Section 1</a>Link Targets & Relations
target='_blank' opens links in a new tab — always pair with rel='noopener noreferrer' for security (prevents tabnabbing attacks). The download attribute triggers file download. mailto: opens email client, tel: initiates phone calls on mobile. The rel attribute defines the relationship (noopener, noreferrer, nofollow for SEO).
<!-- Open in new tab -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
Open in new tab
</a>
<!-- Download link -->
<a href="report.pdf" download>Download PDF</a>
<!-- Email link -->
<a href="mailto:[email protected]?subject=Hello">Email us</a>
<!-- Phone link -->
<a href="tel:+1234567890">Call us</a>Anchor Targets
Any element with an id attribute can be an anchor target. Links with href='#id' scroll to that element. This is essential for table of contents, footnotes, and single-page navigation. Add CSS 'scroll-behavior: smooth' on html for animated scrolling. Use 'scroll-margin-top' to offset fixed headers.
<h2 id="introduction">Introduction</h2>
<p>Content here...</p>
<h2 id="features">Features</h2>
<p>Content here...</p>
<!-- Navigation menu linking to sections -->
<nav>
<a href="#introduction">Intro</a>
<a href="#features">Features</a>
</nav>
<!-- Smooth scroll via CSS: scroll-behavior: smooth -->Image & Figure Links
Wrapping an <img> inside an <a> makes the image clickable. The title attribute creates a tooltip on hover (improves UX but not heavily weighted for SEO). For accessibility, ensure linked images have meaningful alt text. Use figure/figcaption for images with captions that may also be links.
<!-- Image as a link -->
<a href="https://example.com">
<img src="banner.jpg" alt="Click to visit">
</a>
<!-- Thumbnail linking to full image -->
<a href="full-size.jpg">
<img src="thumbnail.jpg" alt="View full image">
</a>
<!-- Link with title tooltip -->
<a href="https://example.com" title="Visit Example">
Hover for tooltip
</a>Navigation Menus
<nav> identifies navigation sections for screen readers and SEO. Use aria-current='page' to indicate the current page. Breadcrumbs help users understand their location in the site hierarchy. Lists inside nav provide semantic structure. Keep navigation consistent across pages for usability.
<nav>
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<!-- Breadcrumb navigation -->
<nav aria-label="Breadcrumb">
<ol>
<li><a href="/">Home</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/blog/post" aria-current="page">Post</a></li>
</ol>
</nav>Images & Multimedia
Images
Always include alt text for accessibility and SEO — it describes the image for screen readers and when the image fails to load. loading='lazy' defers offscreen image loading, improving page speed. srcset and sizes enable responsive images — the browser selects the best image based on device resolution and viewport. Always specify width and height to prevent layout shift.
<!-- Basic image -->
<img src="photo.jpg" alt="A sunset over mountains" width="800" height="600">
<!-- Image with lazy loading -->
<img src="hero.jpg" alt="Hero banner" loading="lazy" decoding="async">
<!-- Responsive image with srcset -->
<img
src="medium.jpg"
srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, 800px"
alt="Responsive photo"
>Picture Element
<picture> provides art direction and format fallback. <source> elements offer alternatives — the browser picks the first supported format (WebP/AVIF are smaller). The media attribute enables different images for different screen sizes. The final <img> is the fallback and must always be present. This is superior to srcset for serving entirely different images.
<picture>
<source srcset="webp-image.webp" type="image/webp">
<source srcset="avif-image.avif" type="image/avif">
<source srcset="wide.jpg" media="(min-width: 800px)">
<img src="fallback.jpg" alt="Art-directed image">
</picture>Audio
<audio> embeds sound content. controls adds play/pause/volume controls. Multiple <source> elements provide format fallbacks (MP3 is most widely supported). autoplay is restricted by browsers — muted autoplay is generally allowed. loop repeats the audio. The text inside is shown if audio is unsupported. Always provide controls for user experience.
<audio controls>
<source src="song.mp3" type="audio/mpeg">
<source src="song.ogg" type="audio/ogg">
Your browser does not support the audio element.
</audio>
<!-- Autoplay (muted required in most browsers) -->
<audio autoplay muted loop>
<source src="background.mp3" type="audio/mpeg">
</audio>Video
<video> embeds video with controls for play, pause, volume, and fullscreen. poster sets a thumbnail before playback. Multiple sources provide format fallback (MP4/H.264 is most compatible). <track> adds subtitles, captions, or descriptions for accessibility. playsinline prevents forced fullscreen on iOS. Always include controls unless autoplaying muted.
<video controls width="640" height="360" poster="thumbnail.jpg">
<source src="movie.mp4" type="video/mp4">
<source src="movie.webm" type="video/webm">
<track kind="subtitles" src="subs.vtt" srclang="en" label="English">
Your browser does not support video.
</video>
<!-- Autoplay muted loop (common for backgrounds) -->
<video autoplay muted loop playsinline>
<source src="bg.mp4" type="video/mp4">
</video>Figure & Figcaption
<figure> groups self-contained content like images, diagrams, code listings, or quotations with their caption. <figcaption> provides a caption or legend. This semantic grouping improves accessibility — screen readers announce the relationship. Figures can be referenced from text ('see Figure 1') and moved in the layout without losing context.
<figure>
<img src="chart.png" alt="Quarterly sales chart showing 20% growth">
<figcaption>Figure 1: Q4 2024 sales growth by region.</figcaption>
</figure>
<figure>
<blockquote>
<p>The best way to predict the future is to invent it.</p>
</blockquote>
<figcaption>— Alan Kay, 1971</figcaption>
</figure>Iframe Embeds
<iframe> embeds external content (videos, maps, other pages). Always include a title attribute for accessibility. loading='lazy' improves performance for offscreen iframes. The allow attribute specifies feature policies. sandbox attribute restricts the iframe's capabilities for security. Be cautious — iframes can impact performance and security.
<!-- Embed a YouTube video -->
<iframe
src="https://www.youtube.com/embed/dQw4w9WgXcQ"
width="560" height="315"
title="YouTube video"
frameborder="0"
allow="accelerometer; autoplay; encrypted-media"
allowfullscreen>
</iframe>
<!-- Embed a map -->
<iframe
src="https://maps.google.com/maps?q=Paris&output=embed"
title="Map of Paris" loading="lazy">
</iframe>Lists
Unordered Lists
<ul> creates an unordered (bulleted) list. Each item is wrapped in <li>. The list-style-type CSS property changes bullet style (disc, circle, square, none). Unordered lists are for items where order doesn't matter. Use list-style: none and custom styling for navigation menus and feature lists.
<ul>
<li>Apple</li>
<li>Banana</li>
<li>Cherry</li>
</ul>
<!-- Custom bullet style via CSS -->
<ul style="list-style-type: square;">
<li>First item</li>
<li>Second item</li>
</ul>Ordered Lists
<ol> creates an ordered (numbered) list for sequential items. The start attribute sets the starting number. reversed displays items in descending order. The type attribute changes numbering style (1, A, a, I, i). Use <ol> for steps, rankings, or any sequence where order matters.
<ol>
<li>First step</li>
<li>Second step</li>
<li>Third step</li>
</ol>
<!-- Start from a specific number -->
<ol start="5">
<li>Fifth item</li>
<li>Sixth item</li>
</ol>
<!-- Reverse order -->
<ol reversed>
<li>Countdown 3</li>
<li>Countdown 2</li>
<li>Countdown 1</li>
</ol>Description Lists
<dl> creates a description list pairing terms (<dt>) with descriptions (<dd>). This is ideal for glossaries, FAQ pages, and term-definition pairs. Multiple <dd> can describe one <dt>, and vice versa. Description lists provide semantic meaning that definition tables lack, improving accessibility.
<dl>
<dt>HTML</dt>
<dd>HyperText Markup Language</dd>
<dt>CSS</dt>
<dd>Cascading Style Sheets</dd>
<dt>JavaScript</dt>
<dd>A programming language for the web</dd>
</dl>Nested Lists
Lists can be nested by placing a <ul> or <ol> inside an <li>. Browsers automatically indent nested lists and may change bullet styles. Nesting creates hierarchical structures like table of contents, file trees, or multi-level menus. Keep nesting reasonable (2-3 levels) for usability. Deeply nested lists become hard to read.
<ul>
<li>Fruits
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
</li>
<li>Vegetables
<ol>
<li>Carrot</li>
<li>Spinach</li>
</ol>
</li>
</ul>List with Links (Navigation)
Lists of links inside <nav> create semantic navigation menus. Nested <ul> elements form dropdown submenus. This structure is accessible to screen readers and easily styled with CSS. Use aria attributes (aria-expanded, aria-haspopup) for interactive dropdowns. List-based navigation is the standard pattern for accessible menus.
<nav>
<ul class="menu">
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a>
<ul class="submenu">
<li><a href="/products/a">Product A</a></li>
<li><a href="/products/b">Product B</a></li>
</ul>
</li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>Tables
Basic Table
<table> creates tabular data. <thead> groups header rows, <tbody> groups body rows, <tfoot> groups footer rows. <tr> is a table row, <th> is a header cell (bold, centered), <td> is a data cell. Using thead/tbody/tfoot improves accessibility and enables better styling. Tables should be used for data, not layout.
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
<th>City</th>
</tr>
</thead>
<tbody>
<tr>
<td>Alice</td>
<td>30</td>
<td>New York</td>
</tr>
<tr>
<td>Bob</td>
<td>25</td>
<td>London</td>
</tr>
</tbody>
</table>Spanning Cells
colspan makes a cell span multiple columns (merge horizontally). rowspan makes a cell span multiple rows (merge vertically). These attributes create complex table layouts like headers that group multiple columns. Be careful with spanning — it can make tables harder to parse for screen readers. Always test accessibility.
<table border="1">
<tr>
<th colspan="2">Name</th>
<th rowspan="2">Age</th>
</tr>
<tr>
<th>First</th>
<th>Last</th>
</tr>
<tr>
<td>Alice</td>
<td>Smith</td>
<td>30</td>
</tr>
</table>Table Caption & Scope
<caption> provides a title for the table, improving accessibility. The scope attribute on <th> tells screen readers whether a header applies to a column (scope='col') or row (scope='row'). This is essential for complex tables. For even more complex tables, use id and headers attributes to explicitly associate cells with their headers.
<table>
<caption>Employee Salary Report 2024</caption>
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Department</th>
<th scope="col">Salary</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Alice</th>
<td>Engineering</td>
<td>$95,000</td>
</tr>
</tbody>
</table>Column Groups
<colgroup> groups columns for styling. <col> elements within it apply styles (especially width) to specific columns. This is more efficient than setting width on every cell. Column groups also support the span attribute to style multiple columns at once. Use this for consistent column widths across all rows.
<table>
<colgroup>
<col style="width: 30%;">
<col style="width: 50%;">
<col style="width: 20%;">
</colgroup>
<tr>
<th>Product</th>
<th>Description</th>
<th>Price</th>
</tr>
<tr>
<td>Laptop</td>
<td>15-inch laptop</td>
<td>$999</td>
</tr>
</table>Styling Tables
border-collapse: collapse merges adjacent borders into one. Use :nth-child(even) for zebra-striped rows (improves readability). :hover highlights rows on mouseover. th styling differentiates headers. Keep table styling clean and readable — avoid excessive borders. Responsive tables may need overflow-x: auto on a wrapper for small screens.
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f4f4f4; }
tr:nth-child(even) { background-color: #f9f9f9; }
tr:hover { background-color: #e0e0e0; }
</style>
<table>
<tr><th>Name</th><th>Score</th></tr>
<tr><td>Alice</td><td>95</td></tr>
<tr><td>Bob</td><td>87</td></tr>
</table>Forms & Input
Form Structure
<form> wraps input controls. action specifies the submission URL, method is GET (visible in URL) or POST (hidden). enctype='multipart/form-data' is required for file uploads. <fieldset> groups related fields, <legend> provides a caption. Always associate <label> with inputs using for/id matching for accessibility.
<form action="/submit" method="POST" enctype="multipart/form-data">
<fieldset>
<legend>Personal Information</legend>
<label for="name">Name:</label>
<input type="text" id="name" name="name" required>
<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
</fieldset>
<button type="submit">Submit</button>
</form>Input Types
HTML5 introduced many input types with built-in validation and mobile-friendly keyboards. type='email' validates email format. type='number' provides spinners. type='date' and 'time' show native pickers. type='color' opens a color picker. type='range' creates a slider. type='file' with accept restricts file types. type='hidden' stores data not shown to users.
<input type="text" name="username" placeholder="Username">
<input type="email" name="email" required>
<input type="password" name="pwd" minlength="8">
<input type="number" name="age" min="0" max="120" step="1">
<input type="date" name="birthday">
<input type="time" name="appointment">
<input type="color" name="favcolor" value="#ff0000">
<input type="range" name="volume" min="0" max="100">
<input type="file" name="upload" accept="image/*">
<input type="hidden" name="token" value="abc123">Select & Textarea
<select> creates a dropdown. <optgroup> groups related options with a label. <option> defines choices; the selected attribute pre-selects one. <textarea> is for multi-line text input. rows and cols set the visible size. maxlength limits character count. placeholder provides a hint. Both support required and disabled attributes.
<label for="country">Country:</label>
<select id="country" name="country">
<optgroup label="North America">
<option value="us">United States</option>
<option value="ca">Canada</option>
</optgroup>
<optgroup label="Europe">
<option value="uk">United Kingdom</option>
<option value="fr">France</option>
</optgroup>
</select>
<label for="bio">Bio:</label>
<textarea id="bio" name="bio" rows="4" cols="40"
maxlength="500" placeholder="Tell us about yourself"></textarea>Checkboxes & Radio Buttons
Checkboxes allow multiple selections; radio buttons allow only one (when they share the same name). The checked attribute pre-selects an option. Always wrap inputs in <label> for clickable text. Use <fieldset> and <legend> to group related choices for accessibility. The value is sent to the server only if selected.
<fieldset>
<legend>Select your interests:</legend>
<label><input type="checkbox" name="interest" value="tech" checked> Tech</label>
<label><input type="checkbox" name="interest" value="music"> Music</label>
<label><input type="checkbox" name="interest" value="sports"> Sports</label>
</fieldset>
<fieldset>
<legend>Choose a plan:</legend>
<label><input type="radio" name="plan" value="free" checked> Free</label>
<label><input type="radio" name="plan" value="pro"> Pro</label>
<label><input type="radio" name="plan" value="enterprise"> Enterprise</label>
</fieldset>Form Validation
HTML5 provides built-in client-side validation. required prevents submission if empty. minlength/maxlength constrain text length. pattern uses regex for custom validation (the title attribute guides users). type='email' and type='url' validate format. min/max work with numbers and dates. Add novalidate to the form to disable validation.
<form>
<input type="text" name="username" required
minlength="3" maxlength="20"
pattern="[A-Za-z0-9_]+"
title="3-20 alphanumeric characters">
<input type="email" name="email" required>
<input type="url" name="website"
placeholder="https://example.com">
<input type="number" name="age" min="18" max="99">
<button type="submit">Submit</button>
</form>Buttons & Datalist
type='submit' submits the form, type='reset' clears all fields, type='button' is a custom button. <datalist> provides autocomplete suggestions for <input> elements — link them with list/id. Unlike <select>, users can enter custom values. This combines the flexibility of text input with the convenience of suggestions.
<!-- Button types -->
<button type="submit">Submit Form</button>
<button type="reset">Reset</button>
<button type="button" onclick="alert('Hi')">Click Me</button>
<!-- Datalist for autocomplete suggestions -->
<label for="browser">Choose a browser:</label>
<input list="browsers" name="browser" id="browser">
<datalist id="browsers">
<option value="Chrome">
<option value="Firefox">
<option value="Safari">
<option value="Edge">
</datalist>Semantic HTML
Header & Navigation
<header> represents introductory content — typically a logo, heading, and navigation. It can appear at the top of a page or within an <article> or <section>. <nav> identifies navigation links, helping screen readers skip to navigation. A page can have multiple headers (e.g., one per section). Header is not the same as head — it's visible content.
<header>
<h1>My Website</h1>
<nav>
<ul>
<li><a href="/">Home</a></li>
<li><a href="/blog">Blog</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
</header>Main & Article
<main> wraps the dominant content of the page (only one per page). <article> represents self-contained content that could be distributed independently (blog post, news article, forum post). <time> with datetime provides machine-readable dates for SEO. <section> groups thematically related content with a heading. These tags improve document outline and accessibility.
<main>
<article>
<h1>Blog Post Title</h1>
<p>Published on <time datetime="2024-01-15">January 15, 2024</time></p>
<p>The main content of the article...</p>
<section>
<h2>Subsection</h2>
<p>More content...</p>
</section>
</article>
</main>Section & Aside
<section> groups related content with a heading — use it when content has a natural heading. <aside> represents content tangentially related to the main content (sidebars, pull quotes, ads, related links). Both improve document structure. Sections can be nested. Asides are typically styled as sidebars but are semantic, not presentational.
<section>
<h2>Services</h2>
<p>We offer the following services:</p>
<article>
<h3>Web Design</h3>
<p>Custom website design...</p>
</article>
</section>
<aside>
<h3>Related Articles</h3>
<ul>
<li><a href="/post1">Related Post 1</a></li>
<li><a href="/post2">Related Post 2</a></li>
</ul>
</aside>Footer
<footer> contains footer content for a page or section — copyright, links to related documents, contact info, sitemap. A page can have multiple footers (e.g., one per article section). Footers are not required to be at the bottom visually, but semantically represent concluding content. Use © for the copyright symbol.
<footer>
<div>
<h3>About Us</h3>
<p>Company information...</p>
</div>
<nav>
<a href="/privacy">Privacy Policy</a>
<a href="/terms">Terms of Service</a>
<a href="/sitemap">Sitemap</a>
</nav>
<p>© 2024 My Company. All rights reserved.</p>
</footer>Details & Summary
<details> creates an interactive disclosure widget — content is hidden until the user clicks <summary>. The open attribute shows it expanded by default. This is native HTML, no JavaScript needed. Great for FAQs, collapsible sections, and progressive disclosure. Nested details create multi-level accordions. Style with the [open] attribute selector.
<details>
<summary>Click to expand FAQ</summary>
<p>This content is hidden by default and shown when the summary is clicked.</p>
</details>
<!-- Open by default -->
<details open>
<summary>What is HTML?</summary>
<p>HTML (HyperText Markup Language) is the standard language for creating web pages.</p>
</details>
<!-- Nested details -->
<details>
<summary>Advanced Topics</summary>
<details>
<summary>Sub-topic</summary>
<p>Nested content...</p>
</details>
</details>Meta Tags & SEO
Charset & Viewport
charset='UTF-8' declares character encoding — essential for displaying all characters correctly. The viewport meta tag is critical for responsive design: width=device-width matches the device width, initial-scale=1.0 sets the zoom level. Without it, mobile browsers render desktop-width pages. These two meta tags should be in every HTML document.
<head>
<!-- Character encoding -->
<meta charset="UTF-8">
<!-- Responsive viewport -->
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<!-- Internet Explorer compatibility -->
<meta http-equiv="X-UA-Compatible" content="IE=edge">
</head>Description & Keywords
The description meta tag is the most important for SEO — search engines display it under your title in results (keep it under 160 characters). keywords is largely ignored by modern search engines. robots controls indexing: index/noindex, follow/nofollow. author credits the content creator. Write compelling descriptions to improve click-through rates.
<meta name="description"
content="Free HTML tutorials for beginners. Learn HTML tags, attributes, and semantic markup with examples.">
<meta name="keywords"
content="HTML, tutorial, web development, markup">
<meta name="author" content="John Doe">
<meta name="robots" content="index, follow">Open Graph (Facebook)
Open Graph (og:) meta tags control how your page appears when shared on Facebook, LinkedIn, and other platforms. og:title, og:description, og:image, and og:url are the most important. og:type can be article, website, product, etc. The image should be at least 1200x630 pixels. Test with Facebook's Sharing Debugger before deploying.
<meta property="og:title" content="My Amazing Article">
<meta property="og:description" content="A brief description of the article.">
<meta property="og:image" content="https://example.com/image.jpg">
<meta property="og:url" content="https://example.com/article">
<meta property="og:type" content="article">
<meta property="og:site_name" content="My Website">
<meta property="og:locale" content="en_US">Twitter Cards
Twitter Card meta tags control how links appear on Twitter/X. twitter:card can be summary, summary_large_image, or player. The large image card is most engaging. twitter:site and twitter:creator link to Twitter accounts. If Open Graph tags are present, Twitter falls back to them, but explicit Twitter tags give more control. Test with Twitter Card Validator.
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="My Amazing Article">
<meta name="twitter:description" content="A brief description.">
<meta name="twitter:image" content="https://example.com/image.jpg">
<meta name="twitter:site" content="@mywebsite">
<meta name="twitter:creator" content="@author">Canonical & Structured Data
The canonical link tag tells search engines the preferred URL for a page, preventing duplicate content penalties. JSON-LD structured data helps search engines understand your content and enables rich snippets (star ratings, breadcrumbs, FAQ accordions in search results). Use schema.org types like Article, Product, Event, or FAQPage. Validate with Google's Rich Results Test.
<!-- Canonical URL (prevents duplicate content issues) -->
<link rel="canonical" href="https://example.com/article">
<!-- Structured data (JSON-LD) -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "My Article Title",
"author": "John Doe",
"datePublished": "2024-01-15",
"image": "https://example.com/image.jpg"
}
</script>SVG & Canvas
SVG Basics
SVG (Scalable Vector Graphics) creates resolution-independent graphics that scale without quality loss. Inline SVG can be styled with CSS and manipulated with JavaScript. Common shapes: <circle>, <rect>, <line>, <ellipse>, <polygon>, <path>. SVG is ideal for icons, logos, and diagrams. Unlike raster images, SVGs are crisp at any size.
<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
<circle cx="100" cy="100" r="50" fill="blue" stroke="black" stroke-width="2"/>
<text x="100" y="105" text-anchor="middle" fill="white">Circle</text>
</svg>
<!-- Inline SVG (can be styled with CSS) -->
<svg width="100" height="100">
<rect x="10" y="10" width="80" height="80" fill="red" rx="10"/>
</svg>SVG Shapes
SVG shapes use coordinate-based attributes. <rect> needs x, y, width, height; rx/ry create rounded corners. <circle> needs cx, cy (center) and r (radius). <ellipse> uses rx and ry for different x/y radii. <line> connects x1,y1 to x2,y2. <polygon> takes a points list. fill sets interior color, stroke sets outline.
<svg width="300" height="200">
<!-- Rectangle -->
<rect x="10" y="10" width="80" height="50" fill="orange" rx="10" ry="10"/>
<!-- Circle -->
<circle cx="200" cy="50" r="40" fill="green"/>
<!-- Ellipse -->
<ellipse cx="100" cy="150" rx="60" ry="30" fill="purple"/>
<!-- Line -->
<line x1="10" y1="180" x2="290" y2="180" stroke="black" stroke-width="2"/>
<!-- Polygon -->
<polygon points="250,10 290,80 210,80" fill="pink"/>
</svg>SVG Paths
<path> is the most powerful SVG element, using a d attribute with commands: M (moveto), L (lineto), H/V (horizontal/vertical line), C (cubic Bezier), Q (quadratic Bezier), A (arc), Z (close path). Uppercase = absolute coordinates, lowercase = relative. viewBox defines the coordinate system. SVG icons use paths for scalable, styleable icons.
<svg width="200" height="200">
<!-- M=move, L=line, C=curve, Z=close -->
<path d="M 10 10 L 100 10 L 100 100 Z" fill="none" stroke="black"/>
<!-- Bezier curve -->
<path d="M 10 100 C 50 10, 150 10, 190 100" fill="none" stroke="red"/>
<!-- Arc -->
<path d="M 10 100 A 90 90 0 0 1 190 100" fill="none" stroke="blue"/>
</svg>
<!-- SVG icon example -->
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
<path d="M12 2L2 7v10l10 5 10-5V7z"/>
</svg>Canvas Basics
<canvas> is a bitmap drawing surface manipulated via JavaScript. Get the 2D context with getContext('2d'). Unlike SVG, canvas is pixel-based (not scalable) and not part of the DOM. Canvas is better for complex animations, games, and image processing. SVG is better for static graphics, icons, and interactive charts. Choose based on your needs.
<canvas id="myCanvas" width="300" height="200"></canvas>
<script>
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
// Draw a rectangle
ctx.fillStyle = 'blue';
ctx.fillRect(10, 10, 100, 80);
// Draw a circle
ctx.beginPath();
ctx.arc(200, 50, 30, 0, Math.PI * 2);
ctx.fillStyle = 'red';
ctx.fill();
// Draw text
ctx.font = '20px Arial';
ctx.fillText('Hello Canvas', 50, 150);
</script>Inline SVG Icons
SVG <symbol> defines reusable icons that are referenced with <use href='#id'>. This is the SVG sprite technique — define icons once, use them anywhere. The symbol is hidden (display:none) and instantiated via <use>. Icons inherit color from CSS 'color' via fill='currentColor'. This approach is efficient (one definition, many uses) and fully styleable.
<!-- Reusable SVG symbol -->
<svg style="display:none">
<symbol id="icon-home" viewBox="0 0 24 24">
<path d="M12 2L2 12h3v8h6v-6h2v6h6v-8h3z"/>
</symbol>
</svg>
<!-- Use the icon -->
<svg width="24" height="24" fill="currentColor">
<use href="#icon-home"/>
</svg>
<!-- Styled icon -->
<svg width="32" height="32" class="icon" style="color: blue;">
<use href="#icon-home"/>
</svg>Accessibility (ARIA)
ARIA Roles
ARIA roles define what an element does for screen readers. Landmark roles (banner, navigation, main, complementary, contentinfo) let screen reader users jump between page regions. HTML5 semantic elements (<header>, <nav>, <main>) have implicit ARIA roles, so adding role attributes is often redundant — but useful for older browsers. Always prefer semantic HTML over ARIA when possible.
<!-- Landmark roles for page structure -->
<header role="banner">Site Header</header>
<nav role="navigation">Main Menu</nav>
<main role="main">Primary Content</main>
<aside role="complementary">Sidebar</aside>
<footer role="contentinfo">Footer</footer>
<form role="search">Search Form</form>
<!-- Document structure roles -->
<article role="article">Blog Post</article>
<section role="region" aria-label="Comments">Comments</section>aria-label & aria-labelledby
aria-label defines an accessible name when no visible label exists (e.g., icon-only buttons). aria-labelledby references the ID of visible text element(s) that label the control — useful when a visible label is already on screen. When multiple IDs are listed, their text is concatenated in order. Prefer aria-labelledby when visible text exists, as it keeps labels in sync.
<!-- aria-label provides accessible name directly -->
<button aria-label="Close menu" onclick="closeMenu()">✕</button>
<input type="search" aria-label="Search products" />
<!-- aria-labelledby references visible text -->
<div id="billing-label">Billing Address</div>
<input type="text" aria-labelledby="billing-label" />
<!-- Multiple labels combined -->
<span id="city">City</span>
<span id="required">required</span>
<input aria-labelledby="city required" />aria-describedby & Tooltips
aria-describedby links an element to additional descriptive text via element ID. Screen readers announce this description after the label. It is ideal for help text, hints, and error messages. Use role='alert' on error messages so they are announced immediately when they appear. Set aria-invalid='true' on fields with errors so screen readers indicate the invalid state.
<label for="password">Password</label>
<input type="password" id="password"
aria-describedby="pwd-help pwd-rules" />
<p id="pwd-help">Must be at least 8 characters</p>
<p id="pwd-rules">Include uppercase, number, and symbol</p>
<!-- Error messaging -->
<input type="email" id="email" aria-describedby="email-error" aria-invalid="true" />
<p id="email-error" role="alert">Please enter a valid email address</p>Live Regions (aria-live)
Live regions announce dynamic content changes to screen reader users. aria-live='polite' waits for a pause before announcing (good for status updates). aria-live='assertive' interrupts immediately (for critical errors). aria-atomic='true' reads the entire region content instead of just the changed portion. aria-relevant specifies which change types (additions, removals, text) trigger announcements. Essential for SPAs, chat apps, and auto-updating content.
<!-- Polite: announces when user is idle -->
<div aria-live="polite" id="status">Saving...</div>
<!-- Assertive: announces immediately, interrupts -->
<div aria-live="assertive" role="alert" id="errors">
Connection lost!
</div>
<!-- aria-atomic reads entire region, not just changes -->
<div aria-live="polite" aria-atomic="true" id="cart">
3 items in cart
</div>
<!-- aria-relevant controls what changes are announced -->
<div aria-live="polite" aria-relevant="additions text">
Chat messages appear here
</div>Focus Management & tabindex
tabindex controls keyboard focus behavior. tabindex='0' makes non-interactive elements (divs, spans) focusable in the natural DOM order — use for custom widgets. tabindex='-1' makes elements focusable only programmatically via JS .focus() — useful for modals and skip targets. Never use positive tabindex values as they override the natural tab order and create confusion. Always provide skip links as the first focusable element on a page.
<!-- tabindex="0": element is focusable in natural order -->
<div tabindex="0" role="button" onkeypress="handleKey()">
Custom clickable div
</div>
<!-- tabindex="-1": focusable only via JS, removed from tab order -->
<div tabindex="-1" id="modal">Modal content</div>
<script>
document.getElementById('modal').focus();
</script>
<!-- tabindex="1+": DO NOT USE — breaks natural tab order -->
<!-- Avoid: <div tabindex="2"> -->
<!-- Skip link for keyboard users -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<main id="main-content">...</main>Hidden Content & Screen Reader Only
aria-hidden='true' removes elements from the accessibility tree while keeping them visible — use for decorative icons, redundant text, or off-screen content. The .sr-only pattern hides text visually but keeps it readable by screen readers — essential for icon-only buttons. Never use aria-hidden on focusable elements, as this creates a disconnect between keyboard and screen reader navigation. The clip technique is the most robust visually-hidden method.
<!-- aria-hidden: visible but hidden from screen readers -->
<div aria-hidden="true">
<i class="icon-decoration"></i> Decorative only
</div>
<!-- visually hidden but available to screen readers -->
<style>
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
</style>
<button>
<span class="sr-only">Delete item</span>
<i class="icon-trash" aria-hidden="true"></i>
</button>HTML5 APIs (Geolocation, Storage, Drag & Drop)
Geolocation API
The Geolocation API lets you request the user's physical location. Browsers always prompt for permission — the user must explicitly grant access. getCurrentPosition takes success and error callbacks plus an options object (enableHighAccuracy, timeout, maximumAge). For continuous tracking, use watchPosition() which returns an ID you can pass to clearWatch(). Always handle errors gracefully (permission denied, position unavailable, timeout). Geolocation requires HTTPS in modern browsers.
<!-- Request user location -->
<button onclick="getLocation()">Get My Location</button>
<p id="demo"></p>
<script>
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(pos) => {
document.getElementById("demo").innerHTML =
"Lat: " + pos.coords.latitude +
"<br>Lon: " + pos.coords.longitude;
},
(err) => {
alert("Error: " + err.message);
},
{ enableHighAccuracy: true, timeout: 5000 }
);
} else {
alert("Geolocation not supported.");
}
}
</script>Web Storage (localStorage & sessionStorage)
Web Storage provides key-value storage on the client. localStorage persists indefinitely; sessionStorage is cleared when the tab closes. Both store strings only — use JSON.stringify/parse for objects. Storage limit is ~5-10MB per origin. Unlike cookies, storage data is not sent with every HTTP request. Be aware: storage is synchronous and blocks the main thread, and is accessible by any script on the same origin (not suitable for sensitive data).
<!-- localStorage: persists until cleared -->
<script>
// Store data
localStorage.setItem("username", "alice");
localStorage.setItem("prefs", JSON.stringify({theme: "dark", lang: "en"}));
// Retrieve data
const user = localStorage.getItem("username");
const prefs = JSON.parse(localStorage.getItem("prefs") || "{}");
// Remove single item
localStorage.removeItem("username");
// Clear all items
localStorage.clear();
// sessionStorage: cleared when tab closes
sessionStorage.setItem("tempToken", "abc123");
</script>Drag and Drop API
The HTML5 Drag and Drop API enables native drag interactions. Set draggable='true' on the source element. ondragstart fires when dragging begins — use dataTransfer.setData() to store the dragged data. ondragover on the drop target MUST call preventDefault() to allow dropping. ondrop handles the actual drop — retrieve data with dataTransfer.getData(). You can drag files from the OS into the browser using event.dataTransfer.files. For complex apps, consider libraries like SortableJS for better cross-browser support.
<div id="drag1" draggable="true" ondragstart="drag(event)">
Drag me!
</div>
<div id="dropzone" ondrop="drop(event)" ondragover="allowDrop(event)">
Drop here
</div>
<script>
function allowDrop(ev) {
ev.preventDefault(); // necessary to allow dropping
}
function drag(ev) {
ev.dataTransfer.setData("text", ev.target.id);
}
function drop(ev) {
ev.preventDefault();
const data = ev.dataTransfer.getData("text");
ev.target.appendChild(document.getElementById(data));
}
</script>Page Visibility API
The Page Visibility API tells you whether the page is visible to the user (not minimized, not in a background tab). Use it to pause expensive animations, video playback, or polling when the user isn't looking — saving battery and CPU. document.hidden is a boolean; document.visibilityState returns 'visible', 'hidden', or 'prerender'. The visibilitychange event fires on transitions. This is more reliable than blur/focus for detecting actual visibility.
<script>
document.addEventListener("visibilitychange", () => {
if (document.hidden) {
console.log("Tab is hidden — pause video/animation");
video.pause();
} else {
console.log("Tab is visible — resume");
video.play();
}
});
// Check current state
if (document.visibilityState === "visible") {
console.log("Page is visible");
}
</script>Fullscreen API
The Fullscreen API lets you display any element in fullscreen mode. requestFullscreen() must be triggered by a user gesture (click/keypress). Different browsers may need vendor prefixes (webkit, moz, ms). The fullscreenchange event fires when entering or exiting fullscreen. document.fullscreenElement references the current fullscreen element (null if not in fullscreen). Use the :fullscreen CSS pseudo-class to style fullscreen elements differently.
<button onclick="openFullscreen()">Fullscreen</button>
<button onclick="closeFullscreen()">Exit</button>
<div id="container">Content to fullscreen</div>
<script>
function openFullscreen() {
const elem = document.getElementById("container");
if (elem.requestFullscreen) {
elem.requestFullscreen();
} else if (elem.webkitRequestFullscreen) {
elem.webkitRequestFullscreen();
}
}
function closeFullscreen() {
if (document.exitFullscreen) {
document.exitFullscreen();
}
}
document.addEventListener("fullscreenchange", () => {
console.log("Fullscreen:", !!document.fullscreenElement);
});
</script>Web Components
Custom Elements
Custom Elements let you create reusable HTML tags with encapsulated behavior. The class extends HTMLElement (or HTMLElement subclasses). connectedCallback fires when the element is added to the DOM — use it for rendering. attributeChangedCallback reacts to attribute changes, but only for attributes listed in observedAttributes. Names must contain a hyphen (e.g., 'my-greeting') to avoid conflicts with native HTML. customElements.define() registers the element. Avoid using Shadow DOM unless you need style encapsulation.
<my-greeting name="World"></my-greeting>
<script>
class MyGreeting extends HTMLElement {
constructor() {
super();
this.name = this.getAttribute("name") || "Guest";
}
connectedCallback() {
this.innerHTML = `<h2>Hello, ${this.name}!</h2>`;
}
// Observe attribute changes
static get observedAttributes() {
return ["name"];
}
attributeChangedCallback(name, oldVal, newVal) {
if (name === "name") {
this.name = newVal;
this.connectedCallback();
}
}
}
customElements.define("my-greeting", MyGreeting);
</script>Shadow DOM
Shadow DOM provides style and DOM encapsulation — styles inside the shadow tree don't leak out, and page styles don't leak in. attachShadow({mode:'open'}) creates a shadow root accessible via element.shadowRoot. mode:'closed' denies external access (rarely used). <slot> elements are placeholders where light DOM children are projected. Named slots (<slot name='title'>) match children with matching slot attributes. ::slotted() styles projected content. Shadow DOM is key to building self-contained, reusable components.
<my-card>
<span slot="title">Card Title</span>
<p slot="body">Card content here.</p>
</my-card>
<script>
class MyCard extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({ mode: "open" });
shadow.innerHTML = `
<style>
.card { border: 1px solid #ccc; padding: 16px; border-radius: 8px; }
::slotted([slot="title"]) { font-size: 1.5em; font-weight: bold; }
</style>
<div class="card">
<slot name="title"></slot>
<slot name="body"></slot>
</div>
`;
}
}
customElements.define("my-card", MyCard);
</script>HTML Templates
The <template> element holds HTML that is not rendered when the page loads — its content is inert (scripts don't run, images don't load, styles don't apply). Access the content via .content (a DocumentFragment) and clone it with cloneNode(true) to instantiate. Templates are ideal for repeating structures generated by JavaScript. Unlike innerHTML, templates are parsed once and can be cloned repeatedly without re-parsing. Combined with Custom Elements and Shadow DOM, they form the Web Components standard.
<template id="row-template">
<tr>
<td class="name"></td>
<td class="email"></td>
</tr>
</template>
<script>
function addRow(name, email) {
const template = document.getElementById("row-template");
const clone = template.content.cloneNode(true);
clone.querySelector(".name").textContent = name;
clone.querySelector(".email").textContent = email;
document.querySelector("tbody").appendChild(clone);
}
addRow("Alice", "[email protected]");
addRow("Bob", "[email protected]");
</script>Lifecycle Callbacks
Custom Elements have four lifecycle callbacks. constructor() runs when the element is created (avoid heavy work here). connectedCallback() fires when added to the DOM — ideal for setup and rendering. disconnectedCallback() fires on removal — use for cleanup (event listeners, timers). adoptedCallback() fires when moved to a new document via adoptNode() (rare). attributeChangedCallback() fires for observed attributes only. The upgrade order is always: constructor → connectedCallback. Attribute changes can fire before connectedCallback.
<script>
class LifecycleDemo extends HTMLElement {
constructor() {
super();
console.log("1. constructor: element created");
}
connectedCallback() {
console.log("2. connected: added to DOM");
}
disconnectedCallback() {
console.log("3. disconnected: removed from DOM");
}
adoptedCallback() {
console.log("4. adopted: moved to new document");
}
attributeChangedCallback(attr, oldVal, newVal) {
console.log(`5. attribute '${attr}' changed: ${oldVal} -> ${newVal}`);
}
static get observedAttributes() {
return ["data-status"];
}
}
customElements.define("lifecycle-demo", LifecycleDemo);
</script>Customized Built-in Elements
Customized built-in elements extend native HTML elements (like button, input, div) to inherit their built-in behavior. Use the 'is' attribute instead of a custom tag name. The third argument to customElements.define() specifies which native element is being extended. This is useful when you want to enhance existing elements (e.g., adding validation to <input>) rather than creating entirely new ones. Note: Safari does not support customized built-in elements — consider autonomous custom elements for cross-browser compatibility.
<!-- Extend a native button -->
<button is="my-button">Click me</button>
<script>
class MyButton extends HTMLButtonElement {
constructor() {
super();
this.addEventListener("click", () => {
this.textContent = "Clicked!";
this.style.background = "lightgreen";
});
}
connectedCallback() {
this.style.fontWeight = "bold";
}
}
customElements.define("my-button", MyButton,
{ extends: "button" });
</script>Media Elements (Audio & Video)
Video Element
The <video> element embeds video without plugins. The controls attribute shows built-in play/pause/volume controls. poster specifies a preview image before playback. Multiple <source> tags provide format fallbacks — the browser uses the first it supports (MP4/H.264 is most widely supported, WebM is open and efficient). <track> elements add subtitles, captions, or descriptions via WebVTT files. Always include fallback text for very old browsers. For autoplay, add muted attribute — most browsers block autoplay with sound.
<video width="640" height="360" controls poster="preview.jpg">
<source src="movie.mp4" type="video/mp4">
<source src="movie.webm" type="video/webm">
<track kind="subtitles" src="subs_en.vtt"
srclang="en" label="English" default>
<track kind="captions" src="caps_en.vtt"
srclang="en" label="English Captions">
Your browser does not support the video tag.
</video>Audio Element
The <audio> element embeds sound. controls shows the built-in player UI. Without controls, the element is invisible — control playback via JavaScript (play(), pause(), volume, currentTime). preload='auto' suggests the browser should buffer the file; 'metadata' loads only duration/info; 'none' loads nothing until play. MP3 has universal support; OGG Vorbis is open but not supported in Safari. For games or precise timing, use the Web Audio API instead of <audio> for lower latency and effects.
<!-- Basic audio player -->
<audio controls>
<source src="song.mp3" type="audio/mpeg">
<source src="song.ogg" type="audio/ogg">
Your browser does not support audio.
</audio>
<!-- Audio controlled by JavaScript -->
<audio id="player" src="song.mp3" preload="auto"></audio>
<button onclick="document.getElementById('player').play()">Play</button>
<button onclick="document.getElementById('player').pause()">Pause</button>
<button onclick="document.getElementById('player').volume += 0.1">Vol+</button>Picture & srcset (Responsive Images)
Responsive images improve performance by serving appropriately sized images. srcset with width descriptors (480w, 800w) plus sizes hints let the browser pick the best image — this is preferred for resolution switching. <picture> with media queries enables art direction (different crops for different screens). <source> with type attributes provides modern format fallbacks (WebP, AVIF) with JPG/PNG fallback. Always include a regular <img> as the last child of <picture> for fallback and accessibility.
<!-- srcset: let browser choose resolution -->
<img src="small.jpg"
srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, 800px"
alt="Responsive image">
<!-- picture: art direction with different images -->
<picture>
<source media="(max-width: 600px)" srcset="mobile.jpg">
<source media="(max-width: 1200px)" srcset="tablet.jpg">
<img src="desktop.jpg" alt="Art-directed image">
</picture>
<!-- Modern format with fallback -->
<picture>
<source type="image/webp" srcset="photo.webp">
<source type="image/avif" srcset="photo.avif">
<img src="photo.jpg" alt="With format fallback">
</picture>iframe Embedding
iframes embed another document within the current page. The title attribute is essential for accessibility — screen readers announce it. The sandbox attribute restricts the iframe's capabilities for security: empty value blocks everything; add tokens (allow-scripts, allow-forms, allow-same-origin) to re-enable specific features. loading='lazy' defers loading until near the viewport. allow specifies feature policies (camera, microphone, autoplay). Be cautious embedding untrusted content — sandbox it. Cross-origin iframes cannot be accessed via JavaScript.
<!-- Basic iframe -->
<iframe src="https://example.com"
width="600" height="400"
title="Embedded Content">
</iframe>
<!-- Sandboxed iframe for security -->
<iframe src="untrusted.html"
sandbox="allow-scripts allow-same-origin"
loading="lazy">
</iframe>
<!-- YouTube embed -->
<iframe src="https://www.youtube.com/embed/VIDEO_ID"
allow="accelerometer; autoplay; encrypted-media"
allowfullscreen
title="YouTube video">
</iframe>Embed & Object
<embed> and <object> are older embedding methods, largely replaced by <iframe> and <video>. <embed> is self-closing and simple but offers no fallback content. <object> is more flexible — content inside the tags serves as fallback if the embedded resource can't be displayed. Use <object> for PDFs and SVGs where you need fallback. For modern web development, prefer <iframe> for external pages, <video>/<audio> for media, and inline <svg> for vector graphics. <embed> is mainly used for plugin content (Flash, now deprecated).
<!-- embed: simple, self-closing -->
<embed src="animation.svg" type="image/svg+xml"
width="300" height="200">
<!-- object: more flexible with fallback -->
<object data="document.pdf" type="application/pdf"
width="100%" height="600px">
<p>Unable to display PDF.
<a href="document.pdf">Download it instead.</a>
</p>
</object>
<!-- embed YouTube without iframe -->
<embed src="https://www.youtube.com/v/VIDEO_ID"
type="application/x-shockwave-flash"
width="560" height="315">Input Types Deep Dive
Date & Time Inputs
HTML5 date/time input types provide native pickers without JavaScript libraries. type='date' gives a calendar; type='time' gives a time picker; type='datetime-local' combines both. type='month' and type='week' select months and weeks. min/max constrain the selectable range. step defines granularity (e.g., step='1800' for 30-minute intervals in seconds). The displayed format varies by browser/locale, but the submitted value is always ISO 8601 format (YYYY-MM-DD). Not all browsers support all types equally — test cross-browser.
<label for="birthday">Birthday:</label>
<input type="date" id="birthday" min="1900-01-01" max="2025-12-31">
<label for="appt">Appointment:</label>
<input type="time" id="appt" min="09:00" max="17:00" step="1800">
<label for="meeting">Meeting:</label>
<input type="datetime-local" id="meeting">
<label for="month">Pick a month:</label>
<input type="month" id="month">
<label for="week">Pick a week:</label>
<input type="week" id="week">Number & Range Inputs
type='number' provides a spinner control with up/down arrows. min, max, and step constrain values. For currency, use step='0.01'. type='range' renders a slider — always pair it with an <output> or visible label showing the current value, as the value isn't displayed by default. Number inputs filter non-numeric input but still allow some invalid characters; validate server-side. For quantities that don't need a spinner, consider type='text' with inputmode='numeric' and pattern validation.
<label for="qty">Quantity (1-100):</label>
<input type="number" id="qty" min="1" max="100"
step="1" value="10">
<label for="price">Price ($):</label>
<input type="number" id="price" min="0" step="0.01"
placeholder="0.00">
<label for="volume">Volume:</label>
<input type="range" id="volume" min="0" max="100"
value="50" oninput="out.value=this.value">
<output id="out">50</output>Color & File Inputs
type='color' opens a native color picker and returns a hex value (#rrggbb). type='file' lets users select files. The accept attribute filters by MIME type or extension (image/*, .pdf, image/png). The multiple attribute allows selecting multiple files. Access selected files via the files property (a FileList) in JavaScript. Use URL.createObjectURL() to create a local preview URL for images. For large file uploads, consider chunked uploads or the File API for progress tracking. Always validate file types and sizes server-side.
<label for="color">Pick a color:</label>
<input type="color" id="color" value="#ff0000">
<label for="avatar">Upload avatar:</label>
<input type="file" id="avatar" accept="image/*">
<label for="docs">Upload documents:</label>
<input type="file" id="docs" accept=".pdf,.doc,.docx" multiple>
<!-- File input with image preview -->
<input type="file" accept="image/*" onchange="preview(this)">
<img id="preview-img" hidden>
<script>
function preview(input) {
const file = input.files[0];
if (file) {
document.getElementById("preview-img").src =
URL.createObjectURL(file);
document.getElementById("preview-img").hidden = false;
}
}
</script>datalist (Input Suggestions)
<datalist> provides autocomplete suggestions for input fields. Link it to an input via the list attribute matching the datalist's id. Unlike <select>, users can still type any value — suggestions are optional. It works with text, number, date, color, and range inputs. The browser shows matching suggestions as the user types. This is a lightweight alternative to JavaScript autocomplete libraries for simple use cases. Note: styling options inside datalist is not supported — they render with browser defaults.
<label for="browser">Favorite browser:</label>
<input list="browsers" id="browser" name="browser">
<datalist id="browsers">
<option value="Chrome">
<option value="Firefox">
<option value="Safari">
<option value="Edge">
<option value="Opera">
</datalist>
<!-- Works with various input types -->
<label for="color">Choose color:</label>
<input type="color" list="preset-colors" id="color">
<datalist id="preset-colors">
<option value="#ff0000">
<option value="#00ff00">
<option value="#0000ff">
</datalist>Form Validation Attributes
HTML5 provides built-in client-side validation via attributes. required prevents submission if empty. type='email'/'url' validate format automatically. minlength/maxlength constrain text length. min/max constrain numbers and dates. pattern uses regex for custom validation. The browser shows default error bubbles. Customize messages with setCustomValidity() — but always clear it on input (setCustomValidity('')) to avoid sticky errors. Client-side validation improves UX but must be paired with server-side validation for security, as it can be bypassed.
<form>
<label>Email: <input type="email" required></label>
<label>Username:
<input type="text" required minlength="3" maxlength="20"
pattern="[a-zA-Z0-9_]+">
</label>
<label>Age:
<input type="number" min="18" max="120" required>
</label>
<label>Website:
<input type="url" placeholder="https://example.com">
</label>
<button type="submit">Submit</button>
</form>
<!-- Custom validation message -->
<input type="text" required
oninvalid="this.setCustomValidity('Please enter your name')"
oninput="this.setCustomValidity('')">Service Worker & PWA
Service Worker Registration
A service worker is a JavaScript file that runs in the background, separate from the web page, enabling offline support, push notifications, and background sync. Register it from the main page — it must be served over HTTPS. The service worker file's location determines its scope (it controls pages in its directory and subdirectories). Registration is asynchronous and only happens once per scope. After the first visit, the SW activates on subsequent page loads. Use the load event to delay registration until after the page is interactive.
<!-- Register a service worker in main page -->
<script>
if ("serviceWorker" in navigator) {
window.addEventListener("load", () => {
navigator.serviceWorker
.register("/sw.js")
.then((reg) => {
console.log("SW registered:", reg.scope);
})
.catch((err) => {
console.log("SW registration failed:", err);
});
});
}
</script>Service Worker: Caching
The service worker lifecycle has three phases: install (cache core assets), activate (clean up old caches), and fetch (intercept network requests). The cache-first strategy shown here serves cached responses immediately, falling back to network. Other strategies include network-first (try network, fall back to cache) and stale-while-revalidate (serve cache, update in background). Bump CACHE_NAME to trigger cache updates. The Cache API stores Request/Response pairs. Service workers only run when needed and can be terminated by the browser to save memory.
// sw.js - Cache assets for offline use
const CACHE_NAME = "my-app-v1";
const ASSETS = ["/", "/index.html", "/style.css", "/app.js"];
// Install: cache core assets
self.addEventListener("install", (e) => {
e.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS))
);
});
// Fetch: serve from cache, fall back to network
self.addEventListener("fetch", (e) => {
e.respondWith(
caches.match(e.request).then((cached) => {
return cached || fetch(e.request);
})
);
});
// Activate: clean old caches
self.addEventListener("activate", (e) => {
e.waitUntil(
caches.keys().then((keys) =>
Promise.all(
keys.filter((k) => k !== CACHE_NAME)
.map((k) => caches.delete(k))
)
)
);
});Web App Manifest
The Web App Manifest is a JSON file that makes a web app installable (Add to Home Screen). name is the full name; short_name appears on the home screen icon. display:'standalone' hides the browser UI, making it look like a native app. theme_color affects the browser chrome color. icons must include at least 192px and 512px sizes. 'purpose':'maskable' lets Android adapt the icon to different shapes. A valid manifest plus a registered service worker are the minimum requirements for a PWA. Test with Lighthouse for PWA compliance.
<!-- Link the manifest in HTML -->
<link rel="manifest" href="manifest.json">
<!-- manifest.json -->
{
"name": "My PWA App",
"short_name": "MyApp",
"start_url": "/",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#1976d2",
"orientation": "portrait",
"icons": [
{
"src": "/icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}Push Notifications
Push notifications let you re-engage users even when the tab is closed. First request permission with Notification.requestPermission(). Then subscribe via PushManager — the subscription object contains an endpoint URL and keys. Send this subscription to your server, which uses it to send push messages via the Web Push API (with VAPID keys for authentication). The service worker's push event handler displays the notification. userVisibleOnly:true means every push must show a notification (no silent pushes). Requires HTTPS and a service worker.
<!-- Request notification permission -->
<button onclick="subscribe()">Enable Notifications</button>
<script>
async function subscribe() {
const permission = await Notification.requestPermission();
if (permission !== "granted") return;
const reg = await navigator.serviceWorker.ready;
const subscription = await reg.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_KEY)
});
// Send subscription to your server
await fetch("/api/subscribe", {
method: "POST",
body: JSON.stringify(subscription)
});
}
</script>
<!-- In sw.js: handle push events -->
self.addEventListener("push", (e) => {
const data = e.data.json();
e.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: "/icon-192.png",
badge: "/badge.png"
})
);
});Background Sync
Background Sync lets you defer actions until the user has stable connectivity. When you register a sync event, the browser fires it when connectivity is restored — even if the user has closed the tab. The sync event handler (in the service worker) performs the deferred work. If the work throws an error, the browser automatically retries with exponential backoff. e.tag identifies the sync type — only one sync per tag is queued. Use IndexedDB to store pending data. This is ideal for messaging apps, form submissions, and data sync. Periodic Sync (for regular updates) is available in Chrome but not all browsers.
// Register a sync event from the page
navigator.serviceWorker.ready.then((reg) => {
return reg.sync.register("send-messages");
});
// Handle sync in sw.js
self.addEventListener("sync", (e) => {
if (e.tag === "send-messages") {
e.waitUntil(sendPendingMessages());
}
});
async function sendPendingMessages() {
const messages = await getMessagesFromIndexedDB();
for (const msg of messages) {
try {
await fetch("/api/messages", {
method: "POST",
body: JSON.stringify(msg)
});
await deleteMessageFromIndexedDB(msg.id);
} catch (err) {
throw err; // triggers retry
}
}
}Semantic HTML Deep Dive
Article vs Section vs Div
Choosing the right container element matters for accessibility and SEO. <article> is for content that could be distributed independently (blog posts, news, comments, product cards). <section> groups thematically related content — it should always have a heading. <div> is a last resort for layout/styling when no semantic element fits. Nested <article> elements (like comments inside a post) are valid. The rule of thumb: if content could be syndicated via RSS, use <article>; if it's a thematic chapter, use <section>; if it's purely for layout, use <div>.
<!-- <article>: self-contained, syndicable content -->
<article>
<h2>Blog Post Title</h2>
<p>Content that makes sense on its own...</p>
<article><h3>Comment 1</h3><p>...</p></article>
</article>
<!-- <section>: thematic grouping with a heading -->
<section>
<h2>Chapter 1: Introduction</h2>
<p>Related content...</p>
</section>
<!-- <div>: generic container, no semantic meaning -->
<div class="layout-wrapper">
<div class="grid-item">Styling hook only</div>
</div>Figure & Figcaption
<figure> represents self-contained content referenced from the main text — images, diagrams, code listings, or quotations. <figcaption> provides a caption/title. Unlike a plain <img>, figure/figcaption semantically links the visual to its description. Screen readers announce figures with their captions. Figures can be positioned anywhere relative to the text that references them. Use <figure> for any content that has a caption and is referenced by number (Figure 1, Listing 1). For purely decorative images, use a plain <img> with empty alt.
<figure>
<img src="chart.png" alt="Sales growth chart showing 40% increase">
<figcaption>
Figure 1: Quarterly sales growth from Q1 to Q4 2025.
Data source: Internal sales database.
</figcaption>
</figure>
<!-- Figure can contain code blocks too -->
<figure>
<pre><code>const x = 42;</code></pre>
<figcaption>Listing 1: Variable declaration example</figcaption>
</figure>Details & Summary (Disclosure)
<details> and <summary> create native collapsible (accordion) sections without any JavaScript. Clicking the <summary> toggles the content. The open attribute makes it expanded by default. The toggle event fires when expanded/collapsed. This is excellent for FAQs, settings panels, and progressive disclosure. Screen readers announce it as a disclosure widget. You can style the default triangle marker with CSS (summary::-webkit-details-marker or list-style). For complex interactions, you may still need JavaScript, but for simple toggles, this native solution is ideal.
<!-- Native collapsible without JavaScript -->
<details>
<summary>Click to expand FAQ</summary>
<p>Here is the hidden answer that appears when expanded.</p>
</details>
<!-- Open by default -->
<details open>
<summary>Already expanded</summary>
<p>This content is visible on page load.</p>
</details>
<!-- Nested disclosures -->
<details>
<summary>Level 1</summary>
<details>
<summary>Level 2</summary>
<p>Deeply nested content</p>
</details>
</details>Time & Mark Elements
<time> wraps human-readable dates/times with a machine-readable datetime attribute in ISO 8601 format. This helps search engines, calendars, and assistive technologies parse dates correctly. datetime supports dates (YYYY-MM-DD), times (HH:MM), datetimes with timezone, and durations (PT2H30M). <mark> represents text highlighted for relevance, like search term matches. Unlike <strong> or <em> (which indicate importance/emphasis), <mark> indicates contextual relevance. Both elements improve semantic meaning and SEO.
<!-- <time>: machine-readable dates/times -->
<p>Published on
<time datetime="2025-01-15">January 15, 2025</time>
</p>
<p>Event at
<time datetime="2025-03-20T14:30-05:00">2:30 PM EST</time>
</p>
<p>Duration:
<time datetime="PT2H30M">2 hours 30 minutes</time>
</p>
<!-- <mark>: highlighted/relevant text -->
<p>Search results: the keyword
<mark>HTML5</mark> appears in 3 documents.
</p>Dialog Element
The <dialog> element provides native modal and non-modal dialogs without libraries. showModal() opens it as a modal (with backdrop, blocks page interaction); show() opens it non-modally. close() closes it. form method='dialog' closes the dialog on submit, with the button's value as returnValue. The ::backdrop pseudo-element styles the modal overlay. ESC key closes modal dialogs automatically. Top layer rendering means the dialog appears above all other content regardless of z-index. The close event fires after closing. This is now well-supported and replaces many JavaScript modal libraries.
<!-- Native modal dialog -->
<dialog id="myDialog">
<h2>Confirm Action</h2>
<p>Are you sure you want to proceed?</p>
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="confirm">Confirm</button>
</form>
</dialog>
<button onclick="document.getElementById('myDialog').showModal()">
Open Modal
</button>
<script>
const dialog = document.getElementById("myDialog");
dialog.addEventListener("close", () => {
console.log("Dialog closed with:", dialog.returnValue);
});
// Close on backdrop click
dialog.addEventListener("click", (e) => {
if (e.target === dialog) dialog.close();
});
</script>Advanced Forms & Input
Fieldset & Legend
<fieldset> groups related form controls, and <legend> provides a caption for the group. This is crucial for accessibility — screen readers announce the legend before each control in the group, providing context. The disabled attribute on a fieldset disables all controls within it. Fieldsets also improve visual organization with a default border. For radio buttons, fieldset/legend is the recommended way to label the group. Avoid nesting fieldsets too deeply, as it can confuse screen reader users.
<form>
<fieldset>
<legend>Shipping Address</legend>
<label>Street: <input type="text" name="street" required></label>
<label>City: <input type="text" name="city" required></label>
<label>ZIP: <input type="text" name="zip" pattern="[0-9]{5}"></label>
</fieldset>
<fieldset disabled>
<legend>Billing (same as shipping)</legend>
<label>Card: <input type="text" name="card"></label>
</fieldset>
<fieldset>
<legend>Subscription Plan</legend>
<label><input type="radio" name="plan" value="free"> Free</label>
<label><input type="radio" name="plan" value="pro" checked> Pro</label>
</fieldset>
</form>Output & Progress Elements
<output> displays the result of a calculation — it has a live relationship with form inputs (via the for attribute). It's semantically more meaningful than a span for computed values. <progress> represents completion of a task (value/max); without a value, it shows an indeterminate spinner. <meter> represents a scalar value within a known range (like disk space or test scores) — the low, high, and optimum attributes define thresholds that affect color (green/yellow/red). Both progress and meter have built-in styling that varies by browser but can be customized with CSS.
<form oninput="result.value = parseInt(a.value) + parseInt(b.value)">
<input type="number" id="a" value="10"> +
<input type="number" id="b" value="20"> =
<output name="result" for="a b">30</output>
</form>
<!-- Progress bar -->
<label>Downloading: <progress id="prog" value="70" max="100">70%</progress></label>
<!-- Meter (gauge within a range) -->
<label>Disk usage: <meter value="0.6" min="0" max="1" low="0.5" high="0.8" optimum="0.2">60%</meter></label>
<label>Score: <meter value="85" min="0" max="100" low="40" high="70" optimum="90">85</meter></label>Form Autocomplete
The autocomplete attribute helps browsers fill in forms using stored user data. Use standardized tokens: 'name', 'email', 'tel', 'street-address', 'address-level2' (city), 'postal-code', 'country'. For credit cards: 'cc-name', 'cc-number', 'cc-exp'. autocomplete='off' disables autofill (though browsers may ignore this for non-sensitive fields). autocomplete='one-time-code' triggers SMS code autofill on mobile. Proper autocomplete tokens dramatically improve form completion rates and user experience. They also help password managers identify fields correctly.
<form autocomplete="on">
<!-- Browser can autofill name -->
<label>Name: <input type="text" name="name" autocomplete="name"></label>
<!-- Email autofill -->
<label>Email: <input type="email" name="email" autocomplete="email"></label>
<!-- Address autofill tokens -->
<fieldset>
<legend>Address</legend>
<input autocomplete="street-address">
<input autocomplete="address-level2"> <!-- City -->
<input autocomplete="postal-code">
<input autocomplete="country">
</fieldset>
<!-- Disable autocomplete for sensitive field -->
<label>SSN: <input type="text" autocomplete="off"></label>
<!-- One-time code (SMS) -->
<label>Code: <input type="text" autocomplete="one-time-code"></label>
</form>Form Submission Methods
GET appends form data to the URL (visible, bookmarkable, limited length) — use for searches and filters. POST sends data in the request body (not visible, no length limit) — use for creating/updating data. enctype='multipart/form-data' is required for file uploads. The button's name/value pair is included in submission — multiple submit buttons can trigger different actions via the same name with different values. For AJAX submission, use FormData object and fetch(). Always use POST for sensitive data, as GET data appears in browser history and server logs.
<!-- GET: data in URL query string -->
<form method="GET" action="/search">
<input name="q" value="html5">
<!-- URL: /search?q=html5 -->
</form>
<!-- POST: data in request body -->
<form method="POST" action="/submit" enctype="application/x-www-form-urlencoded">
<input name="name" value="Alice">
</form>
<!-- File upload: multipart -->
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="file" name="document">
</form>
<!-- Form with custom submit button -->
<form method="POST" action="/save">
<button type="submit" name="action" value="save">Save</button>
<button type="submit" name="action" value="publish">Publish</button>
</form>Contenteditable & Spellcheck
contenteditable='true' makes any element directly editable in the browser — the basis of rich text editors. spellcheck='true' enables browser spell checking (red underline for misspellings). For code snippets, set spellcheck='false' to avoid false positives. The contenteditable attribute can be inherited — child elements are editable unless set to 'false'. Saving editable content requires JavaScript (e.g., storing in localStorage on input). Building a full rich text editor with contenteditable is complex (handling paste, formatting, cursor position) — consider libraries like Quill, TipTap, or ProseMirror for production use.
<!-- Editable div -->
<div contenteditable="true">
Click to edit this text directly in the browser.
</div>
<!-- Editable with spellcheck -->
<p contenteditable="true" spellcheck="true">
Typoos will be underlined in red.
</p>
<!-- Turn off spellcheck for code -->
<pre contenteditable="true" spellcheck="false">
const varible = "code"; // no spellcheck
</pre>
<!-- Entire document editable -->
<body contenteditable="true">
<!-- Save editable content -->
<div id="note" contenteditable="true"
oninput="localStorage.setItem('note', this.innerHTML)">
<script>document.getElementById('note').innerHTML =
localStorage.getItem('note') || '';</script>
</div>SVG Basics
Basic Shapes
SVG draws with XML elements. Shapes include rect, circle, ellipse, line, polyline, and polygon. Unlike raster images, SVGs scale without quality loss and can be styled with CSS. fill sets interior color, stroke sets outline.
<svg width="200" height="150" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="80" height="50" fill="steelblue"/>
<circle cx="140" cy="40" r="30" fill="tomato"/>
<line x1="10" y1="100" x2="180" y2="100" stroke="black" stroke-width="2"/>
<ellipse cx="100" cy="120" rx="60" ry="20" fill="none" stroke="green"/>
</svg>Paths
The path element is the most powerful SVG primitive. Commands: M (move), L (line), H/V (horizontal/vertical), C (cubic bezier), Q (quadratic), A (arc), Z (close). Uppercase = absolute coords, lowercase = relative.
<svg width="200" height="200">
<!-- M=move, L=line, C=cubic bezier, Z=close -->
<path d="M 10 10 L 100 10 L 100 100 Z" fill="none" stroke="black"/>
<path d="M 10 150 C 50 50, 150 50, 190 150" stroke="red" fill="none"/>
<path d="M 10 180 Q 100 120 190 180" stroke="blue" fill="none"/>
</svg>Groups & Reuse
Group elements with g to apply shared attributes or transform them together. Define reusable shapes inside defs and reference them with use href="#id". This keeps SVGs DRY and smaller.
<svg width="200" height="200">
<defs>
<g id="star">
<polygon points="50,5 61,39 98,39 68,61 79,95 50,75 21,95 32,61 2,39 39,39"/>
</g>
</defs>
<use href="#star" x="0" y="0" fill="gold"/>
<use href="#star" x="100" y="100" fill="orange"/>
</svg>Text & Styling
SVG text is real text — selectable, searchable, and crisp at any scale. font-family, font-size, font-weight mirror CSS. text-anchor controls horizontal alignment (start/middle/end).
<svg width="300" height="100">
<text x="10" y="50" font-family="Arial" font-size="24" fill="navy">
Hello SVG
</text>
<text x="150" y="80" font-size="16" font-weight="bold" text-anchor="middle">
Centered Bold Text
</text>
</svg>Gradients & Patterns
Gradients are defined in defs and referenced via fill="url(#id)". linearGradient transitions along a line; radialGradient radiates from a center point. Each stop defines a color at a percentage offset.
<svg width="200" height="100">
<defs>
<linearGradient id="lg" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="red"/>
<stop offset="100%" stop-color="blue"/>
</linearGradient>
<radialGradient id="rg">
<stop offset="0%" stop-color="yellow"/>
<stop offset="100%" stop-color="green"/>
</radialGradient>
</defs>
<rect width="100" height="100" fill="url(#lg)"/>
<rect x="100" width="100" height="100" fill="url(#rg)"/>
</svg>Canvas
Drawing Shapes
The Canvas API draws pixels onto a canvas element via a 2D rendering context. fillRect/strokeRect draw rectangles; paths (beginPath, arc, lineTo) build complex shapes before fill() or stroke(). Canvas is raster — scaling blurs the drawing.
const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'tomato';
ctx.fillRect(10, 10, 100, 50);
ctx.strokeStyle = 'navy';
ctx.lineWidth = 3;
ctx.strokeRect(130, 10, 80, 80);
ctx.beginPath();
ctx.arc(200, 150, 40, 0, Math.PI * 2);
ctx.fill();Paths & Lines
Paths build shapes point by point. moveTo starts a new sub-path; lineTo adds straight segments; closePath connects to the start. bezierCurveTo and quadraticCurveTo draw curves with control points.
ctx.beginPath();
ctx.moveTo(50, 50);
ctx.lineTo(150, 50);
ctx.lineTo(100, 150);
ctx.closePath();
ctx.fillStyle = 'gold';
ctx.fill();
ctx.stroke();
// Bezier curves
ctx.beginPath();
ctx.moveTo(0, 200);
ctx.bezierCurveTo(100, 100, 200, 300, 300, 200);
ctx.stroke();Drawing Images
drawImage paints an image, video, or another canvas. The 9-argument form crops a source rectangle and paints it into a destination rectangle — useful for sprite sheets. Wait for onload before drawing.
const img = new Image();
img.src = 'photo.jpg';
img.onload = () => {
ctx.drawImage(img, 0, 0); // full image
ctx.drawImage(img, 0, 0, 200, 150); // scaled
ctx.drawImage(img, 50, 50, 100, 100, 300, 0, 100, 100); // cropped
};Animation Loop
requestAnimationFrame schedules draw() once per browser repaint (~60fps), pausing when the tab is hidden. clearRect wipes the canvas each frame to prevent trails. For physics, multiply movement by delta time for consistent speed.
let x = 0;
function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'steelblue';
ctx.fillRect(x, 50, 40, 40);
x += 2;
if (x > canvas.width) x = -40;
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);Pixel Manipulation
getImageData returns a Uint8ClampedArray of raw RGBA pixel values (0-255 per channel). Direct pixel access enables filters and effects. putImageData writes the modified buffer back. This is slow for large canvases.
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data; // RGBA bytes
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = data[i + 1] = data[i + 2] = avg; // grayscale
}
ctx.putImageData(imageData, 0, 0);Iframes & Embeds
Iframe Basics
An iframe embeds another document inside the current page. The title attribute is required for accessibility. loading="lazy" defers loading until near the viewport. Always set width and height to prevent layout shift.
<iframe
src="https://example.com/widget"
width="600"
height="400"
title="Example Widget"
loading="lazy"
referrerpolicy="no-referrer">
</iframe>Sandbox Attribute
The sandbox attribute restricts an iframe capabilities. An empty value blocks everything. Add tokens to re-enable features: allow-scripts, allow-forms, allow-same-origin, allow-popups. Never combine allow-scripts with allow-same-origin for untrusted content.
<!-- Locks down the iframe completely -->
<iframe src="untrusted.html" sandbox></iframe>
<!-- Selectively re-enable features -->
<iframe
src="widget.html"
sandbox="allow-scripts allow-same-origin allow-forms">
</iframe>Embed & Object
embed and object are legacy elements. object supports fallback content shown when the resource fails. Modern HTML5 elements (video, audio, picture) are preferred. Use iframe for HTML content, video for video.
<!-- Embed for plugins/media -->
<embed src="video.mp4" type="video/mp4" width="400" height="300">
<!-- Object with fallback content -->
<object data="report.pdf" type="application/pdf" width="100%" height="600">
<p>Your browser cannot display PDFs. <a href="report.pdf">Download</a></p>
</object>
<!-- Video with multiple sources -->
<video controls>
<source src="movie.webm" type="video/webm">
<source src="movie.mp4" type="video/mp4">
Your browser does not support video.
</video>postMessage Communication
postMessage is the only way to communicate across iframe boundaries (different origins). Always specify the target origin in the third argument. On the receiving side, verify e.origin before trusting the message.
<!-- Parent page -->
<iframe id="f" src="child.html"></iframe>
<script>
const frame = document.getElementById('f');
frame.contentWindow.postMessage({ type: 'greet', text: 'Hi' }, 'https://example.com');
window.addEventListener('message', (e) => {
if (e.origin !== 'https://example.com') return;
console.log('From child:', e.data);
});
</script>Responsive Iframes
To make iframes responsive (e.g., 16:9 video), wrap them in a container with padding-bottom equal to the aspect ratio percentage (56.25% for 16:9). Position the iframe absolutely to fill the container.
<div style="position:relative; padding-bottom:56.25%; height:0; overflow:hidden;">
<iframe
src="https://youtube.com/embed/abc"
style="position:absolute; top:0; left:0; width:100%; height:100%; border:0;"
title="Video"
allowfullscreen>
</iframe>
</div>Performance Optimization
Resource Hints
Resource hints tell the browser to prepare connections or fetch assets before they are needed. preconnect warms up DNS/TCP/TLS. preload fetches critical current-page assets early. prefetch fetches next-page resources during idle time.
<!-- Preconnect to a third-party origin -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<!-- DNS prefetch for lower-priority origins -->
<link rel="dns-prefetch" href="https://analytics.example.com">
<!-- Preload critical assets -->
<link rel="preload" href="hero.webp" as="image">
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<!-- Prefetch next page -->
<link rel="prefetch" href="/next-page.html">Lazy Loading Images
loading="lazy" defers offscreen image loading until the user scrolls near them. Always set width and height to prevent layout shift. The picture element serves modern formats (WebP/AVIF) with a JPG fallback.
<!-- Native lazy loading -->
<img src="photo.jpg" loading="lazy" width="800" height="600" alt="...">
<!-- Picture with responsive sources -->
<picture>
<source srcset="small.webp" media="(max-width: 600px)" type="image/webp">
<source srcset="large.webp" type="image/webp">
<img src="large.jpg" loading="lazy" width="1200" height="800" alt="...">
</picture>Script Loading Strategies
Without attributes, a script blocks HTML parsing. async downloads in parallel but executes immediately when ready — order not guaranteed. defer downloads in parallel and executes in order after DOM is parsed — best for main scripts.
<!-- Normal: blocks HTML parsing -->
<script src="app.js"></script>
<!-- Async: downloads in parallel, runs ASAP -->
<script src="analytics.js" async></script>
<!-- Defer: downloads in parallel, runs after HTML parse -->
<script src="app.js" defer></script>
<!-- Module scripts defer by default -->
<script type="module" src="app.mjs"></script>Critical CSS
Inlining critical CSS (styles needed for above-the-fold content) eliminates render-blocking CSS requests, speeding first paint. The remaining CSS loads asynchronously via the preload trick. Tools like Critical extract critical CSS automatically.
<!-- Inline above-the-fold CSS in head -->
<head>
<style>
body { margin: 0; font-family: sans-serif; }
.hero { height: 100vh; background: #f0f0f0; }
</style>
<!-- Load the rest asynchronously -->
<link rel="preload" href="full.css" as="style" onload="this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="full.css"></noscript>
</head>Measuring Performance
The Performance API measures real user timing. Key metrics: FCP (First Contentful Paint), LCP (Largest Contentful Paint), CLS (Cumulative Layout Shift), INP (Interaction to Next Paint). Use Lighthouse for lab data.
// PerformanceObserver for Core Web Vitals
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.startTime);
}
}).observe({ entryTypes: ['paint', 'largest-contentful-paint'] });
// Navigation timing
const [nav] = performance.getEntriesByType('navigation');
console.log('DOM Content Loaded:', nav.domContentLoadedEventEnd);
console.log('Load:', nav.loadEventEnd);Forms Advanced
Input Types
HTML5 input types provide built-in validation and UI. email/url validate format. number/date provide pickers. color/range have specialized UI. accept filters file types. Browsers handle validation automatically.
<input type="email" required>
<input type="url" placeholder="https://">
<input type="number" min="0" max="100" step="5">
<input type="date" min="2024-01-01">
<input type="color" value="#ff0000">
<input type="range" min="0" max="10">
<input type="file" accept="image/*">Form Validation
required prevents submission if empty. minlength/maxlength limit length. pattern uses regex validation. The browser prevents submission and shows errors. Customize with setCustomValidity and the invalid event.
<form>
<input type="text" required minlength="3" maxlength="20" pattern="[A-Za-z]+">
<input type="email" required>
<input type="submit" value="Submit">
</form>Fieldset & Legend
fieldset groups related form fields. legend provides a caption. disabled on fieldset disables all fields. Improves accessibility by grouping related inputs. Screen readers announce the legend for each field.
<fieldset>
<legend>Shipping Address</legend>
<label>Street: <input type="text" name="street"></label>
<label>City: <input type="text" name="city"></label>
</fieldset>
<fieldset disabled>
<legend>Billing (disabled)</legend>
<input type="text" name="billing">
</fieldset>Datalist
datalist provides autocomplete suggestions for input. Users can select or type freely. Unlike select, it allows custom values. The list attribute links input to datalist by id. Useful for search and tags.
<label>Choose browser:
<input list="browsers" name="browser">
</label>
<datalist id="browsers">
<option value="Chrome">
<option value="Firefox">
<option value="Safari">
</datalist>Output Element
output displays computed results. The for attribute links to input IDs. Updates live with form changes. No JavaScript needed for simple calculations. Accessible to screen readers as a live region.
<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>Accessibility (a11y)
ARIA Labels
aria-label provides accessible names for elements without visible text. aria-expanded indicates toggle state. aria-controls links to controlled element. Use ARIA only when HTML semantics are insufficient.
<button aria-label="Close menu" onclick="closeMenu()">
<svg>...</svg>
</button>
<nav aria-label="Main navigation">...</nav>
<button aria-expanded="false" aria-controls="menu">Menu</button>Landmark Roles
Landmark roles help screen reader users navigate. Most semantic elements have implicit roles. Add explicit roles only when needed. role="search" on a form creates a search landmark. Avoid role redundancy with semantic elements.
<header role="banner">...</header>
<nav role="navigation">...</nav>
<main role="main">...</main>
<aside role="complementary">...</aside>
<footer role="contentinfo">...</footer>
<form role="search">...</form>Focus Management
Manage focus for keyboard users. When opening modals, move focus inside. When closing, return focus to the trigger. Use tabindex="-1" to make elements focusable programmatically. Never remove focus outlines without replacement.
<button id="open">Open</button>
<div id="modal" hidden>
<button id="close">Close</button>
</div>
<script>
document.getElementById('open').onclick = () => {
modal.hidden = false;
document.getElementById('close').focus();
};
</script>Skip Links
Skip links let keyboard users bypass repetitive navigation. Hidden visually until focused. The href points to the main content ID. Essential for accessibility compliance (WCAG). Improves navigation efficiency.
<body>
<a href="#main" class="skip-link">Skip to main content</a>
<nav>...long navigation...</nav>
<main id="main">...</main>
</body>
<style>
.skip-link { position: absolute; left: -9999px; }
.skip-link:focus { left: 0; }
</style>Alt Text
Alt text describes images for screen readers. Empty alt="" marks decorative images (ignored). Describe the content and purpose, not appearance. For complex images, provide a longer description elsewhere. Never use alt for tooltips (use title).
<!-- Informative image -->
<img src="chart.png" alt="Bar chart showing 30% increase in sales">
<!-- Decorative image -->
<img src="spacer.gif" alt="">
<!-- Complex image -->
<img src="diagram.png" alt="Network topology" longdesc="diagram-desc.html">Common Pitfalls
Missing Alt Text
Missing alt text breaks accessibility. Screen readers read the filename. "image" or "photo" are unhelpful. Describe the content and purpose. Empty alt="" marks decorative images. Never skip the alt attribute entirely.
<!-- BAD: no alt -->
<img src="photo.jpg">
<!-- BAD: unhelpful alt -->
<img src="photo.jpg" alt="image">
<!-- GOOD: descriptive alt -->
<img src="photo.jpg" alt="Team meeting in conference room">
<!-- Decorative: empty alt -->
<img src="border.png" alt="">Div Soup
Using divs for everything removes semantic meaning. Screen readers cannot navigate. SEO cannot understand content structure. Use semantic elements: header, nav, main, article, section, aside, footer. Reserve div for grouping without semantic intent.
<!-- BAD: div for everything -->
<div class="header">...</div>
<div class="nav">...</div>
<div class="article">...</div>
<!-- GOOD: semantic elements -->
<header>...</header>
<nav>...</nav>
<article>...</article>Inline Styles
Inline styles mix content and presentation, making maintenance hard. They have high specificity, overriding stylesheets. Cannot be cached or reused. Use classes and external stylesheets. Reserve inline styles for dynamic values.
<!-- BAD: inline styles -->
<div style="color: red; font-size: 16px;">Text</div>
<!-- GOOD: external CSS -->
<div class="error">Text</div>
<link rel="stylesheet" href="styles.css">Button vs Link
Buttons trigger actions (save, delete, toggle). Links navigate to URLs. Using links for actions breaks keyboard navigation (Space vs Enter) and semantics. Screen readers announce them differently. Use type="button" to prevent form submission.
<!-- BAD: link for actions -->
<a href="#" onclick="save()">Save</a>
<!-- GOOD: button for actions -->
<button type="button" onclick="save()">Save</button>
<!-- Link for navigation -->
<a href="/about">About</a>Heading Hierarchy
Headings create a document outline. Dont skip levels (h1 to h3). Use one h1 per page (main title). Screen reader users navigate by headings. Maintain logical hierarchy. Use CSS for visual styling, not heading levels.
<!-- BAD: skip levels -->
<h1>Title</h1>
<h3>Subtitle</h3> <!-- Skipped h2 -->
<!-- BAD: multiple h1 -->
<h1>Title</h1>
<h1>Another</h1>
<!-- GOOD: hierarchical -->
<h1>Main Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>Related HTML snippets
Copy-paste ready code for common tasks.
Accessible Form with Inputs
Build an accessible HTML form with labels, inputs, select, and checkbox.
Table with Thead Tbody Tfoot
Structure tabular data with thead, tbody, tfoot, and caption in HTML.
Responsive Images and Video
Embed responsive images with srcset, video, audio, and figure in HTML.
Links Anchors and Download
Create internal, external, anchor, email, phone, and download links in HTML.
Semantic Tags
HTML5 semantic structure.
Form Validation
HTML5 form validation.
SVG
Scalable Vector Graphics.
Canvas
Canvas drawing.
Web Components
Custom elements and Shadow DOM.
Accessibility
ARIA and accessibility.
Was this helpful?