HTML Expert
Overview
Advanced expertise in HTML5 — from semantic structure and accessibility to forms, media, SVG, web components, structured data, and performance-oriented markup patterns.
1. Semantic HTML5 Elements
Document Structure
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Page Title — Site Name</title>
</head>
<body>
<header> <!-- site/page header, logo, nav -->
<nav> <!-- primary navigation -->
<ul>
<li><a href="/">Home</a></li>
<li><a href="/about">About</a></li>
</ul>
</nav>
</header>
<main> <!-- unique main content (one per page) -->
<article> <!-- self-contained content (blog post, card) -->
<header> <!-- article header (title, meta) -->
<h1>Article Title</h1>
<time datetime="2025-01-15">January 15, 2025</time>
</header>
<section> <!-- thematic grouping within article -->
<h2>Section Heading</h2>
<p>Content...</p>
</section>
<aside> <!-- tangentially related (sidebar, callout) -->
<p>Related tip...</p>
</aside>
</article>
</main>
<footer> <!-- site/page footer -->
<address> <!-- contact info for nearest article/body -->
<a href="mailto:hi@example.com">hi@example.com</a>
</address>
</footer>
</body>
</html>
Text-Level Semantics
<strong>Strong importance</strong> <!-- bold + semantic weight -->
<em>Emphasized stress</em> <!-- italic + semantic stress -->
<b>Stylistically bold</b> <!-- bold, no extra importance -->
<i>Technical term or thought</i> <!-- italic, no extra emphasis -->
<mark>Highlighted reference</mark> <!-- highlight/search match -->
<small>Fine print or side comment</small>
<s>No longer accurate</s> <!-- strikethrough -->
<del>Deleted content</del> <ins>Inserted</ins>
<abbr title="HyperText Markup Language">HTML</abbr>
<code>inline code</code>
<pre><code>block of code</code></pre>
<kbd>Ctrl</kbd> + <kbd>C</kbd> <!-- keyboard input -->
<samp>terminal output</samp>
<var>variable x</var>
<cite>Book Title</cite>
<q cite="https://...">inline quote</q>
<blockquote cite="https://...">Long quote</blockquote>
<sup>footnote<sup>1</sup></sup> <sub>H<sub>2</sub>O</sub>
<dfn>Definition term</dfn>
<data value="123">Product Name</data>
Heading Hierarchy
<!-- One h1 per page — document outline -->
<h1>Page / Article Title</h1> <!-- 1× -->
<h2>Major Section</h2> <!-- multiple -->
<h3>Subsection</h3> <!-- multiple -->
<h4>Sub-subsection</h4> <!-- sparingly -->
<!-- Never skip levels (h1 → h3) -->
<!-- Never use headings for visual styling — use CSS -->
2. Forms & Inputs
<form action="/submit" method="post" novalidate>
<fieldset>
<legend>Personal Information</legend>
<!-- Text inputs -->
<label for="name">Full Name <span aria-hidden="true">*</span></label>
<input type="text" id="name" name="name" required
autocomplete="name" placeholder="Alice Smith"
aria-required="true" aria-describedby="name-hint" />
<p id="name-hint">Enter your legal name as on your ID.</p>
<!-- Email -->
<label for="email">Email</label>
<input type="email" id="email" name="email"
autocomplete="email" inputmode="email" />
<!-- Password -->
<label for="password">Password</label>
<input type="password" id="password" name="password"
autocomplete="new-password" minlength="8"
aria-describedby="pw-hint" />
<p id="pw-hint">Minimum 8 characters.</p>
<!-- Number -->
<input type="number" min="1" max="100" step="1" />
<!-- Tel -->
<input type="tel" inputmode="tel" autocomplete="tel" />
<!-- Date / Time -->
<input type="date" min="2024-01-01" max="2030-12-31" />
<input type="datetime-local" />
<input type="time" />
<!-- URL -->
<input type="url" inputmode="url" placeholder="https://" />
<!-- Search -->
<input type="search" role="searchbox" aria-label="Search" />
<!-- Range / Color -->
<input type="range" min="0" max="100" value="50" />
<input type="color" value="#3b82f6" />
<!-- Textarea -->
<label for="bio">Bio</label>
<textarea id="bio" name="bio" rows="4" maxlength="500"
aria-describedby="bio-count"></textarea>
<span id="bio-count" aria-live="polite">0/500</span>
<!-- Select -->
<label for="country">Country</label>
<select id="country" name="country" autocomplete="country-name">
<option value="">Select a country</option>
<optgroup label="Americas">
<option value="US">United States</option>
<option value="CA">Canada</option>
</optgroup>
</select>
<!-- Checkbox & Radio -->
<fieldset>
<legend>Interests</legend>
<label><input type="checkbox" name="interests" value="design" /> Design</label>
<label><input type="checkbox" name="interests" value="code" /> Code</label>
</fieldset>
<fieldset>
<legend>Plan</legend>
<label><input type="radio" name="plan" value="free" checked /> Free</label>
<label><input type="radio" name="plan" value="pro" /> Pro</label>
</fieldset>
<!-- File upload -->
<label for="avatar">Avatar</label>
<input type="file" id="avatar" name="avatar"
accept="image/png, image/jpeg, image/webp"
aria-describedby="avatar-hint" />
<p id="avatar-hint">PNG, JPG, or WebP — max 2MB.</p>
<!-- Hidden -->
<input type="hidden" name="csrf_token" value="abc123" />
</fieldset>
<!-- Error state pattern -->
<div role="alert" aria-live="assertive" id="email-error" class="error">
Please enter a valid email address.
</div>
<input type="email" aria-invalid="true" aria-describedby="email-error" />
<button type="submit">Submit</button>
<button type="reset">Clear</button>
</form>
3. Accessibility & ARIA
ARIA Roles
<!-- Landmark roles (prefer semantic HTML elements) -->
<div role="banner"> <!-- = <header> -->
<div role="navigation"> <!-- = <nav> -->
<div role="main"> <!-- = <main> -->
<div role="contentinfo"> <!-- = <footer> -->
<div role="search"> <!-- search region -->
<div role="complementary"> <!-- = <aside> -->
<!-- Widget roles -->
<div role="button" tabindex="0" me</div>
<div role="dialog" aria-modal="true" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm Delete</h2>
</div>
<div role="alert" aria-live="assertive">Error message</div>
<div role="status" aria-live="polite">Saved!</div>
<div role="tooltip" id="tip">Tooltip text</div>
<ul role="listbox" aria-label="Options">
<li role="option" aria-selected="true">Option 1</li>
</ul>
<div role="tablist">
<button role="tab" aria-selected="true" aria-controls="panel-1">Tab 1</button>
<div role="tabpanel" id="panel-1">Content</div>
</div>
ARIA Properties & States
<!-- Labels -->
aria-label="Close dialog"
aria-labelledby="heading-id"
aria-describedby="hint-id error-id"
<!-- State -->
aria-expanded="false" <!-- accordion, dropdown -->
aria-checked="true" <!-- custom checkbox -->
aria-selected="true" <!-- tabs, listbox options -->
aria-disabled="true" <!-- disabled (still focusable) -->
aria-invalid="true" <!-- form validation -->
aria-pressed="false" <!-- toggle button -->
aria-busy="true" <!-- loading region -->
aria-hidden="true" <!-- hide from screen readers -->
<!-- Live regions -->
aria-live="polite" <!-- waits for user to finish -->
aria-live="assertive" <!-- interrupts (errors only) -->
aria-atomic="true" <!-- read entire region on change -->
<!-- Relationships -->
aria-controls="panel-id"
aria-owns="child-id"
aria-haspopup="listbox" <!-- or "dialog", "menu", "tree" -->
aria-current="page" <!-- current nav item -->
aria-setsize="10" aria-posinset="3" <!-- list position -->
Keyboard Accessibility
<!-- Focus management -->
<div tabindex="0">Focusable div</div> <!-- natural order -->
<div tabindex="-1">Focusable by JS only</div>
<!-- Never use tabindex > 0 -->
<!-- Skip link -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<main id="main-content">...</main>
<!-- Focus trap for modals (implement in JS) -->
<!-- Trap focus within dialog when open -->
<!-- Return focus to trigger element when closed -->
4. <head> — Meta Tags & SEO
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- SEO -->
<title>Page Title (50–60 chars) — Site Name</title>
<meta name="description" content="Page description 150–160 chars for search snippets." />
<meta name="robots" content="index, follow" />
<link rel="canonical" href="https://example.com/page" />
<!-- Open Graph (Facebook, LinkedIn, WhatsApp) -->
<meta property="og:type" content="website" />
<meta property="og:title" content="Page Title" />
<meta property="og:description" content="Description" />
<meta property="og:image" content="https://example.com/og-image.jpg" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="og:url" content="https://example.com/page" />
<meta property="og:site_name" content="Site Name" />
<!-- Twitter/X Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@handle" />
<meta name="twitter:title" content="Page Title" />
<meta name="twitter:description" content="Description" />
<meta name="twitter:image" content="https://example.com/twitter-image.jpg" />
<!-- Favicon -->
<link rel="icon" href="/favicon.ico" sizes="32x32" />
<link rel="icon" href="/icon.svg" type="image/svg+xml" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<link rel="manifest" href="/manifest.webmanifest" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preload" as="font" href="/fonts/inter.woff2" crossorigin />
<!-- Stylesheets -->
<link rel="stylesheet" href="/styles.css" />
<!-- Theme color (browser chrome) -->
<meta name="theme-color" content="#3b82f6" />
<meta name="color-scheme" content="light dark" />
</head>
5. Images & Media
<!-- Responsive image -->
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1200.jpg 1200w"
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 800px"
alt="Descriptive alt text — what does the image show and why is it here"
width="800" height="450"
loading="lazy"
decoding="async"
/>
<!-- Art direction with <picture> -->
<picture>
<source media="(max-width: 640px)" srcset="hero-mobile.webp" type="image/webp" />
<source media="(max-width: 640px)" srcset="hero-mobile.jpg" />
<source srcset="hero-desktop.webp" type="image/webp" />
<img src="hero-desktop.jpg" alt="Hero image" width="1200" height="600" />
</picture>
<!-- Decorative image (no alt needed) -->
<img src="decoration.svg" alt="" role="presentation" />
<!-- Video -->
<video controls preload="metadata" poster="thumbnail.jpg" width="800" height="450">
<source src="video.webm" type="video/webm" />
<source src="video.mp4" type="video/mp4" />
<track kind="subtitles" src="subs-en.vtt" srclang="en" label="English" default />
<p>Your browser doesn't support video. <a href="video.mp4">Download it</a>.</p>
</video>
<!-- Audio -->
<audio controls preload="metadata">
<source src="audio.ogg" type="audio/ogg" />
<source src="audio.mp3" type="audio/mpeg" />
</audio>
6. Interactive Elements
<!-- Native dialog (replaces custom modal JS) -->
<dialog id="my-dialog" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm Action</h2>
<p>Are you sure?</p>
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="confirm">Confirm</button>
</form>
</dialog>
<button
<!-- Details / Summary (native accordion) -->
<details>
<summary>Click to expand</summary>
<p>Hidden content revealed on click. No JS needed.</p>
</details>
<!-- Popover API (Chrome 114+) -->
<button popovertarget="my-popover">Toggle Popover</button>
<div id="my-popover" popover>
<p>Popover content — dismiss with Escape or outside click.</p>
</div>
<!-- Progress & Meter -->
<label for="progress">Upload progress:</label>
<progress id="progress" value="70" max="100">70%</progress>
<label for="disk">Disk usage:</label>
<meter id="disk" value="6" min="0" max="10" low="3" high="8" optimum="2">6 GB</meter>
7. Structured Data (JSON-LD)
<!-- Article -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Article",
"headline": "Article Title",
"author": { "@type": "Person", "name": "Alice Smith" },
"datePublished": "2025-01-15",
"image": "https://example.com/image.jpg",
"publisher": {
"@type": "Organization",
"name": "My Site",
"logo": { "@type": "ImageObject", "url": "https://example.com/logo.png" }
}
}
</script>
<!-- FAQ -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [{
"@type": "Question",
"name": "What is this?",
"acceptedAnswer": { "@type": "Answer", "text": "It is..." }
}]
}
</script>
<!-- BreadcrumbList -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{ "@type": "ListItem", "position": 1, "name": "Home", "item": "https://example.com" },
{ "@type": "ListItem", "position": 2, "name": "Blog", "item": "https://example.com/blog" }
]
}
</script>
8. Web Components
<!-- Custom element usage -->
<my-button variant="primary" size="lg">Click me</my-button>
<!-- Template & Slot -->
<template id="card-template">
<style> .card { border: 1px solid #e5e7eb; padding: 1rem; } </style>
<div class="card">
<slot name="title"><h2>Default Title</h2></slot>
<slot>Default content</slot>
<slot name="footer"></slot>
</div>
</template>
<!-- Shadow DOM via JS -->
<script>
class MyCard extends HTMLElement {
constructor() {
super()
const shadow = this.attachShadow({ mode: 'open' })
const template = document.getElementById('card-template')
shadow.appendChild(template.content.cloneNode(true))
}
static get observedAttributes() { return ['variant'] }
attributeChangedCallback(name, oldVal, newVal) { /* react to attr changes */ }
connectedCallback() { /* element added to DOM */ }
disconnectedCallback() { /* element removed from DOM */ }
}
customElements.define('my-card', MyCard)
</script>
<!-- Usage with slots -->
<my-card>
<h2 slot="title">Card Title</h2>
<p>Card body content.</p>
<button slot="footer">Action</button>
</my-card>
9. SVG in HTML
<!-- Inline SVG (best for icons — styleable with CSS) -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"
width="24" height="24" fill="none" stroke="currentColor"
stroke-width="2" aria-hidden="true" focusable="false">
<path stroke-linecap="round" stroke-linejoin="round"
d="M4.5 12.75l6 6 9-13.5" />
</svg>
<!-- SVG sprite pattern -->
<svg style="display:none">
<symbol id="icon-check" viewBox="0 0 24 24">
<path d="M4.5 12.75l6 6 9-13.5" />
</symbol>
</svg>
<!-- Use anywhere -->
<svg aria-hidden="true"><use href="#icon-check" /></svg>
<!-- Accessible SVG with title -->
<svg role="img" aria-labelledby="svg-title">
<title id="svg-title">Company Logo</title>
<!-- paths -->
</svg>
10. Performance HTML Patterns
<!-- Resource hints -->
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin />
<link rel="dns-prefetch" href="https://api.example.com" />
<link rel="preload" as="font" href="/fonts/inter.woff2" crossorigin />
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high" />
<link rel="prefetch" href="/dashboard" />
<link rel="modulepreload" href="/app.js" />
<!-- Script loading -->
<script src="app.js" defer></script> <!-- load async, execute in order after DOMContentLoaded -->
<script src="analytics.js" async></script> <!-- load + execute as soon as available -->
<script type="module" src="app.js"></script> <!-- ESM, deferred by default -->
<!-- Image performance -->
<img src="lcp-image.jpg" fetchpriority="high" loading="eager" /> <!-- LCP image -->
<img src="below-fold.jpg" loading="lazy" decoding="async" /> <!-- below fold -->
<!-- Lazy iframe -->
<iframe src="..." loading="lazy" title="Embedded content"></iframe>
Core Competency Summary
- Write semantic, accessible HTML5 that communicates meaning to browsers and screen readers
- Build inclusive forms with proper labels, ARIA, validation, and autocomplete
- Optimize
<head>with SEO meta tags, Open Graph, favicons, and resource hints - Mark up responsive images with
srcset,sizes, and<picture>for performance - Use native interactive elements:
<dialog>,<details>, Popover API - Add structured data with JSON-LD for rich search results
- Build reusable Web Components with Shadow DOM and slots
- Apply WCAG 2.1 AA accessibility standards throughout all markup