html
SKILL.md
Expert HTML Development
Write clean, semantic, accessible HTML that follows modern web standards and best practices.
MCP Integration - Context7
CRITICAL: Before writing or editing ANY HTML code, ALWAYS use the Context7 MCP server to check for relevant context.
Context7 provides access to a knowledge base that may contain:
- Project-specific HTML patterns and conventions
- Custom component libraries and templates
- Style guide requirements
- Accessibility standards for the project
- Performance benchmarks and requirements
- Team preferences and coding standards
Workflow:
- Before writing code: Query Context7 for relevant patterns, conventions, or examples
- During editing: Check Context7 for project-specific requirements that might affect your changes
- After writing: Verify your code aligns with any Context7 guidance
Use Context7 to search for topics like:
- "HTML conventions"
- "accessibility requirements"
- "component templates"
- "form patterns"
- "performance standards"
- Specific component or pattern names
Never skip the Context7 check - it ensures your HTML aligns with project standards and leverages existing patterns.
Core Principles
- Semantic first - Use elements for their intended meaning, not just appearance
- Accessibility by default - Every user deserves a great experience
- Progressive enhancement - Start with working HTML, layer on CSS/JS
- Performance matters - Optimize for speed and efficiency
Document Structure
Minimal Valid HTML5 Document
<!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>
<meta name="description" content="Clear, concise page description (150-160 chars)">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<header>
<nav aria-label="Main navigation">
<!-- Navigation -->
</nav>
</header>
<main>
<!-- Primary content -->
</main>
<footer>
<!-- Footer content -->
</footer>
<script src="script.js" defer></script>
</body>
</html>
Essential Meta Tags
<head>
<!-- Character encoding (must be first) -->
<meta charset="UTF-8">
<!-- Viewport for responsive design -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- SEO basics -->
<title>Specific, Descriptive Title | Brand</title>
<meta name="description" content="Clear description for search results">
<link rel="canonical" href="https://example.com/page">
<!-- Open Graph for social sharing -->
<meta property="og:title" content="Title for Social Media">
<meta property="og:description" content="Description for social cards">
<meta property="og:image" content="https://example.com/image.jpg">
<meta property="og:url" content="https://example.com/page">
<meta property="og:type" content="website">
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Title for Twitter">
<meta name="twitter:description" content="Description for Twitter">
<meta name="twitter:image" content="https://example.com/image.jpg">
<!-- Favicon (use multiple sizes for best support) -->
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<!-- Preload critical resources -->
<link rel="preload" href="critical-font.woff2" as="font" type="font/woff2" crossorigin>
<!-- Theme color for browser UI -->
<meta name="theme-color" content="#000000" media="(prefers-color-scheme: dark)">
<meta name="theme-color" content="#ffffff" media="(prefers-color-scheme: light)">
</head>
Semantic HTML Elements
Use the Right Element for the Job
<!-- ā Generic divs for everything -->
<div class="header">
<div class="nav">
<div class="nav-item">Home</div>
</div>
</div>
<div class="content">
<div class="article">
<div class="title">Article Title</div>
<div class="text">Article content...</div>
</div>
</div>
<!-- ā
Semantic elements with clear meaning -->
<header>
<nav aria-label="Main navigation">
<a href="/">Home</a>
</nav>
</header>
<main>
<article>
<h1>Article Title</h1>
<p>Article content...</p>
</article>
</main>
Sectioning Elements
| Element | Purpose | When to Use |
|---|---|---|
<header> |
Introductory content | Site/section header, not just "top of page" |
<nav> |
Navigation links | Primary navigation, table of contents, breadcrumbs |
<main> |
Primary content | One per page, skips to main content |
<article> |
Self-contained content | Blog posts, news items, forum posts |
<section> |
Thematic grouping | Chapters, tabs, themed sections (always has heading) |
<aside> |
Tangentially related | Sidebars, pull quotes, related links |
<footer> |
Footer content | Site/section footer, not just "bottom of page" |
Heading Hierarchy
<!-- ā
Logical hierarchy (never skip levels) -->
<h1>Page Title</h1>
<h2>Main Section</h2>
<h3>Subsection</h3>
<h3>Another Subsection</h3>
<h2>Another Main Section</h2>
<!-- ā Bad: skipping levels -->
<h1>Page Title</h1>
<h4>Skipped h2 and h3</h4>
Accessibility Best Practices
ARIA Labels and Roles
<!-- Landmark labels for navigation -->
<nav aria-label="Main navigation">
<ul>
<li><a href="/">Home</a></li>
</ul>
</nav>
<nav aria-label="Footer navigation">
<!-- Footer links -->
</nav>
<!-- Button with accessible name -->
<button aria-label="Close dialog">
<svg aria-hidden="true"><!-- X icon --></svg>
</button>
<!-- Hide decorative content from screen readers -->
<img src="decorative.png" alt="" role="presentation">
<span aria-hidden="true">š§</span>
<!-- Associate labels with inputs -->
<label for="email">Email Address</label>
<input type="email" id="email" name="email" required>
<!-- Or wrap inputs in labels -->
<label>
Email Address
<input type="email" name="email" required>
</label>
Focus Management
<!-- Skip to main content link (first interactive element) -->
<a href="#main" class="skip-link">Skip to main content</a>
<main id="main" tabindex="-1">
<!-- Content -->
</main>
<!-- Ensure custom interactive elements are keyboard accessible -->
<div role="button" tabindex="0" onclick="handleClick()" onkeydown="handleKeyPress(event)">
Custom Button
</div>
Alternative Text
<!-- ā
Descriptive alt text -->
<img src="chart.png" alt="Bar chart showing 40% increase in sales from Q1 to Q2 2024">
<!-- ā
Empty alt for decorative images -->
<img src="decorative-border.png" alt="">
<!-- ā
Complex images with long descriptions -->
<img src="complex-diagram.png" alt="Network topology diagram" aria-describedby="diagram-desc">
<div id="diagram-desc">
Detailed description of the network topology showing...
</div>
<!-- ā Bad alt text -->
<img src="chart.png" alt="image">
<img src="chart.png" alt="chart.png">
ARIA Live Regions
<!-- Announce dynamic content updates -->
<div role="status" aria-live="polite" aria-atomic="true">
<span id="status-message">Loading...</span>
</div>
<!-- Alert for critical messages -->
<div role="alert" aria-live="assertive">
Error: Please correct the form errors below.
</div>
Forms
Accessible Form Structure
<form method="post" action="/submit" novalidate>
<fieldset>
<legend>Personal Information</legend>
<!-- Text input with validation -->
<label for="name">Full Name</label>
<input
type="text"
id="name"
name="name"
required
aria-required="true"
aria-describedby="name-hint"
autocomplete="name"
>
<small id="name-hint">Enter your first and last name</small>
<!-- Email with pattern validation -->
<label for="email">Email</label>
<input
type="email"
id="email"
name="email"
required
aria-required="true"
autocomplete="email"
aria-invalid="false"
>
<span id="email-error" class="error" role="alert"></span>
<!-- Select with grouped options -->
<label for="country">Country</label>
<select id="country" name="country" required>
<option value="">Select a country</option>
<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="de">Germany</option>
</optgroup>
</select>
<!-- Radio buttons (same name groups them) -->
<fieldset>
<legend>Preferred Contact Method</legend>
<label>
<input type="radio" name="contact" value="email" checked>
Email
</label>
<label>
<input type="radio" name="contact" value="phone">
Phone
</label>
</fieldset>
<!-- Checkboxes for multiple selection -->
<fieldset>
<legend>Newsletter Subscriptions</legend>
<label>
<input type="checkbox" name="newsletters" value="weekly">
Weekly Updates
</label>
<label>
<input type="checkbox" name="newsletters" value="monthly">
Monthly Digest
</label>
</fieldset>
<!-- Textarea with character counter -->
<label for="message">Message</label>
<textarea
id="message"
name="message"
rows="5"
maxlength="500"
aria-describedby="char-count"
></textarea>
<small id="char-count">0 / 500 characters</small>
</fieldset>
<button type="submit">Submit Form</button>
<button type="reset">Clear Form</button>
</form>
HTML5 Input Types
<!-- Use specific input types for better UX and validation -->
<input type="email" name="email" autocomplete="email">
<input type="tel" name="phone" autocomplete="tel">
<input type="url" name="website">
<input type="number" name="quantity" min="1" max="100" step="1">
<input type="range" name="volume" min="0" max="100" value="50">
<input type="date" name="dob" autocomplete="bday">
<input type="time" name="appointment">
<input type="datetime-local" name="event-time">
<input type="color" name="theme-color" value="#000000">
<input type="file" name="upload" accept="image/*,.pdf" multiple>
<input type="search" name="query" autocomplete="off">
Input Attributes for Better UX
<!-- Autocomplete for faster input -->
<input type="text" name="given-name" autocomplete="given-name">
<input type="text" name="family-name" autocomplete="family-name">
<input type="text" name="address-line1" autocomplete="address-line1">
<input type="text" name="postal-code" autocomplete="postal-code">
<!-- Pattern validation with custom message -->
<input
type="text"
name="username"
pattern="[a-zA-Z0-9_]{3,16}"
title="Username must be 3-16 characters, letters, numbers, and underscores only"
>
<!-- Autofocus (use sparingly, only once per page) -->
<input type="text" name="search" autofocus>
<!-- Input mode for mobile keyboards -->
<input type="text" inputmode="numeric" name="credit-card">
<input type="text" inputmode="decimal" name="price">
<input type="text" inputmode="email" name="email">
Tables
Accessible Data Tables
<!-- Simple table with headers -->
<table>
<caption>Quarterly Sales Report for 2024</caption>
<thead>
<tr>
<th scope="col">Quarter</th>
<th scope="col">Revenue</th>
<th scope="col">Growth</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Q1</th>
<td>$1.2M</td>
<td>+5%</td>
</tr>
<tr>
<th scope="row">Q2</th>
<td>$1.5M</td>
<td>+25%</td>
</tr>
</tbody>
<tfoot>
<tr>
<th scope="row">Total</th>
<td>$2.7M</td>
<td>+15%</td>
</tr>
</tfoot>
</table>
<!-- Complex table with headers for rows and columns -->
<table>
<caption>Course Schedule by Instructor and Day</caption>
<thead>
<tr>
<th scope="col">Instructor</th>
<th scope="col">Monday</th>
<th scope="col">Tuesday</th>
<th scope="col">Wednesday</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Dr. Smith</th>
<td headers="smith monday">Biology 101</td>
<td headers="smith tuesday">Biology 201</td>
<td headers="smith wednesday">Lab</td>
</tr>
</tbody>
</table>
Lists
Choosing the Right List Type
<!-- Unordered list (order doesn't matter) -->
<ul>
<li>Apples</li>
<li>Bananas</li>
<li>Oranges</li>
</ul>
<!-- Ordered list (sequence matters) -->
<ol>
<li>Preheat oven to 350°F</li>
<li>Mix dry ingredients</li>
<li>Add wet ingredients</li>
<li>Bake for 25 minutes</li>
</ol>
<!-- Description list (key-value pairs) -->
<dl>
<dt>HTML</dt>
<dd>HyperText Markup Language, the standard markup language for web pages.</dd>
<dt>CSS</dt>
<dd>Cascading Style Sheets, used to style HTML elements.</dd>
</dl>
<!-- Nested lists -->
<ul>
<li>Fruits
<ul>
<li>Tropical
<ul>
<li>Mango</li>
<li>Papaya</li>
</ul>
</li>
<li>Citrus
<ul>
<li>Orange</li>
<li>Lemon</li>
</ul>
</li>
</ul>
</li>
</ul>
Performance Optimization
Image Optimization
<!-- Responsive images with srcset -->
<img
src="image-800w.jpg"
srcset="
image-400w.jpg 400w,
image-800w.jpg 800w,
image-1200w.jpg 1200w,
image-1600w.jpg 1600w
"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 50vw, 800px"
alt="Descriptive alt text"
loading="lazy"
decoding="async"
width="800"
height="600"
>
<!-- Art direction with picture element -->
<picture>
<source media="(max-width: 799px)" srcset="mobile-image.jpg">
<source media="(min-width: 800px)" srcset="desktop-image.jpg">
<img src="fallback-image.jpg" alt="Descriptive alt text">
</picture>
<!-- Modern image formats with fallback -->
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="Descriptive alt text">
</picture>
<!-- Lazy loading (native) -->
<img src="below-fold-image.jpg" alt="Alt text" loading="lazy">
<!-- Eager loading for above-fold critical images -->
<img src="hero-image.jpg" alt="Alt text" loading="eager" fetchpriority="high">
Resource Loading Strategies
<head>
<!-- Preconnect to external domains -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com">
<!-- DNS prefetch for less critical connections -->
<link rel="dns-prefetch" href="https://analytics.example.com">
<!-- Preload critical resources -->
<link rel="preload" href="critical-font.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="hero-image.jpg" as="image">
<link rel="preload" href="critical-styles.css" as="style">
<!-- Prefetch for likely next navigation -->
<link rel="prefetch" href="/next-page.html">
<link rel="prefetch" href="next-page-image.jpg" as="image">
</head>
<body>
<!-- Defer non-critical JavaScript -->
<script src="analytics.js" defer></script>
<!-- Async for independent scripts -->
<script src="widget.js" async></script>
<!-- Module scripts (deferred by default) -->
<script type="module" src="app.js"></script>
<!-- Inline critical CSS, load rest async -->
<style>
/* Critical above-fold CSS */
</style>
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">
<noscript><link rel="stylesheet" href="non-critical.css"></noscript>
</body>
HTML Size Optimization
<!-- ā Unnecessary whitespace and comments -->
<div class="container">
<!-- This is a comment -->
<p>
Some text here
</p>
</div>
<!-- ā
Minified HTML (use build tools) -->
<div class="container"><p>Some text here</p></div>
<!-- Remove unused attributes -->
<!-- ā --> <div id="unused-id" class="unused-class"></div>
<!-- ā
--> <div></div>
<!-- Combine inline styles (or better, use CSS) -->
<!-- ā --> <p style="color: red;"><span style="font-weight: bold;">Text</span></p>
<!-- ā
--> <p class="error-text">Text</p>
Modern HTML Features
Details and Summary (Native Disclosure)
<details>
<summary>Click to expand</summary>
<p>Hidden content that can be toggled.</p>
</details>
<!-- Open by default -->
<details open>
<summary>FAQ Question</summary>
<p>Answer to the question.</p>
</details>
Dialog Element (Native Modal)
<dialog id="myDialog">
<form method="dialog">
<h2>Dialog Title</h2>
<p>Dialog content goes here.</p>
<button value="cancel">Cancel</button>
<button value="confirm" autofocus>Confirm</button>
</form>
</dialog>
<button onclick="document.getElementById('myDialog').showModal()">
Open Dialog
</button>
Data Attributes
<!-- Store custom data -->
<article data-post-id="12345" data-author="jane-doe" data-category="tech">
<h2>Article Title</h2>
</article>
<!-- Access in JavaScript: element.dataset.postId -->
<!-- Style in CSS: [data-category="tech"] { } -->
Template Element
<!-- Define reusable markup -->
<template id="item-template">
<li class="item">
<h3 class="item-title"></h3>
<p class="item-description"></p>
</li>
</template>
<!-- Clone and use with JavaScript -->
<script>
const template = document.getElementById('item-template');
const clone = template.content.cloneNode(true);
// Populate and append
</script>
Content Embedding
Videos
<!-- Native video with controls -->
<video
controls
width="640"
height="360"
poster="video-thumbnail.jpg"
preload="metadata"
>
<source src="video.webm" type="video/webm">
<source src="video.mp4" type="video/mp4">
<track
kind="subtitles"
src="subtitles-en.vtt"
srclang="en"
label="English"
>
<track
kind="captions"
src="captions-en.vtt"
srclang="en"
label="English CC"
default
>
<p>Your browser doesn't support HTML5 video.
<a href="video.mp4">Download the video</a> instead.</p>
</video>
<!-- YouTube embed with title for accessibility -->
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/VIDEO_ID"
title="Video Title for Accessibility"
frameborder="0"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowfullscreen
loading="lazy"
></iframe>
Audio
<audio controls preload="metadata">
<source src="audio.mp3" type="audio/mpeg">
<source src="audio.ogg" type="audio/ogg">
<p>Your browser doesn't support HTML5 audio.
<a href="audio.mp3">Download the audio</a> instead.</p>
</audio>
Iframes
<!-- Iframe with proper attributes -->
<iframe
src="https://example.com/embed"
title="Descriptive title for screen readers"
width="600"
height="400"
loading="lazy"
sandbox="allow-scripts allow-same-origin"
></iframe>
Validation and Quality
HTML Validation Checklist
- Valid DOCTYPE declaration
- Proper nesting (no overlapping tags)
- All tags properly closed
- Unique IDs on a page
- Valid attributes for each element
- Proper character encoding (UTF-8)
- Alt text for all images (or empty alt for decorative)
- Form labels associated with inputs
- Heading hierarchy (no skipped levels)
- Lang attribute on html tag
- Valid HTML5 (use W3C validator)
Common HTML Mistakes to Avoid
<!-- ā Block element inside inline element -->
<a href="#"><div>Link content</div></a>
<!-- ā
Inline element or make the link a block -->
<a href="#" style="display: block"><div>Link content</div></a>
<!-- ā Using <br> for spacing -->
<p>First paragraph</p>
<br><br>
<p>Second paragraph</p>
<!-- ā
Use CSS margins -->
<p>First paragraph</p>
<p style="margin-top: 2rem;">Second paragraph</p>
<!-- ā Divitis (unnecessary divs) -->
<div>
<div>
<div>
<p>Content</p>
</div>
</div>
</div>
<!-- ā
Minimal markup -->
<p>Content</p>
<!-- ā Empty elements with no purpose -->
<div></div>
<span></span>
<!-- ā
Remove or add content/styling purpose -->
<!-- ā Using tables for layout -->
<table>
<tr>
<td>Sidebar</td>
<td>Main content</td>
</tr>
</table>
<!-- ā
Use CSS Grid or Flexbox -->
<div class="layout">
<aside>Sidebar</aside>
<main>Main content</main>
</div>
Security Best Practices
Input Sanitization Context
<!-- Never trust user input - always sanitize on server -->
<!-- ā Dangerous: directly embedding user input -->
<div>{{userInput}}</div>
<!-- ā
Escape HTML entities -->
<div><script>alert('safe')</script></div>
<!-- Use Content Security Policy -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://trusted.cdn.com">
External Resources
<!-- Add integrity checks for external resources -->
<script
src="https://cdn.example.com/library.js"
integrity="sha384-HASH_HERE"
crossorigin="anonymous"
></script>
<!-- Use rel="noopener" for external links -->
<a href="https://external-site.com" target="_blank" rel="noopener noreferrer">
External Link
</a>
Progressive Enhancement
Build from HTML Up
<!-- 1. Start with working HTML -->
<details>
<summary>Expandable Section</summary>
<p>Content revealed when expanded.</p>
</details>
<!-- 2. Enhance with CSS (optional) -->
<style>
details { border: 1px solid #ccc; padding: 1rem; }
summary { cursor: pointer; font-weight: bold; }
</style>
<!-- 3. Add JavaScript enhancements (optional) -->
<script>
// Add analytics, animations, or custom behavior
document.querySelectorAll('details').forEach(detail => {
detail.addEventListener('toggle', () => {
if (detail.open) {
// Track expansion
}
});
});
</script>
Tools and Workflow
Recommended Tools
| Purpose | Tool | Why |
|---|---|---|
| Validation | W3C Validator | Check HTML validity |
| Accessibility | axe DevTools | Find a11y issues |
| Performance | Lighthouse | Audit performance |
| HTML Minification | html-minifier | Reduce file size |
| Linting | HTMLHint | Catch common mistakes |
HTML in Build Pipelines
# Validate HTML
npx html-validate src/**/*.html
# Minify HTML
npx html-minifier --collapse-whitespace --remove-comments input.html -o output.html
# Check accessibility
npx pa11y http://localhost:3000
Quick Reference
Common Character Entities
| Character | Entity | Numeric |
|---|---|---|
| < | < |
< |
| > | > |
> |
| & | & |
& |
| " | " |
" |
| ' | ' |
' |
| Ā© | © |
© |
| Ā® | ® |
® |
| ⢠| ™ |
™ |
| non-breaking space | |
  |
| ā (em dash) | — |
— |
| ā (en dash) | – |
– |
Global Attributes
Available on all HTML elements:
id- Unique identifierclass- CSS class names (space-separated)style- Inline CSS stylestitle- Advisory information (tooltip)lang- Language of element contentdir- Text directionality (ltr, rtl, auto)hidden- Hide elementtabindex- Tab order (-1, 0, positive numbers)contenteditable- Make element editabledata-*- Custom data attributesdraggable- Enable drag and dropspellcheck- Enable spell checking
Best Practices Summary
- Always check Context7 MCP before writing/editing code - Leverage project-specific patterns and requirements
- Always use semantic HTML - Choose elements based on meaning, not appearance
- Validate your HTML - Use W3C validator to catch errors
- Prioritize accessibility - Use ARIA attributes, alt text, and keyboard navigation
- Optimize for performance - Lazy load images, defer scripts, minimize HTML
- Use progressive enhancement - Start with HTML, layer on CSS and JavaScript
- Keep it simple - Don't over-engineer with unnecessary divs and complexity
- Test across browsers - Ensure compatibility with all major browsers
- Think mobile-first - Design for small screens, enhance for larger ones
- Use meaningful names - IDs and classes should describe purpose, not appearance
- Comment sparingly - Code should be self-documenting, comments for complex logic only
Weekly Installs
4
Repository
jaredlander/freshbooks-speedFirst Seen
2 days ago
Installed on
claude-code3
windsurf2
trae2
opencode2
codex2
antigravity2