design-html
Skill8 starsChanged 6 months agoMITView on GitHub
Turn an approved mockup, plan or description into production-quality, reflowing HTML/CSS.
- Installs packages
27 other projects use a version of this, 2 of them edited.
Adopted by
Most recently taken up by
- podo/design-agent-skillstheir own version, first seen Nov 8, 2026
- trungtm78/ClaudeSkillstheir own version, first seen Oct 25, 2026
- Marco-Satis/claude-setupedited, first seen Oct 20, 2026
- ignavm1/claude-skillstheir own version, first seen Oct 13, 2026
- leishifu666/skillstheir own version, first seen Oct 9, 2026
- chriswestt/claude-skillstheir own version, first seen Oct 9, 2026
Used with
Projects with it also use ios-clean, ios-fix and 5 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
Saqlain-811Skillios-clean
Remove the DebugBridge SPM package and all #if DEBUG wiring from an iOS app. (gstack)
Also in 5 projects that use this
Saqlain-811Skillios-fix
Autonomous iOS bug fixer. (gstack)
Also in 5 projects that use this
ShreyRaviSkillsetup-gbrain
Set up gbrain for this coding agent: install the CLI, initialize a local PGLite or Supabase brain, register MCP, capture per-remote trust policy. One command from zero to "gbrain is running, and this agent can call it." Use when: "setup gbrain", "connect gbrain", "start gbrain", "install gbrain", "configure gbrain for this machine". (gstack)
Also in 6 projects that use this
ShreyRaviSkillsync-gbrain
Keep gbrain current with this repo's code and refresh agent search guidance in CLAUDE.md. Wraps the gstack-gbrain-sync orchestrator with state probing, native code-surface registration, capability checks, and a verdict block. Re-runnable, idempotent. Use when: "sync gbrain", "refresh gbrain", "re-index this repo", "gbrain search isn't finding things". (gstack)
Also in 6 projects that use this
bastosSkillios-design-review
Review iOS app UI/UX design quality using Apple's design principles and the Liquid Glass-era design language from WWDC25/26. Use when asked for a design review, UX critique, visual design assessment, or evaluation of an app's interface quality, design intent, alignment with the new Apple design system, or usability. Triggers: "design review", "UX review", "visual design critique", "review the UI", "does this design work", "evaluate this interface", "design feedback", "Liquid Glass review".
Also in 5 projects that use this
- +1 more from Saqlain-811
- +1 more from ShreyRavi
Other versions27 projects use a version of this.
Word for word · 0
None found.
Edited · 2
- garrytan/gstack★ 141k27% same
- Marco-Satis/claude-setup★ 037% same
Wrote their own version · 25
- podo/design-agent-skills★ 32same name
- mae616/ai-template★ 9same name
- GTMify/claude-skills-starter★ 2same name
- abrb-dev/prime-workflow★ 1same name
- aryaniyaps/dot-ai★ 1same name
- chloropine/cgstack★ 1same name
- IyedBecheikh/bstack★ 1same name
- jpoindexter/theft-kit★ 1same name
- leishifu666/skills★ 1same name
- mahiunfiltered/claude-code-skills★ 1same name
15 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository62 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- address-pr-reviewSkill
- agent-evalSkill
- ai-cost-guardSkill
- autoplanSkill
- aws-costSkill
- bedrock-guardrailsSkill
- benchmarkSkill
- browseSkill
- canarySkill
- carefulSkill
- claudeSkill
- codexSkill
- connect-chromeSkill
- connect-reviewSkill
- context-restoreSkill
- context-saveSkill
- csoSkill
- design-consultationSkill
- design-reviewSkill
- design-shotgunSkill
- devex-reviewSkill
- diagramSkill
- document-generateSkill
- document-releaseSkill
- freezeSkill
- guardSkill
- healthSkill
- iac-reviewSkill
- improve-archSkill
- investigateSkill
- kb-reviewSkill
- land-and-deploySkill
- landing-reportSkill
- learnSkill
- make-pdfSkill
- mcp-reviewSkill
- office-hoursSkill
- open-browserSkill
- pair-agentSkill
- plan-ceo-reviewSkill
- plan-design-reviewSkill
- plan-devex-reviewSkill
- plan-eng-reviewSkill
- plan-tuneSkill
- pr-summarySkill
- qa-onlySkill
- qaSkill
- retroSkill
- reviewSkill
- scrapeSkill
- setup-browser-cookiesSkill
- setup-deploySkill
- shipSkill
- skillifySkill
- specSkill
- telegramSkill
- unfreezeSkill
- unslopSkill
- vibe-gcSkill
- The first 60 shown.
The file
Show its rules and text
We split this skill into the rules it gives an agent
58 rules. 0 are common, 36 are distinctive, and 22 are very specific to this project.
0
Common
Most of the 18 edited versions we read keep these.
36
Distinctive
This author's own choices. Worth a look.
22
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 20
If this session was compacted, the copy of this skill in context may be cut off.
- Very specific: line 21
Read ${CLAUDE_SKILL_DIR}/SKILL.md from disk and resume at the step you are on.
- Distinctive: line 25
Use when: "finalize this design", "turn this into HTML", "build me a page", "implement this design", or after any planning skill.
- Very specific: line 80
If DESIGN_NOT_AVAILABLE: skip visual mockup generation and fall back to text-based design review.
- Very specific: line 87
CRITICAL PATH RULE: All design artifacts (mockups, comparison boards, approved.json) MUST be saved under …
- Distinctive: line 100
Don't make me think: Every page should be self-evident.
- Distinctive: line 129
Don't innovate on navigation to be clever.
- Distinctive: line 136
Make clickable things obviously clickable: No relying on hover states for discoverability, especially on mobile where hover doesn't exist.
- Distinctive: line 142
Clarity trumps consistency: If making something significantly clearer requires making it slightly inconsistent, choose clarity every time.
- Distinctive: line 171
All the above applies on mobile, just more so.
- Very specific: line 220
If APPROVED was found, read it.
- Very specific: line 228
Read DESIGN.md if it exists in the repo root.
- Very specific: line 237
If evolve: read the existing HTML.
- Very specific: line 238
If fresh or no finalized.html: proceed to Step 1 with the approved PNG as the visual reference.
- Very specific: line 243
If CEO_PLAN or VARIANTS was found but no APPROVED:
- Distinctive: line 245
Read whichever context exists:
- Distinctive: line 246
If CEO plan found: read it and summarize the product vision and design requirements.
- Distinctive: line 247
If variant PNGs found: show them inline using the Read tool.
- Distinctive: line 248
If DESIGN.md found: read it for design tokens and constraints.
- Distinctive: line 257
If A: tell the user to run /design-shotgun, then come back to /design-html.
- Distinctive: line 258
If B: proceed to Step 1 in "plan-driven mode." There is no approved PNG, the plan is the source of truth.
- Distinctive: line 261
If C: accept a PNG file path from the user and proceed with that as the reference.
- Distinctive: line 265
If none of the above produced any context:
- Distinctive: line 274
If A, B, or C: tell the user to run that skill, then come back to /design-html.
- Distinctive: line 275
If D: proceed to Step 1 in "freeform mode." Ask the user for a screen name.
- Distinctive: line 279
After routing, output a brief context summary:
- Distinctive: line 290
Read the approved PNG inline using the Read tool and build the implementation spec from it yourself: colors as hex, typography (family, …
- Distinctive: line 296
If in plan-driven or freeform mode (no approved PNG), design from context:
- Distinctive: line 297
Plan-driven: read the CEO plan and/or design review notes.
- Distinctive: line 301
Freeform: use AskUserQuestion to gather what the user wants to build.
- Very specific: line 309
Read DESIGN.md tokens. These override any extracted values for system-level properties (brand colors, font family, spacing scale).
- Very specific: line 312
When approved.json carries archetype and sections, the user already chose the page's structure in /design-shotgun: build that archetype …
- Distinctive: line 323
When the page exists to make a visitor decide and act (landing, pricing, launch, waitlist), settle these before writing markup:
- Distinctive: line 327
Intake: Ask at most three questions, in one AskUserQuestion, and only what the plan or mockup leaves open: who the visitor is and what …
- Distinctive: line 364
Check if the user's project uses a frontend framework:
- Distinctive: line 370
If a framework is detected, use AskUserQuestion:
- Distinctive: line 375
If the user chooses framework output, ask one follow-up:
- Distinctive: line 381
If no framework detected: default to vanilla HTML, no question needed.
- Distinctive: line 413
Run the detected install command.
- Very specific: line 426
Write a single file using the Write tool.
- Distinctive: line 466
Use these patterns based on the tier selected in Step 2.
- Distinctive: line 613
After writing the HTML file, start a simple HTTP server for live preview:
- Distinctive: line 637
If python3 is not available, fall back to:
- Distinctive: line 643
After each edit, just refresh the browser (Cmd+R) to see changes."
- Distinctive: line 645
When the refinement loop ends (Step 4 exits), kill the server:
- Very specific: line 656
If SETUP printed BROWSE_AVAILABLE, take verification screenshots at 3 viewports.
- Very specific: line 673
If the live-reload server is not running (no python3), skip the screenshots rather than pointing the browser at a file:// path.
- Distinctive: line 681
If issues are found, note them and fix before presenting to the user.
- Very specific: line 683
If SETUP printed BROWSE_NOT_AVAILABLE, skip verification and note: "Browse binary not available.
- Very specific: line 755
If SETUP printed BROWSE_AVAILABLE, run the gate.
- Very specific: line 779
If SETUP printed BROWSE_NOT_AVAILABLE, or the live-reload server is not running, report the gate as not run, text-only: list each slide …
- Very specific: line 790
If no DESIGN.md exists in the repo root, offer to create one from the generated HTML:
- Very specific: line 807
If A: write DESIGN.md to the repo root with the extracted tokens, in the section layout /design-consultation writes (Typography, Color, …
- Very specific: line 815
Write finalized.json alongside the HTML:
- Very specific: line 839
If A: print the "Before this can ship" list again before copying.
- Very specific: line 846
Source of truth fidelity over code elegance: When an approved mockup exists, pixel-match it.
- Distinctive: line 855
Surgical edits in the refinement loop: Use the Edit tool to make targeted changes, not the Write tool to regenerate the entire file.
- Distinctive: line 859
Real content, never invented proof: When a mockup exists, extract text from it.
“Common” is measured: kept by at least 6 in 10 of the 18 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
If this session was compacted, the copy of this skill in context may be cut off.
Read ${CLAUDE_SKILL_DIR}/SKILL.md from disk and resume at the step you are on.
When to invoke
Use when: "finalize this design", "turn this into HTML", "build me a page", "implement this design", or after any planning skill.
Proactively suggest when user has approved a design or has a plan ready.
For a slide deck ("deck", "slides", or --deck), follow Deck mode below.
Voice triggers (speech-to-text aliases): "build the design", "code the mockup", "make it real".
Preamble
eval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)" 2>/dev/null || SLUG="unknown"
_LEARN_FILE="${VIBESTACK_HOME:-$HOME/.vibestack}/projects/${SLUG:-unknown}/learnings.jsonl"
if [ -f "$_LEARN_FILE" ]; then
_LEARN_COUNT=$(wc -l < "$_LEARN_FILE" 2>/dev/null | tr -d ' ')
echo "LEARNINGS: $_LEARN_COUNT entries loaded"
if [ "$_LEARN_COUNT" -gt 5 ] 2>/dev/null; then
"${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-learnings-search" --limit 5 2>/dev/null || true
fi
else
echo "LEARNINGS: none yet"
fi{{include lib/snippets/session-host.md}}
{{include lib/snippets/decision-brief.md}}
{{include lib/snippets/working-protocols.md}}
{{include lib/snippets/state-protocols.md}}
/design-html: Pretext-Native HTML Engine
You generate production-quality HTML where text actually works correctly. Not CSS
approximations — computed layout via Pretext. Text reflows on resize, heights adjust
to content, cards size themselves, chat bubbles shrinkwrap, editorial spreads flow
around obstacles. If you find yourself faking a layout with fixed heights, magic
numbers, or line-clamp, you have left the engine behind and the output is no longer
what this skill promises.
DESIGN SETUP
# Bind $D to vibe-design (OpenAI image backend) when a key is configured.
D=${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-design
if [ -x "$D" ] && [ "$("$D" status 2>/dev/null)" = "DESIGN_AVAILABLE" ]; then
echo "DESIGN_AVAILABLE via $D"
else
echo "DESIGN_NOT_AVAILABLE"
fiIf DESIGN_NOT_AVAILABLE: skip visual mockup generation and fall back to text-based design review.
$D generates image variants — nothing else. There is no vision extractor here: when
this skill needs to read a mockup, you read it yourself with the Read tool. Never
invent a subcommand; anything other than status and variants either reports that
it is unsupported or exits with a usage error.
CRITICAL PATH RULE: All design artifacts (mockups, comparison boards,
approved.json) MUST be saved under ${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG/designs/, NEVER to
.context/, docs/designs/, /tmp/, or any project-local / version-controlled
directory. Design artifacts are USER data, not project files. The finalized HTML is
the exception — that is the deliverable, and it goes wherever the user asks for it.
UX Principles: How Users Actually Behave
These principles govern how real humans interact with interfaces. They are observed behavior, not preferences. Apply them before, during, and after every design decision.
The Three Laws of Usability
- Don't make me think. Every page should be self-evident. If a user stops to think "What do I click?" or "What does this mean?", the design has failed. Self-evident > self-explanatory > requires explanation.
- Clicks don't matter, thinking does. Three mindless, unambiguous clicks beat one click that requires thought. Each step should feel like an obvious choice (animal, vegetable, or mineral), not a puzzle.
- Omit, then omit again. Get rid of half the words on each page, then get rid of half of what's left. Happy talk (self-congratulatory text) must die. Instructions must die. If they need reading, the design has failed.
How Users Actually Behave
- Users scan, they don't read. Design for scanning: visual hierarchy (prominence = importance), clearly defined areas, headings and bullet lists, highlighted key terms. We're designing billboards going by at 60 mph, not product brochures people will study.
- Users satisfice. They pick the first reasonable option, not the best. Make the right choice the most visible choice.
- Users muddle through. They don't figure out how things work. They wing it. If they accomplish their goal by accident, they won't seek the "right" way. Once they find something that works, no matter how badly, they stick to it.
- Users don't read instructions. They dive in. Guidance must be brief, timely, and unavoidable, or it won't be seen.
Billboard Design for Interfaces
- Use conventions. Logo top-left, nav top/left, search = magnifying glass. Don't innovate on navigation to be clever. Innovate when you KNOW you have a better idea, otherwise use conventions. Even across languages and cultures, web conventions let people identify the logo, nav, search, and main content.
- Visual hierarchy is everything. Related things are visually grouped. Nested things are visually contained. More important = more prominent. If everything shouts, nothing is heard. Start with the assumption everything is visual noise, guilty until proven innocent.
- Make clickable things obviously clickable. No relying on hover states for discoverability, especially on mobile where hover doesn't exist. Shape, location, and formatting (color, underlining) must signal clickability without interaction.
- Eliminate noise. Three sources: too many things shouting for attention (shouting), things not organized logically (disorganization), and too much stuff (clutter). Fix noise by removal, not addition.
- Clarity trumps consistency. If making something significantly clearer requires making it slightly inconsistent, choose clarity every time.
Navigation as Wayfinding
Users on the web have no sense of scale, direction, or location. Navigation must always answer: What site is this? What page am I on? What are the major sections? What are my options at this level? Where am I? How can I search?
Persistent navigation on every page. Breadcrumbs for deep hierarchies. Current section visually indicated. The "trunk test": cover everything except the navigation. You should still know what site this is, what page you're on, and what the major sections are. If not, the navigation has failed.
The Goodwill Reservoir
Users start with a reservoir of goodwill. Every friction point depletes it.
Deplete faster: Hiding info users want (pricing, contact, shipping). Punishing users for not doing things your way (formatting requirements on phone numbers). Asking for unnecessary information. Putting sizzle in their way (splash screens, forced tours, interstitials). Unprofessional or sloppy appearance.
Replenish: Know what users want to do and make it obvious. Tell them what they want to know upfront. Save them steps wherever possible. Make it easy to recover from errors. When in doubt, apologize.
Mobile: Same Rules, Higher Stakes
All the above applies on mobile, just more so. Real estate is scarce, but never sacrifice usability for space savings. Affordances must be VISIBLE: no cursor means no hover-to-discover. Touch targets must be big enough (44px minimum). Flat design can strip away useful visual information that signals interactivity. Prioritize ruthlessly: things needed in a hurry go close at hand, everything else a few taps away with an obvious path to get there.
SETUP
{{include lib/snippets/browse-detect.md}}
Step 0: Input Detection
Detect what design context exists for this project. Run all four checks — each is a fresh shell, so each derives the project slug itself:
eval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)"
setopt +o nomatch 2>/dev/null || true
_CEO=$(ls -t "${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG/ceo-plans/"*.md 2>/dev/null | head -1)
[ -n "$_CEO" ] && echo "CEO_PLAN: $_CEO" || echo "NO_CEO_PLAN"eval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)"
setopt +o nomatch 2>/dev/null || true
_APPROVED=$(ls -t "${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG/designs/"*/approved.json 2>/dev/null | head -1)
[ -n "$_APPROVED" ] && echo "APPROVED: $_APPROVED" || echo "NO_APPROVED"eval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)"
setopt +o nomatch 2>/dev/null || true
_VARIANTS=$(ls -t "${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG/designs/"*/variant-*.png 2>/dev/null | head -1)
[ -n "$_VARIANTS" ] && echo "VARIANTS: $_VARIANTS" || echo "NO_VARIANTS"eval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)"
setopt +o nomatch 2>/dev/null || true
_FINALIZED=$(ls -t "${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG/designs/"*/finalized.html 2>/dev/null | head -1)
[ -n "$_FINALIZED" ] && echo "FINALIZED: $_FINALIZED" || echo "NO_FINALIZED"
[ -f DESIGN.md ] && echo "DESIGN_MD: exists" || echo "NO_DESIGN_MD"Now route based on what was found. Check these cases in order:
Case A: approved.json exists (design-shotgun ran)
If APPROVED was found, read it. Extract: the approved image, user feedback, screen
name. The approved image is the file the record's approved_path names. An older
record without approved_path points at variant-<approved_variant>.png in the
directory that holds approved.json. If that file does not exist, stop and tell the
user the approved image is gone and they need to reselect with /design-shotgun; never
substitute another variant. Also read the CEO plan if one exists (it adds strategic
context).
Read DESIGN.md if it exists in the repo root. These tokens take priority for
system-level values (fonts, brand colors, spacing scale).
Then check for prior finalized.html. If FINALIZED was also found, use AskUserQuestion:
Found a prior finalized HTML from a previous session. Want to evolve it (apply new changes on top, preserving your custom edits) or start fresh? A) Evolve — iterate on the existing HTML B) Start fresh — regenerate from the approved mockup
If evolve: read the existing HTML. Apply changes on top during Step 3. If fresh or no finalized.html: proceed to Step 1 with the approved PNG as the visual reference.
Case B: CEO plan and/or design variants exist, but no approved.json
If CEO_PLAN or VARIANTS was found but no APPROVED:
Read whichever context exists:
- If CEO plan found: read it and summarize the product vision and design requirements.
- If variant PNGs found: show them inline using the Read tool.
- If DESIGN.md found: read it for design tokens and constraints.
Use AskUserQuestion:
Found [CEO plan from /plan-ceo-review | design review variants from /plan-design-review | both] but no approved design mockup. A) Run /design-shotgun — explore design variants based on the existing plan context B) Skip mockups — I'll design the HTML directly from the plan context C) I have a PNG — let me provide the path
If A: tell the user to run /design-shotgun, then come back to /design-html. If B: proceed to Step 1 in "plan-driven mode." There is no approved PNG, the plan is the source of truth. Ask the user for a screen name to use for the output directory (e.g., "landing-page", "dashboard", "pricing"). If C: accept a PNG file path from the user and proceed with that as the reference.
Case C: Nothing found (clean slate)
If none of the above produced any context:
Use AskUserQuestion:
No design context found for this project. How do you want to start? A) Run /plan-ceo-review first — think through the product strategy before designing B) Run /plan-design-review first — design review with visual mockups C) Run /design-shotgun — jump straight to visual design exploration D) Just describe it — tell me what you want and I'll design the HTML live
If A, B, or C: tell the user to run that skill, then come back to /design-html. If D: proceed to Step 1 in "freeform mode." Ask the user for a screen name.
Context summary
After routing, output a brief context summary:
- Mode: approved-mockup | plan-driven | freeform | evolve
- Visual reference: path to approved PNG, or "none (plan-driven)" or "none (freeform)"
- CEO plan: path or "none"
- Design tokens: "DESIGN.md" or "none"
- Screen name: from approved.json, user-provided, or inferred from CEO plan
Step 1: Design Analysis
- Read the approved PNG inline using the Read tool and build the implementation spec from it yourself: colors as hex, typography (family, weights, scale), layout structure, and a component inventory. Where the image is ambiguous — an exact hex, a font family you cannot name — say so and take the value from DESIGN.md or the plan rather than guessing a number that will look authoritative and be wrong.
- If in plan-driven or freeform mode (no approved PNG), design from context:
- Plan-driven: read the CEO plan and/or design review notes. Extract the described UI requirements, user flows, target audience, visual feel (dark/light, dense/spacious), content structure (hero, features, pricing, etc.), and design constraints. Build an implementation spec from the plan's prose rather than a visual reference.
- Freeform: use AskUserQuestion to gather what the user wants to build. Ask about: purpose/audience, visual feel (dark/light, playful/serious, dense/spacious), content structure (hero, features, pricing, etc.), and any reference sites they like. In both cases, describe the intended visual layout, colors, typography, and component structure as your implementation spec. Write real copy from the plan or the user's description (never lorem ipsum); proof comes only from the user, under the craft floor below.
- Read
DESIGN.mdtokens. These override any extracted values for system-level properties (brand colors, font family, spacing scale). - When
approved.jsoncarriesarchetypeandsections, the user already chose the page's structure in /design-shotgun: build that archetype with those sections in that order, and do not re-pick the archetype or ask about it under Persuade pages. - Output an "Implementation spec" summary: colors (hex), fonts (family + weights), spacing scale, component list, layout type.
{{include lib/snippets/design-craft-floor.md}}
Persuade pages
When the page exists to make a visitor decide and act (landing, pricing, launch, waitlist), settle these before writing markup:
- Surface line. Open the spec with
Surface: PERSUADE — <who visits> decides <what>. - Intake. Ask at most three questions, in one AskUserQuestion, and only what the plan or mockup leaves open: who the visitor is and what stops them, the one action you want, and what proof the user can actually supply.
- Archetype. Pick one archetype for the page's structure (show the product
working, problem then outcome, compare and price, or a short launch/waitlist page)
and say in one line why it fits this visitor — unless
approved.jsonalready names one (Step 1, item 4). The hero still holds to one headline, one supporting line, one action group. - Primary action. One named action, built with every state: default, hover and focus, invalid (a message beside the field that names the fix), pending (disabled, with a progress label), failure (what went wrong, the input kept, a retry), and success (what happens next).
- Index decision.
indexfor a public page meant to be found,noindexfor a preview, campaign variant or unlaunched page, set with<meta name="robots">and listed in the hand-off.
Step 2: Smart Pretext API Routing
Analyze the approved design and classify it into a Pretext tier. Each tier uses different Pretext APIs for optimal results:
| Design type | Pretext APIs | Use case |
| Simple layout (landing, marketing) | prepare() + layout() | Resize-aware heights |
| Card/grid (dashboard, listing) | prepare() + layout() | Self-sizing cards |
| Chat/messaging UI | prepareWithSegments() + walkLineRanges() | Tight-fit bubbles, min-width |
| Content-heavy (editorial, blog) | prepareWithSegments() + layoutNextLine() | Text around obstacles |
| Complex editorial | Full engine + layoutWithLines() | Manual line rendering |
State the chosen tier and why. Reference the specific Pretext APIs that will be used.
Step 2.5: Framework Detection
Check if the user's project uses a frontend framework:
[ -f package.json ] && cat package.json | grep -o '"react"\|"svelte"\|"vue"\|"@angular/core"\|"solid-js"\|"preact"' | head -1 || echo "NONE"If a framework is detected, use AskUserQuestion:
Detected [React/Svelte/Vue] in your project. What format should the output be? A) Vanilla HTML — self-contained preview file (recommended for first pass) B) [React/Svelte/Vue] component — framework-native with Pretext hooks
If the user chooses framework output, ask one follow-up:
A) TypeScript B) JavaScript
For vanilla HTML: proceed to Step 3 with vanilla output. For framework output: proceed to Step 3 with framework-specific patterns. If no framework detected: default to vanilla HTML, no question needed.
Step 3: Generate Pretext-Native HTML
Pretext Source Embedding
For vanilla HTML output: no Pretext bundle ships with this skill, so the page
imports Pretext from the CDN as an ES module. Pretext is ESM-only and sets no global,
so the import and all page code that calls it go in the same <script type="module">.
The import pins an exact version, so a later release cannot change a page that already
works:
<script type="module">
import { prepare, layout, prepareWithSegments, walkLineRanges, layoutNextLine, layoutWithLines } from 'https://esm.sh/@chenglou/pretext@0.0.9'
// page code (the wiring patterns below) goes here, after the import
</script>Tell the user the page needs the network to lay itself out: on an offline machine or
behind a CSP that blocks esm.sh it renders unlaid-out. If they need a fully
self-contained file, they install @chenglou/pretext in their own project and use
framework output (or their bundler) instead.
For framework output, add to the project's dependencies instead:
# Detect package manager
[ -f bun.lockb ] && echo "bun add @chenglou/pretext" || \
[ -f pnpm-lock.yaml ] && echo "pnpm add @chenglou/pretext" || \
[ -f yarn.lock ] && echo "yarn add @chenglou/pretext" || \
echo "npm install @chenglou/pretext"Run the detected install command. Then use standard imports in the component.
HTML Generation
Resolve the output directory first (fill in <screen-name>):
eval "$("${VIBESTACK_HOME:-$HOME/.vibestack}/bin/vibe-slug" 2>/dev/null)"
_DESIGN_DIR="${VIBESTACK_HOME:-$HOME/.vibestack}/projects/$SLUG/designs/<screen-name>-$(date +%Y%m%d)"
mkdir -p "$_DESIGN_DIR"
echo "DESIGN_DIR: $_DESIGN_DIR"Write a single file using the Write tool. Save to <DESIGN_DIR>/finalized.html,
where <DESIGN_DIR> is the path the block printed on its DESIGN_DIR: line.
For framework output, save to <DESIGN_DIR>/finalized.[tsx|svelte|vue].
Always include in vanilla HTML:
- Pretext imported from esm.sh in a
<script type="module">(see above) - CSS custom properties for design tokens from DESIGN.md / Step 1 extraction
- Google Fonts via
<link>tags +document.fonts.readygate before firstprepare() - Semantic HTML5 (
<header>,<nav>,<main>,<section>,<footer>) - Responsive behavior via Pretext relayout (not just media queries)
- Breakpoint-specific adjustments at 375px, 768px, 1024px, 1440px
- ARIA attributes, heading hierarchy, focus-visible states
contenteditableon text elements + MutationObserver to re-prepare + re-layout on edit- ResizeObserver on containers to re-layout on resize
prefers-color-schememedia query for dark modeprefers-reduced-motionfor animation respect- Real content extracted from the mockup (never lorem ipsum)
Never include by default (AI slop blacklist): An approved mockup that shows one, a DESIGN.md that chose it, or an explicit user request overrides the list: build it and state the tradeoff once.
- Purple/blue gradients as default
- Generic 3-column feature grids
- Center-everything layouts with no visual hierarchy
- Decorative blobs, waves, or geometric patterns not in the mockup
- Stock photo placeholder divs
- "Get Started" / "Learn More" generic CTAs not from the mockup
- Rounded-corner cards with drop shadows as the default component
- Emoji as visual elements
- Generic testimonial sections
- Cookie-cutter hero sections with left-text right-image
- Gradient buttons as the primary CTA pattern
- A colored left border on cards; a card nested inside a card
- Glowing edges, radial halos or spotlight glows on dark surfaces
- Gradient text as emphasis; headings within a step of body size
- A pill-shaped label or kicker floating above the hero headline
- Frosted-glass panels as the default surface
Pretext Wiring Patterns
Use these patterns based on the tier selected in Step 2. These are the correct
Pretext API usage patterns. Follow them exactly. The imports below are for framework
output; in vanilla HTML, import the same names from 'https://esm.sh/@chenglou/pretext@0.0.9'
once, at the top of the page's <script type="module">.
Pattern 1: Basic height computation (Simple layout, Card/grid)
import { prepare, layout } from '@chenglou/pretext'
// 1. PREPARE — one-time, after fonts load
await document.fonts.ready
const elements = document.querySelectorAll('[data-pretext]')
const prepared = new Map()
for (const el of elements) {
const text = el.textContent
const font = getComputedStyle(el).font
prepared.set(el, prepare(text, font))
}
// 2. LAYOUT — cheap, call on every resize
function relayout() {
for (const [el, handle] of prepared) {
const { height } = layout(handle, el.clientWidth, parseFloat(getComputedStyle(el).lineHeight))
el.style.height = `${height}px`
}
}
// 3. RESIZE-AWARE
new ResizeObserver(() => relayout()).observe(document.body)
relayout()
// 4. CONTENT-EDITABLE — re-prepare when text changes
for (const el of elements) {
if (el.contentEditable === 'true') {
new MutationObserver(() => {
const font = getComputedStyle(el).font
prepared.set(el, prepare(el.textContent, font))
relayout()
}).observe(el, { characterData: true, subtree: true, childList: true })
}
}Pattern 2: Shrinkwrap / tight-fit containers (Chat bubbles)
import { prepareWithSegments, walkLineRanges } from '@chenglou/pretext'
// Tight-fit width: lay out at maxWidth, then shrink the bubble to its widest line
function shrinkwrap(text, font, maxWidth) {
const segs = prepareWithSegments(text, font)
let widest = 0
const lineCount = walkLineRanges(segs, maxWidth, (line) => {
widest = Math.max(widest, line.width)
})
return { width: Math.ceil(widest), lineCount }
}Pattern 3: Text around obstacles (Editorial layout)
import { prepareWithSegments, layoutNextLine } from '@chenglou/pretext'
function layoutAroundObstacles(text, font, containerWidth, lineHeight, obstacles) {
const segs = prepareWithSegments(text, font)
let cursor = { segmentIndex: 0, graphemeIndex: 0 }
let y = 0
const lines = []
while (true) {
// Calculate available width at current y position, accounting for obstacles
let availWidth = containerWidth
for (const obs of obstacles) {
if (y >= obs.top && y < obs.top + obs.height) {
availWidth -= obs.width
}
}
const result = layoutNextLine(segs, cursor, availWidth)
if (!result) break
lines.push({ text: result.text, width: result.width, x: 0, y })
cursor = result.end
y += lineHeight
}
return { lines, totalHeight: y }
}Pattern 4: Full line-by-line rendering (Complex editorial)
import { prepareWithSegments, layoutWithLines } from '@chenglou/pretext'
const segs = prepareWithSegments(text, font)
const { lines, height } = layoutWithLines(segs, containerWidth, lineHeight)
// lines = [{ text, width, start, end }, ...] (no x/y: line i sits at y = i * lineHeight)
// Use for Canvas/SVG rendering or custom DOM positioning
lines.forEach((line, i) => {
const span = document.createElement('span')
span.textContent = line.text
span.style.position = 'absolute'
span.style.left = '0px'
span.style.top = `${i * lineHeight}px`
container.appendChild(span)
})Pretext API Reference
PRETEXT API CHEATSHEET:
prepare(text, font) → handle
One-time text measurement. Call after document.fonts.ready.
Font: CSS shorthand like '16px Inter' or 'bold 24px Georgia'.
layout(prepared, maxWidth, lineHeight) → { height, lineCount }
Fast layout computation. Call on every resize. Sub-millisecond.
prepareWithSegments(text, font) → handle
Like prepare() but enables line-level APIs below.
layoutWithLines(segs, maxWidth, lineHeight) → { lineCount, height, lines: [{text, width, start, end}...] }
Full line-by-line breakdown (no x/y: line i is at y = i * lineHeight). For Canvas/SVG rendering.
walkLineRanges(segs, maxWidth, onLine) → lineCount
Calls onLine({ width, start, end }) once per line at maxWidth (no text built).
The widest line's width is the tight-fit container width. For chat bubbles.
layoutNextLine(segs, cursor, maxWidth) → { text, width, start, end } | null
Iterator. Different maxWidth per line = text around obstacles.
Start with cursor { segmentIndex: 0, graphemeIndex: 0 }; pass result.end next.
Returns null when text is exhausted. Takes no lineHeight: advance y yourself.
clearCache() → void
Clears internal measurement caches. Use when cycling many fonts.
setLocale(locale?) → void
Retargets word segmenter for future prepare() calls.Step 3.5: Live Reload Server
After writing the HTML file, start a simple HTTP server for live preview:
# Start a simple HTTP server in the output directory
_OUTPUT_DIR=$(dirname <path-to-finalized.html>)
cd "$_OUTPUT_DIR"
python3 -m http.server 0 --bind 127.0.0.1 &
_SERVER_PID=$!
# The server needs a moment to bind; wait for its listening socket before reading the port.
_PORT=""
for _i in 1 2 3 4 5 6 7 8 9 10; do
_PORT=$(lsof -a -p "$_SERVER_PID" -iTCP -sTCP:LISTEN -P -n -Fn 2>/dev/null | sed -n 's/^n.*:\([0-9][0-9]*\)$/\1/p' | head -1)
[ -n "$_PORT" ] && break
sleep 0.2
done
[ -n "$_PORT" ] || { echo "SERVER_FAILED: no listening port after 2s" >&2; kill "$_SERVER_PID" 2>/dev/null; exit 1; }
echo "SERVER: http://127.0.0.1:$_PORT/finalized.html"
echo "PID: $_SERVER_PID"Remember the printed SERVER: URL and PID: value. Each bash block is a fresh
shell, so $_PORT and $_SERVER_PID are empty in later blocks: restate the URL and
PID literally wherever they are needed.
If python3 is not available, fall back to:
open <path-to-finalized.html>Tell the user: "Live preview running at <SERVER URL>.
After each edit, just refresh the browser (Cmd+R) to see changes."
When the refinement loop ends (Step 4 exits), kill the server:
kill <PID> 2>/dev/null || trueStep 4: Preview + Refinement Loop
Verification Screenshots
If SETUP printed BROWSE_AVAILABLE, take verification screenshots at 3 viewports.
screenshot has no width flag, so set the viewport before each capture. Load the
page through the live-reload server's SERVER: URL (not file://), so the module
import and relative assets resolve. This is a fresh shell, so replace <BROWSE_BIN>
with the path SETUP printed on its BROWSE_BIN: line:
B='<BROWSE_BIN>'
$B goto "<SERVER URL>"
$B viewport 375x812
$B screenshot /tmp/vibestack-verify-mobile.png
$B viewport 768x1024
$B screenshot /tmp/vibestack-verify-tablet.png
$B viewport 1440x900
$B screenshot /tmp/vibestack-verify-desktop.pngIf the live-reload server is not running (no python3), skip the screenshots rather
than pointing the browser at a file:// path.
Show all three screenshots inline using the Read tool. Check for:
- Text overflow (text cut off or extending beyond containers)
- Layout collapse (elements overlapping or missing)
- Responsive breakage (content not adapting to viewport)
If issues are found, note them and fix before presenting to the user.
If SETUP printed BROWSE_NOT_AVAILABLE, skip verification and note:
"Browse binary not available. Skipping automated viewport verification."
Refinement Loop
LOOP:
1. If server is running, tell user to open the printed `SERVER:` URL
Otherwise: open <path>/finalized.html
2. If an approved mockup PNG exists, show it inline (Read tool) for visual comparison.
If in plan-driven or freeform mode, skip this step.
3. AskUserQuestion (adjust wording based on mode):
With mockup: "The HTML is live in your browser. Here's the approved mockup for comparison.
Try: resize the window (text should reflow dynamically),
click any text (it's editable, layout recomputes instantly).
What needs to change? Say 'done' when satisfied."
Without mockup: "The HTML is live in your browser. Try: resize the window
(text should reflow dynamically), click any text (it's editable, layout
recomputes instantly). What needs to change? Say 'done' when satisfied."
4. If "done" / "ship it" / "looks good" / "perfect" → print the craft floor's
"Before this can ship" list, exit loop, go to Step 5
5. Apply feedback using targeted Edit tool changes on the HTML file
(do NOT regenerate the entire file — surgical edits only)
6. Brief summary of what changed (2-3 lines max)
7. If verification screenshots are available, re-take them to confirm the fix
8. Go to LOOPMaximum 10 iterations. If the user hasn't said "done" after 10, use AskUserQuestion: "We've done 10 rounds of refinement. Want to continue iterating or call it done?"
Deck mode
Deck mode is explicit and never the default path. Enter it only when the input is a
slide deck, the user says "deck" or "slides", or passes --deck. Otherwise build a
page as above. In deck mode, Steps 0-3.5 still apply (input detection, design tokens,
output path, live-reload server); this section replaces the page structure and the
Step 4 verification screenshots.
Structure:
- One
<section class="slide">per slide inside<main>, eachheight: 100dvh(with aheight: 100vhline before it for older browsers). - Keyboard (ArrowLeft/ArrowRight, PageUp/PageDown, Space, Home/End), wheel and touch navigation between slides, plus a visible slide index.
- Under
prefers-reduced-motion: reduce, slide changes jump instead of animating: no smooth scrolling, no reveal transitions. - Type and spacing scale with
clamp()(for examplefont-size: clamp(1rem, 0.6rem + 1.6vw, 2.5rem)), never below a readable size.
Density heuristics (a starting point for splitting content, not the gate):
| Slide type | Heuristic |
| Title | 1 heading + 1 line |
| Content | 1 heading + 4-6 bullets |
| Code | 8-10 lines |
| Grid | at most 6 cells |
The viewport-fit gate. At 1920×1080, 1280×720 and 375×667, every slide must pass
scrollHeight <= clientHeight, checked with $B js. A slide that fails is split into
two slides, then the gate runs again; never clip it with overflow: hidden or shrink
its type below the clamp() minimum, since that hides the failure instead of fixing it.
If SETUP printed BROWSE_AVAILABLE, run the gate. As with the verification
screenshots, set the viewport before each capture, load the live-reload server's
SERVER: URL (not file://), and replace <BROWSE_BIN> with the path SETUP printed
on its BROWSE_BIN: line, since this is a fresh shell:
B='<BROWSE_BIN>'
$B goto "<SERVER URL>"
$B viewport 1920x1080
$B js "[...document.querySelectorAll('section.slide')].map((s, i) => s.scrollHeight <= s.clientHeight ? '' : 'OVERFLOW slide ' + (i + 1) + ': ' + s.scrollHeight + ' > ' + s.clientHeight).filter(Boolean).join('; ') || 'ALL_SLIDES_FIT'"
$B screenshot /tmp/vibestack-deck-1920.png
$B viewport 1280x720
$B js "[...document.querySelectorAll('section.slide')].map((s, i) => s.scrollHeight <= s.clientHeight ? '' : 'OVERFLOW slide ' + (i + 1) + ': ' + s.scrollHeight + ' > ' + s.clientHeight).filter(Boolean).join('; ') || 'ALL_SLIDES_FIT'"
$B screenshot /tmp/vibestack-deck-1280.png
$B viewport 375x667
$B js "[...document.querySelectorAll('section.slide')].map((s, i) => s.scrollHeight <= s.clientHeight ? '' : 'OVERFLOW slide ' + (i + 1) + ': ' + s.scrollHeight + ' > ' + s.clientHeight).filter(Boolean).join('; ') || 'ALL_SLIDES_FIT'"
$B screenshot /tmp/vibestack-deck-375.pngEach js line prints ALL_SLIDES_FIT or one OVERFLOW slide N entry per failing
slide. Split every listed slide and rerun the block until all three viewports print
ALL_SLIDES_FIT. An empty or error result is not a pass: report the gate as not run.
Show the three screenshots inline with the Read tool.
If SETUP printed BROWSE_NOT_AVAILABLE, or the live-reload server is not running,
report the gate as not run, text-only: list each slide with its density against the
heuristics above and flag the ones likely to overflow. Never report the deck as fitting
without the gate.
Step 5: Save & Next Steps
Design Token Extraction
If no DESIGN.md exists in the repo root, offer to create one from the generated HTML:
Extract from the HTML:
- CSS custom properties (colors, spacing, font sizes)
- Font families and weights used
- Color palette (primary, secondary, accent, neutral)
- Spacing scale
- Border radius values
- Shadow values
Use AskUserQuestion:
No DESIGN.md found. I can extract the design tokens from the HTML we just built and create a DESIGN.md for your project. This means future /design-shotgun and /design-html runs will be style-consistent automatically. A) Create DESIGN.md from these tokens B) Skip — I'll handle the design system later
If A: write DESIGN.md to the repo root with the extracted tokens, in the section
layout /design-consultation writes (Typography, Color, Spacing, Layout, Motion),
plus a Components section listing each component this HTML built with its
states (default, hover, focus, active, disabled, loading, error — only the states
it has) and a Do's and Don'ts section with the concrete rules the build followed.
Save Metadata
Write finalized.json alongside the HTML:
{
"source_mockup": "<approved variant PNG path or null>",
"source_plan": "<CEO plan path or null>",
"mode": "<approved-mockup|plan-driven|freeform|evolve>",
"html_file": "<path to finalized.html or component file>",
"pretext_tier": "<selected tier>",
"framework": "<vanilla|react|svelte|vue>",
"iterations": <number of refinement iterations>,
"date": "<ISO 8601>",
"screen": "<screen name>",
"branch": "<current branch>"
}Next Steps
Use AskUserQuestion:
Design finalized with Pretext-native layout. What's next? A) Copy to project — copy the HTML/component into your codebase B) Iterate more — keep refining C) Done — I'll use this as a reference
If A: print the "Before this can ship" list again before copying. Copying never
clears a [Needs proof: …] marker; it travels into the codebase with the page.
Important Rules
- Source of truth fidelity over code elegance. When an approved mockup exists,
pixel-match it. If that requires
width: 312pxinstead of a CSS grid class, that's correct. When in plan-driven or freeform mode, the user's feedback during the refinement loop is the source of truth. Code cleanup happens later during component extraction. - Always use Pretext for text layout. Even if the design looks simple, Pretext ensures correct height computation on resize. The overhead is 30KB. Every page benefits.
- Surgical edits in the refinement loop. Use the Edit tool to make targeted changes, not the Write tool to regenerate the entire file. The user may have made manual edits via contenteditable that should be preserved.
- Real content, never invented proof. When a mockup exists, extract text from it. In
plan-driven mode, use content from the plan. In freeform mode, write copy from the
user's description. Never use "Lorem ipsum", "Your text here", or placeholder content,
and never invent proof: a missing quote, logo or number becomes a
[Needs proof: …]marker. - One page per invocation. For multi-page designs, run /design-html once per page. Each run produces one HTML file.
{{include lib/snippets/askuserquestion-split.md}}
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

