---
name: page-template
description: Scaffold a sitemap-organized HTML showcase site (provenance reports, contractor showcases, telemetry dashboards, weekly digests, audit results).
allowed-tools: Read, Write, Edit, Bash
---

# HTML Showcase — Page Template

> **Self-Evolving Skill**: This skill improves through use. If instructions
> are wrong, parameters drifted, or a workaround was needed — fix this file
> immediately, don't defer. Only update for real, reproducible issues.

A static HTML mini-site whose **navigation structure is the filesystem
layout itself**. Every page links to a shared CSS kernel served from
jsDelivr; an auto-discovered nav rail and a master `site-map.html` are
generated by `scripts/build-nav.py` from whatever directories you create
under the site root. Pages are pure HTML with optional per-page CSS
overrides. The architecture is built on five principles — **read
[`references/principles.md`](references/principles.md) first** to
internalize the WHY before extending or forking, and
[`references/sitemap.md`](references/sitemap.md) for the navigation
contract.

## The sitemap is the default

Every site this skill produces has the same shape:

```
<site-root>/
  index.html              ← site home (recommended)
  overrides.css           ← optional per-site tweaks
  auto-nav.css            ← generated by build-nav.py
  auto-nav.js             ← generated by build-nav.py
  site-map.html           ← generated by build-nav.py
  <section-1>/            ← any subdirectory becomes a "section"
    index.html
    page-a.html
    page-b.html
  <2026-05-02-audit>/     ← YYYY-MM-DD- prefix sorts sections newest-first
    index.html
    findings.html
```

You don't hand-write the nav. You add HTML files in the directory shape
you want, then run `scripts/build-nav.py --root <site-root>`. The script
walks the tree, generates `site-map.html`, writes the rail's CSS/JS, and
injects the same nav rail into every page using comment markers. Re-runs
are idempotent.

For the architecture and trade-offs, see
[`references/sitemap.md`](references/sitemap.md).

## Read this skill at the principle level, not the instruction level

Every concrete artifact in this skill (class names, file paths, the
specific CDN URL, the commit-message conventions, even the "section"
naming) is an _instance_ of a small set of underlying principles. If you
understand the principles, you can deviate intelligently from any
specific instance without breaking the architecture. If you only follow
the instructions, you'll bend the system out of shape the first time
something doesn't fit your case.

The principles are catalogued in [`references/principles.md`](references/principles.md):

1. **Single source of truth** — every visual decision lives in one file
2. **Semantic over atomic** — class names describe what an element _is_
3. **Token-driven** — every concrete value flows from a CSS custom property
4. **Cascade discipline** — `@layer` ordering enforces specificity globally
5. **No hidden state** — no JS, no inline CSS, no scattered overrides
6. **Filesystem-as-sitemap** — directory layout IS the navigation graph

Plus AI-collaboration patterns (why this design is LLM-friendly), and
the rationale for using a CDN rather than copies.

## Three-layer hierarchy

| Layer                | Mutability                     | What it controls                                                | Where it lives                       |
| -------------------- | ------------------------------ | --------------------------------------------------------------- | ------------------------------------ |
| **H1 — Kernel**      | Edit once → ripples everywhere | Tokens (color, spacing, type), reset, base elements, components | `assets/showcase.css` (jsDelivr CDN) |
| **H2 — Composition** | Per-page                       | Section order, content, semantic markup                         | The HTML file itself                 |
| **H3 — Overrides**   | Per-page (optional)            | Color or density tweaks for ONE page                            | `overrides.css` next to the HTML     |

The kernel is the SSoT for every visual decision. HTML never invents
styles; it only arranges components defined by the kernel. To customize
one page, drop a few CSS variables into `overrides.css`. To customize
EVERY page, edit the kernel.

The auto-nav rail is _generated_, not hand-written. Its styling lives in
`auto-nav.css` (written by `build-nav.py`); its content is injected
between `<!-- AUTO-NAV-START -->` and `<!-- AUTO-NAV-END -->` markers in
each page's `<body>`.

The rail's runtime behavior:

- **First load**: width auto-fits to the longest unwrapped link (uses
  `width: max-content` to measure each link's true intrinsic width,
  independent of the rail's current size), clamped to `[220, 760]px`.
- **Drag the right-edge handle**: resize manually within `[220, 1200]px`;
  the chosen width is persisted in `localStorage`.
- **Double-click the handle**: clears the saved width and re-runs
  auto-fit (semantically: "reset to smart default").
- The handle is a 14px hit zone with an always-visible 2px indicator
  line and a hover tooltip explaining the dual gesture.

## Search is on by default

Every rail (and the master `site-map.html`) gets a **Search** section
mounted at the top, powered by [Pagefind](https://pagefind.app/) — a
Rust-built static-search tool that produces a self-contained index with
no server, no build pipeline, and a ~70KB client UI.

How it's wired:

- `build-nav.py` injects 4 tags into every page's `<head>` in strict
  order: pagefind CSS → auto-nav CSS → pagefind JS → auto-nav JS.
- The rail's first section is `<div id="auto-nav-search"></div>`;
  `auto-nav.js` calls `new PagefindUI({...})` once Pagefind has loaded.
- The actual index lives at `<site-root>/pagefind/`, generated by
  running `pagefind --site <site-root>`.
- `scripts/site.sh nav` runs `build-nav.py` AND `pagefind --site` —
  search refreshes automatically every time the rail is rebuilt.

If `pagefind` isn't installed, the rail still renders with the search
input present but inert; `auto-nav.js`'s `mountSearch()` short-circuits
when `window.PagefindUI` is undefined. Install via `brew install
pagefind` (or follow the [official install docs](https://pagefind.app/docs/installation/)).

## Push-as-hook auto-resync

`install.sh --hook` installs a pre-push git hook at `.githooks/pre-push`
and wires `git config core.hooksPath .githooks`. After that, every
`git push main` automatically:

1. Auto-detects every site dir in your repo (any directory containing a
   `site-map.html`)
2. Runs `scripts/site.sh nav <site-dir>` (rebuilds rail + search index)
3. Runs `scripts/site.sh push <site-dir>` (rsync to bigblack via
   Tailscale)

The hook is **non-blocking** — if rsync fails (cellular, coffee shop,
bigblack down), the push continues to GitHub anyway. Skip env vars:

| Variable                     | Effect                                     |
| ---------------------------- | ------------------------------------------ |
| `NO_HTMLSHOWCASE_HOOK=1`     | Skip the hook entirely                     |
| `NO_HTMLSHOWCASE_SYNC=1`     | Run nav + search but skip the rsync        |
| `NO_HTMLSHOWCASE_SEARCH=1`   | Skip the pagefind regen step               |
| `HTMLSHOWCASE_SITES="a b c"` | Override auto-detection with explicit list |

## Four contributor stances

Pick the role that matches your task. Full workflow for each in
[`references/contributing.md`](references/contributing.md).

| Role            | Example task                                                  | Edits               | Affects                          |
| --------------- | ------------------------------------------------------------- | ------------------- | -------------------------------- |
| **Consumer**    | "Make me a contractor showcase site"                          | Your HTML           | Just your site                   |
| **Customizer**  | "Re-theme this site with our brand teal"                      | `overrides.css`     | Just your site                   |
| **Contributor** | "Add a `.timeline` component to the kernel"                   | Kernel CSS upstream | Every site using this kernel     |
| **Publisher**   | "Our team forks the kernel and publishes from our own GitHub" | Your fork's kernel  | Sites that pin to _your_ CDN URL |

## When to use this skill

- Creating a static HTML site (one page or many) that records structured
  work: audits, commits, metrics, reports, contractor showcases,
  telemetry views, weekly digests
- Replacing inline-CSS pages with the shared design system + auto-nav
- Bootstrapping a multi-page mini-site that grows into a contractor
  portfolio, weekly-digest archive, or release-notes hub — without ever
  hand-maintaining the navigation

Do NOT use for: blog posts, marketing landing pages, interactive web apps.

## What ships in this skill

| Path                              | Role                                                               |
| --------------------------------- | ------------------------------------------------------------------ |
| `templates/index.html`            | Site home skeleton (hero + 3 example sections + footer + markers)  |
| `templates/section-index.html`    | Section landing-page skeleton                                      |
| `templates/overrides.css.example` | Reference for per-site customization (rename to `overrides.css`)   |
| `templates/lychee.toml`           | Link-checker config                                                |
| `scripts/build-nav.py`            | **Universal sitemap builder — auto-nav + site-map.html generator** |
| `scripts/check-orphan-pages.py`   | Pure-stdlib orphan-page graph validator                            |
| `scripts/site.sh`                 | Build nav + validate + push to bigblack via Tailscale (see below)  |
| `scripts/install.sh`              | **One-shot bootstrap: install all 3 scripts into any repo**        |
| `references/principles.md`        | The WHY — five principles + AI patterns                            |
| `references/sitemap.md`           | The HOW — filesystem-as-sitemap contract, rail rendering           |
| `references/contributing.md`      | The HOW — four stances with full workflows                         |
| `references/publishing.md`        | The WHERE — delivery surfaces (CDN vs tailnet) + bigblack setup    |

The CSS kernel itself lives at the **plugin** level
(`plugins/html-showcase/assets/showcase.css`) and is served from jsDelivr —
the skeleton HTML references the public CDN URL, not a local file.

## Where finished sites get hosted

Two surfaces, two roles:

| Surface                     | What goes there     | When to use                                                             |
| --------------------------- | ------------------- | ----------------------------------------------------------------------- |
| **jsDelivr CDN** (public)   | The kernel CSS only | Always — every page imports the kernel from one shared URL              |
| **bigblack on the tailnet** | Your rendered sites | Default for internal audiences (no DNS, no auth UI, no public exposure) |
| jsDelivr / Pages / Workers  | Your rendered sites | Only when an external reader genuinely needs the page                   |

For internal-audience sites (audit reports, contractor showcases,
telemetry views, weekly digests), the bigblack tailnet path is the
lowest-friction option. Adopting it in any repo is **one command**:

```bash
PLUGIN=${CLAUDE_PLUGIN_ROOT:-~/.claude/plugins/marketplaces/cc-skills/plugins/html-showcase}
bash "$PLUGIN/skills/page-template/scripts/install.sh"
```

That installs all three pipeline scripts (`build-nav.py`,
`check-orphan-pages.py`, `site.sh`) into `<repo>/scripts/` and
appends `**/.published.json` to `.gitignore`. The installer is
idempotent (re-running it is a no-op) and non-destructive
(`--force` to overwrite). To also seed a starter site directory:

```bash
bash "$PLUGIN/skills/page-template/scripts/install.sh" --site contractor-site
```

Then `scripts/site.sh push <site-dir>` regenerates the sitemap, validates
locally (lychee + orphan check), and rsyncs to
`bigblack:~/sites/<repo>/<site-dir>/`, served at
`https://bigblack.tail0f299b.ts.net:8448/<repo>/<site-dir>/`. Push-side
gating (build-nav + lychee + orphan check) is the only gate. Full
mechanics, the URL formula, when NOT to use bigblack, and the bigblack
one-time setup are in [`references/publishing.md`](references/publishing.md).

## Universal density knobs

Two CSS custom properties at the top of `showcase.css` control the entire
visual rhythm. Override either in `overrides.css` to retune one site:

```css
:root {
  --density: 0.85; /* spacing multiplier; 1.0 baseline, lower = tighter */
  --font-scale: 0.94; /* type multiplier; 1.0 baseline, lower = smaller */
}
```

Every padding, gap, margin, and section rhythm in the kernel derives from
the spacing scale; the spacing scale derives from `--density`. Body font
size derives from `--font-scale`. There are no scattered magic numbers in
component CSS — see Principle 3 in `references/principles.md`.

## Component vocabulary

The kernel defines these semantic classes; HTML uses them. To inspect the
full set, open the kernel CSS and search for class selectors.

| Class                                                                                          | Purpose                                                                  |
| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `.hero` + `.hero__inner` / `__eyebrow` / `__title` / `__lede` / `__cta-row`                    | Top banner with gradient                                                 |
| `.chip--solid` / `.chip--ghost`                                                                | Hero CTA buttons                                                         |
| `.metric-grid` + `.metric-card`                                                                | At-a-glance number panel; modifiers `--accent`, `--success`, `--warning` |
| `.phase-grid` + `.phase-card`                                                                  | Phased timeline cards; modifiers `--audit`, `--fix`, `--perf`            |
| `.commit-stack` + `.commit-card`                                                               | Detailed commit cards with SHA chip + details grid                       |
| `.bug-grid` + `.bug-card` (`--high` modifier)                                                  | Compact issue cards                                                      |
| `.feature-grid` + `.feature-card`                                                              | Generic 4-column showcase grid with icon                                 |
| `.reco-list` + `.reco-item` (`--p0` / `--p1` / `--p2`)                                         | Priority-ordered recommendations                                         |
| `.badge` (`--high` / `--medium` / `--low` / `--success` / `--info` / `--neutral` / `--accent`) | Severity / status labels                                                 |
| `.section-head` / `.section-intro`                                                             | Per-section title row + framing paragraph                                |
| `.shell`                                                                                       | Centered content shell with max-width and responsive padding             |
| `.site-footer` + `.site-footer__grid` / `__legal`                                              | Provenance footer                                                        |

The auto-nav rail uses its own non-kernel classes (`.auto-nav-rail`,
`.rail-link`, `.rail-section`, etc.) defined in `auto-nav.css` so the
nav stays self-contained and a repo can adopt the rail without adopting
the kernel.

If your page needs a content component not in this table, you have two
choices — both legitimate, both documented in `references/contributing.md`:

- **Add it to the kernel** (Stance 3): semantic class name in the
  `components` `@layer`, token-referenced values, BEM modifier variants.
- **Use a local override** for one-off cases (Stance 2): only if the
  pattern is genuinely unique to one page; recurring patterns belong in
  the kernel.

## Quick start (Consumer stance, sitemap-organized)

The fastest path: run the installer to bootstrap the pipeline scripts +
a starter site, then iterate.

```bash
PLUGIN=${CLAUDE_PLUGIN_ROOT:-~/.claude/plugins/marketplaces/cc-skills/plugins/html-showcase}

# 1. Bootstrap the pipeline + starter site directory
bash "$PLUGIN/skills/page-template/scripts/install.sh" --site contractor-site

# 2. (Optional) Add one or more sections under contractor-site/
mkdir -p contractor-site/2026-05-02-first-section
cp "$PLUGIN/skills/page-template/templates/section-index.html" \
   contractor-site/2026-05-02-first-section/index.html
cp "$PLUGIN/skills/page-template/templates/index.html" \
   contractor-site/2026-05-02-first-section/page-a.html

# 3. Fill {{ PLACEHOLDERS }} in the HTML, then build the sitemap + nav
scripts/site.sh nav contractor-site

# 4. Validate (lychee + orphan check)
scripts/site.sh check contractor-site

# 5. View — any page reaches every other via the rail
open contractor-site/index.html
open contractor-site/site-map.html
```

Or, if you'd rather copy the templates by hand without the installer:

```bash
PLUGIN=${CLAUDE_PLUGIN_ROOT:-~/.claude/plugins/marketplaces/cc-skills/plugins/html-showcase}
DEST=/path/to/your-site
mkdir -p "$DEST"
cp "$PLUGIN/skills/page-template/templates/index.html"   "$DEST/"
cp "$PLUGIN/skills/page-template/templates/lychee.toml"  "$DEST/"
python3 "$PLUGIN/skills/page-template/scripts/build-nav.py" --root "$DEST"
```

`site.sh` falls back to the plugin-shipped `build-nav.py` when no copy
is present in `<repo>/scripts/`, so even without the installer you can
push to bigblack from any repo using the plugin-shipped script directly.

For the other three stances (Customizer, Contributor, Publisher), see
[`references/contributing.md`](references/contributing.md). For the
publishing path to bigblack via Tailscale, see
[`references/publishing.md`](references/publishing.md).

## CDN versioning

The kernel URL pins to the `@main` branch during early iteration, then
to a tagged release once the kernel stabilizes:

```
@main      → always-latest         → use during development; jsDelivr cache flushed automatically on each release
@v<X.Y.Z>  → immutable, content-locked → use for production-stable pages
@<sha>     → immutable, commit-locked  → use for forensic-grade pinning
```

The release flow auto-purges `@main` and smoke-tests `@v<X.Y.Z>` after
each release. To force-refresh `@main` between releases (e.g., during
heavy iteration on the kernel), run `mise run release:cdn-purge` from
the cc-skills repo. To bypass cache entirely on a single page, append
`?v=$(date +%s)` to the kernel link.

The auto-nav assets (`auto-nav.css`, `auto-nav.js`) are generated locally
by `build-nav.py` and live next to your HTML — they are not CDN-served.
The `?v=N` query string on those URLs is also a cache-bust knob; bump
`--asset-version` when you change the rail's CSS or JS body inside
`build-nav.py`.

## Hard rules

These are baked into the kernel and templates; if you find yourself
wanting to break them, fix the kernel instead (see Stance 3 in
`references/contributing.md`).

- No inline `<style>` blocks.
- No `style=""` attributes on HTML elements.
- No utility-class soup in HTML — class names are semantic
  (`.metric-card`, `.badge--high`), never atomic
  (`flex p-4 bg-blue-500`).
- The kernel is the single source of truth for every visual decision.
- HTML only arranges components; it never invents them.
- The filesystem layout IS the navigation graph; never hand-author the
  rail HTML between `<!-- AUTO-NAV-START -->` and `<!-- AUTO-NAV-END -->`.
  Re-run `scripts/build-nav.py` after any structural change.
- Every site must pass Lychee link-check and the orphan-page detector
  before it's considered shipped.

## Post-Execution Reflection

After this skill completes, reflect before closing the task:

0. **Locate yourself.** — Find this SKILL.md's canonical path before editing.
1. **What failed?** — Fix the instruction. If a kernel component was missing, add it (Stance 3). If the sitemap rail rendered something surprising, fix `build-nav.py` AND `references/sitemap.md`. If a _principle_ was unclear, fix `references/principles.md`.
2. **What worked better than expected?** — If a new section pattern recurs, distill it into a kernel component or into `templates/section-index.html`.
3. **What drifted?** — Keep CDN URL pins, override examples, component vocabulary, and the rail HTML markers aligned across `SKILL.md`, the templates, and `build-nav.py`.
4. **Log it.** — Evolution-log entry with trigger, fix, evidence.

Do NOT defer. The next invocation inherits whatever you leave behind.
