diff --git a/CLAUDE.md b/CLAUDE.md index ef7b6f1..23dbda8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -60,7 +60,7 @@ Pre-commit is configured with `no-commit-to-branch` — direct commits to `main` - `ai/` — AI assets organized by type: `skills/`, `prompts/`, `agents/`, `rules/`, `evals/`, `tools/`, `workflows/`, `mcp/`, `knowledge/`, `memories/`. Most are placeholder directories for now; `skills/` is the populated one. Skills follow the Agent Skills convention — a `SKILL.md` with YAML frontmatter (`name`, `description`) plus optional `references/`. Current content is a `design/` suite for the Claude Design → code workflow: - `skills/design/explore-designs/` — guides using Claude Design to explore design directions (asks about the target frontend stack/tooling). - `skills/design/create-design-system/` — placeholder for design-system setup. - - `skills/design/design-handoff/` — full skill: reconcile a finished Claude Design export into a real codebase (tokens → shadcn/Tailwind v4 OKLCH, `/brand` page, contrast + licensing gates). + - `skills/design/design-handoff/` — full skill: implement a Claude Design `.tar.gz` handoff bundle into a real codebase — either reconcile into an existing design system or **bootstrap a new one** (tokens → shadcn/Tailwind v4 OKLCH, `/brand`, contrast + licensing gates). A lean phased/gated `SKILL.md` plus a `references/` set and bundled `assets/` (a zero-dependency contrast checker + Taskfile snippets). - `snippets/` — small reusable code snippets (placeholder). - `docs/` — project docs, e.g. the new-project checklist. - `test/` — tool configuration used by scans (e.g. `whisperConfig.yml` for Whispers); not actual tests. diff --git a/README.md b/README.md index 514226f..ddfa148 100644 --- a/README.md +++ b/README.md @@ -13,28 +13,28 @@ Author: Evan Harmon ## Repository Structure -| Directory | Contents | -| --- | --- | -| [`templates/`](./templates/) | Copy-paste boilerplates organized by category — see the [template index](#template-index) below | -| [`scripts/`](./scripts/) | Standalone scripts and utilities (AppleScript/Automator apps, command snippets) | -| [`ai/`](./ai/) | AI assets — skills, prompts, agents, rules, evals, etc. — see the [AI assets index](#ai-assets) below | -| [`snippets/`](./snippets/) | Small reusable code snippets (work in progress) | -| [`docs/`](./docs/) | Project docs, e.g. the new-project [checklist](./docs/CHECKLIST.md) | +| Directory | Contents | +| ---------------------------- | ----------------------------------------------------------------------------------------------------- | +| [`templates/`](./templates/) | Copy-paste boilerplates organized by category — see the [template index](#template-index) below | +| [`scripts/`](./scripts/) | Standalone scripts and utilities (AppleScript/Automator apps, command snippets) | +| [`ai/`](./ai/) | AI assets — skills, prompts, agents, rules, evals, etc. — see the [AI assets index](#ai-assets) below | +| [`snippets/`](./snippets/) | Small reusable code snippets (work in progress) | +| [`docs/`](./docs/) | Project docs, e.g. the new-project [checklist](./docs/CHECKLIST.md) | ## Template Index -| Template | Category | Description | -| --- | --- | --- | -| [`ansible.md`](./templates/ansible.md) | Ansible | Standard Ansible project directory structure and setup notes | -| [`docker/genericStack`](./templates/docker/genericStack/) | Docker | Generic multi-service Compose sandbox (Ubuntu, nginx, optional DB stack with Postgres/memcached/Adminer) plus `dc*` helper scripts | -| [`docker/n8n-compose`](./templates/docker/n8n-compose/) | Docker | n8n workflow automation behind Traefik with automatic HTTPS (Let's Encrypt) | -| [`scriptTemplates/shellScriptTemplate.sh`](./templates/scriptTemplates/shellScriptTemplate.sh) | Scripts | Shell script starter with safe defaults, traps, and arg parsing | -| [`scriptTemplates/pythonScriptTemplate.py`](./templates/scriptTemplates/pythonScriptTemplate.py) | Scripts | Python CLI starter with argparse, logging, and validation | -| [`scriptTemplates/goScriptTemplate.go`](./templates/scriptTemplates/goScriptTemplate.go) | Scripts | Go CLI starter with flag parsing, logging, and validation | -| [`serverlessFunctionTemplates/awsLambda.py`](./templates/serverlessFunctionTemplates/awsLambda.py) | Serverless | AWS Lambda handler (Python) with input validation and error responses | -| [`serverlessFunctionTemplates/gcpFunction.py`](./templates/serverlessFunctionTemplates/gcpFunction.py) | Serverless | Google Cloud Function (Python/Flask) with input validation and error responses | -| [`serverlessFunctionTemplates/netlifyFunction.js`](./templates/serverlessFunctionTemplates/netlifyFunction.js) | Serverless | Netlify Function (Node.js) that fetches and returns JSON from an API | -| [`webTemplates/netlifyForm.html`](./templates/webTemplates/netlifyForm.html) | Web | Netlify-ready HTML contact form with honeypot spam protection | +| Template | Category | Description | +| -------------------------------------------------------------------------------------------------------------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| [`ansible.md`](./templates/ansible.md) | Ansible | Standard Ansible project directory structure and setup notes | +| [`docker/genericStack`](./templates/docker/genericStack/) | Docker | Generic multi-service Compose sandbox (Ubuntu, nginx, optional DB stack with Postgres/memcached/Adminer) plus `dc*` helper scripts | +| [`docker/n8n-compose`](./templates/docker/n8n-compose/) | Docker | n8n workflow automation behind Traefik with automatic HTTPS (Let's Encrypt) | +| [`scriptTemplates/shellScriptTemplate.sh`](./templates/scriptTemplates/shellScriptTemplate.sh) | Scripts | Shell script starter with safe defaults, traps, and arg parsing | +| [`scriptTemplates/pythonScriptTemplate.py`](./templates/scriptTemplates/pythonScriptTemplate.py) | Scripts | Python CLI starter with argparse, logging, and validation | +| [`scriptTemplates/goScriptTemplate.go`](./templates/scriptTemplates/goScriptTemplate.go) | Scripts | Go CLI starter with flag parsing, logging, and validation | +| [`serverlessFunctionTemplates/awsLambda.py`](./templates/serverlessFunctionTemplates/awsLambda.py) | Serverless | AWS Lambda handler (Python) with input validation and error responses | +| [`serverlessFunctionTemplates/gcpFunction.py`](./templates/serverlessFunctionTemplates/gcpFunction.py) | Serverless | Google Cloud Function (Python/Flask) with input validation and error responses | +| [`serverlessFunctionTemplates/netlifyFunction.js`](./templates/serverlessFunctionTemplates/netlifyFunction.js) | Serverless | Netlify Function (Node.js) that fetches and returns JSON from an API | +| [`webTemplates/netlifyForm.html`](./templates/webTemplates/netlifyForm.html) | Web | Netlify-ready HTML contact form with honeypot spam protection | See [`templates/README.md`](./templates/README.md) for conventions and per-category details. @@ -42,11 +42,11 @@ See [`templates/README.md`](./templates/README.md) for conventions and per-categ `ai/` collects reusable AI assets organized by type — `skills/`, `prompts/`, `agents/`, `rules/`, `evals/`, `tools/`, `workflows/`, `mcp/`, `knowledge/`, and `memories/`. Most are placeholders for now; the populated area is **skills**, which follow the Agent Skills convention (a `SKILL.md` with `name`/`description` frontmatter). -| Skill | Status | Description | -| --- | --- | --- | -| [`design/explore-designs`](./ai/skills/design/explore-designs/) | Draft | Guides using Claude Design to explore design directions across your frontend stack | -| [`design/create-design-system`](./ai/skills/design/create-design-system/) | Placeholder | Design-system setup | -| [`design/design-handoff`](./ai/skills/design/design-handoff/) | Ready | Reconciles a finished Claude Design export into a real codebase — tokens → shadcn/Tailwind v4 OKLCH, `/brand` page, contrast + licensing gates | +| Skill | Status | Description | +| ------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [`design/explore-designs`](./ai/skills/design/explore-designs/) | Draft | Guides using Claude Design to explore design directions across your frontend stack | +| [`design/create-design-system`](./ai/skills/design/create-design-system/) | Placeholder | Design-system setup | +| [`design/design-handoff`](./ai/skills/design/design-handoff/) | Ready | Implements a Claude Design `.tar.gz` handoff into a real codebase — reconcile an existing design system or bootstrap a new one (tokens → shadcn/Tailwind v4 OKLCH, `/brand`, contrast + licensing gates) | ## Inspired by Other Boilerplate Repos diff --git a/ai/skills/design/design-handoff/SKILL.md b/ai/skills/design/design-handoff/SKILL.md index a54a0ec..82c5de8 100644 --- a/ai/skills/design/design-handoff/SKILL.md +++ b/ai/skills/design/design-handoff/SKILL.md @@ -1,191 +1,287 @@ --- name: design-handoff description: >- - Implement a finished Claude Design in the actual code repo. Use this whenever - the user has exported a design from Claude Design (Anthropic's design canvas product) - and wants to turn it into real code — phrases like "I finished designing in - Claude Design", "implement this design", "do the design handoff", "I exported - the handoff bundle", "turn this design into code", etc. This is the Claude Design → - code-repo implementation workflow for the stack in this repo. - NOTE: this is NOT session/context handoff between - agent sessions — it is specifically about implementing a visual/UX design in code. - Trigger it even if the user doesn't say the word "skill". + 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: phrases like "I finished designing in Claude Design", "implement this design", "do the design + handoff", "Handoff to Claude Code", "I exported the handoff bundle / tokens.css / a .tar.gz design", + "turn this design into code", or "set up a design system from this design". Handles both a single + feature AND establishing a new design system in a repo that has none. Targets React + Vite + + Tailwind v4 + shadcn/ui (Astro 6 and TanStack Router fully supported). This is NOT session/context + handoff between agent sessions — it is about implementing a visual/UX design in code. Trigger it + even if the user doesn't say the word "skill". --- # Design Handoff (Claude Design → repo) -Turn a finished Claude Design into working, on-brand code in this repo. The native Claude Design export drops a **handoff bundle** (usually standalone HTML/CSS/JS, screenshots, the tokens it used, and a README) into the repo; your job is to reconcile that generic bundle into _this repo's_ conventions — not to paste it in verbatim. - -The core principle running through every step: **`src/styles/globals.css` is the canonical runtime token source, and `DESIGN.md` is the AI-facing statement of intent.** When they disagree, `globals.css` wins for runtime. The handoff bundle is the reference for the _intended_ design — it 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. +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** — commonly a `.tar.gz` from "Handoff to Claude Code" holding a +README, the design **chat transcript**, prototype HTML/JSX/CSS, a token file, and uploads. But that +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 -Copy this checklist into your reply at the **start** of the run and tick each -box as you finish it. Do not report the handoff complete until every box is -checked. The three **gates** are blocking — you may not take the action a gate -guards until its box is true. +**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 -- [ ] Implemented from the canonical **un-suffixed** sources, not the `?v=N` snapshots -- [ ] Tokens merged into `globals.css` by **role** (not export name), for Tailwind v4, OKLCH, three-layer — export never blind-pasted -- [ ] `.dark` authored by hand: brand hue held constant, neutrals inverted -- [ ] `DESIGN.md` reconciled, not clobbered +- [ ] 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.css` are by **role** (not export name) — Tailwind v4, OKLCH, three-layer; + `.dark` authored (brand hue held, neutrals inverted); `--tw-prose-*` mapped if prose is used +- [ ] `DESIGN.md` reconciled (not clobbered); a system change (`establish-design-system`/`evolve-design-system`, or a feature + extension) carries a **DDR + SemVer bump** Implementation -- [ ] shadcn/ui + Lucide only; styled **exclusively** with semantic tokens (zero arbitrary hex / one-off color literals) +- [ ] 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 -- [ ] `/brand` updated in the **same** change (can't drift from `DESIGN.md`/`globals.css`) -- [ ] Assets placed correctly: static → repo, dynamic/user media → R2; fonts self-hosted OFL/Apache `.woff2` in public/fonts; favicons generated from the mark -- [ ] OKLCH for color values; **three-tier semantic** token naming (primitive → semantic → component). - -Gates (each blocks the action it guards — do not proceed until true) +- [ ] Responsive (mobile-first): holds at phone/tablet/desktop with no horizontal overflow +- [ ] `/brand` built/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 + +Gates — post **PASS + evidence** in the chat before taking the guarded action + +- [ ] **① Static contrast** (blocks implementation — Phase 2): `task lint:design` is 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 verify` green 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) -- [ ] **Licensing** (blocks commit): every font/icon/image cleared for commercial use; anything unclear stopped and flagged, not guessed -- [ ] **Contrast** (blocks sign-off): static `task lint:design` green **and** rendered ratios measured on the running page — both themes, every text role incl. long-form prose — reported as **numbers**, never "looks fine". WCAG AA (4.5:1). -- [ ] **Sign-off** (blocks deletion): screenshots shown (both themes, all built states), deltas surfaced, user has **explicitly approved** — not inferred from a green build or your own confidence - -Close-out (only once the sign-off gate is true) - -- [ ] `task verify` green and the build compiles; hooks never bypassed (`--no-verify` prohibited) - [ ] Handoff bundle deleted (or a thin screenshot + intent note extracted first if states remain) -- [ ] `docs/design/` updated; DDR flagged if a real design-system decision was made -- [ ] Conventional Commit; PR opened for human review (no direct merge to `main`) - -## Inputs +- [ ] `docs/design/` and `/brand` updated; 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-verify` prohibited); no direct merge to `main` -- A handoff bundle at `docs/design/handoff-/`. 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/design/`, `Taskfile.yml`, and the project's `CLAUDE.md`. +## Inputs & stack -## Stack (target for all implementation) - -TypeScript 6 or above, React, Vite, pnpm, Node 24 or above, Astro 6 or above, Tailwind CSS v4 or above, shadcn/ui, Cloudflare Pages/Workers. Favor **shadcn/ui** for components and **Lucide** for icons. +- A handoff bundle, usually unpacked to `docs/design/handoff-/`. 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/design/`, `Taskfile.yml`, and + the project's `CLAUDE.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 through these in order. Explanations of _why_ are included because they change how you make judgment calls. - -### 0. Locate and read the bundle - -`view` the `docs/design/handoff-/` directory. Read the README (it states the design intent and structure), look at the screenshots, and note the tokens and component structure it used. This is your spec — but it's a _generic_ spec, not yet adapted to the stack. - -**Load the file the prototype actually renders, not a stale snapshot.** Claude Design exports cache-bust its scripts with query strings, e.g. `` holding every token's name, `oklch`, + hex, and (for pairs) contrast ratio, plus the breakpoints, type scale, and component inventory. + Optionally also serve it at `/brand.json` for heavier automation. +- **Keep the DOM semantic and stable** (correct heading order, consistent specimen markup) so scraping + and visual-regression stay reliable across builds. Every specimen renders from tokens (never + hardcoded) and works under the light/dark toggle and at every breakpoint — so one page powers token + docs, visual-regression, and contrast auditing at once. + +### Always-on essentials + +- A **light/dark toggle** wired to the `.dark` class so every specimen is checkable in both themes. +- An **accessibility note** — the target (WCAG 2.2 AA), the contrast commitment, focus/keyboard support, + and reduced-motion. +- Link the route discreetly — e.g. from the footer. + +## Tier 2 — downloadable brand / press kit (opt-in) + +The brand/press kit is the **external-facing** counterpart to the style guide: the assets, metadata, +and copy a partner, journalist, vendor, or downstream system needs to represent the brand correctly. +Build it as both a human page **and** a machine-consumable endpoint, generated from the same tokens and +source as `globals.css`/`DESIGN.md` so it never drifts. + +### Contents + +- **Logo suite** — primary lockup, monogram/mark, and wordmark; horizontal and stacked variants; in + full-color, single-color (black, white), and reversed (for dark backgrounds). Each as **SVG** + (primary) and transparent **PNG** at several resolutions; add PDF/EPS when print vendors need them. + Include the clear-space rule, minimum size, and a misuse row. +- **Favicon & app-icon set** — `favicon.ico`, `favicon.svg`, `apple-touch-icon.png` (180), PWA icons + (192/512 plus a maskable), and `site.webmanifest` (the set generated in `assets-fonts-favicons.md`). +- **Color** — the palette as swatches with **hex, oklch, RGB, and CMYK** (add Pantone/spot for print), + plus a downloadable Adobe swatch file (`.ase`) and a JSON block. +- **Typography** — the brand font files (or links and license), the fallback stack, and a one-page + type spec (families, roles, scale, weights). +- **Brand guidelines** — a downloadable **PDF brand book** (logo usage, color, type, spacing, voice, + do's and don'ts): the portable form of Tier 1. +- **Boilerplate copy** — company/product descriptions in short (~25 words), medium (~50), and long + (~100) forms; the tagline; founder/team bios; key facts (founded, location, category); and a + press/contact email. +- **Imagery** — approved hero/product shots, team/founder photos, and any brand illustration, at both + web and print resolution, with usage and credit notes. +- **Links & license** — canonical site, social handles, press contact, and a short usage statement + (what third parties may and may not do — e.g. don't alter or recolor the logo). + +### Packaging & download + +- A single **`brand-kit.zip`** at a stable URL, organized into `logo/`, `favicon/`, `color/`, `type/`, + `guidelines/`, `images/`, and `copy/`, with a `README` manifest at the root. +- A human **press-kit page** (e.g. `/brand/press-kit`, or a section of `/brand`) that previews + everything and links each asset plus the full zip — with the same stable anchors and `data-*` hooks + as Tier 1. + +### As an automation endpoint + +Expose the kit so other systems and agents can consume it programmatically, not just humans: + +- **`/brand/kit.json`** (or `/press-kit.json`) — a machine-readable **manifest** carrying a `version` + and `updatedAt`, every asset with its `name`, `description`, and per-format / per-resolution URLs, the + full color set (hex/oklch/rgb/cmyk/pantone), font names and license URLs, the boilerplate copy + variants, social links, and the press contact. Generate it from the same source as the tokens so it + can't drift. +- **Stable, versioned asset URLs** — each logo/icon/image at a durable path (e.g. `/brand/assets/...`), + and `brand-kit.zip` at a fixed URL automations can fetch. +- **CORS** — if the JSON or zip will be fetched cross-origin (partner sites, agents), enable permissive + CORS on those endpoints. +- Keep the manifest the single thing other tools read, and render the page from it — so page and + endpoint can never disagree. This extends Tier 1's automation structure outward: Tier 1's + `#brand-tokens` JSON is the _internal_ token truth; this manifest is the _external_ asset/metadata + truth. + +## Tier 3 — collateral (opt-in, chosen by group) + +Collateral spans dozens of artifact types, so the user picks **groups** during intake (see "Decide how +far to take it"). Generate only the selected groups, build every piece from the same tokens so it stays +on-brand, and confirm the specifics (which platforms, sizes, formats) before producing. The four +selectable buckets: + +- **Social & web** — social profile art (avatar, cover/banner), post/story templates sized per platform + (LinkedIn, Instagram, X, Facebook, Bluesky, TikTok, YouTube thumbnails), and podcast cover art; + OG/share cards (static or dynamically generated); display/banner ads in standard IAB sizes. Export at + the correct per-platform dimensions. +- **Email** — transactional and marketing templates, a newsletter layout, and an email signature; built + with **React Email**, shipped as HTML/CSS bundles, and tested against major clients (Gmail, Outlook, + Apple Mail). +- **Print** — business cards, letterhead, flyers, posters, brochures, stickers, and signage as + **print-ready PDFs** (CMYK, 300dpi, with bleed and crop marks). +- **Presentations & documents** — an **editable** `.pptx` (and/or Google Slides) deck with real text and + shapes (never a flat, image-based deck), a pitch deck, and a one-pager/sales sheet; plus document + templates (proposals, reports, case studies, invoices, résumé) for Word / Google Docs / Notion. + +Pick **Other** and name it for the long tail: + +- **Motion & video** — animated logo (Lottie), social-video templates, intro/outro stingers, animated + GIFs. +- **Merch & environmental** — apparel, stickers, tote bags, mugs; event/booth banners; office and + wayfinding signage. +- **Product & app-store** — app-store screenshots and listing graphics, in-app illustration and + empty-state art, onboarding graphics. +- **Audio & bespoke** — audio-brand stings, sonic logos, or anything one-off. + +These groups map onto the design suite's artifact taxonomy in `ai/skills/README.md` (Tokens / +Components / Pages & Templates / Assets / Collateral), so `/brand` stays aligned with the broader +design-suite roadmap. + +## Where `/brand` lives per framework + +- **TanStack Router** — file-based route at `src/routes/brand.tsx`. +- **React Router / plain React** — a normal route/component mounted at `/brand`. +- **Astro 6** — `src/pages/brand.astro`. Static specimens (swatches, type, scales) render as `.astro` + with zero JS; the **live/interactive** pieces — the theme toggle, stateful component demos, and the + `getComputedStyle` swatch readouts (which run client-side) — are React **islands** with the right + `client:*` directive (see `components-and-states.md`). + +## Keep it in lockstep + +`/brand` is a standing deliverable, listed in the skill's Definition of Done. Treat "update `/brand`" +as part of **any** change that touches tokens, type, components, or brand assets — it explains the +system and must never fall behind `globals.css` / `DESIGN.md`. diff --git a/ai/skills/design/design-handoff/references/components-and-states.md b/ai/skills/design/design-handoff/references/components-and-states.md new file mode 100644 index 0000000..8fa3931 --- /dev/null +++ b/ai/skills/design/design-handoff/references/components-and-states.md @@ -0,0 +1,91 @@ +# Components & states: porting the prototype into real UI + +Read this during **Phase 3**. Turn the bundle's prototype JSX into idiomatic, typed components in the +repo's stack — styled exclusively with semantic tokens, and covering every UI state, not just the +happy path the prototype shows. + +## shadcn-first: don't build what already exists + +Before building any custom component, check whether **shadcn already provides it** — button, input, +dialog, dropdown-menu, card, tabs, sheet, sonner (toast), etc. shadcn copies _source_ into your repo +(`src/components/ui`), so you own it and restyle it through tokens — there's no runtime dependency and +very low lock-in. Add with: + +```bash +pnpm dlx shadcn@latest add button card dialog input # etc. +``` + +Only build a custom component when the design introduces something shadcn doesn't have — and even +then, compose it from shadcn primitives where you can. + +## Port the JSX, don't copy it + +The bundle's `.jsx` runs on in-browser Babel + UMD React (see `ingesting-the-bundle.md`). +Re-implement it; never drop it into `src/`: + +- convert to **typed `.tsx`** with real module imports (no `window` globals); +- replace inline style objects and ad-hoc classes with **Tailwind v4 utilities**; +- extract repeated structure into small components; +- preserve the prototype's structure and intent (cross-check `chats/chat1.md`), but write it the way + this repo writes components. + +## Style only with semantic tokens + +Never introduce an arbitrary hex or a one-off color literal. Use the semantic token utilities: +`bg-background text-foreground`, `bg-primary text-primary-foreground`, `bg-muted text-muted-foreground`, +`border-border`, `ring-ring`, `bg-destructive text-destructive-foreground`, and so on. The off-palette +guard in `task lint:design` fails the build on arbitrary color utilities like `bg-[#1a1c1e]` or +`text-[oklch(...)]` (see `assets/Taskfile.design.yml`). **Why it matters:** semantic tokens flip +automatically in dark mode and are contrast-checked by the gate; a hardcoded color does neither, so it +silently breaks theming _and_ accessibility the moment someone toggles the theme. + +## Icons: Lucide, named imports only + +- Use **Lucide** (`lucide-react`) and import **by name**: + `import { Camera, Check } from "lucide-react"`. Named imports tree-shake — only the icons you use + ship to users. +- **Caveat:** Vite does **not** tree-shake in _dev_, so the dev bundle pulls the whole library and can + feel heavy. Judge the real cost in the **production** build / bundle analyzer, not in dev. For very + large icon counts, per-icon subpath imports (`lucide-react/icons/camera`) trim a little more. +- **Never mix icon libraries.** Two icon sets bloat the bundle and look visually inconsistent + (different grids, stroke widths, optical sizing). Pick Lucide and map the design's icons to the + nearest Lucide glyph; if one is genuinely missing, draw a one-off SVG that matches Lucide's 24×24 / + 2px-stroke conventions so it sits in the same visual family. + +## Cover every UI state (the prototype usually shows one) + +The bundle typically renders only the default, populated state. Real components need the full matrix. +For each interactive surface, build **and** verify: + +- **default** — the populated, resting state. +- **empty** — no data yet (lists, search results, dashboards): a deliberate, designed empty state, not + a blank rectangle. +- **loading** — skeletons or spinners; reserve space so layout doesn't jump when data arrives. +- **error** — a failed fetch or validation: a clear, recoverable message styled with `destructive` + tokens. +- **disabled** — non-interactive and visually distinct (reduced emphasis), and not focusable where + that's appropriate. + +Plus the interaction states the tokens already support: **hover**, **focus-visible** (the `ring`), +**active**, and **checked/selected**. Never signal state by color alone (WCAG 1.4.1) — pair color with +an icon, text, or shape so it survives color-blindness and grayscale. You'll screenshot each built +state in **both themes** during Phase 5. + +## Framework notes (routing/mounting differs; the rules above don't) + +- **TanStack Router** — file-based routes under `src/routes/`. Put page entries there; co-locate + route-specific components or keep shared ones in `src/components`. +- **React Router / plain React** — mount components on normal routes. +- **Astro 6** — interactive shadcn components must be React **islands**: wrap them with a `client:*` + directive — `client:load` for above-the-fold interactivity, `client:visible` to defer until + scrolled into view, `client:idle` for low-priority widgets. Purely static specimens can stay + `.astro` and ship zero JS. A stateful shadcn component placed in an `.astro` file _without_ a client + directive will render but never hydrate — it won't be interactive, which is a common and confusing + Astro mistake. + +## Then + +Place assets and fonts (`assets-fonts-favicons.md`), build responsively across viewports and engines +(`responsive-and-cross-browser.md`), clear licensing (`ethics-and-licensing.md`), build and maintain +`/brand` (`brand-page.md`), and verify with sign-off (`verification-and-signoff.md` and +`accessibility-verification.md`). diff --git a/ai/skills/design/design-handoff/references/deliverables-checklist.md b/ai/skills/design/design-handoff/references/deliverables-checklist.md new file mode 100644 index 0000000..ca4507e --- /dev/null +++ b/ai/skills/design/design-handoff/references/deliverables-checklist.md @@ -0,0 +1,74 @@ +# Deliverables coverage checklist + +A completeness check, **not** a build list. Consult it in **`establish-design-system`** and +**`evolve-design-system`** mode, and when building **`/brand`**, to make sure you haven't missed a token +category, component family, or brand asset. **Awareness, not a mandate:** only implement what the +handoff actually needs — this list prevents _missing_ things, it doesn't force _adding_ everything. It's +a self-check, **not** part of the chat-tracked Definition of Done. Each line points to the reference +that explains it. + +## Tokens & foundations → `token-reconciliation.md`, `brand-page.md` + +- [ ] Color — brand/primary, secondary, accent; neutral ramp; semantic (success/warning/error/info); + surfaces (base/card/popover/overlay); foreground (default/muted/on-color); border/input/ring; + interactive states; **data-viz / chart palette**; **named gradients**; light **and** dark +- [ ] Typography — families (display/body/mono); scale; weights; line-height; tracking; + heading/body/prose/link/list/code styles; responsive type; font loading and fallbacks +- [ ] Spacing & sizing — spacing scale; sizing/control heights; container/max-width; layout grid; + breakpoints; aspect-ratios +- [ ] Shape — radius scale; border widths +- [ ] Elevation — shadow scale; **named z-index layers** + (dropdown/sticky/overlay/modal/popover/toast/tooltip); focus ring +- [ ] Effects — opacity scale; blur/backdrop; overlay/scrim +- [ ] Motion — durations; easings; transition presets; **named animations** + (spinner/skeleton/toast/accordion); reduced-motion variants +- [ ] Interaction states — hover, focus-visible, active, disabled, loading, selected, error, read-only + +## Components (each in all states) → `components-and-states.md` + +- [ ] Actions; form inputs; labels and helpers; data display + (tables/lists/cards/stats/charts/avatars/badges); feedback and status; overlays and surfaces; + navigation; containers and layout; composed patterns (search+filter, bulk actions, user menu, + onboarding, consent, paywall) + +## Brand identity → `brand-page.md` (Tier 1/2), `ethics-and-licensing.md` + +- [ ] Logo system (lockup/mark/wordmark, mono/reversed/responsive, clear-space, min-size, misuse, + SVG/PNG/PDF exports) +- [ ] Brand color values in HEX / RGB / OKLCH / CMYK / Pantone; usage proportions +- [ ] Brand typography and licensing (OFL/Apache); web/print/fallback; pairings +- [ ] Voice & tone — personality, tagline, boilerplate (S/M/L), terminology, naming conventions +- [ ] Visual language — iconography (Lucide), illustration, photography direction, patterns/textures, + data-viz style, motion principles +- [ ] Brand book (web and downloadable PDF) with in-context application examples + +## Assets → `assets-fonts-favicons.md`, `brand-page.md` + +- [ ] Icon set; favicon (.ico and PNG); app icons (iOS/Android/PWA); apple-touch-icon +- [ ] Illustrations / graphics (spot, hero, empty-state); patterns / backgrounds +- [ ] **OG / social share images** (template and per-page variants) +- [ ] PWA `manifest.json` icons and theme color; splash screens + +## Collateral (opt-in groups) → `brand-page.md` (Tier 3) + +- [ ] Social & web; email; print; presentations & documents; and the "Other" long tail + (motion/video, merch/environmental, product/app-store, audio) — only the groups the user selected + +## Design-system docs → `evolving-the-system.md`, `brand-page.md` + +- [ ] `DESIGN.md` (intent and soft rules); token reference; component usage docs; accessibility + guidelines; **versioning / changelog** (SemVer and DDRs) + +## Scoped OUT — belongs to the Claude Design _creation_ skill, not this one + +This skill **implements what was handed off**; it doesn't commission the full design program. The +following are produced _in_ Claude Design and are out of scope here — a separate creation skill owns +them: + +- Page/screen catalog (marketing-site set, auth/account set, full app screens) +- UX-process artifacts (personas, jobs-to-be-done, IA/sitemaps, user flows/journeys, wireframes, + clickable prototypes, usability test plans) +- Marketing _production_ (vehicle wraps, trade-show booths, swag, printed mailers) + +If the handoff bundle happens to include any of these, implement what it contains — just don't treat +their absence as a gap in _this_ skill. diff --git a/ai/skills/design/design-handoff/references/ethics-and-licensing.md b/ai/skills/design/design-handoff/references/ethics-and-licensing.md new file mode 100644 index 0000000..1b5c809 --- /dev/null +++ b/ai/skills/design/design-handoff/references/ethics-and-licensing.md @@ -0,0 +1,75 @@ +# Ethics & licensing: the commercial-use gate + +Read this during **Phase 4**. This repo ships commercial products, so **every font, icon, and image +must permit commercial use before it is committed.** This is a blocking gate: if any asset's license +is unclear, **stop and flag it to the user** — don't guess, and don't silently treat an unclear asset +as cleared. None of this is legal advice; when the stakes are high, the user should consult a +professional. + +## Fonts — OFL / Apache only + +- **Safe:** SIL Open Font License (OFL) and Apache-2.0 fonts — free for commercial use, embeddable, + and modifiable. Good modern defaults: **Inter** (OFL), **IBM Plex** (OFL), **Geist** (OFL). +- **Reserved names:** an OFL font may carry a Reserved Font Name — you may use and even modify the + font, but you can't ship a modified version under that name (don't call a derivative "Plex"). +- **Reject proprietary faces:** Helvetica, **SF Pro** (Apple platforms only), Gotham, Proxima Nova, and + other foundry/licensed fonts — unless the user has bought a license. If the design calls for one, + flag it and propose the nearest OFL alternative. +- **Google Fonts** qualify (the library is OFL/Apache) — but **self-host** the `.woff2` + (`assets-fonts-favicons.md`), don't hotlink. + +## Icons — Lucide is safe + +- **Lucide** is **ISC-licensed**: free commercial use, modification, and redistribution, with **no + attribution required**. Use it (named imports — `components-and-states.md`). +- **Font Awesome Free** is CC-BY-4.0 → **requires attribution**; flag it if used. Pro requires a paid + license. +- Any other set: check the license before adopting — and don't mix sets regardless (bundle size + + visual consistency). + +## Images & stock + +- **"Free to download" ≠ "free for commercial use."** Confirm the actual license (Unsplash/Pexels + terms, the specific CC variant, or purchased-stock terms). +- Watch **CC-BY** (attribution required), **CC-BY-SA** (share-alike — can force you to license your own + work alike), and **NC** (non-commercial — disqualifying for a commercial product). +- AI-generated images carry the same copyright caveat as logos, below. + +## AI-generated logos — the trap + +A prompt-generated logo is not what most people assume: + +- **Copyright:** purely AI-generated output is **not copyrightable in the US** — there's no human + authorship (Thaler v. Perlmutter, affirmed; the Copyright Office requires human authorship). A pure + AI logo gives you **zero copyright protection** against someone copying it. +- **Trademark:** it **can** be trademarked — trademark needs distinctive use in commerce, not human + authorship. That's where brand protection actually comes from. +- **Practical advice to surface to the user:** + - add meaningful **human modification** so the human-authored parts can carry copyright; + - run a **clearance search** (USPTO TESS plus common-law/web) before adopting it, to avoid + infringing an existing mark; + - **use it in commerce** to build trademark rights; + - generic AI logos may be refused registration absent acquired distinctiveness. +- Don't silently treat an AI logo as fully protected — flag this; it's a business decision. + +## Vendor lock-in — flag, don't decide + +When the design or its implementation introduces a vendor dependency, surface the lock-in tradeoff so +the user chooses consciously, and record genuine decisions as a **DDR** (see +`verification-and-signoff.md`): + +- **Convex** (backend): open-source (FSL → Apache-2.0 after two years) and self-hostable, but + self-hosting is single-node and you own migrations/backups/scaling; the programming model stays + Convex-specific. Professional is ~$25/developer/month. +- **Cloudflare** (Pages / R2 / Workers): lower egress cost than S3, but platform-specific APIs. +- **Tailwind / shadcn:** **low** lock-in — shadcn copies source into your repo so you own it; Tailwind + is just CSS. + +These are conscious tradeoffs, not blockers. A genuine, debatable choice (a backend, a token +architecture, a palette-philosophy shift) warrants a DDR in `/decisions/` — not prose buried in +`DESIGN.md`. + +## The gate + +Before committing (Phase 4 → Phase 7): every font, icon, and image cleared for commercial use; any AI +logo flagged with the copyright/trademark reality; anything unclear stopped and raised with the user. diff --git a/ai/skills/design/design-handoff/references/evolving-the-system.md b/ai/skills/design/design-handoff/references/evolving-the-system.md new file mode 100644 index 0000000..c68bbd6 --- /dev/null +++ b/ai/skills/design/design-handoff/references/evolving-the-system.md @@ -0,0 +1,67 @@ +# Evolving an established design system + +Read this during **Phase 2** in **`evolve-design-system`** mode — when the bundle deliberately changes the design +system itself (not a single feature). The doctrine is the same as everywhere: the repo is truth, the +bundle is a proposal. Evolve means reconciling a proposed token change into canonical `globals.css` +**carefully and versioned** — never a blind overwrite, because much of a bundle's token block is a +re-emission that may have drifted from canonical. If the change adds a new role, cross-check +`deliverables-checklist.md` so the system stays complete (e.g. a new semantic color also needs its +`-foreground`, its `/brand` swatch, and a contrast pass). + +## 1. Diff before you write + +Parse the **current** semantic tokens from `globals.css` and the **incoming** set from the bundle, and +produce a three-bucket diff: + +- **Added** — semantic tokens in the bundle that don't exist in canonical. +- **Changed** — same token name, different value. Use an OKLCH **closeness tolerance** so a rounding + difference isn't flagged as a real change (see `token-reconciliation.md`). +- **Removed / renamed** — present in canonical, absent from the bundle. + +Show this diff to the user and apply only the deltas they approve. The re-emitted bundle is not +authority; the diff is the unit of change. + +## 2. Classify the change with SemVer + +Version the token set (the industry norm — IBM Carbon, Salesforce, and the W3C Design Tokens spec all +version tokens). Record the version in `DESIGN.md` (or a `tokens` header): + +- **patch** — value tweaks that don't change a token's role (e.g. nudging `--primary` lightness). +- **minor** — additive, backward-compatible new tokens (a new `--warning`, a new surface). +- **major** — renamed/removed tokens or role changes (breaking). + +Batch breaking changes into a single major release rather than scattering them across handoffs. + +## 3. Handle breaking changes with aliasing + deprecation (not deletion) + +Treat tokens like API endpoints — version, alias, and deprecate slowly: + +- When a token is renamed or removed, **alias** the old name to the new value for a migration window + (keep the old `--token` pointing at the new var). +- **Search the codebase** for every reference to the old token and migrate them. +- **Delete only after** all references are migrated. Don't yank a token out from under the components + that use it — that's the costly path everyone warns about. + +## 4. Record a DDR with the version bump + +Every system change is a **DDR** in `/decisions/` — your design-system changelog and governance trail. +A token-change DDR carries: a unique ID (e.g. `DDR-0xx`), status, context (why the change), the decision +(the exact token delta), alternatives considered, consequences (which components are affected, what +migration is needed), and the resulting **SemVer bump**. DDRs are append-only (the ADR tradition) — if a +later change reverses one, write a new superseding DDR rather than editing the old one. This gives Claude +Code a durable, queryable rationale trail for why the system is the way it is. + +## 5. `/brand` is the regression check + +After any system change, the `/brand` route renders every token and core component, so drift or breakage +is visually obvious — treat it as a visual test surface. Re-run the screenshot sweep +(`responsive-and-cross-browser.md`) and the contrast gate (`accessibility-verification.md`): a token +change can quietly break a contrast pair or a component that depended on the old value. + +## Why this care + +A system's tokens are referenced across the whole app, so changing them ripples. Renaming a token after +hundreds of components reference it means touching every reference — which is why `establish-design-system` builds +semantic naming in from the start and `evolve-design-system` aliases rather than renames in place. This discipline is +what lets the system change without breaking everything downstream — the difference between a design +system and a pile of values. diff --git a/ai/skills/design/design-handoff/references/greenfield-bootstrap.md b/ai/skills/design/design-handoff/references/greenfield-bootstrap.md new file mode 100644 index 0000000..a79ad17 --- /dev/null +++ b/ai/skills/design/design-handoff/references/greenfield-bootstrap.md @@ -0,0 +1,118 @@ +# Greenfield bootstrap: standing up a design system from nothing + +Read this during **Phase 1**, only when the repo has **no design system yet**. It installs and +configures the design-system layer so the rest of the handoff has somewhere to land. It assumes a +**working frontend app already exists** (a Vite/React or Astro project that builds and runs) — it does +**not** scaffold the framework itself. If there's no app at all, stop and tell the user to create one +first. + +## Detect "no design system" + +Treat the repo as greenfield when **all** of these are true: + +- no `src/styles/globals.css` (or it exists but has no real `:root` semantic tokens), and +- no `components.json` (shadcn isn't initialized), and +- no `/brand` route, and +- styling is ad-hoc (inline styles, CSS modules, or default Tailwind with no token layer). + +If a design system _is_ present, skip this file — go straight to Phase 2 and reconcile into the +existing `globals.css` (`token-reconciliation.md`). + +## What bootstrap produces + +1. Tailwind v4 + shadcn/ui installed and wired for the detected framework. +2. A starter `src/styles/globals.css` in shadcn three-layer OKLCH form (default neutral tokens — Phase + 2 replaces the _values_ with the design's). +3. A `/brand` route stub (filled in during Phase 3 — see `brand-page.md`). +4. `DESIGN.md` (root, AI-facing intent) and `docs/design/` human docs. +5. The design Taskfile gates: `scripts/check-contrast.mjs` copied in, and `lint:design` / + `ingest:design` merged into `Taskfile.yml`. + +Order matters: install + `shadcn init` first (it writes a default `globals.css`), **then** Phase 2 +reconciles the design's tokens into that file. Don't hand-write `globals.css` before `shadcn init` — +let the tool establish the structure, then edit values. + +## Per-framework setup + +shadcn/ui is React-only. The named primary stack is **React + Vite + TanStack Router**; **React +Router / plain React** and **Astro 6** are fully supported. For any other framework, map the same +three roles — _global stylesheet import_, _component directory_, _route entry_ — onto that +framework's conventions; never block on an unrecognized router. + +### React + Vite + TanStack Router (primary) + +```bash +pnpm add tailwindcss @tailwindcss/vite +pnpm dlx shadcn@latest init # choose a base color (Neutral/Stone/Zinc/Gray/Slate); writes components.json + globals.css +``` + +- **Vite plugin:** add Tailwind to `vite.config.ts`: + + ```ts + import tailwindcss from "@tailwindcss/vite"; + export default defineConfig({ + plugins: [tailwindcss() /*, …router plugin, react() */], + }); + ``` + +- **Path alias:** ensure `@/*→ ./src/*` in `tsconfig.json` and `resolve.alias` in `vite.config.ts` + (shadcn init prompts for this; components import as `@/components/ui/...`). +- **Global stylesheet:** `src/styles/globals.css` (starts with `@import "tailwindcss";`), imported + once at the app root (e.g. `src/main.tsx` or `src/routes/__root.tsx`). +- **Components:** shadcn lands in `src/components/ui`. +- **`/brand` route:** file-based at `src/routes/brand.tsx`. + +### React + Vite + React Router (or plain React) + +Identical install and Tailwind/shadcn wiring as above. Differences: + +- **Global stylesheet:** import `src/styles/globals.css` in `src/main.tsx`. +- **`/brand` route:** a normal route — `} />` (React Router) or + a `Brand` component mounted at `/brand`. + +### Astro 6 (first-class) + +```bash +pnpm astro add tailwind # wires Tailwind v4 via @tailwindcss/vite +pnpm astro add react # @astrojs/react — required: shadcn components are React islands +pnpm dlx shadcn@latest init # configures components.json + path aliases for Astro +``` + +- **Global stylesheet:** create `src/styles/globals.css` (`@import "tailwindcss";`) and import it in + your base layout: `import "../styles/globals.css";` inside `src/layouts/Base.astro`. +- **Components:** shadcn lands in `src/components/ui` (React `.tsx`). +- **`/brand` route:** `src/pages/brand.astro`. Static specimens (swatches, type scale) can be plain + `.astro`; **interactive** specimens (a theme toggle, hover/focus demos, anything stateful) must be + React **islands** with a `client:*` directive — e.g. ``, + ``. See `components-and-states.md` for the island rules. + +## Scaffold the design records + +- **`DESIGN.md` (root)** — the durable, AI-facing statement of intent. Seed it with sections for + palette, type scale, spacing, radii, component rules, and the prose "do's and don'ts" tokens can't + capture. Phase 2/3 fill it from the design; `globals.css` wins for _runtime_ values, `DESIGN.md` + carries _intent_. +- **`docs/design/`** — human-facing docs: `brand.md`, `design-system.md`, `components.md`, + `accessibility.md`, `ux.md`. Stubs are fine now; they grow as the design lands. +- **`/brand`** — create the route stub now; build it out in Phase 3 (`brand-page.md`). + +## Wire the quality gates + +- Copy `assets/check-contrast.mjs` (from this skill) into the repo at `scripts/check-contrast.mjs`. It + is zero-dependency — it needs only Node, no install. +- Merge `assets/Taskfile.design.yml`'s tasks into the repo's `Taskfile.yml` (create missing ones): + `lint:design` (the static contrast + off-palette gate), `ingest:design`, and either a + `verify:design` aggregator or — preferably — add `lint:design` to the repo's existing `verify`/ + `check` deps so the design gate runs on every verification pass. + +A note on the starter tokens: shadcn's default `muted-foreground` sits right at the AA borderline, so +`task lint:design` may flag it on the raw defaults. That's expected — tightening it is one of the +first things Phase 2 reconciliation does. Don't chase a green gate on the defaults; get it green after +the design's real tokens are in. + +## Then continue + +With the design system stood up, proceed to **Phase 2 — token reconciliation** +(`token-reconciliation.md`): replace the default token _values_ with the design's, author `.dark`, and +get the static contrast gate green before implementing components. Before you call the system complete, +run it past `deliverables-checklist.md` so no token category or component family is missed. diff --git a/ai/skills/design/design-handoff/references/ingesting-the-bundle.md b/ai/skills/design/design-handoff/references/ingesting-the-bundle.md new file mode 100644 index 0000000..02c1b16 --- /dev/null +++ b/ai/skills/design/design-handoff/references/ingesting-the-bundle.md @@ -0,0 +1,94 @@ +# Ingesting the bundle: what Claude Design actually hands off + +Read this during **Phase 0**. It covers what the "Handoff to Claude Code" export really is, how to +open it, what to read and in what order, and the single most important mental shift: **the bundle is +a prototype, not production code — you _port_ it, you don't copy it.** + +## Be defensive about the format + +Claude Design is a fast-moving research preview, and its handoff is a **proprietary, non-standard** +format (not DTCG/W3C tokens, not Figma) that can change without notice. So **don't hard-code the +layout** — parse for _intent_, not for exact filenames or folders. The anatomy below is the common +shape as of now, not a contract: read what's actually present, and if a piece is named or structured +differently, adapt. The same posture lets this skill handle other design tools (e.g. Google Stitch), +whose bundles differ but carry the same ingredients — intent, tokens, markup, assets. Whatever the +format, the destination is identical: the repo's canonical `globals.css` / `DESIGN.md` / `/brand`, with +the bundle treated as a proposal. + +## What the export actually is + +"Handoff to Claude Code" produces a **gzipped tarball** (`.tar.gz`, served as `application/gzip`) — +**not** a `.zip`. (Claude Design's separate "Download as .zip" menu item is a different raw-assets +export; the coding handoff is the tarball.) Decompress it into the repo's design folder: + +```bash +tar -xzf .tar.gz -C docs/design/ # or: task ingest:design BUNDLE=.tar.gz +``` + +It extracts to a single project directory. Move/rename it to `docs/design/handoff-/`. + +## Anatomy (verified, current as of mid-2026) + +```text +/ + README.md # headed "CODING AGENTS: READ THIS FIRST" + chats/chat1.md # the full Claude Design conversation — design intent & rationale + project/ + *.html # an HTML shell + a print-ready variant + js/*.jsx # small JSX component files; the root app.jsx is imported LAST + styles/tokens.css # design-system primitives (palette, fonts, spacing, radii, type scale) + styles/site.css # brand overrides, sometimes including a few dark-mode bits + uploads/ # YOUR uploaded inputs (photos, sketches) — NOT rendered UI screenshots +``` + +File names are project-specific, not a fixed schema — treat the _shape_ (README + chat + HTML/JSX + +`tokens.css`/`site.css` + uploads) as the contract, not the exact filenames. + +## Read order (the README dictates it) + +1. **`README.md`** — it states the structure and tells coding agents what to read. +2. **`chats/chat1.md`** — the intent. This is the bundle's real advantage over a static Figma export: + it preserves _why_ — the decisions made, what was tried and rejected, the rationale behind the + palette and layout. Read it; it resolves ambiguity that later steps would otherwise have to guess. +3. **The entry HTML, in full** — see how the sections compose into a page. +4. **Follow the imports** — `project/js/*.jsx` (root `app.jsx` last), then `styles/tokens.css` and + `styles/site.css`. +5. **`uploads/`** — your original source inputs, for reference. + +## It's a prototype — port it, don't copy it + +The bundle runs in a browser with **no build step**: pinned-unpkg **UMD React 18.3.1** + ReactDOM + +`@babel/standalone`, JSX transpiled in the browser via `type="text/babel"`, components shared on +`window` through `Object.assign`, and uniquely-named inline style objects. That is _prototype-grade_. +It must become real code in the repo's stack — this translation is the central job of the handoff: + +| Bundle (prototype) | Repo (production) | +| ---------------------------------------- | ----------------------------------------------------------------------------------- | +| in-browser Babel + UMD React on `window` | the repo's Vite + React + TypeScript build, real ES module imports | +| `type="text/babel"` `.jsx` | typed `.tsx` components (`components-and-states.md`) | +| inline style objects / ad-hoc classes | Tailwind v4 utilities driven by semantic tokens (`token-reconciliation.md`) | +| the static HTML shell | the repo's routing — TanStack Router / React Router / Astro pages (`brand-page.md`) | +| `tokens.css` flat primitives | shadcn three-layer OKLCH `globals.css` (`token-reconciliation.md`) | + +Read the prototype for **structure and intent**, then re-implement it idiomatically. **Do not** drop +the bundle's `.jsx`/`.html` into `src/`. + +## What the bundle does NOT contain (correct these assumptions) + +- **No machine-readable spec / `tokens.json` / DTCG file.** The "spec" _is_ the code plus the chat. + Parse `tokens.css` and the JSX/HTML; don't wait for a structured token export that isn't there. +- **No per-state screenshots.** `uploads/` holds _your_ inputs, not rendered UI states. You generate + screenshots yourself during Phase 5 verification. +- **No `?v=N` cache-busted files.** Current exports use clean, un-suffixed paths. _Defensive note:_ if + you ever do encounter a `sections.jsx?v=18`-style file beside a plain `sections.jsx`, the plain file + is canonical — read it and ignore the `?v=…` snapshot. This is not expected in current bundles; if + you see it widely, the export format changed and this skill needs revisiting. + +## After ingest + +Inventory what you got — the `tokens.css` palette/scales, the component list under `js/`, the fonts +referenced, the `uploads/` — then **detect the framework, router, and mode** (SKILL.md Phase 0): +`establish-design-system` (no system yet — `greenfield-bootstrap.md`), `evolve-design-system` +(changing the system — `evolving-the-system.md`), or `implement-feature` (a feature against an existing +system). Leave the bundle in `docs/design/handoff-/` **untouched**: it is the reference the +user signs off against in Phase 6 and is not removed until Phase 7, after explicit approval. diff --git a/ai/skills/design/design-handoff/references/responsive-and-cross-browser.md b/ai/skills/design/design-handoff/references/responsive-and-cross-browser.md new file mode 100644 index 0000000..7881c3d --- /dev/null +++ b/ai/skills/design/design-handoff/references/responsive-and-cross-browser.md @@ -0,0 +1,134 @@ +# Responsive & cross-browser verification + +Read this during **Phase 3** (build responsively) and **Phase 5** (verify the matrix). The bundle's +prototype is usually a single desktop layout; production has to work across viewport sizes **and** +rendering engines. Responsive and cross-browser are two axes of the same job: prove the design holds at +phone/tablet/desktop widths, on Chromium, Firefox, and WebKit. + +## Build responsive (mobile-first) + +- **Mobile-first.** Style the small screen first, then layer up with Tailwind breakpoints: `sm` 640px, + `md` 768px, `lg` 1024px, `xl` 1280px, `2xl` 1536px. An unprefixed utility applies at all sizes; `md:` + applies at ≥768px. Design the phone layout, then add `md:`/`lg:` overrides for wider screens. +- **No fixed-width layouts.** Use fluid and relative units, `max-w-*` with `mx-auto`, and grid/flex + that reflows. Reach for `clamp()` on type and spacing where the scale should breathe between + breakpoints. +- **Container queries** (`@container` with `@sm:`/`@md:` variants, built into Tailwind v4) when a + component should respond to **its own container** rather than the viewport — e.g. a card that's + full-width on mobile but sits in a narrow sidebar on desktop. +- **Touch.** Targets ≥44px on coarse pointers (the 24px floor is in `accessibility-verification.md`), + and never gate an essential action behind `hover` — there is no hover on touch, so provide a tap + path. +- **Mobile viewport height.** Use `dvh`/`svh`, not `vh`, for full-height layouts — mobile browser + chrome makes `100vh` overflow and jump as the address bar collapses. +- **Responsive images** (`srcset`/`sizes`, AVIF/WebP) are covered in `assets-fonts-favicons.md`. +- **Check as you build** at representative widths: ~375 (phone), ~768 (tablet / iPad portrait), ~1024 + (iPad landscape / small laptop), ~1280–1440 (desktop). Watch for horizontal overflow at 320–375px — + it's the most common responsive bug. + +## The engines that matter + +Three rendering engines cover the real-world field: + +- **Chromium** → Chrome, Edge, Brave, most Android browsers. +- **Gecko** → Firefox. +- **WebKit** → Safari (macOS) **and** Mobile Safari (iOS/iPadOS — every iOS browser is WebKit under the + hood, even "Chrome" on iPhone). + +Pass on all three at your breakpoints and you've covered the matrix. + +## Tooling: Playwright is the primary tool + +Playwright drives **chromium, firefox, and webkit** from one config, emulates devices/viewports, and +produces repeatable screenshots — and it can read computed styles, so it doubles for the rendered +contrast check in `accessibility-verification.md`. + +**Set it up if the repo doesn't have it.** Check for `@playwright/test`; if it's missing (always the +case in a greenfield repo), provision it — the same way this skill provisions its other gates: + +```bash +pnpm add -D @playwright/test +npx playwright install # download the chromium/firefox/webkit binaries (once) +``` + +Then write `playwright.config.ts` with the projects below and add the `verify:browsers` task from +`assets/Taskfile.design.yml`, so the cross-engine sweep runs like every other gate. + +```ts +// playwright.config.ts — baseURL + dev server, then the cross-engine × device matrix +import { defineConfig, devices } from "@playwright/test"; +export default defineConfig({ + use: { baseURL: "http://localhost:5173" }, // your dev server (Vite 5173, Astro 4321, …) + webServer: { + command: "pnpm dev", + url: "http://localhost:5173", + reuseExistingServer: true, + }, + projects: [ + { name: "chromium", use: { ...devices["Desktop Chrome"] } }, + { name: "firefox", use: { ...devices["Desktop Firefox"] } }, + { name: "webkit", use: { ...devices["Desktop Safari"] } }, + { name: "mobile-safari", use: { ...devices["iPhone 14"] } }, + { name: "tablet", use: { ...devices["iPad (gen 7) landscape"] } }, + ], +}); +``` + +For a quick ad-hoc capture without a test file, the Playwright CLI takes one screenshot per device: + +```bash +npx playwright screenshot --device="iPhone 14" http://localhost:5173/brand brand-iphone.png +``` + +For the full Phase 5 sweep, use the bundled **`assets/brand-screenshots.spec.ts`** template, run by +`task verify:browsers`. It is a parameterized harness, not a from-scratch write: the engine × device +axis comes from the `projects` above, and the spec adds the route × theme axis, writing one full-page +PNG per route × theme per project. Fill in three spots and it works: + +- **`ROUTES`** — always include `/brand`, plus the feature's pages. +- **`baseURL` and `webServer`** (in the config above) — point them at the dev server. +- **`setTheme()`** — defaults to shadcn's `.dark` class on ``; change it only if the repo toggles + dark mode differently (data attribute, cookie). + +To capture per-specimen shots on `/brand`, extend the spec to locate the `data-brand-specimen` hooks +(`brand-page.md`); the template includes a commented starting point. + +**WebKit ≈ Safari, not identical.** Playwright's WebKit is the open-source engine — very close to +Safari and the best automated proxy for Safari and iOS Safari, but it lacks Apple-proprietary bits and +may differ in version. For a high-stakes Safari-only issue, confirm on a real device / actual Safari or +a cloud lab (BrowserStack, LambdaTest, Sauce Labs). + +### Playwright vs agent-browser + +- **agent-browser** (Chromium / Chrome DevTools Protocol) is great for the agent to interactively + explore, click through flows, and grab quick screenshots — use it for ad-hoc visual checks and + dogfooding. It is Chromium-only, so it does **not** prove cross-engine behavior. +- **Playwright** is the systematic matrix tool (three engines × viewports × themes) and is what + produces the cross-browser sign-off evidence. + +Use whatever the repo standardizes on for interactive poking, but use Playwright (or a cloud lab) for +the actual cross-engine verification. + +If Playwright genuinely can't run here and only **agent-browser** is available, you can still do the +Chromium responsive pass with it — but the **cross-engine** half of gate ③ is then unmet. Provision +Playwright (or use a cloud lab); if neither is possible, **flag the gap to the user** rather than +reporting the cross-browser gate as PASS. + +## The Phase 5 matrix + +Capture every implemented view across `{light, dark}` × `{phone ~375, tablet ~768/1024, desktop +~1280}` × `{chromium, firefox, webkit}`. That's the screenshot set you present at sign-off. You don't +need every cell for a trivial view — but every distinct layout, and the `/brand` page, should cover the +full matrix, and any layout that changes at a breakpoint must be shown on both sides of it. Re-run the +rendered-contrast check at these widths too: a reflow can change what overlaps what. + +## Common responsive / cross-engine gotchas + +- **Horizontal overflow** at narrow widths (the #1 RWD bug) — check 320–375px first. +- **`100vh` overflow** on mobile → use `dvh`/`svh`. +- **iOS:** inputs need ≥16px font or Safari auto-zooms on focus; honor safe-area insets + (`env(safe-area-inset-*)`) for notches; mind the tap-highlight color. +- **WebKit/Safari:** older flex `gap`, `backdrop-filter`, `position: sticky` inside `overflow`, + date/time input styling. Let Autoprefixer / Tailwind emit `-webkit-` prefixes — don't hand-write + them. +- **Firefox:** scrollbar styling, the odd grid/subgrid edge case, form-control rendering. diff --git a/ai/skills/design/design-handoff/references/token-reconciliation.md b/ai/skills/design/design-handoff/references/token-reconciliation.md index 8dedb08..3c4f012 100644 --- a/ai/skills/design/design-handoff/references/token-reconciliation.md +++ b/ai/skills/design/design-handoff/references/token-reconciliation.md @@ -1,49 +1,107 @@ -# Token Reconciliation: Claude Design export → shadcn `globals.css` - -Read this during **step 2** of the `design-handoff` skill. It explains how to merge the design's token export into this repo's `src/styles/globals.css` correctly. The export is **not** a drop-in; pasting it verbatim breaks the system. - -## Why you can't paste the export in - -The two token models have different _shapes_, and four specific things conflict: - -1. **Flat vs three-layer.** The export (`task export:design` → `.design/theme.scratch.css`) emits a single flat `@theme { … }` block of primitive values. shadcn uses three layers: `:root`/`.dark` semantic tokens + an `@theme inline` reference layer. -2. **Value-named vs role-named.** The export's `primary`/`secondary`/`tertiary`/`neutral` are _palette_ names (a value). shadcn's `--primary`, `--background`, etc. are _roles_, and roles require `-foreground` pairs plus `card`/`popover`/`muted`/`accent`/`destructive`/`border`/`input`/`ring` that the export doesn't have. -3. **Hex vs OKLCH.** The export tends to emit hex/sRGB. This repo standardizes on **OKLCH** (perceptually uniform — makes dark mode and contrast predictable). -4. **Light-only vs dual-mode.** The export has **no dark mode** (the DESIGN.md token schema has no scheme dimension). shadcn needs both `:root` and `.dark`. **You author `.dark` yourself.** - -So: treat the export as a **palette source**, read the `DESIGN.md` prose for _role intent_, and merge by hand into the shadcn skeleton. - -## The shadcn `globals.css` skeleton (keep this intact) +# Token reconciliation: Claude Design `tokens.css` → shadcn `globals.css` + +Read this during **Phase 2**. The bundle ships a token file (commonly `tokens.css`, plus a `site.css` +of brand/dark-mode overrides) carrying the design's primitives — palette, fonts, spacing, radii, type +scale. What you do with it depends on the **mode**, but one rule is constant: **the bundle's tokens are +a proposal; `globals.css` is truth.** + +- **`establish-design-system`** (no system yet) — _write canonical_ tokens from the bundle. That's the bulk of this + doc: the conflicts, the skeleton, the by-role merge recipe, and authoring `.dark`. +- **`implement-feature`** (a feature against an existing system) — _consume-first_: diff and map to the + existing tokens, adding nothing by default. **Start at "Consume-first" below.** +- **`evolve-design-system`** (changing the system) — diff and version the change; see `evolving-the-system.md`. + +Either way it is **not** a drop-in — pasting a token file into `globals.css` breaks the system. + +## Consume-first (`implement-feature`): diff, don't redefine + +When the repo already has a design system, the feature bundle's tokens are a re-emission that may have +drifted — so you **consume** them, you don't rewrite: + +1. **Map every value to an existing semantic token.** For each color/spacing/radius/type value in the + bundle, resolve it to the matching token and use the utility (`bg-primary`, `text-muted-foreground`, + `rounded-lg`, …). Replace any inline `oklch(...)`/hex in the markup with the token. Add **nothing** + to `globals.css` by default. +2. **Match by OKLCH closeness, with a tolerance.** Treat a bundle color as "the same as" a canonical + token when it's within a small ΔL/ΔC/Δh bound (pick a default; let the user tune it), and **report + drift** rather than silently adopting the bundle's value — e.g. "bundle `--primary` is + `oklch(0.62 0.19 255)`, canonical is `oklch(0.60 0.16 250)` — keeping canonical." +3. **A value with no close match is a DECISION POINT — stop and surface it.** Two legitimate + resolutions: + - **Force-fit** (preferred for one-offs): snap to the nearest existing token. Use when the + difference is incidental or the value appears once. + - **Deliberately extend** (when the need is real and reusable): add the token _additively_ (the + shadcn pattern — define `--warning`/`--warning-foreground` in `:root` and `.dark`, expose via + `@theme inline`), update `/brand` and `DESIGN.md`, and record a DDR. That's an `evolve-design-system`-style + change folded into the feature, so it carries a SemVer bump (`evolving-the-system.md`). + - **Heuristic:** extend only if the value (a) is a _semantic role_, not just a shade, (b) will + plausibly recur, and (c) you'd document it in `/brand`. Otherwise force-fit. +4. **Never overwrite `globals.css` wholesale** from a feature bundle — it may re-emit the whole token + block; you apply only deliberate, approved additions. + +The rest of this doc (the conflicts, skeleton, and merge recipe) is the `establish-design-system` path — you'll also +dip into it when a feature legitimately extends the system. + +## Why you can't paste it in + +The bundle's token model and shadcn's have different _shapes_. Four specific things conflict: + +1. **Flat vs three-layer.** `tokens.css` is a flat list of primitives (`--color-*`, `--font-*`, + `--space-*`, `--radius-*` — sometimes with oddities like `--radius-pill: 980px`). shadcn uses + three layers: `:root`/`.dark` **semantic** tokens, plus an `@theme inline` **reference** layer + that exposes them to Tailwind as `--color-*` utilities. +2. **Value/palette-named vs role-named.** The bundle names colors by their _value_ (e.g. + `--color-ink`, `--color-paper`, `--color-terracotta`, or numbered scales). shadcn's `--primary`, + `--background`, etc. are _roles_ — and roles require `-foreground` partners plus + `card`/`popover`/`muted`/`accent`/`destructive`/`border`/`input`/`ring`, which the bundle does + not contain. +3. **Hex/sRGB vs OKLCH.** `tokens.css` tends to emit hex or sRGB. This repo standardizes on + **OKLCH** because it is perceptually uniform: stepping lightness by a fixed amount _looks_ like a + fixed step across every hue, which makes dark mode and contrast predictable (in HSL, yellow blows + out bright while blue stays dark at the same `L`). +4. **Light-only vs dual-mode.** `tokens.css` + `site.css` rarely carry a complete dark scheme. + shadcn needs both `:root` and `.dark`. **You author/complete `.dark` yourself.** + +So: treat `tokens.css` as a **palette + scale source**, read `chats/chat1.md` and `site.css` for each +color's _intended job_, and merge by hand into the shadcn skeleton below. Map by role, never by name — +the bundle's `--color-primary` is a paint value and is **not** necessarily shadcn's `--primary` role. + +## The shadcn `globals.css` skeleton (keep this structure intact) ```css -@import 'tailwindcss'; +/* Any hosted-font @import MUST sit above this line — see assets-fonts-favicons.md. */ +@import "tailwindcss"; @custom-variant dark (&:is(.dark *)); :root { --radius: 0.625rem; - --background: oklch(…); /* surface */ - --foreground: oklch(…); /* text/ink on surface */ - --primary: oklch(…); /* main interaction/brand color */ - --primary-foreground: oklch(…); /* text/icon on --primary */ + + --background: oklch(…); /* page surface */ + --foreground: oklch(…); /* ink on the surface */ + --card: oklch(…); + --card-foreground: oklch(…); + --popover: oklch(…); + --popover-foreground: oklch(…); + --primary: oklch(…); /* main brand/interaction color */ + --primary-foreground: oklch(…); /* ink/icon on --primary */ --secondary: oklch(…); --secondary-foreground: oklch(…); --muted: oklch(…); - --muted-foreground: oklch(…); + --muted-foreground: oklch(…); /* low-emphasis text */ --accent: oklch(…); --accent-foreground: oklch(…); - --destructive: oklch(…); + --destructive: oklch( + … + ); /* error/danger — usually a red NOT from the brand palette */ + --destructive-foreground: oklch(…); --border: oklch(…); --input: oklch(…); - --ring: oklch(…); - --card: oklch(…); - --card-foreground: oklch(…); - --popover: oklch(…); - --popover-foreground: oklch(…); - /* chart-* and sidebar-* if used */ + --ring: oklch(…); /* focus ring */ + /* --chart-1..5 and --sidebar-* only if the app uses charts / a sidebar */ } .dark { - /* same token names, dark values you author (see below) */ + /* same token names, dark values you author — see "Author the .dark block" below */ } @theme inline { @@ -51,41 +109,117 @@ So: treat the export as a **palette source**, read the `DESIGN.md` prose for _ro --color-foreground: var(--foreground); --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); - /* …one --color-* reference per semantic token… */ + /* …one --color-* line per semantic token above… */ + --radius-lg: var(--radius); - /* font roles also live here, e.g. --font-sans, set under @theme */ + --radius-md: calc(var(--radius) - 2px); + --radius-sm: calc(var(--radius) - 4px); + + /* font roles live here too, pointing at the families you set up under @theme */ + --font-sans: var(--font-sans); + --font-mono: var(--font-mono); } ``` -The `inline` keyword matters: it makes the `.dark` overrides flow through to the generated utilities automatically. **Never hard-code a color value into `@theme inline`** — it must stay a `var(--…)` reference, or you break dark mode and theming. +The `inline` keyword matters: it makes the `.dark` overrides flow through to the generated utilities +automatically. **Never hard-code a color into `@theme inline`** — every entry must stay a `var(--…)` +reference, or you break dark mode and theming. `@theme inline` is wiring, not values. ## The merge recipe -1. **Map palette → roles by reading the prose.** Don't map by name (the export's `primary` ≠ shadcn's `--primary`). Read `DESIGN.md`'s Colors/Overview prose to learn each color's _job_, then place it. Typical mapping: - - the deep ink / darkest neutral → `--foreground` +1. **Map palette → roles by reading intent, not names.** Read `chats/chat1.md` (the design + conversation) and `site.css` to learn each color's _job_, then place it. Typical mapping: + - the deepest ink / darkest neutral → `--foreground` - the lightest neutral / page background → `--background` - - the color the prose calls the interaction/accent driver → `--primary` + - the color the design calls the interaction/accent driver → `--primary` - a mid neutral → `--border` / `--input` - - pick `-foreground` partners that hit **4.5:1** contrast against their surface. -2. **Convert to OKLCH.** Express the merged values as `oklch(L C H)`. Keep the brand hue (`H`) consistent across related tokens. -3. **Fill the gaps shadcn needs but the export lacks** — `card`/`popover` (often = `--background` or a near neighbor), `muted`/`accent` (subtle neutral surfaces), `destructive` (a red not from the brand palette), `ring` (usually the brand/primary hue). Derive these from the palette; the export won't have them. -4. **Author the `.dark` block.** The export can't give you this. A reliable rule: **keep the brand accent hue constant** across modes and **invert neutral lightness** — i.e. for `--primary` use the same `H` (and similar `C`), only nudging `L`; for neutrals (`--background`/`--foreground`/`--card`/etc.) flip the lightness so dark surfaces get dark `L` and their foregrounds get light `L`. Re-check contrast in dark mode separately. -5. **Lift scalar tokens more directly.** `--radius`, spacing, and font sizes/weights from the export can map almost 1:1 — less reconciliation needed than color. -6. **Wire fonts.** Put font _files_ (self-hosted OFL/Apache `.woff2`) referenced via `@font-face`, and font _roles_ (`--font-sans`, `--font-display`, `--font-mono`) under `@theme`. Confirm any hosted-font `@import` sits **above** `@import "tailwindcss"`. + - pick `-foreground` partners that clear **4.5:1** against their surface. +2. **Convert to OKLCH.** Express each merged value as `oklch(L C H)` (L 0–1, C 0–~0.4, H 0–360). Keep + the brand hue (`H`) consistent across related tokens. Keep chroma `C` under ~0.30 so colors stay + inside the sRGB gamut on ordinary monitors. +3. **Fill the gaps shadcn needs but the bundle lacks.** `card`/`popover` are often `--background` or a + near neighbor; `muted`/`accent` are subtle neutral surfaces; `destructive` is a red sourced + outside the brand palette; `ring` is usually the brand/primary hue. Derive these — the bundle + won't have them. +4. **Author the `.dark` block.** The bundle can't give you a reliable one. The dependable rule: **hold + the brand accent hue constant** across modes and **invert neutral lightness**. For `--primary`, + keep the same `H` (and similar `C`), nudging only `L`; for neutrals + (`--background`/`--foreground`/`--card`/…), flip the lightness so dark surfaces get a low `L` and + their foregrounds get a high `L`. Re-check contrast in dark mode independently — it is not implied + by light mode passing. +5. **Lift scalar tokens almost 1:1, but sanitize oddities.** `--radius`, spacing, and type + sizes/weights map nearly directly. Watch for prototype artifacts: a `--radius-pill: 980px` is a + "fully rounded" hack — express it as a `rounded-full` usage, don't feed 980px into `--radius`. +6. **Wire fonts.** Font _roles_ (`--font-sans`, `--font-display`, `--font-mono`) go under `@theme`; + the font _files_ are self-hosted and declared with `@font-face`. See `assets-fonts-favicons.md` + for the `@import`-order rule that trips everyone up. +7. **Map `--tw-prose-*` if the app renders long-form prose** — see next section. + +## Map `--tw-prose-*` (the Typography-plugin override) + +If the app uses `@tailwindcss/typography` (any `.prose` content — articles, docs, marketing copy), +the plugin sets its _own_ text colors through `--tw-prose-*` variables in a later cascade layer. At +runtime those **override your semantic tokens**: body copy paints in the plugin's default grey +instead of your `--foreground`, and dark mode silently breaks — even though `globals.css` is correct +and the static contrast gate is green. This is the canonical "tokens pass, render fails" trap; it's +why Phase 5 measures the _rendered_ page (see `accessibility-verification.md`). + +Fix it once by pointing the prose variables at your semantic tokens. Because the tokens already flip +in `.dark`, prose then follows dark mode automatically — you don't need `dark:prose-invert`: + +```css +.prose { + --tw-prose-body: var(--foreground); + --tw-prose-headings: var(--foreground); + --tw-prose-bold: var(--foreground); + --tw-prose-links: var(--primary); + --tw-prose-quotes: var(--foreground); + --tw-prose-quote-borders: var(--border); + --tw-prose-bullets: var(--muted-foreground); + --tw-prose-counters: var(--muted-foreground); + --tw-prose-captions: var(--muted-foreground); + --tw-prose-code: var(--foreground); + --tw-prose-pre-code: var(--card-foreground); + --tw-prose-pre-bg: var(--card); + --tw-prose-hr: var(--border); + --tw-prose-th-borders: var(--border); + --tw-prose-td-borders: var(--border); +} +``` + +## Verify the merge before moving on + +Run the static contrast gate the skill ships (copied into the repo as `scripts/check-contrast.mjs` +and wired to `task lint:design` — see `assets/check-contrast.mjs` and +`assets/Taskfile.design.yml`): + +```bash +node scripts/check-contrast.mjs src/styles/globals.css # or: task lint:design +``` + +It parses every foreground/background pair from `:root` and `.dark` and **fails on any sub-AA text +pair, in either theme** (exit 1). Fix every `FAIL` before you implement components. This is necessary +but **not sufficient** — it sees the tokens, not the painted pixel; rendered contrast is measured in +Phase 5. ## Worked example (illustrative) -Say `DESIGN.md` describes an "antiqued" palette: `primary #1A1C1E` (deep ink), `tertiary #B8422E` ("the sole interaction driver"), `neutral #F7F5F2` (warm paper), `secondary #6C7278` (muted gray). Read by _role_: +Say `chats/chat1.md` describes an "antiqued" palette and `tokens.css` carries `--color-ink #1A1C1E` +(deep ink), `--color-terracotta #B8422E` ("the sole interaction driver"), `--color-paper #F7F5F2` +(warm paper), `--color-stone #6C7278` (muted gray). Read by _role_: -- `#1A1C1E` deep ink → `--foreground` (and a near-black for dark `--background`) -- `#F7F5F2` warm paper → `--background` (light) / its inverse for dark `--foreground` -- `#B8422E` interaction driver → `--primary` (keep this hue constant in `.dark`) +- `#1A1C1E` deep ink → `--foreground` (and the basis for a near-black dark `--background`) +- `#F7F5F2` warm paper → `--background` (light) / its inverse for the dark `--foreground` +- `#B8422E` interaction driver → `--primary` (hold this hue constant in `.dark`) - `#6C7278` muted gray → `--border` / `--muted-foreground` -Then convert each to `oklch(...)`, add `--primary-foreground` (a light tone meeting 4.5:1 on the terracotta), synthesize `card`/`popover`/`muted`/`accent`/`destructive`/`ring`, and author the `.dark` block by holding the terracotta hue and inverting the neutrals. +Convert each to `oklch(...)`; add a `--primary-foreground` light enough to clear 4.5:1 on the +terracotta; synthesize `card`/`popover`/`muted`/`accent`/`destructive`/`ring`; then author `.dark` by +holding the terracotta hue and inverting the neutrals. Run the gate; fix fails; only then move on. -## After merging +--- -- Run `task lint:design` — confirms `DESIGN.md` token refs resolve and flags WCAG AA contrast failures. -- For repeatable future syncs, prefer regenerating `task export:design` to a _scratch_ file and re-merging deltas over hand-diffing the whole file. Never let the export write directly into `globals.css`. -- If you ever need a true multi-platform pipeline (iOS/Android, or a second brand), that's the trigger to promote `.design/tokens.json` (DTCG) to canonical and compile with Style Dictionary. +**Footnote — multi-platform.** DTCG / Style Dictionary and a structured `tokens.json` are only worth +it if you later need to ship the same tokens to iOS/Android or a second brand. For a single web app, +`globals.css` in shadcn three-layer form **is** the source of truth — don't over-engineer a token +pipeline you don't need yet. diff --git a/ai/skills/design/design-handoff/references/verification-and-signoff.md b/ai/skills/design/design-handoff/references/verification-and-signoff.md new file mode 100644 index 0000000..6ab5d9b --- /dev/null +++ b/ai/skills/design/design-handoff/references/verification-and-signoff.md @@ -0,0 +1,85 @@ +# Verification & sign-off: prove it, get approval, then close out + +Read this during **Phases 5–7**. This is where you prove the implementation with numbers and +screenshots, get the user's explicit approval, and only **then** clean up and open a PR. Two of the +skill's four gates fall in these phases — **③ Verification** (Phase 5) and **④ Sign-off** (Phase 6); the +upstream two (**① static contrast** at Phase 2, **② licensing** at Phase 4) are already behind you. + +## Phase 5 — Verify (don't trust a green build) + +1. **Run the gates** through the repo's Taskfile, and **create any task that's missing** (from + `assets/Taskfile.design.yml`): + + - `task lint:design` — static token-contrast gate (`scripts/check-contrast.mjs`) + off-palette + scan. + - `task check` — typecheck + lint + format (fast static verification). + - `task verify` — the fuller pass (build, etc.). + - `task verify:browsers` — the Playwright cross-engine/viewport screenshot sweep. Set Playwright up + if the repo lacks it (`responsive-and-cross-browser.md`); agent-browser is Chromium-only and does + **not** substitute for it. + + **Never use `--no-verify`** or otherwise skip git hooks — the hooks and CI are the authoritative + gates, and bypassing them defeats the point. If a needed task doesn't exist in the repo, **add it** + (copy `check-contrast.mjs` into `scripts/`, merge the design tasks) rather than skipping the check. + +2. **Build** to confirm it compiles. +3. **Make it viewable.** Run the app (`task dev` / the project's run skill) and exercise every + implemented screen. +4. **Screenshot every implemented view** — in both light and dark, for every state (default, empty, + loading, error, disabled), and across key breakpoints (phone/tablet/desktop) and rendering engines + (Chromium/Firefox/WebKit incl. mobile Safari). Playwright drives the whole matrix from one config — + see `responsive-and-cross-browser.md`. These are what the user signs off against. +5. **Measure rendered contrast** (`accessibility-verification.md`): computed colors on the running + page, both themes, every text role including long-form prose. Report the numbers. Fix and + re-measure anything that fails **before** showing the user. + +## Phase 6 — Sign-off gate (blocks deletion) + +Do **not** assume the implementation is correct, and do **not** proceed to cleanup on your own +judgment. Before anything is deleted: + +1. **Show the screenshots** (both themes, all states) and the measured contrast numbers. +2. **Compare to the bundle.** Put your screenshots beside the bundle's intent (`chats/chat1.md`, the + prototype) and call out every delta you already know about — what you adapted, simplified, or + deferred. Let the user decide with full information, not a happy summary. +3. **Ask explicitly** (`AskUserQuestion` or a direct question): "Does this match the intended design, + or are there things to fix before I remove the handoff files?" +4. **Iterate until approved.** If the user wants changes, make them and re-verify. **Loop here — do not + advance — until the user explicitly approves.** Approval is the user's call, never inferred from a + green build or your own confidence. + +The handoff bundle stays fully in place for this entire step — it is the reference the user compares +against. + +## Phase 7 — Close out (only after explicit approval) + +1. **Delete the bundle.** Once approved, delete `docs/design/handoff-/` so a stale runnable + snapshot can't later mislead an agent into treating it as a source of truth. The one exception: if + the design includes states not yet built, extract a _thin_ screenshot + intent note into the + relevant spec first, then delete the rest. The durable records are the merged code, + `DESIGN.md`/`globals.css`, `/brand`, and any DDR. +2. **Update the docs.** `docs/design/` (`brand.md`, `design-system.md`, `components.md`, + `accessibility.md`, `ux.md`) and the `/brand` page whenever brand-level things changed — keep them + in lockstep with `DESIGN.md`/`globals.css`. Update `README.md` if new scripts or usage were + introduced. +3. **Flag decisions.** If the design embodied a genuine, debatable design-system decision (a new token + architecture, a palette-philosophy shift, a vendor choice), tell the user it warrants a **DDR** in + `/decisions/`. Architecture decisions don't belong in this skill or in `DESIGN.md` prose — they + belong in a decision record. +4. **Commit & PR.** Use **Conventional Commits**. Commit on a **feature branch** — direct commits to + `main` are blocked and wrong. The agent **never merges to `main` directly**: open a **PR** for human + review of the diff. + +## The four gates, in order + +- **① Static contrast** (Phase 2) blocks **implementation** — `task lint:design` green; WCAG AA in both + themes. +- **② Licensing** (Phase 4) blocks **commit** — every font/icon/image cleared for commercial use. +- **③ Verification** (Phase 5) blocks **sign-off** — `task verify` green and the build compiles; + rendered contrast measured as **numbers** (both themes); responsive at phone/tablet/desktop; + cross-browser on Chromium/Firefox/WebKit (incl. mobile Safari). +- **④ Sign-off** (Phase 6) blocks **deletion & close-out** — explicit user approval, never inferred. + +Then **close-out** (Phase 7 — not a gate): bundle removed, docs and `/brand` updated, DDR + SemVer bump +recorded, Conventional Commit, PR opened — hooks never bypassed (`--no-verify` prohibited), no direct +merge to `main`.