implement-design
Implement a finished Claude Design in this code repo — the Claude Design → code handoff. Use whenever the user has a design from Claude Design (Anthropic's design canvas) to turn into real code: phras
SKILL.mdai/skills/frontend/implement-design/SKILL.md
GitHubImplement Design (Claude Design → repo)
Turn a finished design from Claude Design (or a similar tool) into working, on-brand code in this
repo. The export is a handoff bundle — a .tar.gz or .zip from "Handoff to Claude Code" holding a
README, the design chat transcript, prototype HTML/JSX/CSS, a token file, and uploads. Identify it
by that content shape, not the extension: Claude Design's separate "Download as .zip" menu item is
a raw-assets export with no README/chats — if the archive lacks the shape, it's the wrong export
(ingesting-the-bundle.md). The format is an unstable research preview, not a standard, so parse
defensively: read what's actually present rather than assuming exact filenames or folders. That same defensiveness lets the skill absorb
Claude Design's format changes and adapt to other tools (e.g. Google Stitch). The prototype code is
prototype-grade (it often runs on in-browser Babel + UMD React); your job is to port it into
this repo's stack, not paste it in.
Three modes — detect which one you're in (Phase 0) and route accordingly:
establish-design-system— the repo has no design system yet; bootstrap one from the bundle.evolve-design-system— the repo has a design system and the bundle deliberately changes it (new/changed/removed tokens or brand); reconcile and version the change.implement-feature— the repo has a design system and the bundle is a bounded feature; build it consume-first, honoring the design without clobbering the established system.
The core principle running through all three: src/styles/globals.css is the canonical runtime token
source and DESIGN.md is the AI-facing statement of intent — the repo is the source of truth, and the
bundle is a proposal. When they disagree, globals.css wins; never overwrite it wholesale from a
bundle — diff and apply deliberate changes only. The bundle stays in place until the user has
reviewed the implementation and approved it, and only then is it removed before merge. Never assume
your implementation is correct; the user decides whether it matches the intent.
Definition of done
Render this in the chat — don't just leave it buried in this file. At the start of the run, paste the checklist below into your reply. Re-post it with boxes ticked as you finish each phase, and at every gate state in the chat whether it PASSED, with the evidence (the measured numbers, the screenshots taken, the user's words). Do not declare the handoff done until every box is ticked in the chat and all four gates show PASS with evidence. Keeping the criteria visible in the conversation is how you and the user know nothing was skipped.
The four gates are blocking: you may not take the action a gate guards until it is green and you have shown why in the chat.
Reconciliation
- Bundle ingested defensively (parse what's there; don't assume exact filenames): intent/README
→ chat transcript → markup → token file → assets, read for intent and ported, never pasted
into
src/ - Mode detected —
establish-design-system/evolve-design-system/implement-feature(asked if ambiguous); framework + router detected; up-front questions asked - Bundle tokens treated as a proposal — diffed against canonical
globals.css, never overwritten wholesale; new tokens resolved (force-fit to existing, or deliberate extension) - Tokens in
globals.cssare by role (not export name) — Tailwind v4, OKLCH, three-layer;.darkauthored (brand hue held, neutrals inverted);--tw-prose-*mapped if prose is used -
DESIGN.mdreconciled (not clobbered); a system change (establish-design-system/evolve-design-system, or a feature extension) carries a DDR + SemVer bump
Implementation
- shadcn/ui + Lucide (named imports) only; styled exclusively with semantic tokens (zero arbitrary hex / one-off color literals)
- States covered: default, empty, loading, error, disabled
- Responsive (mobile-first): holds at phone/tablet/desktop with no horizontal overflow
-
/brandbuilt/updated in the same change (scope chosen up front during intake) - Assets placed (static → repo, user media → R2); fonts self-hosted OFL/Apache
.woff2; favicons generated from the mark - Logos downloadable on
/brand— SVG + PNG size matrix (square mark 64–1024, lockup/wordmark 400/800/1600 wide, full-color / reversed/white / single-color black, padded solid-bg avatar) +logos.zip, generated bytask brand:assets; a missing mark flagged to the user, never silently skipped
Gates — post PASS + evidence in the chat before taking the guarded action
- ① Static contrast (blocks implementation — Phase 2):
task lint:designis green; every token pair meets WCAG AA (4.5:1 text, 3:1 large/UI) in both themes. Evidence: the checker output. - ② Licensing (blocks commit — Phase 4): every font/icon/image cleared for commercial use; AI logos flagged; anything unclear stopped, not guessed. Evidence: the per-asset license list.
- ③ Verification (blocks sign-off — Phase 5):
task verifygreen and the build compiles; rendered contrast measured as numbers (both themes, every text role incl. long-form prose); responsive at phone/tablet/desktop with no overflow; cross-browser on Chromium/Firefox/WebKit (incl. mobile Safari). Evidence: the numbers + the screenshot matrix. - ④ Sign-off (blocks deletion & close-out — Phase 6): screenshots shown (both themes, all states, key breakpoints), deltas surfaced, user has explicitly approved. Evidence: the user's approval in the chat — never inferred from a green build or your own confidence.
Close-out (only once gate ④ is green)
- Handoff bundle deleted (or a thin screenshot + intent note extracted first if states remain)
-
docs/architecture/design-language.mdand/brandupdated; a system change recorded as a DDR with a SemVer bump - Conventional Commit on a feature branch; PR opened for human review; hooks never bypassed
(
--no-verifyprohibited); no direct merge tomain
Inputs & stack
- A handoff bundle, usually unpacked to
specs/handoff-<feature>/. If you can't find one, ask the user where the export landed (or whether they've exported yet) before proceeding. - The existing repo:
DESIGN.md(root),src/styles/globals.css,docs/architecture/design-language.md,Taskfile.yml, and the project'sCLAUDE.md. - Stack target: TypeScript, React, Vite, pnpm, Tailwind CSS v4, shadcn/ui, Lucide, Cloudflare Pages/Workers. Named primary router TanStack Router; React Router/plain React and Astro 6 fully supported. Favor shadcn/ui for components and Lucide for icons.
Procedure
Work the phases in order, and track them in the chat (see "Definition of done"): post the checklist up front, tick boxes as you go, and report each gate's PASS with evidence. Explanations of why live in the referenced files — read the reference when you reach its phase.
Phase 0 — Ingest, detect & decide
All the up-front orientation happens here, before any building:
- Ingest (defensively). Unpack the bundle and read it for intent — don't hard-code its layout; the
format is an unstable preview and varies by tool. The common Claude Design shape is a
.tar.gzor.zipwith a README ("CODING AGENTS: READ THIS FIRST") →chats/*.md(the design conversation — the real intent) → the entry HTML → a token file (tokens.css/site.css) → components →uploads/. Read whatever is actually present in that spirit (intent/README → transcript → markup → tokens → assets), and adapt if a piece is named or shaped differently. The prototype code is prototype-grade — read it for structure and intent, then port it; don't paste markup intosrc/. Locate the bundle (oftenspecs/handoff-*/); if you can't find it, ask where the export landed. (ingesting-the-bundle.md) - Detect mode, framework & router. Mode —
establish-design-system(no design system: no real:roottokens inglobals.cssand no/brandroute),evolve-design-system(a system exists and the bundle is token/brand-dominant, or intent says "update the design system"), orimplement-feature(a system exists and the bundle is a page/feature, its tokens mostly a re-emission of the existing set). Judge from the bundle's content (tokens vs. screens), the chat intent, and the repo state; if it's ambiguous, ask (fold the question into the batch below) rather than guess — a wrong guess risks clobbering the system. Framework + router drive file placement: TanStack →src/routes/brand.tsxandsrc/components/ui; React Router/plain → a normal/brandroute; Astro →src/pages/brand.astrowith React islands for interactive specimens; any other framework maps the same three roles (global stylesheet import, component dir, route entry) — never block on an unrecognized router.establish-design-systemruns Phase 1 next;evolve-design-system/implement-featureskip to Phase 2. - Decide up front — one
AskUserQuestionbatch. Now that you've read the intent and know the stack, ask everything you'll need at once (up to 4 questions) so Phases 1–5 run uninterrupted: the/brandscope (core guide → brand/press kit → collateral groups;brand-page.md), plus any genuine ambiguity the transcript left open (routes/pages in scope, a required font/icon set, dark mode if unclear). Only ask what you can't determine yourself. The one thing that can't be front-loaded is the Phase 6 sign-off — it approves the built result.
Phase 1 — establish-design-system: greenfield bootstrap (only if no design system exists)
If detection found no design system, stand one up before reconciling: install and configure Tailwind v4
and shadcn for the detected framework, let shadcn init write the default three-layer globals.css,
scaffold the /brand route, DESIGN.md, and docs/architecture/design-language.md, copy scripts/check-contrast.mjs, and add
the design Taskfile tasks. Normalize whatever the bundle emits (often HSL or inline values) into the
canonical OKLCH three-layer form, and record a DDR establishing the system at v1.0.0. Assumes a
working frontend app already exists. Use deliverables-checklist.md to confirm the full token and
component set is covered, not just the brand colors. See greenfield-bootstrap.md. (An existing system
skips to Phase 2.)
Phase 2 — Reconcile tokens — GATE: static contrast
The most error-prone step, and where the modes diverge — the bundle's tokens are a proposal,
globals.css is truth. Read token-reconciliation.md (and evolving-the-system.md for evolve-design-system):
establish-design-system— write canonical tokens from the bundle: map by role into the shadcn three-layer OKLCHglobals.css, author.darkby hand (hold brand hue, invert neutrals), map--tw-prose-*if prose is used.implement-feature(consume-first) — diff against canonical and map each value to the existing token (inline hex/oklch →bg-primary,text-muted-foreground, …); add nothing by default. A value with no close match is a decision point: force-fit to the nearest token, or extend the system additively with a DDR — never inline.evolve-design-system— a three-bucket diff (added/changed/removed), classified with SemVer, breaking changes handled by aliasing + deprecating (not deleting), recorded as a DDR + version bump. A wholesale replacement (new brand; every consumer rewritten in the same change) is still evolve governance — major bump + DDR — executed with establish mechanics: write canonical tokens fresh; aliasing serves nobody when no consumer of the old tokens survives the change.
Reconcile DESIGN.md (don't clobber it). Then run task lint:design (scripts/check-contrast.mjs) and
fix every sub-AA pair — this static gate must be green before you implement. Numbers in
accessibility-verification.md.
Phase 3 — Implement components, assets & /brand
Build the UI in the stack: shadcn-first (check for an existing component before building), port the
prototype JSX to typed components, Lucide named imports, and style exclusively with semantic
tokens — never arbitrary hex. Cover the states the bundle won't show: empty, loading, error, disabled.
Build mobile-first and responsive — Tailwind breakpoints, fluid layout, no fixed widths, dvh, and
container queries where apt. Place assets (static → repo, user media → R2), self-host OFL/Apache
.woff2 fonts (mind the @import order), and generate the favicon set and the downloadable logo
exports — icons alone are not enough; every run ships grab-able logo files in the standard sizes
(Stripe/GitHub/LinkedIn/README/social). Build/maintain the living
/brand page to the scope chosen during intake (no need to ask again here). See
components-and-states.md, responsive-and-cross-browser.md, assets-fonts-favicons.md,
brand-page.md.
Phase 4 — Licensing gate (blocks commit)
Every font, icon, and image must permit commercial use. Fonts OFL/Apache only; Lucide (ISC) is safe;
confirm image licenses ("free to download" ≠ commercial). Flag AI-generated logos: usually
trademark-able but not copyrightable — recommend human edits + a clearance search before they
become the brand. If any license is unclear, stop and flag it rather than guessing. See
ethics-and-licensing.md.
Phase 5 — Verify — GATE: verification (contrast, responsive, cross-browser)
Run the gates (task lint:design, check, verify — create any that's missing; never --no-verify).
Build, run the app, and screenshot every view in both light and dark, for every state, and across
key breakpoints (phone/tablet/desktop) and engines (Chromium/Firefox/WebKit incl. mobile Safari) —
Playwright drives all three from one config; set it up if the repo lacks it (agent-browser is
Chromium-only and doesn't substitute) — see responsive-and-cross-browser.md. Then
measure rendered contrast on the running page (static tokens passing isn't enough — a runtime layer
like .prose can override them): computed colors, both themes, every text role incl. long-form prose,
reported as numbers. Fix and re-measure failures before showing the user. See
verification-and-signoff.md, accessibility-verification.md.
Phase 6 — Sign-off gate (blocks deletion)
Do not assume the implementation is correct. Show the user the screenshots (both themes, all states)
and measured ratios, compare against the bundle's intent and surface every delta, then ask
explicitly whether it matches before anything is removed. Iterate and re-verify; loop here until
the user explicitly approves. The bundle stays fully in place through this step. See
verification-and-signoff.md.
Phase 7 — Close out (only after approval)
Delete specs/handoff-<feature>/ (or extract a thin screenshot + intent note first if states
remain), update docs/architecture/design-language.md and /brand, and record a DDR (with a SemVer bump) in docs/decisions/
for any design-system change — establish-design-system (v1.0.0), evolve-design-system (patch/minor/major), or a feature token
extension. Commit with Conventional Commits on a feature branch (direct
commits to main are blocked) and open a PR for human review — never merge to main directly. See
verification-and-signoff.md.
Guardrails (apply throughout)
- Port, don't paste. The bundle is a prototype; re-implement it idiomatically in the stack.
- Semantic tokens only. Never arbitrary hex or one-off Tailwind color literals — they skip dark mode and the contrast gate.
globals.csswins overDESIGN.mdfor runtime. If they drift, flag it.- The bundle is a proposal; the repo is truth. Never overwrite
globals.csswholesale from a bundle — diff against canonical and apply deliberate, approved changes only. - New tokens are a decision point. Map to an existing token, or extend the system deliberately (additive, with a DDR) — never invent ad-hoc values inline.
- Record system changes as a DDR with a SemVer bump (
establish-design-system= v1.0.0;evolve-design-system= patch/minor/major; a feature extension = minor). /brandis a maintained route, not a doc. Keep it synced with the tokens; never let it drift.- Get sign-off before deleting anything. Approval is the user's decision, never inferred from a green build or your own confidence.
- Don't bypass hooks (
--no-verifyis prohibited). - Conventional Commits; feature branch; PR for human review; no direct merges to
main.
Reference files
ingesting-the-bundle.md— Phase 0: defensive, format-agnostic ingest; the common Claude Design anatomy; and the prototype→production port.token-reconciliation.md— Phase 2: the proposal-vs-canonical doctrine —establish-design-systemwrites canonical tokens by role;implement-featurediffs and maps to existing (the new-token decision point). shadcn three-layer OKLCH,.dark, and the--tw-prose-*mapping.evolving-the-system.md— Phase 2 (evolve-design-system): the token diff (added/changed/removed), SemVer classification, aliasing + deprecation, and the DDR/version record.greenfield-bootstrap.md— Phase 1 (establish-design-system): stand up Tailwind v4 + shadcn +globals.css+/brand+ Taskfile, per framework.components-and-states.md— Phase 3: port the JSX, shadcn-first, Lucide, the full UI-state matrix.assets-fonts-favicons.md— Phase 3: asset placement, self-hosted fonts (+ the@importorder rule), favicon generation.responsive-and-cross-browser.md— Phases 3 & 5: build mobile-first/responsive and verify across viewports and rendering engines with Playwright (Chromium/Firefox/WebKit, incl. mobile Safari).accessibility-verification.md— Phases 2 & 5: the dual (static + rendered) WCAG AA contrast gate.brand-page.md— Phase 3: the living/brandstyle guide, the runtime scope question, and the collateral tiers.ethics-and-licensing.md— Phase 4: the commercial-use gate, the AI-logo reality, and vendor lock-in.verification-and-signoff.md— Phases 5–7: the gates, the sign-off loop, cleanup, and commit + PR.deliverables-checklist.md— a completeness self-check for token, component, brand, asset, and collateral coverage; used inestablish-design-system/evolve-design-systemand when building/brand.
Bundled assets (the skill installs these into the target repo):
assets/check-contrast.mjs— zero-dependency static WCAG-AA token-contrast checker (resolvesvar()chains, merges the@theme/:root/.darkcascade, auto-discovers the*-textstatus roles so the "status text on light" rule is enforced by default, and is alpha-aware: translucent foregrounds are composited before scoring, translucent backgrounds are flagged for the rendered check instead of passing on the opaque value); copy toscripts/.assets/check-off-palette.sh— the off-palette half of the static gate: fails on arbitrary color literals (bg-[#…], gradient hex, legacy + modern color functions — rgba/hsla/hwb/lab/lch/oklch/color()— anywhere in a style value or SVG attr); copy toscripts/(chmod +x). With check-contrast.mjs it backstask lint:design.assets/measure-rendered-contrast.mjs— the rendered half of the gate: samples computed colors on real pages in both themes (parses Chromium's oklch/oklab serialization, composites alpha, and fails as UNSUPPORTED any sample over a gradient/image/pseudo-element ground it can't model); copy toscripts/, fillSAMPLES. Run bytask verify:contrast.assets/ingest-design.sh— safe bundle extraction fortask ingest:design: takesBUNDLE/DESTfrom the environment (no shell interpolation), selects tar vs unzip by validated extension, and rejects unsafe archive entries (absolute paths,..traversal, links) before extracting; copy toscripts/(chmod +x).assets/Taskfile.design.yml— design task snippets (lint:design,ingest:design,verify:browsers,verify:contrast) to merge into the repo'sTaskfile.yml.assets/playwright.config.ts— the cross-browser config (baseURL +build && previewwebServer + the Chromium/Firefox/WebKit + mobile Safari/Chrome matrix); copy to the repo root.assets/brand-screenshots.spec.ts— a parameterized Playwright sweep (route × theme × the config's engine/device matrix, chunking pages taller than the browser capture cap) plus a route × theme horizontal-overflow guard; fill inROUTES,baseURL/webServer, and the theme mechanism. Run bytask verify:browsers.
Complements
This skill handles the reconciliation and wiring. It pairs well with the frontend-design skill
(anti-"AI-slop" aesthetic direction) during Phase 3. It does not depend on any third-party skill.