| name | deckify |
| description | Generate a complete HTML-slide Design System markdown file from a reference URL. Studies the page's color palette, typography, logo, and aesthetic mood, then writes a Design System that bakes in proven engineering rules (1280ร720 fit contract, three-layer overflow safety net, single-absorber rule, mobile inline-flex trap catch-all, flip-card mobile fix, logo-as-SVG-symbol-with-currentColor pattern, 12px readability floor) while letting the brand identity drive the visuals. Use this whenever the user gives you a URL and asks to "build a design system from this site," "extract a design system from", "make slides like this brand," "skin slides to match this site's visual language," or wants to author HTML slides in the visual language of any specific website. Also use when the user names a brand site as a slide-deck reference, even if they don't say "design system" explicitly. |
| dependencies | ["agent-browser","python3"] |
deckify
Turn a reference URL into a production-ready Design System markdown plus a verified 9-slide HTML sample deck. Another agent (or you) can use the DS to build precise, mobile-friendly HTML slides in the brand's visual language. The sample deck is the proof that the DS spec works.
Opening โ print this banner once when the skill activates
Right before Phase 0, print exactly one of these two banners. Pick the language from how the user is currently writing to you โ if their invocation message is in Chinese, print the ไธญๆ banner; otherwise print the English one. You don't need to wait for Phase 2 / decisions.json โ at this point the conversation language is the only signal you need, and it's already obvious from what the user just typed.
Print the banner cleanly with surrounding blank lines, then continue straight into Phase 0 โ do not add a "Hi! I'm deckify, what's your brand?" intro on top of this. The user already gave you the URL; the slogan is the entire hello.
English (default):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The next deck writes itself.
Train deckify on your brand once.
Every deck after that is just a prompt.
โ deckify
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ไธญๆ (after Round 0 confirms zh):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
PPT ็ฟป็ฏไบ๏ผ
AI ็จ HTML ๆฅ็ฎก่ฎฒๆ
ไบ็่พน็ใ
โ deckify
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Why this skill exists (read first)
"Make slides in the visual language of brand X" is deceptively hard. The naive approach โ show Claude the URL and say "make slides like this" โ produces drift: colors shift, fonts get substituted, the logo renders as an emoji or a placeholder, and slides clip the deck at 720px because nobody told the model that a slide is a fixed-size box, not a scrolling document.
This skill solves the problem by separating what changes per brand from what never should:
- Brand-variable (you do recon and let the user confirm): philosophy, colors, type, logo, aesthetic emphasis
- Engineering DNA (baked in unchanged, every time): fit contract, single-absorber rule, three-layer overflow safety net, logo-as-symbol pattern, mobile inline-flex catch-all, flip-card mobile fix, 12px readability floor, the 40-item pre-ship checklist
That split is the whole game. Without it, every new brand = relearning the same hard-won bugs.
The pipeline (4 phases โ do not skip any)
Phase 0 โ Verify dependencies (one-time)
Before the first run on any new machine, verify the standalone agent-browser CLI is installed:
python3 scripts/setup.py
This script:
- Confirms
python3 is on PATH
- Confirms
agent-browser is on PATH and can open a page
- If either is missing, prints the recommended install command for the platform (npm / brew / cargo) and the project URL: https://github.com/vercel-labs/agent-browser
- Reminds the user to run
agent-browser install after first install (downloads Chrome from Chrome for Testing)
If the user is on Linux and Chrome won't download, suggest agent-browser install --with-deps (pulls in libnss3, etc.).
Do NOT silently fall back to curl if agent-browser is missing โ modern brand sites hydrate client-side and curl returns a near-empty shell, breaking the whole pipeline. Surface the install command and stop.
All user artefacts land in ~/deckify/ โ independent of the cwd you run from. The brand's DS markdown, verification deck, persisted recon source, and per-run reports all go to ~/deckify/decks/<brand>/ and ~/deckify/reports/. Do not write user artefacts into the deckify source repo, into the cwd, into a temp dir, or anywhere else. ~/deckify/ is created on first use; subsequent runs reuse it. This is enforced by scripts/persist_brand_source.py at Phase 6 โ it always resolves to ~/deckify/, ignoring cwd. (If you're a skill maintainer running the Phase A panel, see tools/phase-a/README.md โ that path is separate and writes to the deckify repo's tests/, not to ~/deckify/.)
Phase 1 โ Reconnaissance (5 steps: thin deterministic harness + LLM exploration)
Architecture: Python scripts only do what's deterministic (fetch, enumerate, quality-gate, embed). Every act of judgment โ which subpages to crawl, which candidate is the real wordmark, what the brand palette actually is โ is done by you, the LLM running this skill, using the guideline files in references/llm-prompts/.
1a. fetch_sitemap.py โ 1b. (LLM picks pages) โ 1c. fetch_pages.py
โ
1f. embed_logo.py โ 1e. (LLM synthesizes) โ 1d. enumerate_assets.py
Each step's output is the next step's input. Don't skip.
Phase 1 happens in a transient workspace inside the OS temp dir โ these are large, regenerable recon files (DOM dumps, screenshots, raw asset pools), not durable artifacts. The OS sweeps them on its own schedule (macOS ~3 days idle, Linux on reboot, Windows by user / Disk Cleanup). Durable artifacts (brand.json / decisions.json / assets / DS markdown / deck HTML) get copied out at Phase 6.
WS=$(python3 scripts/init_workspace.py <brand-slug>)
echo "Workspace: $WS"
1a โ Fetch sitemap + home (deterministic)
python3 scripts/fetch_sitemap.py <URL> "$WS"
Pulls recon/home.html + recon/home.png + recon/sitemap.xml + recon/sitemap-urls.txt + recon/nav-links.json + recon/jsonld.json.
1b โ Pick subpages to fetch (LLM, you)
Read references/llm-prompts/discover-pages.md. Then read the discovery files from 1a, decide which 5โ8 subpages are most likely to expose the brand's real wordmark / palette / typography / aesthetic, and write them to $WS/pages.txt (one URL per line, # comments allowed).
1c โ Batch-fetch the chosen pages (deterministic)
python3 scripts/fetch_pages.py "$WS/pages.txt" "$WS"
For each URL: desktop screenshot + DOM dump + per-page probe (every inline SVG anywhere with metadata, every <img> with size+region, every background-image: url(), every JSON-LD logo field, all <link rel*=icon>, all :root vars, computed bg/color/font on a wide selector set, @font-face srcs, preload font URLs). Lands at $WS/recon/pages/<slug>/{dom.html,shot.png,probe.json}.
1d โ Aggregate into a candidate pool (deterministic)
python3 scripts/enumerate_assets.py "$WS"
Merges every page's probe.json into one $WS/raw-assets.json. Each logo candidate gets a stable 8-char id. Color frequency, computed palette per surface, font frequencies, font-face URLs are all aggregated. Candidates are ranked by interestingness as a hint, but nothing is filtered out โ that's your job.
1e โ Synthesize brand from raw assets (LLM, you)
Read references/llm-prompts/synthesize-brand.md. Then:
- Read
$WS/raw-assets.json
- Look at
$WS/recon/pages/<slug>/shot.png for each indexed page using your host's image-viewing capability (the Read tool in Claude Code, the equivalent in Codex / OpenClaw, or your own image loader if scripted)
- Pick the chosen logo (by candidate
id), brand_primary / secondary / ink / paper / accents (with hex + evidence), display + body fonts, spacing/radius/shadow style, aesthetic mood + precedents
- Write
$WS/brand.json per the schema in synthesize-brand.md, including chosen_logo.id plus 2โ3 alt_logo_ids
1f โ Materialize the chosen logo (deterministic)
python3 scripts/embed_logo.py "$WS"
Looks up chosen_logo.id in raw-assets, downloads if remote, runs the quality gate (SVG must have <path d> โฅ 40 chars or <image> child; raster โฅ 64ร64), and writes $WS/assets/logo.{svg|png|โฆ} + $WS/assets/logo.embed.html (a <symbol id="brand-wm"> snippet ready to paste into Phase 3) + $WS/assets/logo.dataurl.
If quality-gate fails (exit 1), swap chosen_logo.id to the next item in alt_logo_ids in brand.json and rerun. If all alternatives fail, stop and ask the user for a logo URL or file path. Never invent a typographic placeholder.
Phase 2 โ Confirm with the user (latent โ interactive Q&A)
Goal: take the brand.json you produced in 1e and resolve the decisions YOU couldn't make alone โ language, ambiguous logo picks, contested colour direction, proprietary typography fallbacks. Don't ask the user to confirm things YOU already have strong evidence for โ that's noise.
Host-conditional interaction rule (this is a hard rule on Claude Code, a recommendation elsewhere โ read carefully):
| Host environment | Required mechanism | If unavailable |
|---|
| Claude Code / Claude Desktop | MUST use the AskUserQuestion tool for every Round below. If the tool is deferred (schema not loaded at session start), invoke ToolSearch with query: "select:AskUserQuestion" first to load its schema, then call it. Do not substitute plain text questions โ that bypasses the structured-choice UI, loses analytics, and produces ambiguous free-text answers the rest of this phase depends on. | No graceful fallback โ refuse to proceed and tell the user the tool is missing. |
| Codex CLI / OpenClaw / similar AI hosts | Use the host's nearest equivalent structured-question mechanism if it exists (each host names it differently). | If no structured mechanism exists, fall back to a single block of clearly-numbered text questions with explicit option labels. |
| Headless / scripted runs (no human in the loop) | Skip Phase 2 entirely; commit brand.json as-is and write decisions.json with interaction_mode: "headless-defaults" and the synthesizer's best evidence-based picks for every required field. | N/A โ by definition there's no user to ask. |
After Phase 2 finishes, always record which mode was actually used in decisions.json:
{
"interaction_mode": "ask-user-question" | "host-equivalent" | "text-fallback" | "headless-defaults",
"host": "claude-code" | "codex" | "openclaw" | "other-name" | "headless",
}
This field is what audit_skill.py and build_report.py use to detect "this run silently downgraded interaction quality" โ a recurring failure mode where an agent skips structured Q&A and the resulting brand DS reflects guesswork instead of user choice.
Question philosophy: ask only where you are genuinely uncertain. A confident, evidence-backed brand.json shouldn't need 4 confirmation rounds โ it needs maybe 1 or 2 for the genuinely ambiguous decisions, plus the always-required language round. If a decision is unambiguous (single canonical SVG logo, dominant chord with 100ร CSS frequency, body font is the only computed font), commit and don't waste the user's attention.
Round 0 (ALWAYS first): language. Ask the user with exactly two options: ไธญๆ (Simplified Chinese) and English. These are the only two languages with maintained sister templates (ds-template.zh.md and ds-template.md). Do NOT offer Japanese / Spanish / Other free-text โ there is no ds-template.<other>.md to route to, so any non-zh/en answer would silently fall back to the English template + a per-run translation pass, which produces drift and undercuts the language_consistency hard check. If the user genuinely needs another language, they can author it in ไธญๆ or English and re-translate downstream. After this answer, conduct every subsequent round AND Phase 3 generation in that language. Token names (--primary, --accent, etc.), code snippets, CSS, hex values, viewBox numbers, and HTML comment anchors (<!-- ENGINEERING-DNA: ... -->) stay in English regardless โ only the prose changes.
Round 1 (CONDITIONAL โ only if logo is ambiguous): logo selection. Trigger when you have โฅ 2 candidates that all pass the quality gate AND no clear winner from the synthesize-brand priority order. Surface 2โ3 specific candidates with their evidence:
- Each option labeled with where it came from (e.g. "Nav inline-SVG, viewBox 0 0 67 44, single path" / "JSON-LD knowledge_graph_logo.png, RGB-only" / "apple-touch-icon, 180ร180 RGBA").
- Description explains the trade-off (e.g. "RGB PNG flips badly on dark covers; SVG is cleaner but might be a wordmark not the silhouette").
- If candidates differ only trivially, pick yourself โ don't ask.
Round 2 (CONDITIONAL โ only if dominant chord is contested): palette anchor. Trigger when colour-frequency analysis has 2+ candidates with similar counts (within 1.5ร) AND the screenshots don't clearly resolve which is the brand's actual primary chord (vs a campaign accent). Surface the top 2โ3 colours with their hex + role evidence; ask which is the brand's actual --primary.
Round 3 (CONDITIONAL โ only if typography needs licensing decision): font fallback. Trigger when font.frequencies show a proprietary face (UnileverDesire, SF Pro, Sรถhne, Stripe Sans) that won't be licensable for a typical standalone slide deck. Surface 2โ3 fallback options:
- "Helvetica Neue โ system-ui" (Apple's actual fallback chain)
- "Inter / Source Sans" (modern open source)
- "system-ui only" (zero webfont load, generic)
Round 4 (ALWAYS, but short): slide-type emphasis for the verification deck. Multi-select from the 8 required slide types per references/verification-deck-spec.md โ which 2โ3 should the deck particularly lean into for this brand's voice? (e.g. Apple โ Type J pullquote + Type F image + Type H chart.)
Round 5+ (CONDITIONAL โ only when needed): scope-of-deck decisions. Quarterly review vs annual letter vs internal deck โ only ask if the user's original prompt didn't clarify. If they said "build me a DS for SaaS pitch decks", that's enough; don't re-ask.
Take the user's responses. Save the final decisions to $WS/decisions.json. If the user redirects on a token (e.g., "actually use the secondary as primary"), update brand.json's palette accordingly so Phase 3 generation reflects it.
Anti-pattern: asking 4 ceremonial confirmation questions when the recon was unambiguous. Each interactive question costs the user attention โ earn it with genuine choices, not with reciting back what you already know.
If the user gives no clear direction on something, default to brand.json's pick (LLM's best evidence-based guess) and note it in the output.
Phase 3 โ Generation (latent, template-driven)
Step 0 โ Pick the template by language. Read decisions.json for language:
English โ use references/ds-template.md (the English source-of-truth)
ไธญๆ / Chinese / zh โ use references/ds-template.zh.md (the Chinese sister template; same engineering-DNA anchors, prose pre-translated)
These are the two and only two supported languages โ Phase 2 Round 0 only offers them, so decisions.json.language will always be one of these. There is no general translation-pass fallback; if you ever see another value here, treat it as a bug in Round 0 and re-ask the language question instead of silently translating.
Both templates contain identical:
- Brand-variable placeholders:
{{BRAND_NAME}}, {{BRAND_SLUG}}, {{PHILOSOPHY_PARAGRAPH}}, {{PRIMARY_HEX}}, {{ACCENT_HEX}}, {{BRAND_PALETTE_TOKENS}}, etc.
- Engineering DNA baked in verbatim: the entire fit contract section, the single-absorber rule, the mobile section including the inline-flex trap, the flip-card mobile fix, the pre-ship checklist. Never edited, never "simplified."
- HTML comment ID anchors:
<!-- ENGINEERING-DNA: fit-contract -->, <!-- ENGINEERING-DNA: scale-to-fit -->, etc. These are language-agnostic stable identifiers used by evals/hard_checks.py:check_ds_engineering_dna.
Steps:
-
Fill brand-variable placeholders from $WS/decisions.json + $WS/brand.json (palette hex, font families, brand name, etc.).
-
Logo embed: paste the contents of $WS/assets/logo.embed.html directly into the ยง4 Logo section's "Definition (once per HTML file)" block. embed_logo.py already produced the correct <symbol id="brand-wm"> snippet โ vector or base64-raster form, with hardcoded fills stripped so currentColor works. Do not regenerate this; do not re-extract paths.
-
Set {{LOGO_VIEWBOX}} placeholder in ยง4's .logo usage examples to match the viewBox in the embed snippet.
-
Language pass (only when Round 0 picked ไธญๆ): if Step 0 already routed you to ds-template.zh.md, the prose is already pre-translated โ your work in this step is limited to (a) translating the brand-variable text you injected via placeholders ({{PHILOSOPHY_PARAGRAPH}}, {{TYPE_PHILOSOPHY_NOTE}}, etc.) and (b) checking that no English sentences leaked through. The output must be single-language throughout โ no leftover English sentences mixed into a Chinese DS.
Translate everything that is prose, including:
- Chapter titles (
## 1. Design Philosophy โ ## 1. ่ฎพ่ฎก็ๅฟต)
- Section narration paragraphs
- The Constraints / Freedom / Bespoke bullet contents
- The Design Taste anti-AI-slop rules
- Slide-type descriptions in ยง6
- The pre-ship Checklist labels (ยง13)
- Anti-pattern explanations
- All the words around the code blocks
Never translate:
- Token names:
--primary, --accent, --surface, --ink, --mid, --rule, --tint, --green, --red, --warn, --teal, plus brand-palette tokens
- All CSS / JS / HTML code blocks (every fenced
css js html block stays as-is)
- Hex values, viewBox numbers, file names, URLs
- HTML comment anchors:
<!-- ENGINEERING-DNA: fit-contract -->, <!-- ENGINEERING-DNA: typography-safety -->, etc. These are stable identifiers โ the prose around them adapts; the IDs stay.
How the engineering-DNA hard check works under translation: evals/hard_checks.py:check_ds_engineering_dna reads the markdown looking for <!-- ENGINEERING-DNA: <id> --> HTML comments โ NOT for the English chapter titles. As long as you preserve the comment anchors verbatim, the chapter heading and surrounding prose can be in any language. So ### 5.1 ๅ้กต้้
ๅฅ็บฆ <!-- ENGINEERING-DNA: fit-contract --> passes the check the same as the English original.
-
Write the final markdown to the user's chosen path. Default: ./{{BRAND_SLUG}}-PPT-Design-System.md. Confirm path before writing if it would overwrite an existing file.
Phase 4 โ Verification deck + runtime hard checks (MANDATORY, not optional)
The DS markdown is the deliverable. The verification deck is the proof that the DS produces correct slides. Skipping this means shipping an unverified spec โ don't.
The Phase B fix-routing rule, enforced structurally. When a hard check
fails, the fix lives in the brand DS โ never in the deck alone. This is
not advice; this is the workflow contract that makes Phase B work at all,
because if you patch the deck to make the check pass, you've healed one
slide and left the spec wrong. The next deck built from this DS will have
the same bug; every other brand sharing the affected DS pattern will too.
Starting now this is checked: hard_checks.py includes a
phase_b_workflow check that hashes the deck and the DS each run. On a
re-run, if the deck changed but the DS didn't, the check FAILS with
deck_modified_without_ds_update. There is no way around this short of
also editing the DS โ which is the point.
The correct loop is:
- Run hard_checks โ see which check fails.
- Look up that check in
references/verification-deck-spec.md ยง8 โ it
names the brand DS section that owns the rule.
- Edit that DS section.
- Regenerate the deck from the updated DS (literally re-do Phase 4a
using the new DS as source).
- Re-run hard_checks.
If after step 5 the same check still fails, the fix you applied to the
DS didn't actually capture the rule โ go back to step 2 and read the
fix-mapping more carefully, or pick a different DS section.
Step 4a โ Write the verification deck.
Read references/verification-deck-spec.md. It defines the 8 required slide types (cover, narrative+pullquote, two-column, data table, chart, flip cards, timeline, big pull-quote) and 6 coverage rules (multi-column collapse, click interaction, semantic colour, real numbers, bespoke composition, absorber variety). Write the deck to ~/deckify/decks/<brand>/<brand>-deck.html using copy drawn from the recon corpus โ never invented stats.
Where artefacts live: every brand the user generates lands under ~/deckify/decks/<brand>/ (a fixed home-directory convention โ independent of the cwd you happen to be in, the deckify source repo, or any project structure). The brand DS markdown, the verification deck, the persisted recon source, and per-run hard-check reports all live under that single tree. ~/deckify/ is created on first use; it is the only path you should write user artefacts to. Do not write into the deckify source repo (the maintainer's decks/ there is a reference panel โ not your output target).
Step 4b โ Run the runtime hard checks (deterministic).
For Layer 2 (single brand the user just generated), run hard_checks.py directly. Cross-platform โ only Python, no shell required:
REPORTS="$HOME/deckify/reports/runs/$(date +%Y-%m-%dT%H-%M-%S)"
mkdir -p "$REPORTS/per-sample/<brand>"
python3 <skill-path>/evals/hard_checks.py \
"$HOME/deckify/decks/<brand>/<brand>-deck.html" \
"$HOME/deckify/decks/<brand>/<brand>-PPT-Design-System.md" \
"$REPORTS/per-sample/<brand>"
Phase A skill-author note: the multi-brand panel runner is python3 tools/phase-a/run_phase_a.py โ see tools/phase-a/README.md. That tool is for skill maintainers tuning the deckify skill against the reference brand panel; it writes to the deckify repo's tests/reports/, not to ~/deckify/. End-user runs always use ~/deckify/.
Step 4c โ On any hard-check failure, FIX THE BRAND DS โ never the deck alone.
See the <EXTREMELY-IMPORTANT> block above. The brand DS markdown is your tunable; the deck is the verification artifact. When a check fails, trace it to the relevant section of your brand's DS (use the fail โ DS-section mapping table in references/verification-deck-spec.md ยง8), update the DS, regenerate the deck from the updated DS, re-run hard checks.
Iterate until hard checks are 10/11+ PASS (cjk_font_quality is soft for en decks; the rest including phase_b_workflow must all pass).
Phase 5 โ Visual judge (LLM, you) โ MANDATORY
Hard checks measure DOM shapes; they don't measure whether the deck looks on-brand. That's your job.
Phase 5 is single-brand โ it scores the one deck the user just generated. Do not iterate over a panel here; that's Layer 1 (skill-author territory, see evals/run_phase_a.py).
Step 5a โ Read the per-slide screenshots.
For the user's brand at ~/deckify/reports/runs/<latest>/per-sample/<brand>/slides/, view the PNGs using your host's image-viewing capability (Read tool in Claude Code, equivalents elsewhere). Re-read the brand's DS markdown for context.
Step 5b โ Score against the rubric.
Read evals/rubric.json โ 6 dimensions (logo present and branded, slide visual quality, brand fidelity, content substantive, engineering DNA visible in DS, CJK typography quality for zh decks), each 0โ5. Plus 5 disqualifiers (D1 logo missing, D2 dimensions wrong, D3 console errors, D4 mobile horizontal scroll, D5 DS template violated).
For cjk_typography_quality: this is a vision-judged quality score, not a hard constraint. The hard check cjk_font_quality only catches the absolute bug (zero CJK fonts in chain). The judge dimension catches "the CJK is technically rendering, but looks thin / cheap / mismatched with Latin / wrong weight / wrong serif-vs-sans pairing." If you score this โค 3, the fix routes to DS ยง3 CJK ๅญไฝๅ้้พ โ typically swap CJK family to front of font-family chain AND bump body font-weight to 500/600. en decks return 5 for this dimension (not applicable).
Step 5c โ Write judge.json.
Land at ~/deckify/reports/runs/<latest>/per-sample/<brand>/judge.json with this schema:
{
"ok": true,
"judged_by": "<your model id>",
"scores": {
"scores": {
"logo_present_and_branded": 0,
"slide_visual_quality": 0,
"brand_fidelity": 0,
"content_substantive": 0,
"engineering_dna_visible_in_ds": 0,
"cjk_typography_quality": 0
},
"reasoning": "<2-3 sentences explaining the lowest scores>",
"regression_flags": []
}
}
Step 5d โ Aggregate (single brand).
python3 <skill-path>/evals/build_report.py <run-dir> 4 \
"<brand>|<source-url>|$HOME/deckify/decks/<brand>/<brand>-deck.html|$HOME/deckify/decks/<brand>/<brand>-PPT-Design-System.md"
PASS criteria (per rubric.json): hard 9/10+ AND judge avg โฅ 4 AND no disqualifier triggered.
Step 5e โ Failure handling. All routes go to the user's brand DS (this is Layer 2; never edit skill source from a Phase 5 failure):
- Judge score < 4 on
brand_fidelity โ revisit ~/deckify/decks/<brand>/source/brand.json (was the mood paragraph too generic? did the palette flatten to white+grey+single-accent?). Update with sharper evidence, regenerate the brand DS ยง1 + ยง2, regenerate the deck, re-judge.
- Judge score < 4 on
slide_visual_quality โ tighten the user's brand DS ยง6 (per-slide-type spec) or ยง7 (component density), regenerate the deck, re-judge.
- Judge score < 4 on
content_substantive โ the Phase 1 recon corpus was thin; widen pages.txt to broader subpages OR pull more verbatim phrases from existing recon screenshots into the deck.
- Judge score < 4 on
engineering_dna_visible_in_ds โ the user's brand DS is missing a required chapter or it got diluted during the language pass. Restore the chapter verbatim from references/ds-template.md (or .zh.md for zh decks).
- Judge score < 4 on
cjk_typography_quality โ fix ~/deckify/decks/<brand>/<brand>-PPT-Design-System.md ยง3 CJK ๅญไฝๅ้้พ, then propagate the new font-family chain into the deck.
- Disqualifier triggered โ see the fail-mapping table in
references/verification-deck-spec.md ยง8.
Iterate until PASS. The skill is not done until both hard 9/10+ AND judge โฅ 4.
Phase 6 โ Persist + hand back
Step 6a โ Persist durable artifacts out of the temp workspace. Before the OS sweeps $WS, copy the parts that encode user judgment + LLM synthesis into the user's home deckify directory:
python3 <skill-path>/scripts/persist_brand_source.py "$WS" <brand-slug>
This copies brand.json, decisions.json, pages.txt, and assets/ (logo files) from $WS into ~/deckify/decks/<brand-slug>/source/ โ sitting next to the DS markdown and the deck HTML. The recon/ DOM dumps + raw-assets.json are intentionally not copied โ those are large + regenerable from the URL, and the OS will sweep them on its own.
After this step, the brand has a complete persisted bundle in the user's home tree:
~/deckify/
โโโ decks/
โโโ <brand>/
โโโ <brand>-PPT-Design-System.md โ the deliverable
โโโ <brand>-deck.html โ verification deck
โโโ source/
โโโ brand.json โ LLM synthesis
โโโ decisions.json โ user choices (Phase 2)
โโโ pages.txt โ LLM-picked subpages
โโโ assets/ โ embedded logo files
โโโ logo.svg
โโโ logo.embed.html
โโโ logo.dataurl
โโโ logo.report.json
Per-run hard-check + judge reports live alongside, under ~/deckify/reports/runs/<ts>/per-sample/<brand>/.
Step 6b โ Hand back to the user.
Give the user:
- The DS markdown path + the deck HTML path (both under
~/deckify/decks/<brand>/)
- A 5-line summary: palette, font, logo source, aesthetic mood, slide-type emphasis
- The eval scoreboard: hard 10/11+, judge avg, status PASS/WARN/FAIL
- Path to
~/deckify/reports/runs/<latest>/summary.md for the full per-brand breakdown
Step 6c โ Print the closing message. After the summary above, print exactly one of these two closing blocks depending on decisions.json.language. By Phase 6 you have a confirmed language choice from Phase 2 โ use that. The closing is a real piece of writing, not flavour text โ preserve every word, including the line breaks. Print it AFTER the deliverable summary so the user has already seen what they got, then read this as the parting thought.
English (default):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Done.
You're holding two things: a Design System markdown that you โ or any
AI agent โ can use forever to build precise, brand-faithful slides;
and a sample deck that proves the spec works.
Notice what just happened. You didn't open PowerPoint. You didn't
move a single text box. You didn't fight a template. PPT was built
for **humans to draw with their hands** โ every box, every gradient,
every line spacing, hand-placed. That made sense for fifty years.
The job has changed. Slides are no longer drawn; they're **imagined
and described**. The author has shifted from human to AI, and AI's
native medium isn't `.pptx` binary โ it's HTML. Living markup,
animatable, queryable, transformable, copy-paste-able into any
conversation. Where PPT slows AI down, HTML lets it run.
Welcome to the deck for the AI era.
โ deckify
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ไธญๆ (when decisions.json.language === "ไธญๆ"):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
ๅฎๆไบใ
ไฝ ็ฐๅจๆฅๆไธคไปฝไธ่ฅฟ๏ผไธไปฝ Design System markdown โโ ไฝ ๆไปปไฝ AI agent ้ฝ่ฝ
ๆฟๅฎๆ ้ๆฌกๅฐ็ๆ่ดดๅ่ฟไธชๅ็็็ฒพๅ slides๏ผไธไปฝ็คบ่ deck๏ผ่ฏๆ่ฟๅฅ่ง่็กฎๅฎ
ๅฏไปฅๅทฅไฝใ
็ๆไธไธๅๆๅ็ไบไปไนใไฝ ๆฒกๆๆๅผ PowerPointใไฝ ๆฒกๆๆชๅจไธไธชๆๆฌๆกใไฝ ๆฒก
ๆ่ทๆจกๆฟ่พๅฒใPPT ๆฏ **ไธบใไบบๆ็ปใ่่ฎพ่ฎก็** โโ ๆฏไธชๆนๅใๆฏๆฎตๆธๅใๆฏ่ก
้ด่ท๏ผ้ฝ้ ๆๆใ่ฟๅจ่ฟๅปไบๅๅนด้่ฎฒๅพ้ใ
ไฝ slide ็ๅถไฝๆนๅผๅไบใslide ไธๅ่ขซใ็ปใๅบๆฅ๏ผๅฎไปฌ่ขซ **ๆณ่ฑกๅบๆฅใๆ่ฟฐ
ๅบๆฅ**ใๅไฝ่
ๅทฒ็ปไปไบบๅๆไบ AI๏ผ่ AI ็ๆฏ่ฏญไธๆฏ `.pptx` ไบ่ฟๅถ โโ ๆฏ
HTMLใๆดป็ๆ ็ญพใๅฏๅจ็ปใๅฏ่ขซๆฅ่ฏขใๅฏ่ขซๆนๅใๅฏ็ฒ่ดด่ฟไปปไฝๅฏน่ฏใPPT ๆๆ
ข
AI ็ๅฐๆน๏ผHTML ่ฎฉ AI ่ท่ตทๆฅใ
ๆฌข่ฟๆฅๅฐ AI ๆถไปฃ็ deckใ
โ deckify
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Hard rules โ engineering DNA (NEVER violate, NEVER simplify)
These parts of the generated DS must be copied verbatim from references/ds-template.md. They came from real, painful bugs. If you find yourself thinking "this section is overkill, let me trim it," stop โ you are about to reintroduce a bug someone else already fixed.
- Single-Slide Fit Contract: 1280ร720 fixed canvas; three-layer
overflow:hidden safety net (.slide, .sw .sc, and any flex:1 absorber); single-absorber rule; 602px content-height budget math; pre-build height checklist; verify at native 1280ร720 (the transform:scale() masks overflow at scaled sizes).
- Logo as SVG
<symbol> + currentColor: <symbol> defined once in a hidden <svg> block at the top of <body>; referenced via <use href="#โฆ">; fill: currentColor set on the outer <svg> (NOT on the <path> โ selectors don't pierce shadow DOM). Variants: .W (white-on-dark), .L (brand-dark-on-light).
- Token-only color: every color in slides comes from
:root CSS variables. No ad-hoc hex values. Token names stay stable across brands (--primary, --accent, --surface, --ink, --mid, --rule, --tint, --green, --red, --warn, --teal) even when the actual hex differs โ so downstream slide code doesn't need to know which DS it's using.
- Type scale + 12px readability floor: nothing below 12px ever; enforced default sizes for title (50px), card headline (28px), body (16px), subtitle (20px); changing layout when content doesn't fit, never shrinking font.
- Mobile media query (โค768px) collapses everything to single column AND inline-flex trap catch-all CSS (
@media rules that override style="display:flex" and style*="grid-template-columns" in case bespoke slides used inline styles). This is how mobile parity survives bespoke slide compositions.
- Flip card mobile fix: every flip card carries
onclick="this.classList.toggle('on')"; mobile CSS kills 3D transforms and uses display:none/flex to swap front/back. CSS :hover doesn't work on touch.
- Constraints vs Freedom philosophy (ยง1): explicit list of hard constraints + explicit list of reusable components + explicit "bespoke compositions encouraged within tokens" paragraph. This is what stops the next agent from either (a) reinventing the system or (b) restricting itself to a rigid catalog.
- No emoji, typographic symbols only:
โ โ ! ร โ โ permitted; ๐๐๐ฅ etc. forbidden. (Brand may override toward more or fewer symbols, but the no-emoji rule is invariant.)
- Pre-ship checklist (ยง13): the ~40-item checklist that catches every common bug. Keep all groups: brand & tokens, typography & readability, slide structure, fit contract, components & interaction, visual & imagery, animation, responsive (verify at 375px width).
If any of these is missing from the generated DS, the DS will produce broken decks. The whole point of skillifying this is so future you doesn't have to remember any of it.
What legitimately changes per brand
- ยง1 Design Philosophy (the mood paragraph + the Constraints/Freedom list contents, not the framing itself)
- ยง2 Colour Tokens (hex values; token names stay stable)
- ยง3 Typography (family, weights, fallback chain, line-height tweaks)
- ยง4 Logo (SVG path)
- ยง6 Slide Types (emphasis order, illustrative example contents)
- Cosmetic component params: border-radius, accent thickness, hover lift distance
Anti-patterns
- Skipping Phase 2 confirmation. Without user confirmation, you ship overfit garbage โ possibly the wrong mood, possibly the wrong dominant color (sites often have hero accents that aren't the actual brand color).
- Renaming color tokens to brand-specific names (
--unilever-navy, --stripe-purple). Tokens are abstractions โ keep names stable.
- "Improving" the engineering DNA by simplifying the fit contract, dropping the absorber rule, removing the inline-flex catch-all, etc. Those rules are non-negotiable.
- Generating without a logo. If
download_logo.sh failed, ASK the user for a logo URL or local file before generating ยง4.
- Using emoji in the generated DS or its sample slides. Typographic symbols only.
- Trusting the homepage hero color as the brand primary. Hero gradients are often campaign-specific; the actual brand primary is usually in the nav, footer, or buttons.
extract_brand.py reports candidates with frequency โ prefer the higher-frequency one and confirm.
- Skipping mobile verification. Every line of the ยง10 Mobile section in the template is there because a real production deck broke in that exact way. Always render at 375px once before declaring done.
Companion files
references/ds-template.md โ full DS template with placeholders + engineering DNA (incl. ยง3.1 Typography Safety, ยง4 multi-format logo embed, ยง5 scale-to-fit runtime, ยง6 Type E row-count rule, ยง10 mobile cov/sw fill rule). Read in Phase 3.
references/verification-deck-spec.md โ Phase 4 contract: the 8 required slide types every verification deck must include, plus the 6 coverage rules. Read in Phase 4a.
references/decision-questions.md โ Phase 2 structured decision checklist + Round 0 language framing.
references/llm-prompts/discover-pages.md โ guideline you (LLM) read at step 1b to decide which subpages to fetch.
references/llm-prompts/synthesize-brand.md โ guideline you (LLM) read at step 1e. Includes Design Taste anti-AI-slop guardrails (no Inter as the design choice, no even-weighted accent grids, no SaaS-default chrome).
scripts/fetch_sitemap.py โ step 1a: home + sitemap + nav-links + JSON-LD.
scripts/fetch_pages.py โ step 1c: batch-fetch a URL list with full per-page probes.
scripts/enumerate_assets.py โ step 1d: aggregate all probes into raw-assets.json with stable candidate ids.
scripts/embed_logo.py โ step 1f: navigate-to-asset-then-same-origin-fetch + quality-gate + base64-embed the chosen logo. Handles cross-origin CDN (Contentful, Cloudinary).
scripts/setup.py โ Phase 0 dependency check.
evals/ โ quality contract directory (see evals/README.md). Two layers in one folder:
evals/evals.json + evals/trigger_evals.json โ marketplace harness contract (Anthropic skill-eval format).
evals/hard_checks.py + evals/rubric.json + evals/build_report.py โ Layer 2 runtime auto-eval invoked by Phase 4-5 (cross-platform Python; no shell wrapper).
evals/run_phase_a.py โ Layer 1 multi-brand panel orchestrator (skill author use only, not part of the runtime path).
tests/ โ unit + integration + smoke tests.