/html - Self-Contained HTML Artifacts
Generate single self-contained .html files that replace markdown when the output needs color, interactivity, layout, or visualization. Auto-detect artifact shape from the request, load shape-specific patterns, generate, validate, deliver.
Core constraint: Every artifact is ONE .html file. All CSS in <style>, all JS in <script>. No CDN links, no frameworks, no build steps, no external dependencies. Works offline, opens in any browser.
Instructions
Overview
5-phase pipeline: DETECT SHAPE, LOAD CONTEXT, GENERATE, VALIDATE, DELIVER. Phase 1 classifies the request into one of 8 shapes via deterministic script. Phase 2 loads the Birchline design system plus shape-specific reference. Phase 3 dispatches a subagent to generate the HTML. Phase 4 validates structure. Phase 5 delivers the file path and offers browser preview. Phase 6 EXPORT (optional) renders to PDF when the user asks for one.
Phase 0: CHECK SAVED TEMPLATE (clone-first)
Before detecting a shape, check whether the request names or matches a saved template. A saved template is a frozen, human-authored layout; cloning it beats regenerating structure because the layout cannot drift.
Run: python3 skills/meta/html-artifact/scripts/fill-template.py --list
If the request names a listed template (e.g. "project kickoff", "business review", "system design") or clearly matches one:
- Read
templates/saved/<name>.slots.json to learn the slots.
- Generate ONLY the slot content — never the layout, CSS, or chrome.
- Write the slot values to a JSON file and run
fill-template.py --template <name> --slots <file> --out <artifact>.
- Skip Phases 1–3 (shape detection, assembly, generation). Go to Phase 4 VALIDATE.
The fill script fails loud on a missing required slot, an undeclared slot name, or a leftover marker. Fix the slot JSON; do not edit the template.
If no saved template matches, continue to Phase 1.
See "Fidelity & Authority" below for the content-vs-layout rule that governs clone mode.
Phase 1: DETECT SHAPE
Classify the user's request into one of 8 artifact shapes.
Run: python3 skills/meta/html-artifact/scripts/detect-shape.py --request "{user_request}"
The script outputs a shape name and confidence score.
| Shape |
Trigger Signals |
What It Produces |
| spec |
plan, explore options, compare N approaches, brainstorm |
Side-by-side grids, Pro/Con badges, SVG data-flow diagrams, risk tables |
| code-review |
review PR, explain diff, annotate code, understand module |
Diff rendering, severity colors, margin annotations, jump links |
| prototype |
prototype, animation, tune, try options, component variants |
Sliders, CSS var live update, animation sandbox, contact sheets |
| report |
report, summarize, status update, explain how X works, incident |
TL;DR box, collapsible sections, timeline, metric callouts, SVG diagrams |
| editor |
reorder, triage, edit config, tune prompt, pick values |
Drag-drop, kanban, toggle switches, split-pane, export buttons |
| data-viz |
visualize, chart, dashboard, show data, trends |
SVG charts, canvas, interactive tooltips, filter controls |
| diagram |
diagram, flowchart, architecture, sequence, SVG, illustrate, figure |
Inline SVG diagrams, annotated flowcharts, figure sheets, interactive node details |
| deck |
slides, presentation, deck, talk, pitch |
Arrow-key navigable slide deck, 16:9 aspect ratio, slide types, progress bar |
Gate: Shape detected with medium+ confidence.
-- because low-confidence classification produces artifacts that mix concerns and satisfy no shape well. Fallback to "report" (safest general-purpose shape) if confidence is low or ambiguous.
Hybrid Shapes
Real content often combines two shapes — a report with embedded diagrams, a spec with data-viz charts. When detect-shape.py returns a primary shape with medium/high confidence but the request also contains signals for a secondary shape, use the hybrid pattern:
| Primary Shape |
+ Secondary |
Result |
| report |
+ diagram |
Report layout (TL;DR, collapsibles, TOC) with inline SVG diagrams between sections |
| report |
+ data-viz |
Report layout with embedded SVG charts illustrating key metrics |
| spec |
+ diagram |
Comparison grid with SVG flow diagrams showing each option's architecture |
| spec |
+ data-viz |
Comparison grid with charts showing performance/cost per option |
| diagram |
+ report |
Figure sheet with explanatory text sections between diagram groups |
Detection: After running detect-shape.py, check if the secondary_shape field is non-null. If so, load BOTH shape references in Phase 2.
Generation rule: Primary shape controls page layout (outer structure). Secondary shape provides embedded components (inner elements). The html-builder agent receives both shape patterns and uses primary for structure, secondary for visual elements within sections.
Example: "create a visual companion for my pipelines article with diagrams and explanations" → primary: report (explain, article), secondary: diagram (visual, diagrams). Load shape-report-research.md AND shape-diagram-illustration.md.
Phase 2: ASSEMBLE TEMPLATE + LOAD CONTEXT
Two parallel steps: (A) run the template assembler to produce a pre-filled HTML skeleton, and (B) load principle-focused reference files for the builder agent.
Step A -- Assemble template (deterministic):
Run: python3 skills/meta/html-artifact/scripts/assemble-template.py --shape {shape} --title "{title}" --components {components}
The script reads CSS/JS from templates/ and injects:
- CSS reset (
templates/base-reset.css)
- Full theme tokens (
templates/themes/{theme}.css)
- Shape-specific layout CSS (
templates/shapes/{shape}.css)
- Component CSS + JS (
templates/components/{name}.{css,js})
The assembler also emits a self-describing stamp as the FIRST CSS comment, so a later run can re-audit the build statelessly (recover shape/theme from output):
/* vexjoy-artifact: shape=<shape> theme=<name> contrast=<pass|fail|n/a> */
shape and theme come from this build's decisions. contrast=n/a at assembly time because the assembler runs no WCAG check; the stamp is a claim, not proof — Phase 4's slop scan verifies the rendered CSS independently rather than trusting it.
Select components based on shape needs:
| Shape |
Typical Components |
| spec |
tabs,copy-button,theme-toggle |
| code-review |
collapsible,filter,keyboard-nav,theme-toggle |
| prototype |
slider,copy-button,theme-toggle |
| report |
collapsible,theme-toggle,copy-button |
| editor |
drag-drop,filter,copy-button |
| data-viz |
filter,theme-toggle |
| diagram |
copy-button,theme-toggle |
| deck |
keyboard-nav,theme-toggle |
Step B -- Load reference files (principles + guidance):
Always load:
references/design-system.md -- Theme selection, token architecture, accessibility checklist, SVG conventions, common mistakes
references/interaction-patterns.md -- Component descriptions, when-to-use guidance, accessibility rules, composition guide
Load per detected shape:
| Shape |
Reference File |
Key Content |
| spec |
references/shape-spec-exploration.md |
Layout descriptions, composition guide, common mistakes |
| code-review |
references/shape-code-review.md |
Severity system, interaction patterns, section ordering |
| prototype |
references/shape-design-prototype.md |
Control types, export requirements, layout patterns |
| report |
references/shape-report-research.md |
Section ordering, TL;DR placement, metric patterns |
| editor |
references/shape-custom-editor.md |
Editor types, export bar rules, common mistakes |
| data-viz |
references/shape-data-visualization.md |
Chart types, coordinate system, color scales |
| diagram |
references/shape-diagram-illustration.md |
SVG construction rules, diagram types, interaction patterns |
| deck |
references/shape-slide-deck.md |
Slide types, navigation, print styles |
Gate: Template assembled + required references loaded.
-- because the template provides deterministic CSS/JS injection, and references provide the judgment guidance the builder needs.
Phase 3: GENERATE
Dispatch the html-builder subagent with the pre-assembled template.
- Read
agents/html-builder.md for the subagent prompt
- Dispatch with: pre-assembled template (from Step A), design system principles, interaction pattern guidance, shape-specific reference, user request
- Agent fills in the content structure using CSS classes already defined in the template
- Agent writes a single
.html file to the project directory (or /tmp/html-artifacts/ if no project context)
Self-contained file constraints (inline here because they govern generation):
| Constraint |
Reason |
All CSS in <style> tag |
No external stylesheets -- file must work offline |
All JS in <script> tag |
No CDN imports -- no React, Vue, Tailwind CDN, Bootstrap CDN |
| Vanilla JS only |
Single file, no build step, no transpilation |
Must include <title> |
Browser tab identification, validation requirement |
Must include <meta charset="utf-8"> |
Consistent rendering across platforms |
Must include <meta name="viewport"> |
Responsive on mobile/tablet |
| Semantic HTML sections |
<header>, <main>, <section>, <footer> for structure |
SVG inline, not <img src> |
No external file references |
| Max 500KB file size |
Keeps generation time reasonable, prevents bloated inline assets |
Constraint: No framework boilerplate.
-- because React/Vue/Svelte require build steps and external imports that violate the single-file self-contained requirement. Vanilla JS handles all 8 shapes adequately.
Constraint: Generate HTML directly, never generate markdown then convert.
-- because markdown-to-HTML conversion loses the shape-specific layout, interactivity, and visual structure that justifies using HTML in the first place.
Gate: .html file exists on disk.
-- because Phase 4 validation reads the file; a missing file means generation failed silently.
Phase 4: VALIDATE
Run deterministic validation on the generated file.
Run: python3 skills/meta/html-artifact/scripts/validate-artifact.py {html_file_path}
The script checks:
| Check |
Fails When |
| Valid HTML structure |
Missing <html>, <head>, or <body> |
| No external dependencies |
Any src= or href= pointing to external URLs |
Has <title> |
Missing or empty <title> tag |
| Has charset meta |
Missing <meta charset> |
| Has viewport meta |
Missing viewport meta tag |
| File size under 500KB |
Excessive inline assets or animation keyframes |
| No broken internal refs |
href="#id" pointing to nonexistent id attributes |
| Rendered-CSS slop scan |
css_slop_rules.scan_css over the file content (warnings only, non-blocking) |
The slop scan (vendored css_slop_rules.py) flags 7 rendered-CSS patterns — transition-all, universal-hover-scale, gradient-text-headline, focus-ring-fade, emoji-feature-icon, two-line-cta, contrast-canary. It is shape-agnostic (checks CSS/markup, not page structure), so it applies to all 8 shapes including hero-less ones. Findings surface as warnings and do not fail the build yet; promote a rule to error in css_slop_rules.py to make it blocking.
Gate: All validation checks pass.
-- because an HTML file with external dependencies fails offline, missing meta tags render inconsistently across browsers, and missing structure breaks accessibility.
If validation fails: Read the specific failures from script output, fix the identified issues in the HTML file, re-run validation. Maximum 3 fix attempts before showing the user the remaining issues and asking for guidance.
Phase 5: DELIVER
- Print the absolute file path
- Print a 1-line summary of what was generated (shape + key features)
- Ask user: "Open in browser?"
- If yes: run
open {file} (macOS) or xdg-open {file} (Linux)
Constraint: Detect headless/SSH environments before offering browser open.
-- because xdg-open fails without a display server, producing confusing errors. Check $DISPLAY on Linux or $SSH_TTY presence. If headless, print path only and skip the open offer.
Phase 6: EXPORT (optional)
Render the generated HTML to PDF. Opt-in only — HTML stays the default deliverable.
Fires when the user message contains any of: "PDF", "export PDF", "make a PDF", "as PDF", "send as PDF", "save as PDF", "PDF version", "PDF export". Without one of those signals, this phase stays dormant.
Runs:
python3 skills/meta/html-artifact/scripts/to-pdf.py \
--input <generated.html> \
--output <generated.pdf> \
--json
The script auto-detects shape from <body data-shape="..."> (the assembler adds it in Phase 2). Page size, orientation, and margins come from a per-shape map: deck renders 13.333in × 7.5in landscape with no margin; spec, code-review, prototype, data-viz, and diagram render Letter landscape; report and editor render Letter portrait. Falls back to Letter portrait when shape is unknown.
Delivers both file paths to the user. The JSON output reports {"output", "page_count", "shape", "bytes"} — page_count reflects slide count for decks, 0 otherwise.
If Playwright is unavailable, exit code 2 surfaces install instructions: pip install -e ".[pdf]" && playwright install chromium. Pass that hint along to the user instead of failing silently.
See references/pdf-export.md for the full page-size table, troubleshooting (font fallback, image timing, install failures), and per-shape print stylesheet inventory.
Phase 7: EXPORT-PPTX (optional)
Render the generated HTML deck to an editable Microsoft PowerPoint .pptx. Opt-in only — HTML stays the default deliverable. Mirrors Phase 6 EXPORT-PDF in shape: signal-triggered, deterministic, runs after the HTML is built.
Fires when the user message contains any of: "pptx", ".pptx", "powerpoint", "editable deck", "editable", "as pptx", "export pptx", "hand-off", "corporate template". Without one of those signals, this phase stays dormant.
Only valid when the detected shape is deck. Other shapes (report, spec, diagram, etc.) cannot be exported to PPTX — fall back to Phase 6 PDF if the user asks.
Runs:
python3 skills/meta/html-artifact/scripts/pptx-bridge/run-unified.py \
--input <generated.html> \
--format pptx \
--out <generated.pptx> \
--no-render
The bridge re-authors slides natively via python-pptx because no general HTML→PPTX converter preserves CSS-rich layout. Output is 13.333 × 7.5 in (16:9), dark navy theme, Aptos body / Cascadia Code mono. Each <section class="slide"> becomes one editable slide; up to 12 layout types (title, content, metric_grid, layer_rows, pipeline, code_block, compare_table_2col/3col, outcome_grid, split_narrow, closing) map 1:1 to native python-pptx builders.
--out accepts either a .pptx file path (single-file mode) or a directory (writes the .pptx plus slides.json, report.md, optional render/ siblings). --no-render skips the optional LibreOffice QA step; required on hosts without soffice.
If python-pptx is unavailable, exit code 1 surfaces install instructions: pip install python-pptx. Pass that hint along to the user instead of failing silently.
See references/pptx-export.md for the full layout table, THEME dict, CLI reference, validation criteria, and failure modes.
Error Handling
| Error |
Cause |
Solution |
| detect-shape.py returns low confidence |
Ambiguous request mapping to multiple shapes |
Fall back to "report" shape -- safest general-purpose format |
| Generated HTML has external dependencies |
Builder included CDN links or external src refs |
Regenerate with explicit constraint: "no external deps, all CSS/JS inline" |
| File exceeds 500KB |
Excessive inline SVGs or animation keyframes |
Simplify SVG paths, reduce keyframe count, compress data |
| Browser won't open |
No display server (headless, SSH, WSL without WSLg) |
Print path only, suggest scp or a local preview: python3 -m http.server --bind 127.0.0.1. For public access use nginx, not http.server; see the public-web-deploy skill. |
| Validation fails repeatedly (3+ attempts) |
Structural issue the builder cannot self-correct |
Show validation output to user, ask for guidance |
| Shape misclassified |
Auto-detection picked wrong shape for request |
User overrides with /html --shape=<name> <request> |
Preferred Patterns
Pattern 1: CDN and Framework Imports
What it looks like: <link href="https://cdn.jsdelivr.net/..."> or <script src="https://unpkg.com/react@18/..."> in the generated HTML.
Why wrong: Breaks the self-contained contract. File fails offline, introduces version drift, adds weight the user didn't ask for.
Do instead: Inline all CSS in <style>. Write vanilla JS in <script>. The Birchline design system in references/design-system.md provides the full token set.
Pattern 2: Markdown-to-HTML Conversion
What it looks like: Generating a markdown document first, then running it through a converter or wrapping it in <pre> tags.
Why wrong: Loses shape-specific layout, interactivity, SVG diagrams, and responsive grid structures. Produces "markdown in a browser" instead of a native HTML artifact.
Do instead: Generate HTML directly using shape-specific patterns from references. The HTML structure IS the output format, not a rendering layer on top of text.
Pattern 3: Monolithic Unstructured HTML
What it looks like: One giant <div> with inline styles on every element, no semantic structure, no comments.
Why wrong: Unreadable source, hard to debug, impossible for the user to modify. Accessibility tools cannot navigate it.
Do instead: Use semantic HTML (<header>, <main>, <section>, <footer>). Define CSS classes in <style>. Add section comments. Group related elements logically.
Pattern 4: Over-Engineering Simple Requests
What it looks like: Generating a full interactive dashboard when the user asked for a simple comparison table.
Why wrong: 2-4x generation time for features the user didn't request. Complexity without value.
Do instead: Match artifact complexity to request complexity. A comparison of 3 options needs a grid with cards, not a filterable dashboard with animations.
Anti-Rationalization
| Rationalization |
Why Wrong |
Required Action |
| "Markdown is fine for this" |
If shape detection triggered, the request has visual/interactive needs markdown can't serve |
Generate HTML; user opts out with "as markdown" |
| "I'll add Tailwind CDN for faster styling" |
Breaks self-contained requirement, fails offline |
Use Birchline tokens from design-system.md |
| "The HTML looks right, skip validation" |
Visual inspection misses missing meta tags, broken internal links, external deps |
Run validate-artifact.py every time |
| "Report shape works for everything" |
Each shape has distinct layout and interaction patterns; report is a fallback, not a default |
Use the detected shape; report only when confidence is genuinely low |
Reference Loading Table
| Signal |
Load These Files |
Why |
| Any html-artifact invocation |
references/design-system.md |
Theme selection, token architecture, accessibility, common mistakes |
| Any html-artifact invocation |
references/interaction-patterns.md |
Component descriptions, when-to-use, accessibility rules |
| Shape = spec |
references/shape-spec-exploration.md |
Layout descriptions, composition guide, common mistakes |
| Shape = code-review |
references/shape-code-review.md |
Severity system, interaction patterns, section ordering |
| Shape = prototype |
references/shape-design-prototype.md |
Control types, export requirements, layout patterns |
| Shape = report |
references/shape-report-research.md |
Section ordering, TL;DR placement, metric patterns |
| Shape = editor |
references/shape-custom-editor.md |
Editor types, export bar rules, common mistakes |
| Shape = data-viz |
references/shape-data-visualization.md |
Chart types, coordinate system, color scales |
| Shape = diagram |
references/shape-diagram-illustration.md |
SVG construction rules, diagram types, interaction patterns |
| Shape = deck |
references/shape-slide-deck.md |
Slide types, navigation, print styles |
| Request mentions scroll, reveal, animate on scroll, progressive |
references/scrollytelling-patterns.md |
IntersectionObserver scroll animations, stagger, counters, progress bar |
| Request mentions PDF, export PDF, as PDF, PDF version |
references/pdf-export.md |
Phase 6 trigger conditions, page-size table, troubleshooting, install instructions |
| Request mentions pptx, .pptx, powerpoint, editable deck, hand-off, corporate template |
references/pptx-export.md |
Phase 7 trigger conditions, layout types, THEME dict, CLI reference, failure modes |
Shape = diagram OR request contains "SVG", "architecture diagram", "flowchart", "sequence diagram" |
references/diagram-layering.md |
SVG layer order, masking rect technique, semantic color system for dark-theme diagrams |
Shape = data-viz OR request contains "infographic", "layout", "visualize data", "chart type" |
references/infographic-layouts.md |
21 layout types with content-type pairings and 22 visual styles |
| Request mentions animated text, rolling/slot text, kinetic headline, typewriter |
../../frontend/distinctive-frontend-design/references/roll-text.md, ../../frontend/distinctive-frontend-design/references/text-animation-patterns.md |
Zero-npm roll/slot text plus reveal, typewriter, crossfade patterns to inline |
Shared Patterns
This skill uses:
Reference Files
references/design-system.md: Theme selection, token architecture, accessibility checklist, SVG conventions, common mistakes
references/interaction-patterns.md: Component descriptions, when-to-use guidance, accessibility rules, composition guide
references/shape-spec-exploration.md: Spec shape -- layout, composition guide, common mistakes
references/shape-code-review.md: Code review shape -- severity system, interaction patterns, section ordering
references/shape-design-prototype.md: Prototype shape -- control types, export requirements, layout patterns
references/shape-report-research.md: Report shape -- section ordering, TL;DR placement, metric patterns
references/shape-custom-editor.md: Editor shape -- editor types, export bar rules, common mistakes
references/shape-data-visualization.md: Data viz shape -- chart types, coordinate system, color scales
references/shape-diagram-illustration.md: Diagram shape -- SVG construction rules, diagram types, interaction patterns
references/shape-slide-deck.md: Deck shape -- slide types, navigation, print styles
agents/html-builder.md: Subagent prompt for HTML generation
references/scrollytelling-patterns.md: IntersectionObserver scroll animation patterns
references/pdf-export.md: Phase 6 EXPORT — trigger conditions, page-size table, print stylesheet inventory, troubleshooting
references/pptx-export.md: Phase 7 EXPORT-PPTX — trigger conditions, layout types, THEME dict, CLI reference, failure modes
references/diagram-layering.md: SVG layer order, masking rect technique, dark design system constants, semantic color palette
references/infographic-layouts.md: 21 layout types with structure and use guidance, 22 visual styles, content-type pairings
scripts/detect-shape.py: Deterministic shape classification from user request
scripts/assemble-template.py: Template assembly with theme, shape, and component CSS/JS injection
scripts/validate-artifact.py: HTML structure, self-containment, and rendered-CSS slop validation
scripts/css_slop_rules.py: vendored slop scanner (scan_css) — 7 rendered-CSS rules, dependency-free. Keep in sync with distinctive-frontend-design.
scripts/to-pdf.py: Playwright-based PDF rendering with per-shape page sizing
scripts/pptx-bridge/: HTML deck → editable PPTX (extract_slides.py, _pptx_engine.py, render_pptx.py, run-unified.py)
templates/: CSS/JS template files organized by themes/, shapes/, components/, print/
Saved Templates
Pre-built, reusable artifact templates for recurring requests. Two renderer patterns:
- Specialized renderer — a script that fetches live data and fills a bespoke template (
github-issues).
- Generic slot filler —
scripts/fill-template.py clones any frozen template in templates/saved/ and substitutes caller-supplied slot values. One skill, many template files: add a layout, not a skill.
| Template |
Renderer |
Use For |
templates/saved/github-issues.html |
scripts/render-github-issues.py |
"show me my GitHub issues" / "show me my tickets" — assigned + mentioned + review-requested across all repos, with per-issue discussion expanders and 5 client-side sort modes |
templates/saved/business-review.html |
scripts/fill-template.py |
"business review" — KPIs, segment results, priorities, decisions, outlook |
templates/saved/project-kickoff.html |
scripts/fill-template.py |
"project kickoff" — agenda, foundation, scope, workstreams + owners, milestone gates, decisions, risks |
templates/saved/system-design.html |
scripts/fill-template.py |
"system design" — requirements, architecture, components, data flow, tradeoffs, operations |
See templates/saved/README.md to add a template.
Fidelity & Authority
Governs clone mode (Phase 0). Adapted from the OpenAI curated-template skills, which keep on-brand output by cloning a fixed reference instead of regenerating it.
Clone, don't regenerate. When a saved template applies, clone its layout unchanged and fill only the content slots. Do not rebuild the structure, restyle the CSS, or "improve" the chrome. Regeneration is where visual drift and AI slop enter; a frozen template removes that risk.
Content-vs-layout authority. One rule resolves every "should I restyle this?" question:
User instructions control requested content and explicit deviations. The retained template controls layout and formatting where the user has not requested a change.
So: change layout only when the user asks for a layout change. Otherwise the template wins. fill-template.py enforces this mechanically — it substitutes slot values and touches nothing else.
Fail loud, don't degrade. If a required slot has no content, stop and get the content — do not silently ship a half-filled template. The fill script exits non-zero on a missing required slot, an undeclared slot, or a leftover marker.
1---2name: html-artifact3description: Generate rich self-contained HTML artifacts instead of markdown. Auto-detects artifact shape (spec, code-review, prototype, report, editor, data-viz, diagram, deck) and loads shape-specific patterns. Bundles Birchline design system with 4 theme presets. Use for "make HTML", "as HTML", "HTML artifact", or auto-injected by router when output benefits from rich visualization.4---56# /html - Self-Contained HTML Artifacts78Generate single self-contained `.html` files that replace markdown when the output needs color, interactivity, layout, or visualization. Auto-detect artifact shape from the request, load shape-specific patterns, generate, validate, deliver.910**Core constraint:** Every artifact is ONE `.html` file. All CSS in `<style>`, all JS in `<script>`. No CDN links, no frameworks, no build steps, no external dependencies. Works offline, opens in any browser.1112---1314## Instructions1516### Overview17185-phase pipeline: DETECT SHAPE, LOAD CONTEXT, GENERATE, VALIDATE, DELIVER. Phase 1 classifies the request into one of 8 shapes via deterministic script. Phase 2 loads the Birchline design system plus shape-specific reference. Phase 3 dispatches a subagent to generate the HTML. Phase 4 validates structure. Phase 5 delivers the file path and offers browser preview. Phase 6 EXPORT (optional) renders to PDF when the user asks for one.1920---2122### Phase 0: CHECK SAVED TEMPLATE (clone-first)2324Before detecting a shape, check whether the request names or matches a saved template. A saved template is a frozen, human-authored layout; cloning it beats regenerating structure because the layout cannot drift.2526Run: `python3 skills/meta/html-artifact/scripts/fill-template.py --list`2728If the request names a listed template (e.g. "project kickoff", "business review", "system design") or clearly matches one:29301. Read `templates/saved/<name>.slots.json` to learn the slots.312. Generate ONLY the slot content — never the layout, CSS, or chrome.323. Write the slot values to a JSON file and run `fill-template.py --template <name> --slots <file> --out <artifact>`.334. Skip Phases 1–3 (shape detection, assembly, generation). Go to Phase 4 VALIDATE.3435The fill script fails loud on a missing required slot, an undeclared slot name, or a leftover marker. Fix the slot JSON; do not edit the template.3637If no saved template matches, continue to Phase 1.3839**See "Fidelity & Authority" below for the content-vs-layout rule that governs clone mode.**4041---4243### Phase 1: DETECT SHAPE4445Classify the user's request into one of 8 artifact shapes.4647Run: `python3 skills/meta/html-artifact/scripts/detect-shape.py --request "{user_request}"`4849The script outputs a shape name and confidence score.5051| Shape | Trigger Signals | What It Produces |52|---|---|---|53| spec | plan, explore options, compare N approaches, brainstorm | Side-by-side grids, Pro/Con badges, SVG data-flow diagrams, risk tables |54| code-review | review PR, explain diff, annotate code, understand module | Diff rendering, severity colors, margin annotations, jump links |55| prototype | prototype, animation, tune, try options, component variants | Sliders, CSS var live update, animation sandbox, contact sheets |56| report | report, summarize, status update, explain how X works, incident | TL;DR box, collapsible sections, timeline, metric callouts, SVG diagrams |57| editor | reorder, triage, edit config, tune prompt, pick values | Drag-drop, kanban, toggle switches, split-pane, export buttons |58| data-viz | visualize, chart, dashboard, show data, trends | SVG charts, canvas, interactive tooltips, filter controls |59| diagram | diagram, flowchart, architecture, sequence, SVG, illustrate, figure | Inline SVG diagrams, annotated flowcharts, figure sheets, interactive node details |60| deck | slides, presentation, deck, talk, pitch | Arrow-key navigable slide deck, 16:9 aspect ratio, slide types, progress bar |6162Gate: Shape detected with medium+ confidence.63-- because low-confidence classification produces artifacts that mix concerns and satisfy no shape well. Fallback to "report" (safest general-purpose shape) if confidence is low or ambiguous.6465---6667### Hybrid Shapes6869Real content often combines two shapes — a report with embedded diagrams, a spec with data-viz charts. When `detect-shape.py` returns a primary shape with medium/high confidence but the request also contains signals for a secondary shape, use the hybrid pattern:7071| Primary Shape | + Secondary | Result |72|---|---|---|73| report | + diagram | Report layout (TL;DR, collapsibles, TOC) with inline SVG diagrams between sections |74| report | + data-viz | Report layout with embedded SVG charts illustrating key metrics |75| spec | + diagram | Comparison grid with SVG flow diagrams showing each option's architecture |76| spec | + data-viz | Comparison grid with charts showing performance/cost per option |77| diagram | + report | Figure sheet with explanatory text sections between diagram groups |7879**Detection:** After running `detect-shape.py`, check if the `secondary_shape` field is non-null. If so, load BOTH shape references in Phase 2.8081**Generation rule:** Primary shape controls page layout (outer structure). Secondary shape provides embedded components (inner elements). The html-builder agent receives both shape patterns and uses primary for structure, secondary for visual elements within sections.8283**Example:** "create a visual companion for my pipelines article with diagrams and explanations" → primary: `report` (explain, article), secondary: `diagram` (visual, diagrams). Load `shape-report-research.md` AND `shape-diagram-illustration.md`.8485---8687### Phase 2: ASSEMBLE TEMPLATE + LOAD CONTEXT8889Two parallel steps: (A) run the template assembler to produce a pre-filled HTML skeleton, and (B) load principle-focused reference files for the builder agent.9091**Step A -- Assemble template (deterministic):**9293Run: `python3 skills/meta/html-artifact/scripts/assemble-template.py --shape {shape} --title "{title}" --components {components}`9495The script reads CSS/JS from `templates/` and injects:961. CSS reset (`templates/base-reset.css`)972. Full theme tokens (`templates/themes/{theme}.css`)983. Shape-specific layout CSS (`templates/shapes/{shape}.css`)994. Component CSS + JS (`templates/components/{name}.{css,js}`)100101The assembler also emits a self-describing stamp as the FIRST CSS comment, so a later run can re-audit the build statelessly (recover shape/theme from output):102103```104/* vexjoy-artifact: shape=<shape> theme=<name> contrast=<pass|fail|n/a> */105```106107`shape` and `theme` come from this build's decisions. `contrast=n/a` at assembly time because the assembler runs no WCAG check; the stamp is a claim, not proof — Phase 4's slop scan verifies the rendered CSS independently rather than trusting it.108109Select components based on shape needs:110111| Shape | Typical Components |112|---|---|113| spec | `tabs,copy-button,theme-toggle` |114| code-review | `collapsible,filter,keyboard-nav,theme-toggle` |115| prototype | `slider,copy-button,theme-toggle` |116| report | `collapsible,theme-toggle,copy-button` |117| editor | `drag-drop,filter,copy-button` |118| data-viz | `filter,theme-toggle` |119| diagram | `copy-button,theme-toggle` |120| deck | `keyboard-nav,theme-toggle` |121122**Step B -- Load reference files (principles + guidance):**123124**Always load:**1251. `references/design-system.md` -- Theme selection, token architecture, accessibility checklist, SVG conventions, common mistakes1262. `references/interaction-patterns.md` -- Component descriptions, when-to-use guidance, accessibility rules, composition guide127128**Load per detected shape:**129130| Shape | Reference File | Key Content |131|---|---|---|132| spec | `references/shape-spec-exploration.md` | Layout descriptions, composition guide, common mistakes |133| code-review | `references/shape-code-review.md` | Severity system, interaction patterns, section ordering |134| prototype | `references/shape-design-prototype.md` | Control types, export requirements, layout patterns |135| report | `references/shape-report-research.md` | Section ordering, TL;DR placement, metric patterns |136| editor | `references/shape-custom-editor.md` | Editor types, export bar rules, common mistakes |137| data-viz | `references/shape-data-visualization.md` | Chart types, coordinate system, color scales |138| diagram | `references/shape-diagram-illustration.md` | SVG construction rules, diagram types, interaction patterns |139| deck | `references/shape-slide-deck.md` | Slide types, navigation, print styles |140141Gate: Template assembled + required references loaded.142-- because the template provides deterministic CSS/JS injection, and references provide the judgment guidance the builder needs.143144---145146### Phase 3: GENERATE147148Dispatch the html-builder subagent with the pre-assembled template.1491501. Read `agents/html-builder.md` for the subagent prompt1512. Dispatch with: pre-assembled template (from Step A), design system principles, interaction pattern guidance, shape-specific reference, user request1523. Agent fills in the content structure using CSS classes already defined in the template1534. Agent writes a single `.html` file to the project directory (or `/tmp/html-artifacts/` if no project context)154155**Self-contained file constraints (inline here because they govern generation):**156157| Constraint | Reason |158|---|---|159| All CSS in `<style>` tag | No external stylesheets -- file must work offline |160| All JS in `<script>` tag | No CDN imports -- no React, Vue, Tailwind CDN, Bootstrap CDN |161| Vanilla JS only | Single file, no build step, no transpilation |162| Must include `<title>` | Browser tab identification, validation requirement |163| Must include `<meta charset="utf-8">` | Consistent rendering across platforms |164| Must include `<meta name="viewport">` | Responsive on mobile/tablet |165| Semantic HTML sections | `<header>`, `<main>`, `<section>`, `<footer>` for structure |166| SVG inline, not `<img src>` | No external file references |167| Max 500KB file size | Keeps generation time reasonable, prevents bloated inline assets |168169Constraint: No framework boilerplate.170-- because React/Vue/Svelte require build steps and external imports that violate the single-file self-contained requirement. Vanilla JS handles all 8 shapes adequately.171172Constraint: Generate HTML directly, never generate markdown then convert.173-- because markdown-to-HTML conversion loses the shape-specific layout, interactivity, and visual structure that justifies using HTML in the first place.174175Gate: `.html` file exists on disk.176-- because Phase 4 validation reads the file; a missing file means generation failed silently.177178---179180### Phase 4: VALIDATE181182Run deterministic validation on the generated file.183184Run: `python3 skills/meta/html-artifact/scripts/validate-artifact.py {html_file_path}`185186The script checks:187188| Check | Fails When |189|---|---|190| Valid HTML structure | Missing `<html>`, `<head>`, or `<body>` |191| No external dependencies | Any `src=` or `href=` pointing to external URLs |192| Has `<title>` | Missing or empty `<title>` tag |193| Has charset meta | Missing `<meta charset>` |194| Has viewport meta | Missing viewport meta tag |195| File size under 500KB | Excessive inline assets or animation keyframes |196| No broken internal refs | `href="#id"` pointing to nonexistent `id` attributes |197| Rendered-CSS slop scan | `css_slop_rules.scan_css` over the file content (warnings only, non-blocking) |198199The slop scan (vendored `css_slop_rules.py`) flags 7 rendered-CSS patterns — `transition-all`, `universal-hover-scale`, `gradient-text-headline`, `focus-ring-fade`, `emoji-feature-icon`, `two-line-cta`, `contrast-canary`. It is shape-agnostic (checks CSS/markup, not page structure), so it applies to all 8 shapes including hero-less ones. Findings surface as warnings and do not fail the build yet; promote a rule to error in `css_slop_rules.py` to make it blocking.200201Gate: All validation checks pass.202-- because an HTML file with external dependencies fails offline, missing meta tags render inconsistently across browsers, and missing structure breaks accessibility.203204**If validation fails:** Read the specific failures from script output, fix the identified issues in the HTML file, re-run validation. Maximum 3 fix attempts before showing the user the remaining issues and asking for guidance.205206---207208### Phase 5: DELIVER2092101. Print the absolute file path2112. Print a 1-line summary of what was generated (shape + key features)2123. Ask user: "Open in browser?"2134. If yes: run `open {file}` (macOS) or `xdg-open {file}` (Linux)214215Constraint: Detect headless/SSH environments before offering browser open.216-- because `xdg-open` fails without a display server, producing confusing errors. Check `$DISPLAY` on Linux or `$SSH_TTY` presence. If headless, print path only and skip the open offer.217218---219220### Phase 6: EXPORT (optional)221222Render the generated HTML to PDF. Opt-in only — HTML stays the default deliverable.223224Fires when the user message contains any of: `"PDF"`, `"export PDF"`, `"make a PDF"`, `"as PDF"`, `"send as PDF"`, `"save as PDF"`, `"PDF version"`, `"PDF export"`. Without one of those signals, this phase stays dormant.225226Runs:227228```bash229python3 skills/meta/html-artifact/scripts/to-pdf.py \230 --input <generated.html> \231 --output <generated.pdf> \232 --json233```234235The script auto-detects shape from `<body data-shape="...">` (the assembler adds it in Phase 2). Page size, orientation, and margins come from a per-shape map: deck renders 13.333in × 7.5in landscape with no margin; spec, code-review, prototype, data-viz, and diagram render Letter landscape; report and editor render Letter portrait. Falls back to Letter portrait when shape is unknown.236237Delivers both file paths to the user. The JSON output reports `{"output", "page_count", "shape", "bytes"}` — `page_count` reflects slide count for decks, 0 otherwise.238239If Playwright is unavailable, exit code 2 surfaces install instructions: `pip install -e ".[pdf]" && playwright install chromium`. Pass that hint along to the user instead of failing silently.240241See `references/pdf-export.md` for the full page-size table, troubleshooting (font fallback, image timing, install failures), and per-shape print stylesheet inventory.242243---244245### Phase 7: EXPORT-PPTX (optional)246247Render the generated HTML deck to an editable Microsoft PowerPoint `.pptx`. Opt-in only — HTML stays the default deliverable. Mirrors Phase 6 EXPORT-PDF in shape: signal-triggered, deterministic, runs after the HTML is built.248249Fires when the user message contains any of: `"pptx"`, `".pptx"`, `"powerpoint"`, `"editable deck"`, `"editable"`, `"as pptx"`, `"export pptx"`, `"hand-off"`, `"corporate template"`. Without one of those signals, this phase stays dormant.250251Only valid when the detected shape is `deck`. Other shapes (report, spec, diagram, etc.) cannot be exported to PPTX — fall back to Phase 6 PDF if the user asks.252253Runs:254255```bash256python3 skills/meta/html-artifact/scripts/pptx-bridge/run-unified.py \257 --input <generated.html> \258 --format pptx \259 --out <generated.pptx> \260 --no-render261```262263The bridge re-authors slides natively via `python-pptx` because no general HTML→PPTX converter preserves CSS-rich layout. Output is 13.333 × 7.5 in (16:9), dark navy theme, Aptos body / Cascadia Code mono. Each `<section class="slide">` becomes one editable slide; up to 12 layout types (title, content, metric_grid, layer_rows, pipeline, code_block, compare_table_2col/3col, outcome_grid, split_narrow, closing) map 1:1 to native python-pptx builders.264265`--out` accepts either a `.pptx` file path (single-file mode) or a directory (writes the .pptx plus `slides.json`, `report.md`, optional `render/` siblings). `--no-render` skips the optional LibreOffice QA step; required on hosts without `soffice`.266267If `python-pptx` is unavailable, exit code 1 surfaces install instructions: `pip install python-pptx`. Pass that hint along to the user instead of failing silently.268269See `references/pptx-export.md` for the full layout table, THEME dict, CLI reference, validation criteria, and failure modes.270271---272273## Error Handling274275| Error | Cause | Solution |276|---|---|---|277| detect-shape.py returns low confidence | Ambiguous request mapping to multiple shapes | Fall back to "report" shape -- safest general-purpose format |278| Generated HTML has external dependencies | Builder included CDN links or external `src` refs | Regenerate with explicit constraint: "no external deps, all CSS/JS inline" |279| File exceeds 500KB | Excessive inline SVGs or animation keyframes | Simplify SVG paths, reduce keyframe count, compress data |280| Browser won't open | No display server (headless, SSH, WSL without WSLg) | Print path only, suggest `scp` or a local preview: `python3 -m http.server --bind 127.0.0.1`. For public access use nginx, not http.server; see the public-web-deploy skill. |281| Validation fails repeatedly (3+ attempts) | Structural issue the builder cannot self-correct | Show validation output to user, ask for guidance |282| Shape misclassified | Auto-detection picked wrong shape for request | User overrides with `/html --shape=<name> <request>` |283284---285286## Preferred Patterns287288### Pattern 1: CDN and Framework Imports289290**What it looks like:** `<link href="https://cdn.jsdelivr.net/...">` or `<script src="https://unpkg.com/react@18/...">` in the generated HTML.291292**Why wrong:** Breaks the self-contained contract. File fails offline, introduces version drift, adds weight the user didn't ask for.293294**Do instead:** Inline all CSS in `<style>`. Write vanilla JS in `<script>`. The Birchline design system in `references/design-system.md` provides the full token set.295296### Pattern 2: Markdown-to-HTML Conversion297298**What it looks like:** Generating a markdown document first, then running it through a converter or wrapping it in `<pre>` tags.299300**Why wrong:** Loses shape-specific layout, interactivity, SVG diagrams, and responsive grid structures. Produces "markdown in a browser" instead of a native HTML artifact.301302**Do instead:** Generate HTML directly using shape-specific patterns from references. The HTML structure IS the output format, not a rendering layer on top of text.303304### Pattern 3: Monolithic Unstructured HTML305306**What it looks like:** One giant `<div>` with inline styles on every element, no semantic structure, no comments.307308**Why wrong:** Unreadable source, hard to debug, impossible for the user to modify. Accessibility tools cannot navigate it.309310**Do instead:** Use semantic HTML (`<header>`, `<main>`, `<section>`, `<footer>`). Define CSS classes in `<style>`. Add section comments. Group related elements logically.311312### Pattern 4: Over-Engineering Simple Requests313314**What it looks like:** Generating a full interactive dashboard when the user asked for a simple comparison table.315316**Why wrong:** 2-4x generation time for features the user didn't request. Complexity without value.317318**Do instead:** Match artifact complexity to request complexity. A comparison of 3 options needs a grid with cards, not a filterable dashboard with animations.319320---321322## Anti-Rationalization323324| Rationalization | Why Wrong | Required Action |325|---|---|---|326| "Markdown is fine for this" | If shape detection triggered, the request has visual/interactive needs markdown can't serve | Generate HTML; user opts out with "as markdown" |327| "I'll add Tailwind CDN for faster styling" | Breaks self-contained requirement, fails offline | Use Birchline tokens from design-system.md |328| "The HTML looks right, skip validation" | Visual inspection misses missing meta tags, broken internal links, external deps | Run validate-artifact.py every time |329| "Report shape works for everything" | Each shape has distinct layout and interaction patterns; report is a fallback, not a default | Use the detected shape; report only when confidence is genuinely low |330331---332333## Reference Loading Table334335| Signal | Load These Files | Why |336|---|---|---|337| Any html-artifact invocation | `references/design-system.md` | Theme selection, token architecture, accessibility, common mistakes |338| Any html-artifact invocation | `references/interaction-patterns.md` | Component descriptions, when-to-use, accessibility rules |339| Shape = spec | `references/shape-spec-exploration.md` | Layout descriptions, composition guide, common mistakes |340| Shape = code-review | `references/shape-code-review.md` | Severity system, interaction patterns, section ordering |341| Shape = prototype | `references/shape-design-prototype.md` | Control types, export requirements, layout patterns |342| Shape = report | `references/shape-report-research.md` | Section ordering, TL;DR placement, metric patterns |343| Shape = editor | `references/shape-custom-editor.md` | Editor types, export bar rules, common mistakes |344| Shape = data-viz | `references/shape-data-visualization.md` | Chart types, coordinate system, color scales |345| Shape = diagram | `references/shape-diagram-illustration.md` | SVG construction rules, diagram types, interaction patterns |346| Shape = deck | `references/shape-slide-deck.md` | Slide types, navigation, print styles |347| Request mentions scroll, reveal, animate on scroll, progressive | `references/scrollytelling-patterns.md` | IntersectionObserver scroll animations, stagger, counters, progress bar |348| Request mentions PDF, export PDF, as PDF, PDF version | `references/pdf-export.md` | Phase 6 trigger conditions, page-size table, troubleshooting, install instructions |349| Request mentions pptx, .pptx, powerpoint, editable deck, hand-off, corporate template | `references/pptx-export.md` | Phase 7 trigger conditions, layout types, THEME dict, CLI reference, failure modes |350| Shape = `diagram` OR request contains "SVG", "architecture diagram", "flowchart", "sequence diagram" | `references/diagram-layering.md` | SVG layer order, masking rect technique, semantic color system for dark-theme diagrams |351| Shape = `data-viz` OR request contains "infographic", "layout", "visualize data", "chart type" | `references/infographic-layouts.md` | 21 layout types with content-type pairings and 22 visual styles |352| Request mentions animated text, rolling/slot text, kinetic headline, typewriter | `../../frontend/distinctive-frontend-design/references/roll-text.md`, `../../frontend/distinctive-frontend-design/references/text-animation-patterns.md` | Zero-npm roll/slot text plus reveal, typewriter, crossfade patterns to inline |353354---355356## Shared Patterns357358This skill uses:359- [Anti-Rationalization](../../shared-patterns/anti-rationalization-core.md) -- Prevents shortcut rationalizations360- [Verification Checklist](../../shared-patterns/verification-checklist.md) -- Pre-completion checks361- [Gate Enforcement](../../shared-patterns/gate-enforcement.md) -- Phase transitions362363---364365## Reference Files366367- `references/design-system.md`: Theme selection, token architecture, accessibility checklist, SVG conventions, common mistakes368- `references/interaction-patterns.md`: Component descriptions, when-to-use guidance, accessibility rules, composition guide369- `references/shape-spec-exploration.md`: Spec shape -- layout, composition guide, common mistakes370- `references/shape-code-review.md`: Code review shape -- severity system, interaction patterns, section ordering371- `references/shape-design-prototype.md`: Prototype shape -- control types, export requirements, layout patterns372- `references/shape-report-research.md`: Report shape -- section ordering, TL;DR placement, metric patterns373- `references/shape-custom-editor.md`: Editor shape -- editor types, export bar rules, common mistakes374- `references/shape-data-visualization.md`: Data viz shape -- chart types, coordinate system, color scales375- `references/shape-diagram-illustration.md`: Diagram shape -- SVG construction rules, diagram types, interaction patterns376- `references/shape-slide-deck.md`: Deck shape -- slide types, navigation, print styles377- `agents/html-builder.md`: Subagent prompt for HTML generation378- `references/scrollytelling-patterns.md`: IntersectionObserver scroll animation patterns379- `references/pdf-export.md`: Phase 6 EXPORT — trigger conditions, page-size table, print stylesheet inventory, troubleshooting380- `references/pptx-export.md`: Phase 7 EXPORT-PPTX — trigger conditions, layout types, THEME dict, CLI reference, failure modes381- `references/diagram-layering.md`: SVG layer order, masking rect technique, dark design system constants, semantic color palette382- `references/infographic-layouts.md`: 21 layout types with structure and use guidance, 22 visual styles, content-type pairings383- `scripts/detect-shape.py`: Deterministic shape classification from user request384- `scripts/assemble-template.py`: Template assembly with theme, shape, and component CSS/JS injection385- `scripts/validate-artifact.py`: HTML structure, self-containment, and rendered-CSS slop validation386- `scripts/css_slop_rules.py`: vendored slop scanner (`scan_css`) — 7 rendered-CSS rules, dependency-free. Keep in sync with distinctive-frontend-design.387- `scripts/to-pdf.py`: Playwright-based PDF rendering with per-shape page sizing388- `scripts/pptx-bridge/`: HTML deck → editable PPTX (extract_slides.py, _pptx_engine.py, render_pptx.py, run-unified.py)389- `templates/`: CSS/JS template files organized by themes/, shapes/, components/, print/390391---392393## Saved Templates394395Pre-built, reusable artifact templates for recurring requests. Two renderer patterns:396397- **Specialized renderer** — a script that fetches live data and fills a bespoke template (`github-issues`).398- **Generic slot filler** — `scripts/fill-template.py` clones any frozen template in `templates/saved/` and substitutes caller-supplied slot values. One skill, many template files: add a layout, not a skill.399400| Template | Renderer | Use For |401|---|---|---|402| `templates/saved/github-issues.html` | `scripts/render-github-issues.py` | "show me my GitHub issues" / "show me my tickets" — assigned + mentioned + review-requested across all repos, with per-issue discussion expanders and 5 client-side sort modes |403| `templates/saved/business-review.html` | `scripts/fill-template.py` | "business review" — KPIs, segment results, priorities, decisions, outlook |404| `templates/saved/project-kickoff.html` | `scripts/fill-template.py` | "project kickoff" — agenda, foundation, scope, workstreams + owners, milestone gates, decisions, risks |405| `templates/saved/system-design.html` | `scripts/fill-template.py` | "system design" — requirements, architecture, components, data flow, tradeoffs, operations |406407See `templates/saved/README.md` to add a template.408409---410411## Fidelity & Authority412413Governs clone mode (Phase 0). Adapted from the OpenAI curated-template skills, which keep on-brand output by cloning a fixed reference instead of regenerating it.414415**Clone, don't regenerate.** When a saved template applies, clone its layout unchanged and fill only the content slots. Do not rebuild the structure, restyle the CSS, or "improve" the chrome. Regeneration is where visual drift and AI slop enter; a frozen template removes that risk.416417**Content-vs-layout authority.** One rule resolves every "should I restyle this?" question:418419> User instructions control requested content and explicit deviations. The retained template controls layout and formatting where the user has not requested a change.420421So: change layout only when the user asks for a layout change. Otherwise the template wins. `fill-template.py` enforces this mechanically — it substitutes slot values and touches nothing else.422423**Fail loud, don't degrade.** If a required slot has no content, stop and get the content — do not silently ship a half-filled template. The fill script exits non-zero on a missing required slot, an undeclared slot, or a leftover marker.