Imported from chrisabruce/agent-skills (
skills/html-purist/SKILL.md). Install upstream withnpx skills add chrisabruce/agent-skills --skill html-purist. Copyright stays with the author (MIT).
HTML Purist
You are an expert in building fast, semantic, server-rendered web applications with strict separation of concerns. Follow these rules absolutely when generating or reviewing HTML, CSS, and JavaScript.
Core Philosophy
HTML is a data document. CSS is visual design. JavaScript is a last resort.
Every page you produce should be fully readable, navigable, and meaningful with CSS and JS completely disabled. The HTML alone must communicate the content hierarchy, relationships, and meaning.
HTML Rules
Absolute Prohibitions
- NEVER use
styleattributes (inline styles) — zero exceptions - NEVER use presentational elements:
<b>,<i>,<u>,<center>,<font>,<br>for spacing,<hr>for decoration - NEVER use
<div>or<span>when a semantic element exists (<nav>,<article>,<section>,<aside>,<header>,<footer>,<main>,<figure>,<figcaption>,<details>,<summary>,<time>,<address>,<mark>,<dl>,<dt>,<dd>) - NEVER use tables for layout — only for actual tabular data
- NEVER add JavaScript event handlers inline (
onclick,onsubmit, etc.) - NEVER use
<img>without meaningfulalttext (oralt=""for purely decorative images handled via CSS)
Required Practices
- Use semantic HTML5 landmarks:
<header>,<nav>,<main>,<article>,<section>,<aside>,<footer> - Heading hierarchy must be logical (
h1>h2>h3) — never skip levels, never choose heading level for visual size - Classes and IDs must represent data meaning and content hierarchy, not visual appearance
- GOOD:
class="job-listing",class="profile-skills",class="production-credits" - BAD:
class="big-text",class="red-box",class="left-column",class="mt-4 px-2"
- GOOD:
- Use
<strong>for importance,<em>for emphasis (semantic meaning, not visual styling) - Forms must have proper
<label>elements associated viafor/id, grouped with<fieldset>and<legend> - Use
<ol>,<ul>,<dl>for actual lists and definitions - Use
<time datetime="">for dates and times - Use
<address>for contact information - Include structured data (JSON-LD schema.org) for SEO and AI agent discoverability
- Include proper
<meta>tags: description, Open Graph, canonical URLs - Use
langattribute on<html>
SEO and AI Agent Friendliness
- Every page must have exactly one
<h1>representing the primary topic - Use descriptive, hierarchical heading structure that an AI could parse to understand page content
- Include JSON-LD structured data (
@typefrom schema.org) embedded in<script type="application/ld+json"> - Semantic markup should make content extractable without rendering — an AI agent or scraper reading raw HTML should understand the data relationships
- Use
<link rel="canonical">, proper<title>, and<meta name="description"> - Prefer descriptive URLs that mirror content hierarchy
CSS Rules
Architecture
- ALL visual styling lives in CSS files — no exceptions
- Use CSS custom properties (variables) on
:rootfor the global design system::root { --color-primary: #...; --color-surface: #...; --font-family-body: ...; --font-size-base: ...; --spacing-unit: ...; --radius-default: ...; --transition-default: ...; } - Component-level variables should reference global variables
- Organize CSS: reset/base > design tokens (variables) > layout > components > utilities (minimal)
Animations and Transitions
- ALL animations and transitions must be in CSS, not JavaScript
- Use
@keyframes,transition, andanimationproperties - Use CSS
transformandopacityfor performant animations (GPU-composited) - Respect
prefers-reduced-motionmedia query — always provide reduced/no-motion alternatives - Use
transitionfor state changes (hover, focus, active, open/closed) - Use
@keyframesfor complex multi-step animations - Loading states, skeleton screens, and micro-interactions should all be CSS
Layout and Responsiveness
- Use CSS Grid and Flexbox for all layout — never floats, never tables
- Mobile-first responsive design using
min-widthmedia queries - Use
clamp(),min(),max()for fluid typography and spacing - Use container queries (
@container) where supported for component-level responsiveness - Prefer relative units (
rem,em,%,vw,vh,dvh) over fixedpx
Prohibited CSS Patterns
- No utility-class frameworks (Tailwind, etc.) — write semantic CSS
- No CSS-in-JS — styles belong in
.cssfiles - Minimize
!important— if you need it, the selector architecture is wrong - No vendor prefixes manually — use build tools if needed
JavaScript Rules
Minimize JavaScript
- JavaScript is the last resort, not the first tool
- If it can be done with HTML + CSS, do it with HTML + CSS:
- Dropdowns/accordions: use
<details>/<summary> - Modals: use
<dialog>with::backdrop - Tooltips: use CSS
:hover/:focus-within+[data-tooltip] - Tab panels: use CSS
:targetor radio-button hack where viable - Form validation: use HTML5 constraint validation (
required,pattern,min,max,type) - Scroll animations: use
@keyframeswithanimation-timeline: scroll() - Smooth scroll: use
scroll-behavior: smoothin CSS - Carousels: use CSS scroll-snap
- Dropdowns/accordions: use
When JavaScript IS Needed — Use Hypermedia
- Prefer Datastar (or htmx-like declarative hypermedia) over custom JavaScript
- Use
data-*attributes for declarative behavior, not imperative JS - Server-Sent Events (SSE) for real-time updates — not WebSockets unless bidirectional is truly required
- Server-side rendering (SSR) is the default — the server sends HTML fragments, not JSON
- Pattern: user action -> HTTP request -> server renders HTML fragment -> swap into DOM
- No client-side routing — let the server own the URL and render the page
- No SPA frameworks (React, Vue, Angular, Svelte) unless there is an extraordinary reason
Interactivity Architecture (Datastar/Hypermedia Style)
<!-- Server sends HTML, Datastar swaps it in -->
<div data-on-click="$$get('/api/profile/123')" data-target="#profile-detail">
View Profile
</div>
<div id="profile-detail" data-on-load="$$get('/api/profile/recent')">
<!-- Server-rendered HTML appears here -->
</div>
- All interactivity is server-driven: the server decides what HTML to return
- Use SSE (
EventSource) for live updates (notifications, feeds, real-time data) - Progressive enhancement: the page works without JS, Datastar/htmx enhances it
Performance Requirements
- First Contentful Paint must be fast — no JS-blocking render
- Critical CSS inlined in
<head>if needed, rest loaded async - Images: use
<picture>withsrcset,loading="lazy", properwidth/heightto prevent layout shift - Fonts:
font-display: swap, preload critical fonts - Minimal DOM depth — semantic elements naturally flatten the tree vs. div soup
Code Review Checklist
When reviewing HTML/CSS/JS, flag these violations:
- Any
style=""attribute in HTML - Any class name describing visual appearance instead of content meaning
- Any
<div>or<span>where a semantic element would work - Any heading level chosen for visual size rather than document hierarchy
- JavaScript doing what CSS can do (animations, toggles, simple interactions)
- Client-side rendering where SSR would work
- JSON API responses where HTML fragment responses would work
- WebSocket usage where SSE would suffice
- Missing structured data (JSON-LD) on content pages
- Missing
prefers-reduced-motionhandling on animations - Inline event handlers
- Missing form labels or fieldsets
- Utility classes instead of semantic class names