---
name: html-purist
description: >
  Enforce strict separation of HTML (structure/data) and CSS (visual design) when writing
  or reviewing web markup. Use this skill when generating HTML, CSS, or JavaScript for any
  web page, component, or template — even if not explicitly asked for semantic markup. Covers
  zero inline styles, semantic-only elements, CSS custom properties, CSS-first animations,
  minimal JavaScript, Datastar/htmx hypermedia over SPAs, SSR + SSE, and JSON-LD for
  SEO/AI agents.
metadata:
  author: chrisabruce
  version: "1.0.0"
  category: web-development
license: 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 `style` attributes (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 meaningful `alt` text (or `alt=""` 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"`
- Use `<strong>` for importance, `<em>` for emphasis (semantic meaning, not visual styling)
- Forms must have proper `<label>` elements associated via `for`/`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 `lang` attribute 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 (`@type` from 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 `:root` for the global design system:
  ```css
  :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`, and `animation` properties
- Use CSS `transform` and `opacity` for performant animations (GPU-composited)
- Respect `prefers-reduced-motion` media query — always provide reduced/no-motion alternatives
- Use `transition` for state changes (hover, focus, active, open/closed)
- Use `@keyframes` for 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-width` media 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 fixed `px`

### Prohibited CSS Patterns
- No utility-class frameworks (Tailwind, etc.) — write semantic CSS
- No CSS-in-JS — styles belong in `.css` files
- 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 `:target` or radio-button hack where viable
  - Form validation: use HTML5 constraint validation (`required`, `pattern`, `min`, `max`, `type`)
  - Scroll animations: use `@keyframes` with `animation-timeline: scroll()`
  - Smooth scroll: use `scroll-behavior: smooth` in CSS
  - Carousels: use CSS scroll-snap

### 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)
```html
<!-- 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>` with `srcset`, `loading="lazy"`, proper `width`/`height` to 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-motion` handling on animations
- [ ] Inline event handlers
- [ ] Missing form labels or fieldsets
- [ ] Utility classes instead of semantic class names
