# HTML Skill

> This skill should be used when the user asks about HTML, HTML5 elements, semantic HTML, accessibility (ARIA, WCAG), forms and inputs, HTML attributes, meta tags, SEO markup, Open Graph, structured data (JSON-LD, Schema.org), iframes, canvas, SVG, web components, custom elements, shadow DOM, HTML templating, favicons, head elements, or any HTML markup topic. Trigger when the user mentions "html", "semantic html", "aria", "aria-label", "role attribute", "meta tags", "open graph", "schema.org", "json-ld", "html form", "input type", "fieldset", "dialog element", "details/summary", "picture element", "srcset", "web components", "shadow dom", "slot", "template tag", or asks about HTML structure, elements, or accessibility.

- Skill: `sirhamza/html-skill` (Agent Skill)
- Install (CLI): `npx skillmds@latest add sirhamza/html-skill`
- Raw SKILL.md: https://api.skillmd.com/api/skills/sirhamza/html-skill/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- Author: SirHamza (https://skillmd.com/u/sirhamza)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/sirhamza/html-skill

---


# 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
```html
<!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
```html
<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
```html
<!-- 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

```html
<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
```html
<!-- 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" onkeydown="...">Click 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
```html
<!-- 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
```html
<!-- 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

```html
<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

```html
<!-- 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

```html
<!-- 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 onclick="document.getElementById('my-dialog').showModal()">Open</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)

```html
<!-- 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

```html
<!-- 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

```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

```html
<!-- 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

