Dev-flow contract
This skill participates in the dev-flow workflow. When invoked on a project that has a .workflow/ folder at its root:
- Input is
<root>/.workflow/DESIGN.md (mandatory) and <root>/.workflow/meta.json#stack (preferred — if framework and ui are set, use them; if not, ask the user once and persist).
- Output is the codebase scaffold at
<project-root>/, alongside the existing .workflow/ directory (NOT nested inside it). The framework's standard layout — package.json, app/, components/, lib/, etc. — sits at the top so cd <project-root> && pnpm dev works the way every developer expects.
- State is updated by setting
meta.json#phase = "scaffolded", refreshing updated_at, and appending an entry to meta.json#history describing the run (skill name, inputs, outputs, phase delta).
- Standalone mode (no
.workflow/ present) is still supported — fall back to the original "ask the user where to scaffold" behavior. The contract is opt-in.
The canonical contract spec is in references/contracts.md. Read it if any of the rules above are unclear.
DESIGN.md → App
Take a DESIGN.md (Google design.md spec — see references/spec.md) and produce a working React app where shadcn/ui or MUI components are already themed and ready to compose into the actual product. The user picks the library; this skill turns design tokens into theme code, customizes component variants according to the components block, and leaves the user with a runnable scaffold plus a /showcase page they can visually verify.
When this skill applies
Trigger on any of:
- A
DESIGN.md (or design.md) file path or content provided by the user.
- Explicit requests: "fai partire l'app dal DESIGN.md", "scaffold from design.md", "customize shadcn/MUI from this DESIGN.md", "apply these tokens to shadcn/MUI".
- A user describing wanting to start a project where the design system is already specified in
DESIGN.md form.
If the user has a DESIGN.md but only mentions shadcn/MUI without referencing the file, still trigger — they likely want their tokens applied.
What you produce
A scaffolded (or augmented) React + TypeScript project where:
- The chosen UI library (shadcn/ui or MUI) is installed and configured.
- Design tokens from the YAML frontmatter are wired into the theme:
colors → CSS variables (shadcn) or palette (MUI). Always two modes: dark + light, with the source DESIGN.md driving the canonical mode and the other auto-derived (see §Dark + light below).
typography → typography scale (Tailwind theme extension or theme.typography).
rounded → radius scale (borderRadius extension or theme.shape).
spacing → spacing scale (Tailwind extension or theme.spacing overrides).
- Components from the
components block are reflected in:
- shadcn: variant overrides on the corresponding component (e.g.
button-primary → Button default variant in components/ui/button.tsx). All shadcn primitives are installed (shadcn add --all) so the app comes out of the box ready to compose.
- MUI:
theme.components.MuiXxx.styleOverrides and/or defaultProps.
- A
/showcase page renders every styled primitive so the user can verify the result.
- Fonts referenced in
typography.fontFamily are loaded — via next/font/google when on Next, via direct <link> for Vite/Remix when the font is on Google Fonts, otherwise self-hosted from a local public/fonts/ (see §Font loading).
- A
_design-md-mapping.json is written at the project root showing exactly how each DESIGN.md token resolved to library values — useful for debugging and for the user to verify the mapping at a glance.
- The markdown body's qualitative rules (gradients, glow, glassmorphism, "do's and don'ts") are encoded as utility classes / theme effects where they translate to CSS, and otherwise summarized in a
STYLE_NOTES.md so the user — and future agents — can apply them consistently.
Never invent the spec. When in doubt about a token shape, valid units, references like {colors.primary}, or canonical section ordering, read references/spec.md first.
Workflow
Step 1 — Locate and parse the DESIGN.md
- Preferred (dev-flow mode): if a
.workflow/meta.json exists at the project root, the DESIGN.md is at .workflow/DESIGN.md. Don't search further.
- Fallback: if the user gave a file path, use it. Otherwise look for
DESIGN.md in the project root (case-insensitive). If multiple are found, ask which one.
- Run
python scripts/parse_design_md.py <path> to get a normalized JSON dump of { frontmatter, body_sections, resolved_components } where token references like {colors.primary} are resolved to literal values.
- If parsing fails (malformed YAML, duplicate sections), surface the error and stop. The user must fix the source.
A third shape exists, and it is not an input. design.md now names two unrelated
artefacts. Ours is the Google spec below: token blocks in the frontmatter. The other is an
Agent Skill — frontmatter of exactly name + description, body in prose, zero tokens.
https://vercel.com/design.md is one: Vercel's brand guidance
for agents writing report pages, served as text/markdown so any agent can load it.
Fed one of those, the parser used to exit 0 with empty tokens — which shape 2 below reads as
"body-only, extract from prose", and every value in the app ends up invented. It now exits 2
and says so. If a user hands you a URL or a file that turns out to be a skill, the answer is not
to parse it: it carries no design system.
Two valid input shapes — handle both:
Frontmatter + body (the canonical Google design.md spec). The parser returns a populated frontmatter dict; resolved_components is non-empty. This is the easy path — token values are already structured for you.
Body-only / prose-only (no --- fences, the spec written entirely as markdown). The parser returns frontmatter: {} and everything goes into body_sections. This is now common in the wild — many DESIGN.md files written for AI agents are pure prose with section headings like ## 2. Color Palette, ## 3. Typography Rules, etc.
When frontmatter is empty, don't stop and ask for YAML. Extract tokens directly from the body sections. Map sections to token categories by their heading (case-insensitive partial match):
- "Color" / "Palette" / "Colors" → colors
- "Typography" / "Type" / "Fonts" → typography levels + font families
- "Component" / "Components" / "Cards" / "Buttons" → component variants
- "Layout" / "Spacing" / "Grid" → spacing scale, container widths
- "Radius" / "Shapes" / "Border" → radius scale
- "Depth" / "Elevation" / "Shadow" → shadow utilities
- "Do's and Don'ts" / "Notes" / "Known Gaps" → STYLE_NOTES.md content + mode/font opt-outs
Read each section as the source of truth for its category — extract hex values from prose, copy typography tables, lift component prose specs verbatim. Document this extraction path explicitly in _design-md-mapping.json under a top-level extraction_method field so a reviewer can see what came from prose inference vs. from a structured field.
Step 2 — Pick the library
Ask the user once: shadcn/ui, Base UI, MUI, or Coss/UI? Offer a recommendation based on references/library-choice.md. Common heuristics:
- Highly custom visual identity (glassmorphism, brutalist, editorial, distinctive shapes) + user wants to edit source → shadcn.
- Same custom-Tailwind philosophy as shadcn but the user prefers a library they can
pnpm update (no CLI / components.json overhead) → Base UI.
- Material-leaning, dashboard, enterprise CRUD, lots of data tables / dialogs out of the box → MUI.
- Tailwind already in use, or the user wants utility-first → shadcn or Base UI (toss-up; Base UI for less source-maintenance, shadcn for max control).
- Heavy Material Icons / Material Design heritage → MUI.
- Best accessibility track record without Material visuals → Base UI (same MUI team, headless).
If they pick shadcn, ask the follow-up: which primitive base — Base UI, Radix, or React Aria? (shadcn create --base base|radix|aria). shadcn CLI v4 builds on any of them, with the same component API and blocks across variants. Default Base UI (shadcn's default for new projects since 2026-07; full component + block coverage, MUI-team a11y). Pick Radix (the long-standing base, still fully supported) or React Aria (--base aria, Adobe's a11y-first primitives, first-class since 2026-07) explicitly. Record as stack.ui_base. This is distinct from picking standalone Base UI (stack.ui = "base-ui", no shadcn CLI) — see references/library-choice.md. Hybrid asking (per dev-flow): also ask icon_library (lucide default), and rtl only when the project is multilingual/RTL. Don't ask base color / theme — the DESIGN.md tokens own the visual layer; css_variables stays true.
- Wants the Cal.com design system / an AI-first, MCP-friendly copy-paste kit on Base UI, and Tailwind v4 is acceptable → Coss/UI (
stack.ui = "coss") — hand off to the coss-ui skill.
Mapping skill choice → meta.json#stack.ui → references/<lib>-mapping.md:
- "shadcn" →
references/shadcn-mapping.md (+ stack.ui_base = base|radix|aria)
- "base-ui" →
references/base-ui-mapping.md (standalone Base UI, no shadcn CLI)
- "mui" →
references/mui-mapping.md
- "coss" → hand off to
coss-ui/SKILL.md (Coss/UI — Cal.com DS on Base UI via the shadcn @coss/* registry; ui_base = "base"; requires Tailwind v4; mixed MIT/AGPLv3 license). Coss's tokens are shadcn CSS vars, so DESIGN.md overrides apply as in the shadcn path.
State the suggestion with a one-line rationale ("Suggerisco Base UI perché vuoi l'aesthetics flessibile di shadcn ma senza la source-maintenance del CLI"), then accept whatever the user picks. After picking, load the matching <lib>-mapping.md and follow it for installation, theming, and component wiring.
Step 3 — Pick the project target, framework, and scope
Ask the user two things in this step:
3a. Project target
- Dev-flow mode (
.workflow/meta.json exists): scaffold the codebase at <project-root>/ alongside .workflow/ (e.g., pnpm create next-app . from the project root). Do NOT create a sub-directory like app/ or code/ to nest the codebase. The framework comes from meta.json#stack.framework if set, otherwise ask. The user does not pick the path — the contract pins it.
- New project (standalone) → ask which framework: Next.js (App Router) [default], Vite + React, or Remix. Then scaffold in a folder of the user's choice with TS + the chosen UI library wired in (see the relevant mapping reference for exact init commands).
- Existing project? → ask for the project root, then detect the framework by inspecting
package.json and the file layout:
- Next.js →
next in deps + app/ or pages/.
- Vite + React →
vite + @vitejs/plugin-react in deps.
- Remix →
@remix-run/* in deps.
- Anything else → tell the user what you found and ask how to proceed (most often: "write the theme files anyway and I'll wire them up").
The mapping references describe the framework-specific glue (font loading, root layout/provider wiring, where to put theme.ts / globals.css). Don't assume Next.js paths — branch on the detected framework.
3b. Scope: full scaffold vs. theme-only
Ask: "Vuoi lo scaffold completo (showcase + STYLE_NOTES + provider wiring) o solo le patch al tema?" Two modes:
Full scaffold (default for new projects): everything described in §What you produce — theme files + component overrides + /showcase route + STYLE_NOTES.md + _design-md-mapping.json + provider wiring. Golden rule 2 — wire i18n now (follow the doc-grounded how-to in references/i18n-next-intl.md, don't improvise the setup): install next-intl (stack.i18n), scaffold messages/en.json + messages/it.json (minimum stack.locales = ["en","it"], default en), the [locale] routing (routing.ts/navigation.ts/proxy.ts/request.ts) with setRequestLocale + generateStaticParams, and <NextIntlClientProvider> in the root layout. All scaffolded copy uses useTranslations keys — never hardcoded strings (the forms skill already assumes this). Adding i18n later touches every page, so it's part of the initial scaffold, not deferred.
Theme-only patch (recommended for mature existing projects): write only the design-token files as a reviewable diff:
- shadcn:
globals.css (Tailwind v4 is CSS-first — tokens live in @theme inside globals.css; only touch tailwind.config.ts if the project still uses a v3 JS config), the cva blocks of components named in DESIGN.md.
- MUI:
lib/theme.ts only (or wherever the theme already lives).
- Plus
_design-md-mapping.json and STYLE_NOTES.md because they're cheap to add and useful for the reviewer.
In theme-only mode, don't create /showcase, don't install new dependencies, don't touch the root layout/provider unless the user explicitly asks. The goal is a diff a senior reviewer can read in five minutes.
Default the choice based on context: existing project with extensive code → suggest theme-only; new project or empty repo → full scaffold. State the suggestion in one line, accept whatever the user picks.
If overwriting any existing theme/config (globals.css, tailwind.config.*, theme.ts), show a diff of what will change and confirm before writing.
Step 4 — Apply the chosen mapping
Confirmation gate (BLOCKING — do this before scaffolding). Before running any
shadcn create / install command, print a recap of the full resolved configuration and
wait for the user to confirm or adjust. Do not scaffold until they say go. Show every
value that will be passed to the CLI, including the ones derived from DESIGN.md (so the user
sees them even though they weren't prompted):
Sto per scaffoldare con shadcn create. Configurazione:
• framework : next (App Router) ← stack.framework
• base (primitivi): base (Base UI) ← stack.ui_base
• base color : neutral ← stack.base_color (poi sovrascritto dai token DESIGN.md)
• theme : (dai token DESIGN.md) ← stack.ui_theme
• icone : lucide ← stack.icon_library
• css variables : on ← stack.css_variables
• rtl : no ← stack.rtl
• monorepo : no ← stack.framework
Confermi, o vuoi cambiare qualcosa?
Resolve each value from meta.json#stack (with the documented defaults). On "cambia X",
update meta.json#stack and re-print the recap. Only after explicit confirmation, proceed.
For MUI / standalone Base UI, print the equivalent lighter recap (library, framework, base
color source) and confirm the same way. Never scaffold on assumed config. When
stack.shadcn_preset is set, run pnpm dlx shadcn@latest preset decode <code> and show the
decoded config in the recap (so the user confirms what the preset carries).
Read the relevant mapping reference and follow it:
- Any conversational surface or rendered markdown →
references/chat-and-typeset.md (shadcn chat components + typeset + streamdown, the standard — never hand-roll chat or render model markdown as plain text).
- shadcn →
references/shadcn-mapping.md. Monorepo first: if meta.json#stack.framework == "monorepo" and the monorepo is web-centric (no NativeWind/mobile side — e.g. web-only or web + agent), follow that reference's "Monorepo (shared packages/ui)" section: scaffold with shadcn init --monorepo so primitives land in a shared packages/ui (@workspace/ui), NOT in apps/web/components/ui/. The single-app flow below (and --no-monorepo) is for non-monorepo projects (or the web+mobile case where components stay app-local). Two visual-config paths — they are mutually exclusive:
- A) Preset path (when
stack.shadcn_preset is set): the preset owns the visual layer. Scaffold with pnpm dlx shadcn@latest init --preset ${stack.shadcn_preset} --template ${framework} --base ${stack.ui_base ?? "base"} --yes, then pnpm dlx shadcn@latest add --all --yes. Skip build_registry.py / the registry.json token install — the preset already encodes colors/theme/fonts/icons/radius. (Helpers: preset decode to inspect, preset url/preset open to view in browser.)
- B) DESIGN.md-first path (default, no preset): the recommended token-first install via
registry.json (see the dedicated section in that reference). Three steps: scaffold framework → emit registry.json from DESIGN.md tokens (use scripts/build_registry.py) → run pnpm dlx shadcn@latest init ./registry.json --yes followed by pnpm dlx shadcn@latest add --all --yes. add --all stays — every primitive lands in components/ui/* and gets customized in the next step per the DESIGN.md components block (cva edits).
- Pass the create parameters from
meta.json#stack (shadcn CLI v4): --base ${stack.ui_base ?? "base"} (Base UI / Radix / React Aria primitives), --base-color ${stack.base_color ?? "neutral"}, ${stack.css_variables === false ? "--no-css-variables" : "--css-variables"}, and --rtl when stack.rtl. The DESIGN.md tokens (via registry.json) override base color / theme / fonts, so those flags are only the starting scaffold. --base is NOT overridden by DESIGN.md — it picks the primitive engine, so honor stack.ui_base exactly. If stack.ui_base is unset, ask before scaffolding (don't silently pick a base on a fresh project).
- Every shadcn block/component exists in both Radix and Base UI variants; once
--base is set, shadcn add pulls the matching variant automatically.
- MUI →
references/mui-mapping.md
Each reference describes:
- the exact files to write per framework (Next.js / Vite / Remix),
- how each token category maps,
- which components to install/override (shadcn:
add --all so the user has the full kit pre-themed; MUI: theme overrides cover all primitives by default),
- how to encode the qualitative rules from the markdown body that can be expressed in code (e.g. radial background gradients, glass surfaces with backdrop-filter, ambient glow as a custom utility),
- what to put in
STYLE_NOTES.md for the rest (e.g. "Use radial gradients for hero backgrounds — see DESIGN.md §Layout").
The asset templates in assets/shadcn/ and assets/mui/ are starting points — read them, fill the placeholders with resolved tokens, write to disk. Do not paste them verbatim if the DESIGN.md doesn't define a value; fall back to library defaults rather than invent.
After writing the theme files, also write _design-md-mapping.json at the project root. This is a debug artifact: a JSON dump of { token_path → resolved_value → library_target } for every color/typography/component that was mapped, plus the list of fallbacks that were used because the DESIGN.md didn't define a value. Add _design-md-mapping.json to .gitignore if the user wants to keep it local-only — by default leave it tracked since it's useful for reviewers.
Step 4.3 — Library primitive priority (mandatory, supersedes custom components)
Rule: when the chosen UI library (shadcn or MUI) ships a primitive for the pattern, use it. Don't roll custom.
This is the single most-violated rule in scaffolders. The skill installs shadcn add --all (or MUI's full theme), giving the project access to dozens of pre-built, accessible, mobile-aware, theme-integrated primitives — and then the skill writes a custom 100-line <Sidebar> from scratch using <aside> + flex + lucide icons. The custom version is worse on every axis: less accessible, no mobile drawer, no collapsed state, no tooltip support, no keyboard shortcuts, no persistent state.
The mandate
Before authoring any component beyond the simplest (Eyebrow, simple text wrappers), scan components/ui/*.tsx for a primitive that matches the pattern. Common shadcn primitives projects miss:
| Pattern in DESIGN.md / Figma |
shadcn primitive |
What you get for free |
| Vertical app sidebar (icon-only or expanded) |
Sidebar + SidebarProvider + SidebarMenu* |
Collapsible state (icon ↔ expanded), mobile drawer via Sheet, tooltips on hover when collapsed, Cmd+B toggle, persistent state in cookies, active-route detection via context |
| Top navigation menu with hover dropdowns |
NavigationMenu |
Keyboard navigation, ARIA menu, hover delays |
| Dialog / modal |
Dialog + AlertDialog |
Focus trap, Escape close, scroll lock, portal |
| Hamburger drawer (mobile nav) |
Sheet |
Slide-in animation, focus management, swipe close |
| Autocomplete / type-ahead |
Combobox (Command + Popover) |
Async search, keyboard nav, fuzzy match |
| Command palette / cmd+k |
Command (CMDK) |
Categorized search, keyboard, animations |
| Chat / conversation / AI console / support inbox |
MessageScroller + Message + Bubble + Marker (shadcn chat, Jun 2026) |
Autoscroll that yields to the user, scroll-to-bottom button, edge scroll-fade, virtualization — see references/chat-and-typeset.md |
| Rendered markdown / AI output / rich description |
.typeset (shadcn/typeset, Jul 2026) + streamdown |
Consistent typography on rendered markdown; whitespace-pre-wrap on model output leaves **bold** literal — see references/chat-and-typeset.md |
| Map / location / route / store-locator |
mapcn (MapLibre GL, shadcn registry) — Map + Markers/Popups/Routes/Clusters/Controls |
Theme-aware, Tailwind-styled, declarative composition; ⚠️ default CARTO tiles need an Enterprise license for commercial use — swap the tile provider before shipping. See references/maps-mapcn.md; record stack.maps = "mapcn" |
| Date range picker |
Calendar + Popover |
Locale-aware, keyboard nav, range selection |
| Tabs (route-driven or panel) |
Tabs |
ARIA roles, keyboard nav, animations |
| Sheet / slide-out panel |
Sheet |
Right/left/top/bottom variants |
| Toast / notification |
Toast (shadcn's first-party Toast, Base UI, 2026-07 — actions, status types, promises, stacking, swipe-dismiss; sonner still fine on Radix) |
ARIA live region, stacking, swipe |
| Form with validation |
Form + react-hook-form adapter |
Zod-validated, inline errors, accessible labels |
| Table with sort/filter |
Table (+ optional tanstack-table recipe) |
ARIA grid, sortable columns, virtualizable |
| Combobox toggle group |
ToggleGroup |
Single/multi select, keyboard |
| Resizable panel layout |
Resizable (react-resizable-panels) |
Saved sizes, accessible, keyboard resize |
| Right-click menu |
ContextMenu |
Submenu, keyboard, ARIA |
| Tooltip |
Tooltip + TooltipProvider |
Delay, position, accessible |
| Slider |
Slider |
Range / single, keyboard, ARIA |
| Progress bar |
Progress |
ARIA progressbar, animations |
| Skeleton loading |
Skeleton |
Pulse animation, theme-aware |
| Avatar + fallback initials |
Avatar + AvatarFallback |
Image error fallback, accessible alt |
| Pagination controls |
Pagination |
Keyboard, ellipsis, ARIA |
The same logic applies to MUI: prefer Drawer, AppBar, Modal, Autocomplete, DatePicker, Tabs, Snackbar, Table, etc. over <div> constructions.
When custom IS appropriate
- The pattern doesn't exist in the library (e.g., a brand-specific WordmarkFooter, a project-specific KpiCard with a particular icon-badge + progress-bar shape).
- The library's primitive is too rigid for what the DESIGN.md describes (rare — usually you compose primitives, not replace them).
- The component is so trivial it'd be 5 lines either way (Eyebrow, simple section wrappers).
In all other cases: default to the primitive. The custom version is technical debt the user has to maintain forever.
Anti-patterns
- ❌ Writing
<aside class="flex flex-col w-64 ..."> when Sidebar exists.
- ❌ Building a hamburger drawer with
useState + <div className="fixed inset-0"> when Sheet exists.
- ❌ Authoring a "command palette" lookalike with
<input> + useEffect when Command exists.
- ❌ Custom date pickers, custom modals, custom tooltips, custom comboboxes — all have shadcn primitives.
How to apply at scaffold time
- Read the DESIGN.md / screenshot to identify the pattern (sidebar, modal, picker, etc).
- Grep
components/ui/ for a matching primitive (grep -l Sidebar components/ui/*.tsx).
- Check the primitive's exports + usage docs at https://ui.shadcn.com (or the primitive's source).
- Compose with the primitive. Style overrides via
className + the design tokens already in globals.css make it brand-faithful without re-implementing the behavior.
When in doubt: open components/ui/<primitive>.tsx and read its API. If the API supports your pattern, use it.
Step 4.4 — Folder convention (mandatory)
Once add --all lands the shadcn primitives, the project has too many "where does this go?" decisions waiting to happen. Pin the convention now, before writing application code, so every subsequent skill (and human) knows where to put things.
The Next.js App Router 2026 convention this skill enforces (canonical spec: docs/superpowers/specs/2026-06-06-folder-structure-refactor.md):
| Path |
What lives here |
app/<route>/_components/ |
L0 page-private: sections unique to ONE page. _ prefix is Next.js privacy marker. Default for every new component. |
app/(group)/_components/ |
L1 route-group shared: components used by 2+ pages within the same route group ((marketing), (auth), (app)). Includes layout shells like AppShell, AppSidebar, AppHeader. |
components/shared/<dominio>/<Component>.tsx |
L2 globally shared: components used by pages of multiple route groups. Domain folder name reflects business (post/, user/, billing/), never generic ("shared"/"common"). |
components/ui/ |
shadcn primitives. Untouched after add --all except for cva variant customization per DESIGN.md components block. |
components/theme/ |
ThemeProvider, ModeToggle, useThemeColor — explicit theme system folder. |
lib/server/<domain>.ts |
Server actions per domain (practices.ts, clients.ts). Always "use server";. |
lib/queries/<domain>.ts |
Server-side data reads called from RSC. |
lib/db/ |
Drizzle (or equivalent) schema + connection — owned by module-add db. |
lib/auth/ |
better-auth client + helpers — owned by module-add auth. |
lib/utils.ts |
Pure utilities (cn(), formatters). |
hooks/ |
Custom React hooks shared cross-route (useDebounce, useMediaQuery). |
Rule of Three for promotion (canonical):
- New component →
app/<route>/_components/ (L0).
- Second use in another page → COPY the file (tolerated duplicate at L0).
- Third use → promote to L1 if same group, L2 if different groups. The
promote-component skill automates this.
Key rules:
- Default L0 always: every new component starts in the page's
_components/. Do not pre-emptively put something in components/shared/.
components/shared/ is L2 only: it holds components used across multiple route groups. Lower-level shared (within a single group) lives in app/(group)/_components/.
- No generic naming: never name a folder under
components/shared/ "shared", "common", "global", "misc". Use the business domain (post/, user/, billing/, auth/).
- No cross-group imports: a page in
(app)/ MUST NOT import from (marketing)/_components/. If it needs to, promote the component to L2 (components/shared/).
- Server actions in lib/:
"use server" files belong in lib/server/<domain>.ts, not under app/.
When screenshot-to-page builds a new route and notices it's reusing a component from another route, it suggests calling promote-component to move it up the hierarchy with automated import rewriting.
When this skill scaffolds the project, the folder skeleton mirrors the canonical structure:
app/
├── (route_groups from meta.json#stack.route_groups)/
│ ├── _components/ # L1 group-shared (created empty)
│ ├── layout.tsx
│ └── (pages with their own _components/ as added)
├── api/
├── layout.tsx # root: HTML + ThemeProvider wrap
└── globals.css # CSS variables + Tailwind directives
components/
├── ui/ # populated by `shadcn add --all`
├── theme/ # ThemeProvider, ModeToggle (scaffolded)
└── shared/ # empty initially; populated by promote-component
lib/
├── server/ # empty initially
├── queries/ # empty initially
└── utils.ts # populated by `shadcn init`
hooks/ # empty initially
Step 4.5 — Generate placeholder routes for declared navigation
If the DESIGN.md, the source Figma screenshots, or the PRD describes a primary navigation (sidebar / topbar / nav menu) with N items, every navigable item must resolve to a real route in the scaffold. A nav that points to <Link href="/clienti"> and that link goes to /_not-found is a worse first impression than no nav at all — it makes the user think the app is broken.
This step is mandatory in dev-flow mode when a navigation is detected. It is independent of screenshot-to-page: that skill builds one rich page from a single screenshot. This step builds stub pages for the remaining nav items so nothing 404s.
Detection
Look for navigation declarations in this order:
- The first screenshot in
.workflow/screenshots/ (typically the dashboard / home) usually shows the primary nav. Identify the items visually.
- The DESIGN.md
## Components section, if it documents nav items.
- The PRD's user stories, which often imply navigation (e.g., "as a user I want to see /clienti, /pratiche, /scadenze").
- If unclear, ask the user once: "Quali voci di navigazione iniziali devo creare come placeholder? (es. /clienti, /pratiche, /scadenze, /impostazioni)".
What to write
For each nav item that is not the primary screen (which screenshot-to-page will build properly), generate a stub at the canonical framework path:
- Next App Router:
<project-root>/app/<slug>/page.tsx
- Vite/Remix/Astro: equivalent path per
references/<framework>-<ui>.md
The stub:
- Renders inside the same
<AppShell> (or layout component) used by the home page, so the sidebar + topbar are consistent.
- Sets
active="/<slug>" so the sidebar item highlights correctly.
- Shows a "empty-state" card: an icon (relevant to the section), the route name as title, a 1-sentence description of what the page will eventually do, and a CTA button (no-op for now).
- Optionally: a "Task pianificati" list inside the empty state, pulled from
tasks.md for that user story (e.g., for /clienti, list the 3 tasks tagged with the relevant user story). This makes the placeholder useful — the user sees what's coming.
- Does not include real data, real forms, or any business logic.
- The icon here is a UI icon (
stack.icon_library), not an illustration. A hand-drawn illustration is a stylistic commitment — only if DESIGN.md's visual language admits it, only at emotional moments (first-run empty state, onboarding, 404), and only a handful per product. See references/illustrations.md (Koboyo, stack.illustrations — default null); when in doubt, type and spacing beat borrowed art.
Reusable placeholder component
Generate a single <PlaceholderPage> component that all stubs use, so:
- The visual consistency is automatic.
- Removing a stub later (when the real page lands) is replacing one file, not refactoring.
- The component itself becomes a shadcn-themed example that proves the design system works on a non-trivial layout.
What this step is NOT
- Not feature implementation. Stubs are visual scaffolding. Real CRUD lives in tasks owned by the user (or by
screenshot-to-page when a screenshot becomes available).
- Not a substitute for
screenshot-to-page. When the user has a screenshot for /clienti, screenshot-to-page replaces the stub with the real page.
- Not for "every imaginable route". Only for items declared in nav. A page like
/admin/users/new is too deep — it's a feature task, not a top-level nav stub.
After writing stubs: rerun pnpm run build to confirm everything still compiles.
Step 4.5b — Read .workflow/screenshots/ before authoring the home page (mandatory)
The single most common scaffolder failure mode: generating a generic home page when the source Figma file has a canonical product layout in screenshots/. The user expects the scaffold to mirror what's in the Figma, not invent a homepage from design tokens alone.
This step pins the discipline.
The protocol
List .workflow/screenshots/. If empty, skip — generate the default home from design tokens (the Constellation marketplace pattern).
For each screenshot, classify it by file name + visual content (you can Read each PNG — the Read tool returns image content):
cover / welcome / intro → marketing/onboarding frames, not the canonical product layout
style-guide / design-system → reference for the /showcase page, not the home
inspiration / dashboard / home / app / product → CANONICAL product layout — this is what the home page must mirror
components / cards / <component-name> → component-level references, can be borrowed for cards inside other pages
If a canonical layout exists, READ it visually with the Read tool. Then identify:
- Layout pattern: sidebar + main? topbar only? full-width hero? split-pane?
- Information density: how many cards/widgets per row? what kind?
- Specific components: the exact KPI labels, the exact chart types, the exact statuses, the exact navigation items
- Interactive surfaces: search input position, profile avatar, primary CTA, mode toggle
Build the home page faithful to that screenshot. Use the exact metric names ("Total Orders Today" not "Revenue"), the exact chart types (radar + area + pie + bar + heatmap, not generic bars), the exact sidebar pattern (vertical 64-72px wide if that's what the screenshot shows). The design tokens are how you style it; the Figma frame is how you compose it.
State the source explicitly in the hand-off message: "Home page mirrors the inspiration-dark-dashboard.png frame from Figma — sidebar + 4 KPI cards (Orders/Conversion/Clients/Revenue Ratio) + 5 chart types (radar, area, pie, bar, heatmap)."
Why this is mandatory
When the user provides a Figma URL, they expect the scaffold to look like the Figma. A generic dashboard "themed with the design tokens" misses the point — the design system isn't a paint job, it's a layout vocabulary too. The icons in KPI cards, the progress bars, the way the radar chart relates to the area chart — these aren't decoration. They're the design.
When to fall back to a generic home
.workflow/screenshots/ is empty AND no PROJECT.md describes a specific product (rare).
- The screenshots are all marketing / cover / intro frames with no canonical product layout.
- The user explicitly says "ignore the Figma frames, build a generic dashboard for now".
In these cases, default to the generic pattern but state it in the hand-off: "No canonical product layout found in screenshots/ — generated a generic home with the design tokens. Run screenshot-to-page later when you have a target frame."
Anti-patterns
- ❌ Generating a generic 4-card KPI grid when the Figma shows specific KPI cards with icon badges + progress bars.
- ❌ Using a horizontal pill nav when the Figma shows a vertical icon sidebar.
- ❌ Inventing chart types (the user got "bars" when the Figma had radar + area + pie + bar + heatmap).
- ❌ Skipping the
Read step on the canonical PNG and writing the layout from imagination.
Step 4.5c — Verbatim copy from screenshots, never invent (mandatory)
A separate failure mode from Step 4.5b: the skill reads the canonical
screenshot, identifies a layout pattern correctly, and then fills in
the layout with invented copy — plausible-sounding placeholder text
that sounds like what the project might say but didn't actually appear
in the Figma. The result: a scaffold that visually looks right but
ships marketing copy the user never wrote and didn't approve.
This step pins the rule.
The rule
When a screenshot shows visible copy, transcribe it verbatim. Never
substitute "plausible-feeling" placeholder text. Specifically:
Display headlines, eyebrows, button labels, nav items — these are
typically large enough to read directly from the rendered PNG. Copy
them character-for-character. Don't reword. Don't add punctuation
the source didn't have. Don't translate.
Body copy in cards / sections — often smaller and harder to read.
Crop the PNG at full source resolution (no downscale) for the
relevant region and re-read. If still illegible, mark as
<TBD — body copy in Figma too small to extract verbatim. Replace before launch.>
in the generated code, NOT plausible filler.
Numbered lists / step descriptions — same treatment as body copy.
Headings are usually readable; step body often isn't. Copy what you
can read; mark the rest TBD.
Footer columns / contact info / hours — these are the most often
verbatim-extractable details (large monospace, tabular). Always copy
verbatim, including formatting (e.g., MON – FRI vs Mon–Fri is a
verbatim concern; don't normalize).
Pages NOT in the Figma at all — the scaffold may need a sign-in,
contact, legal, or detail route the source design doesn't cover.
These are unavoidable for shipping but the content inside them is
100% invented. Add a <TbdBanner> component at the top of these
pages explaining "Placeholder content — not from Figma source."
The banner should be visually warning-styled (yellow / orange / brand
alert color) so the user can't miss that the content is invented.
When to crop higher resolution
If body copy is unreadable at the typical 1500-wide preview crop:
# Crop the same region from the SOURCE cover.png (no downscale)
img = Image.open('.workflow/screenshots/cover.png')
section = img.crop((x0, y0, x1, y1)) # Source-resolution coords
section.save('.workflow/screenshots/_<region>.png')
Then Read the new crop. Body text that's illegible at 1× becomes
legible at the source resolution; tracking + leading details that
matter typographically also become extractable.
How invented copy poisons the project
The user trusts the scaffold. They open the running site, see a
"FOR THE COMMITTED" card with a "Train like an athlete with top-tier
equipment and expert programming. Whether you're building muscle or
breaking PRs, we help you push past limits with structured cycles
and a coach who actually knows your name." body — and they think
that's what the brand says. They don't compare back to the Figma
because they trust you did. So the invented copy ships, gets passed
to their copywriter as "the existing copy", gets edited around, and
never gets corrected. That's the harm.
Anti-patterns
- ❌ Reading "FOR THE COMMITTED" body in Figma and writing
"Train like an athlete with top-tier equipment AND expert programming."
when the source said "Train like an athlete WITH top-tier equipment"
(subtle but verbatim matters).
- ❌ Filling in numbered list bodies with plausib
…(truncated)
1---2name: design-md-to-app3description: Generate or customize a frontend app from a DESIGN.md (Google design.md spec): reads its tokens and produces a working React app pre-styled to match. Next.js 16 + App Router only; pre-16 refused. Four UI libraries — shadcn/ui, Base UI, MUI, Coss/UI (via `coss-ui`) — and TanStack Form + Zod (default) or react-hook-form. Bumps phase to `scaffolded`. Use when the user has a DESIGN.md and wants to scaffold an app, set up a theme, or customize shadcn / Base UI / MUI from its tokens: "use this DESIGN.md to start an app", "DESIGN.md → app", "scaffold from design.md", "applica il DESIGN.md a shadcn / Base UI / MUI", "crea l'app dal DESIGN.md", "init shadcn con questo DESIGN.md". Not for: mobile apps (use rn-bootstrap), writing PRDs (use prd-from-idea), or generating pages after the scaffold exists (use screenshot-to-page).4---56## Dev-flow contract78This skill participates in the **dev-flow** workflow. When invoked on a project that has a `.workflow/` folder at its root:910- **Input** is `<root>/.workflow/DESIGN.md` (mandatory) and `<root>/.workflow/meta.json#stack` (preferred — if `framework` and `ui` are set, use them; if not, ask the user once and persist).11- **Output** is the codebase scaffold at `<project-root>/`, **alongside** the existing `.workflow/` directory (NOT nested inside it). The framework's standard layout — `package.json`, `app/`, `components/`, `lib/`, etc. — sits at the top so `cd <project-root> && pnpm dev` works the way every developer expects.12- **State** is updated by setting `meta.json#phase = "scaffolded"`, refreshing `updated_at`, and appending an entry to `meta.json#history` describing the run (skill name, inputs, outputs, phase delta).13- **Standalone mode** (no `.workflow/` present) is still supported — fall back to the original "ask the user where to scaffold" behavior. The contract is opt-in.1415The canonical contract spec is in `references/contracts.md`. Read it if any of the rules above are unclear.1617# DESIGN.md → App1819Take a `DESIGN.md` (Google design.md spec — see `references/spec.md`) and produce a working React app where shadcn/ui or MUI components are already themed and ready to compose into the actual product. The user picks the library; this skill turns design tokens into theme code, customizes component variants according to the `components` block, and leaves the user with a runnable scaffold plus a `/showcase` page they can visually verify.2021## When this skill applies2223Trigger on any of:24- A `DESIGN.md` (or `design.md`) file path or content provided by the user.25- Explicit requests: "fai partire l'app dal DESIGN.md", "scaffold from design.md", "customize shadcn/MUI from this DESIGN.md", "apply these tokens to shadcn/MUI".26- A user describing wanting to start a project where the design system is already specified in `DESIGN.md` form.2728If the user has a `DESIGN.md` but only mentions shadcn/MUI without referencing the file, still trigger — they likely want their tokens applied.2930## What you produce3132A scaffolded (or augmented) **React + TypeScript** project where:33341. The chosen UI library (shadcn/ui or MUI) is installed and configured.352. **Design tokens** from the YAML frontmatter are wired into the theme:36 - `colors` → CSS variables (shadcn) or `palette` (MUI). **Always two modes**: dark + light, with the source DESIGN.md driving the canonical mode and the other auto-derived (see §Dark + light below).37 - `typography` → typography scale (Tailwind theme extension or `theme.typography`).38 - `rounded` → radius scale (`borderRadius` extension or `theme.shape`).39 - `spacing` → spacing scale (Tailwind extension or `theme.spacing` overrides).403. **Components** from the `components` block are reflected in:41 - shadcn: variant overrides on the corresponding component (e.g. `button-primary` → `Button` `default` variant in `components/ui/button.tsx`). All shadcn primitives are installed (`shadcn add --all`) so the app comes out of the box ready to compose.42 - MUI: `theme.components.MuiXxx.styleOverrides` and/or `defaultProps`.434. A `/showcase` page renders every styled primitive so the user can verify the result.445. Fonts referenced in `typography.fontFamily` are loaded — via `next/font/google` when on Next, via direct `<link>` for Vite/Remix when the font is on Google Fonts, otherwise self-hosted from a local `public/fonts/` (see §Font loading).456. A `_design-md-mapping.json` is written at the project root showing exactly how each DESIGN.md token resolved to library values — useful for debugging and for the user to verify the mapping at a glance.467. The markdown body's qualitative rules (gradients, glow, glassmorphism, "do's and don'ts") are encoded as utility classes / theme effects where they translate to CSS, and otherwise summarized in a `STYLE_NOTES.md` so the user — and future agents — can apply them consistently.4748**Never invent the spec.** When in doubt about a token shape, valid units, references like `{colors.primary}`, or canonical section ordering, read `references/spec.md` first.4950## Workflow5152### Step 1 — Locate and parse the DESIGN.md5354- **Preferred (dev-flow mode):** if a `.workflow/meta.json` exists at the project root, the DESIGN.md is at `.workflow/DESIGN.md`. Don't search further.55- **Fallback:** if the user gave a file path, use it. Otherwise look for `DESIGN.md` in the project root (case-insensitive). If multiple are found, ask which one.56- Run `python scripts/parse_design_md.py <path>` to get a normalized JSON dump of `{ frontmatter, body_sections, resolved_components }` where token references like `{colors.primary}` are resolved to literal values.57- If parsing fails (malformed YAML, duplicate sections), surface the error and stop. The user must fix the source.5859**A third shape exists, and it is not an input.** `design.md` now names two unrelated60artefacts. Ours is the Google spec below: token blocks in the frontmatter. The other is an61**Agent Skill** — frontmatter of exactly `name` + `description`, body in prose, zero tokens.62[`https://vercel.com/design.md`](https://vercel.com/design.md) is one: Vercel's brand guidance63for agents writing report pages, served as `text/markdown` so any agent can load it.6465Fed one of those, the parser used to exit `0` with empty tokens — which shape 2 below reads as66"body-only, extract from prose", and every value in the app ends up invented. It now **exits `2`67and says so**. If a user hands you a URL or a file that turns out to be a skill, the answer is not68to parse it: it carries no design system.6970**Two valid input shapes — handle both:**71721. **Frontmatter + body** (the canonical Google design.md spec). The parser returns a populated `frontmatter` dict; `resolved_components` is non-empty. This is the easy path — token values are already structured for you.73742. **Body-only / prose-only** (no `---` fences, the spec written entirely as markdown). The parser returns `frontmatter: {}` and everything goes into `body_sections`. This is now common in the wild — many DESIGN.md files written for AI agents are pure prose with section headings like `## 2. Color Palette`, `## 3. Typography Rules`, etc.7576 When `frontmatter` is empty, **don't stop and ask for YAML**. Extract tokens directly from the body sections. Map sections to token categories by their heading (case-insensitive partial match):77 - "Color" / "Palette" / "Colors" → colors78 - "Typography" / "Type" / "Fonts" → typography levels + font families79 - "Component" / "Components" / "Cards" / "Buttons" → component variants80 - "Layout" / "Spacing" / "Grid" → spacing scale, container widths81 - "Radius" / "Shapes" / "Border" → radius scale82 - "Depth" / "Elevation" / "Shadow" → shadow utilities83 - "Do's and Don'ts" / "Notes" / "Known Gaps" → STYLE_NOTES.md content + mode/font opt-outs8485 Read each section as the source of truth for its category — extract hex values from prose, copy typography tables, lift component prose specs verbatim. Document this extraction path explicitly in `_design-md-mapping.json` under a top-level `extraction_method` field so a reviewer can see what came from prose inference vs. from a structured field.8687### Step 2 — Pick the library8889Ask the user once: **shadcn/ui, Base UI, MUI, or Coss/UI?** Offer a recommendation based on `references/library-choice.md`. Common heuristics:9091- Highly custom visual identity (glassmorphism, brutalist, editorial, distinctive shapes) + user wants to edit source → **shadcn**.92- Same custom-Tailwind philosophy as shadcn but the user prefers a library they can `pnpm update` (no CLI / `components.json` overhead) → **Base UI**.93- Material-leaning, dashboard, enterprise CRUD, lots of data tables / dialogs out of the box → **MUI**.94- Tailwind already in use, or the user wants utility-first → **shadcn** or **Base UI** (toss-up; Base UI for less source-maintenance, shadcn for max control).95- Heavy Material Icons / Material Design heritage → **MUI**.96- Best accessibility track record without Material visuals → **Base UI** (same MUI team, headless).9798**If they pick shadcn, ask the follow-up: which primitive base — Base UI, Radix, or React Aria?** (`shadcn create --base base|radix|aria`). shadcn CLI v4 builds on any of them, with the same component API and blocks across variants. **Default Base UI** (shadcn's default for new projects since 2026-07; full component + block coverage, MUI-team a11y). Pick **Radix** (the long-standing base, still fully supported) or **React Aria** (`--base aria`, Adobe's a11y-first primitives, first-class since 2026-07) explicitly. Record as `stack.ui_base`. This is distinct from picking standalone Base UI (`stack.ui = "base-ui"`, no shadcn CLI) — see `references/library-choice.md`. **Hybrid asking** (per dev-flow): also ask `icon_library` (lucide default), and `rtl` only when the project is multilingual/RTL. **Don't** ask base color / theme — the DESIGN.md tokens own the visual layer; `css_variables` stays `true`.99100- Wants the **Cal.com design system** / an AI-first, MCP-friendly copy-paste kit on Base UI, and Tailwind v4 is acceptable → **Coss/UI** (`stack.ui = "coss"`) — hand off to the `coss-ui` skill.101102Mapping skill choice → `meta.json#stack.ui` → `references/<lib>-mapping.md`:103- "shadcn" → `references/shadcn-mapping.md` (+ `stack.ui_base` = base|radix|aria)104- "base-ui" → `references/base-ui-mapping.md` (standalone Base UI, no shadcn CLI)105- "mui" → `references/mui-mapping.md`106- "coss" → **hand off to `coss-ui/SKILL.md`** (Coss/UI — Cal.com DS on Base UI via the shadcn `@coss/*` registry; `ui_base = "base"`; requires **Tailwind v4**; mixed **MIT/AGPLv3** license). Coss's tokens are shadcn CSS vars, so DESIGN.md overrides apply as in the shadcn path.107108State the suggestion with a one-line rationale ("Suggerisco Base UI perché vuoi l'aesthetics flessibile di shadcn ma senza la source-maintenance del CLI"), then accept whatever the user picks. After picking, load the matching `<lib>-mapping.md` and follow it for installation, theming, and component wiring.109110### Step 3 — Pick the project target, framework, and scope111112Ask the user **two** things in this step:113114**3a. Project target**115116- **Dev-flow mode** (`.workflow/meta.json` exists): scaffold the codebase **at `<project-root>/` alongside `.workflow/`** (e.g., `pnpm create next-app .` from the project root). Do NOT create a sub-directory like `app/` or `code/` to nest the codebase. The framework comes from `meta.json#stack.framework` if set, otherwise ask. The user does **not** pick the path — the contract pins it.117- **New project (standalone)** → ask which framework: **Next.js (App Router)** [default], **Vite + React**, or **Remix**. Then scaffold in a folder of the user's choice with TS + the chosen UI library wired in (see the relevant mapping reference for exact init commands).118- **Existing project?** → ask for the project root, then detect the framework by inspecting `package.json` and the file layout:119 - Next.js → `next` in deps + `app/` or `pages/`.120 - Vite + React → `vite` + `@vitejs/plugin-react` in deps.121 - Remix → `@remix-run/*` in deps.122 - Anything else → tell the user what you found and ask how to proceed (most often: "write the theme files anyway and I'll wire them up").123124The mapping references describe the framework-specific glue (font loading, root layout/provider wiring, where to put `theme.ts` / `globals.css`). Don't assume Next.js paths — branch on the detected framework.125126**3b. Scope: full scaffold vs. theme-only**127128Ask: **"Vuoi lo scaffold completo (showcase + STYLE_NOTES + provider wiring) o solo le patch al tema?"** Two modes:129130- **Full scaffold** (default for new projects): everything described in §What you produce — theme files + component overrides + `/showcase` route + `STYLE_NOTES.md` + `_design-md-mapping.json` + provider wiring. **Golden rule 2 — wire i18n now** (follow the doc-grounded how-to in `references/i18n-next-intl.md`, don't improvise the setup): install **next-intl** (`stack.i18n`), scaffold `messages/en.json` + `messages/it.json` (minimum `stack.locales = ["en","it"]`, default `en`), the `[locale]` routing (`routing.ts`/`navigation.ts`/`proxy.ts`/`request.ts`) with `setRequestLocale` + `generateStaticParams`, and `<NextIntlClientProvider>` in the root layout. All scaffolded copy uses `useTranslations` keys — never hardcoded strings (the `forms` skill already assumes this). Adding i18n later touches every page, so it's part of the initial scaffold, not deferred.131- **Theme-only patch** (recommended for mature existing projects): write *only* the design-token files as a reviewable diff:132 - shadcn: `globals.css` (Tailwind v4 is CSS-first — tokens live in `@theme` inside `globals.css`; only touch `tailwind.config.ts` if the project still uses a v3 JS config), the `cva` blocks of components named in DESIGN.md.133 - MUI: `lib/theme.ts` only (or wherever the theme already lives).134 - **Plus** `_design-md-mapping.json` and `STYLE_NOTES.md` because they're cheap to add and useful for the reviewer.135136 In theme-only mode, **don't** create `/showcase`, **don't** install new dependencies, **don't** touch the root layout/provider unless the user explicitly asks. The goal is a diff a senior reviewer can read in five minutes.137138Default the choice based on context: existing project with extensive code → suggest theme-only; new project or empty repo → full scaffold. State the suggestion in one line, accept whatever the user picks.139140If overwriting any existing theme/config (`globals.css`, `tailwind.config.*`, `theme.ts`), show a diff of what will change and confirm before writing.141142### Step 4 — Apply the chosen mapping143144**Confirmation gate (BLOCKING — do this before scaffolding).** Before running any145`shadcn create` / install command, print a recap of the **full resolved configuration** and146**wait for the user to confirm or adjust**. Do not scaffold until they say go. Show every147value that will be passed to the CLI, including the ones derived from DESIGN.md (so the user148sees them even though they weren't prompted):149150```151Sto per scaffoldare con shadcn create. Configurazione:152 • framework : next (App Router) ← stack.framework153 • base (primitivi): base (Base UI) ← stack.ui_base154 • base color : neutral ← stack.base_color (poi sovrascritto dai token DESIGN.md)155 • theme : (dai token DESIGN.md) ← stack.ui_theme156 • icone : lucide ← stack.icon_library157 • css variables : on ← stack.css_variables158 • rtl : no ← stack.rtl159 • monorepo : no ← stack.framework160Confermi, o vuoi cambiare qualcosa?161```162163Resolve each value from `meta.json#stack` (with the documented defaults). On "cambia X",164update `meta.json#stack` and re-print the recap. Only after explicit confirmation, proceed.165For MUI / standalone Base UI, print the equivalent lighter recap (library, framework, base166color source) and confirm the same way. **Never scaffold on assumed config.** When167`stack.shadcn_preset` is set, run `pnpm dlx shadcn@latest preset decode <code>` and show the168decoded config in the recap (so the user confirms what the preset carries).169170Read the relevant mapping reference and follow it:171172- Any conversational surface or rendered markdown → `references/chat-and-typeset.md` (shadcn chat components + typeset + streamdown, the standard — never hand-roll chat or render model markdown as plain text).173- shadcn → `references/shadcn-mapping.md`. **Monorepo first:** if `meta.json#stack.framework == "monorepo"` and the monorepo is web-centric (no NativeWind/mobile side — e.g. web-only or web + agent), follow that reference's **"Monorepo (shared `packages/ui`)"** section: scaffold with `shadcn init --monorepo` so primitives land in a shared `packages/ui` (`@workspace/ui`), NOT in `apps/web/components/ui/`. The single-app flow below (and `--no-monorepo`) is for non-monorepo projects (or the web+mobile case where components stay app-local). **Two visual-config paths — they are mutually exclusive:**174 - **A) Preset path** (when `stack.shadcn_preset` is set): the preset owns the visual layer. Scaffold with `pnpm dlx shadcn@latest init --preset ${stack.shadcn_preset} --template ${framework} --base ${stack.ui_base ?? "base"} --yes`, then `pnpm dlx shadcn@latest add --all --yes`. **Skip `build_registry.py` / the `registry.json` token install** — the preset already encodes colors/theme/fonts/icons/radius. (Helpers: `preset decode` to inspect, `preset url`/`preset open` to view in browser.)175 - **B) DESIGN.md-first path** (default, no preset): the recommended **token-first install via `registry.json`** (see the dedicated section in that reference). Three steps: scaffold framework → emit `registry.json` from DESIGN.md tokens (use `scripts/build_registry.py`) → run `pnpm dlx shadcn@latest init ./registry.json --yes` followed by `pnpm dlx shadcn@latest add --all --yes`. **`add --all` stays** — every primitive lands in `components/ui/*` and gets customized in the next step per the DESIGN.md `components` block (`cva` edits).176 - **Pass the create parameters from `meta.json#stack`** (shadcn CLI v4): `--base ${stack.ui_base ?? "base"}` (Base UI / Radix / React Aria primitives), `--base-color ${stack.base_color ?? "neutral"}`, `${stack.css_variables === false ? "--no-css-variables" : "--css-variables"}`, and `--rtl` when `stack.rtl`. The DESIGN.md tokens (via `registry.json`) override base color / theme / fonts, so those flags are only the starting scaffold. **`--base` is NOT overridden by DESIGN.md** — it picks the primitive engine, so honor `stack.ui_base` exactly. If `stack.ui_base` is unset, ask before scaffolding (don't silently pick a base on a fresh project).177 - Every shadcn block/component exists in both Radix and Base UI variants; once `--base` is set, `shadcn add` pulls the matching variant automatically.178- MUI → `references/mui-mapping.md`179180Each reference describes:181- the exact files to write per framework (Next.js / Vite / Remix),182- how each token category maps,183- which components to install/override (shadcn: `add --all` so the user has the full kit pre-themed; MUI: theme overrides cover all primitives by default),184- how to encode the qualitative rules from the markdown body that **can** be expressed in code (e.g. radial background gradients, glass surfaces with backdrop-filter, ambient glow as a custom utility),185- what to put in `STYLE_NOTES.md` for the rest (e.g. "Use radial gradients for hero backgrounds — see DESIGN.md §Layout").186187The asset templates in `assets/shadcn/` and `assets/mui/` are starting points — read them, fill the placeholders with resolved tokens, write to disk. Do not paste them verbatim if the DESIGN.md doesn't define a value; fall back to library defaults rather than invent.188189After writing the theme files, also write `_design-md-mapping.json` at the project root. This is a debug artifact: a JSON dump of `{ token_path → resolved_value → library_target }` for every color/typography/component that was mapped, plus the list of fallbacks that were used because the DESIGN.md didn't define a value. Add `_design-md-mapping.json` to `.gitignore` if the user wants to keep it local-only — by default leave it tracked since it's useful for reviewers.190191### Step 4.3 — Library primitive priority (mandatory, supersedes custom components)192193**Rule**: when the chosen UI library (shadcn or MUI) ships a primitive for the pattern, **use it**. Don't roll custom.194195This is the single most-violated rule in scaffolders. The skill installs `shadcn add --all` (or MUI's full theme), giving the project access to dozens of pre-built, accessible, mobile-aware, theme-integrated primitives — and then the skill writes a custom 100-line `<Sidebar>` from scratch using `<aside>` + flex + lucide icons. The custom version is worse on every axis: less accessible, no mobile drawer, no collapsed state, no tooltip support, no keyboard shortcuts, no persistent state.196197#### The mandate198199Before authoring **any** component beyond the simplest (Eyebrow, simple text wrappers), **scan `components/ui/*.tsx`** for a primitive that matches the pattern. Common shadcn primitives projects miss:200201| Pattern in DESIGN.md / Figma | shadcn primitive | What you get for free |202|---|---|---|203| Vertical app sidebar (icon-only or expanded) | `Sidebar` + `SidebarProvider` + `SidebarMenu*` | Collapsible state (icon ↔ expanded), mobile drawer via Sheet, tooltips on hover when collapsed, `Cmd+B` toggle, persistent state in cookies, active-route detection via context |204| Top navigation menu with hover dropdowns | `NavigationMenu` | Keyboard navigation, ARIA menu, hover delays |205| Dialog / modal | `Dialog` + `AlertDialog` | Focus trap, Escape close, scroll lock, portal |206| Hamburger drawer (mobile nav) | `Sheet` | Slide-in animation, focus management, swipe close |207| Autocomplete / type-ahead | `Combobox` (Command + Popover) | Async search, keyboard nav, fuzzy match |208| Command palette / cmd+k | `Command` (CMDK) | Categorized search, keyboard, animations |209| **Chat / conversation / AI console / support inbox** | `MessageScroller` + `Message` + `Bubble` + `Marker` (shadcn chat, Jun 2026) | Autoscroll that yields to the user, scroll-to-bottom button, edge scroll-fade, virtualization — see `references/chat-and-typeset.md` |210| **Rendered markdown / AI output / rich description** | `.typeset` (shadcn/typeset, Jul 2026) + `streamdown` | Consistent typography on rendered markdown; `whitespace-pre-wrap` on model output leaves `**bold**` literal — see `references/chat-and-typeset.md` |211| **Map / location / route / store-locator** | **mapcn** (MapLibre GL, shadcn registry) — `Map` + Markers/Popups/Routes/Clusters/Controls | Theme-aware, Tailwind-styled, declarative composition; ⚠️ default CARTO tiles need an Enterprise license for **commercial** use — swap the tile provider before shipping. See `references/maps-mapcn.md`; record `stack.maps = "mapcn"` |212| Date range picker | `Calendar` + `Popover` | Locale-aware, keyboard nav, range selection |213| Tabs (route-driven or panel) | `Tabs` | ARIA roles, keyboard nav, animations |214| Sheet / slide-out panel | `Sheet` | Right/left/top/bottom variants |215| Toast / notification | `Toast` (shadcn's first-party Toast, Base UI, 2026-07 — actions, status types, promises, stacking, swipe-dismiss; `sonner` still fine on Radix) | ARIA live region, stacking, swipe |216| Form with validation | `Form` + `react-hook-form` adapter | Zod-validated, inline errors, accessible labels |217| Table with sort/filter | `Table` (+ optional `tanstack-table` recipe) | ARIA grid, sortable columns, virtualizable |218| Combobox toggle group | `ToggleGroup` | Single/multi select, keyboard |219| Resizable panel layout | `Resizable` (react-resizable-panels) | Saved sizes, accessible, keyboard resize |220| Right-click menu | `ContextMenu` | Submenu, keyboard, ARIA |221| Tooltip | `Tooltip` + `TooltipProvider` | Delay, position, accessible |222| Slider | `Slider` | Range / single, keyboard, ARIA |223| Progress bar | `Progress` | ARIA progressbar, animations |224| Skeleton loading | `Skeleton` | Pulse animation, theme-aware |225| Avatar + fallback initials | `Avatar` + `AvatarFallback` | Image error fallback, accessible alt |226| Pagination controls | `Pagination` | Keyboard, ellipsis, ARIA |227228The same logic applies to MUI: prefer `Drawer`, `AppBar`, `Modal`, `Autocomplete`, `DatePicker`, `Tabs`, `Snackbar`, `Table`, etc. over `<div>` constructions.229230#### When custom IS appropriate231232- The pattern doesn't exist in the library (e.g., a brand-specific WordmarkFooter, a project-specific KpiCard with a particular icon-badge + progress-bar shape).233- The library's primitive is too rigid for what the DESIGN.md describes (rare — usually you compose primitives, not replace them).234- The component is so trivial it'd be 5 lines either way (Eyebrow, simple section wrappers).235236In all other cases: **default to the primitive**. The custom version is technical debt the user has to maintain forever.237238#### Anti-patterns239240- ❌ Writing `<aside class="flex flex-col w-64 ...">` when `Sidebar` exists.241- ❌ Building a hamburger drawer with `useState` + `<div className="fixed inset-0">` when `Sheet` exists.242- ❌ Authoring a "command palette" lookalike with `<input>` + `useEffect` when `Command` exists.243- ❌ Custom date pickers, custom modals, custom tooltips, custom comboboxes — all have shadcn primitives.244245#### How to apply at scaffold time2462471. Read the DESIGN.md / screenshot to identify the pattern (sidebar, modal, picker, etc).2482. **Grep `components/ui/` for a matching primitive** (`grep -l Sidebar components/ui/*.tsx`).2493. Check the primitive's exports + usage docs at https://ui.shadcn.com (or the primitive's source).2504. Compose with the primitive. Style overrides via `className` + the design tokens already in `globals.css` make it brand-faithful without re-implementing the behavior.251252When in doubt: open `components/ui/<primitive>.tsx` and read its API. If the API supports your pattern, use it.253254### Step 4.4 — Folder convention (mandatory)255256Once `add --all` lands the shadcn primitives, the project has too many "where does this go?" decisions waiting to happen. **Pin the convention now**, before writing application code, so every subsequent skill (and human) knows where to put things.257258The Next.js App Router 2026 convention this skill enforces (canonical spec: `docs/superpowers/specs/2026-06-06-folder-structure-refactor.md`):259260| Path | What lives here |261|---|---|262| `app/<route>/_components/` | **L0 page-private**: sections unique to ONE page. `_` prefix is Next.js privacy marker. Default for every new component. |263| `app/(group)/_components/` | **L1 route-group shared**: components used by 2+ pages within the same route group (`(marketing)`, `(auth)`, `(app)`). Includes layout shells like AppShell, AppSidebar, AppHeader. |264| `components/shared/<dominio>/<Component>.tsx` | **L2 globally shared**: components used by pages of multiple route groups. Domain folder name reflects business (`post/`, `user/`, `billing/`), never generic ("shared"/"common"). |265| `components/ui/` | shadcn primitives. **Untouched** after `add --all` except for `cva` variant customization per DESIGN.md `components` block. |266| `components/theme/` | ThemeProvider, ModeToggle, useThemeColor — explicit theme system folder. |267| `lib/server/<domain>.ts` | Server actions per domain (`practices.ts`, `clients.ts`). Always `"use server";`. |268| `lib/queries/<domain>.ts` | Server-side data reads called from RSC. |269| `lib/db/` | Drizzle (or equivalent) schema + connection — owned by `module-add db`. |270| `lib/auth/` | better-auth client + helpers — owned by `module-add auth`. |271| `lib/utils.ts` | Pure utilities (`cn()`, formatters). |272| `hooks/` | Custom React hooks shared cross-route (useDebounce, useMediaQuery). |273274**Rule of Three for promotion** (canonical):2751. New component → `app/<route>/_components/` (L0).2762. Second use in another page → COPY the file (tolerated duplicate at L0).2773. Third use → promote to L1 if same group, L2 if different groups. The `promote-component` skill automates this.278279**Key rules**:280281- **Default L0 always**: every new component starts in the page's `_components/`. Do not pre-emptively put something in `components/shared/`.282- **`components/shared/` is L2 only**: it holds components used across multiple route groups. Lower-level shared (within a single group) lives in `app/(group)/_components/`.283- **No generic naming**: never name a folder under `components/shared/` "shared", "common", "global", "misc". Use the business domain (post/, user/, billing/, auth/).284- **No cross-group imports**: a page in `(app)/` MUST NOT import from `(marketing)/_components/`. If it needs to, promote the component to L2 (`components/shared/`).285- **Server actions in lib/**: `"use server"` files belong in `lib/server/<domain>.ts`, not under `app/`.286287When `screenshot-to-page` builds a new route and notices it's reusing a component from another route, it suggests calling `promote-component` to move it up the hierarchy with automated import rewriting.288289When this skill scaffolds the project, the folder skeleton mirrors the canonical structure:290291```292app/293├── (route_groups from meta.json#stack.route_groups)/294│ ├── _components/ # L1 group-shared (created empty)295│ ├── layout.tsx296│ └── (pages with their own _components/ as added)297├── api/298├── layout.tsx # root: HTML + ThemeProvider wrap299└── globals.css # CSS variables + Tailwind directives300301components/302├── ui/ # populated by `shadcn add --all`303├── theme/ # ThemeProvider, ModeToggle (scaffolded)304└── shared/ # empty initially; populated by promote-component305306lib/307├── server/ # empty initially308├── queries/ # empty initially309└── utils.ts # populated by `shadcn init`310311hooks/ # empty initially312```313314### Step 4.5 — Generate placeholder routes for declared navigation315316If the DESIGN.md, the source Figma screenshots, or the PRD describes a primary navigation (sidebar / topbar / nav menu) with N items, **every navigable item must resolve to a real route** in the scaffold. A nav that points to `<Link href="/clienti">` and that link goes to `/_not-found` is a worse first impression than no nav at all — it makes the user think the app is broken.317318This step is **mandatory in dev-flow mode** when a navigation is detected. It is independent of `screenshot-to-page`: that skill builds **one** rich page from a single screenshot. This step builds **stub** pages for the remaining nav items so nothing 404s.319320#### Detection321322Look for navigation declarations in this order:3233241. The **first screenshot** in `.workflow/screenshots/` (typically the dashboard / home) usually shows the primary nav. Identify the items visually.3252. The DESIGN.md `## Components` section, if it documents nav items.3263. The PRD's user stories, which often imply navigation (e.g., "as a user I want to see /clienti, /pratiche, /scadenze").3274. If unclear, **ask the user once**: "Quali voci di navigazione iniziali devo creare come placeholder? (es. /clienti, /pratiche, /scadenze, /impostazioni)".328329#### What to write330331For each nav item that is **not** the primary screen (which `screenshot-to-page` will build properly), generate a stub at the canonical framework path:332333- Next App Router: `<project-root>/app/<slug>/page.tsx`334- Vite/Remix/Astro: equivalent path per `references/<framework>-<ui>.md`335336The stub:337338- Renders inside the same `<AppShell>` (or layout component) used by the home page, so the sidebar + topbar are consistent.339- Sets `active="/<slug>"` so the sidebar item highlights correctly.340- Shows a **"empty-state" card**: an icon (relevant to the section), the route name as title, a 1-sentence description of what the page will eventually do, and a CTA button (no-op for now).341- Optionally: a **"Task pianificati"** list inside the empty state, pulled from `tasks.md` for that user story (e.g., for `/clienti`, list the 3 tasks tagged with the relevant user story). This makes the placeholder useful — the user sees what's coming.342- Does **not** include real data, real forms, or any business logic.343- The icon here is a **UI icon** (`stack.icon_library`), not an illustration. A hand-drawn illustration is a *stylistic commitment* — only if DESIGN.md's visual language admits it, only at emotional moments (first-run empty state, onboarding, 404), and only a handful per product. See `references/illustrations.md` (Koboyo, `stack.illustrations` — default `null`); when in doubt, type and spacing beat borrowed art.344345#### Reusable placeholder component346347Generate a single `<PlaceholderPage>` component that all stubs use, so:348- The visual consistency is automatic.349- Removing a stub later (when the real page lands) is replacing one file, not refactoring.350- The component itself becomes a shadcn-themed example that proves the design system works on a non-trivial layout.351352#### What this step is NOT353354- **Not feature implementation.** Stubs are visual scaffolding. Real CRUD lives in tasks owned by the user (or by `screenshot-to-page` when a screenshot becomes available).355- **Not a substitute for `screenshot-to-page`.** When the user has a screenshot for `/clienti`, `screenshot-to-page` replaces the stub with the real page.356- **Not for "every imaginable route".** Only for items declared in nav. A page like `/admin/users/new` is too deep — it's a feature task, not a top-level nav stub.357358After writing stubs: rerun `pnpm run build` to confirm everything still compiles.359360### Step 4.5b — Read `.workflow/screenshots/` before authoring the home page (mandatory)361362The single most common scaffolder failure mode: **generating a generic home page when the source Figma file has a canonical product layout in screenshots/**. The user expects the scaffold to **mirror what's in the Figma**, not invent a homepage from design tokens alone.363364This step pins the discipline.365366#### The protocol3673681. **List `.workflow/screenshots/`.** If empty, skip — generate the default home from design tokens (the Constellation marketplace pattern).3693702. **For each screenshot, classify it by file name + visual content** (you can `Read` each PNG — the Read tool returns image content):371 - `cover` / `welcome` / `intro` → marketing/onboarding frames, not the canonical product layout372 - `style-guide` / `design-system` → reference for the `/showcase` page, not the home373 - `inspiration` / `dashboard` / `home` / `app` / `product` → **CANONICAL product layout** — this is what the home page must mirror374 - `components` / `cards` / `<component-name>` → component-level references, can be borrowed for cards inside other pages3753763. **If a canonical layout exists, READ it visually** with the Read tool. Then identify:377 - **Layout pattern**: sidebar + main? topbar only? full-width hero? split-pane?378 - **Information density**: how many cards/widgets per row? what kind?379 - **Specific components**: the exact KPI labels, the exact chart types, the exact statuses, the exact navigation items380 - **Interactive surfaces**: search input position, profile avatar, primary CTA, mode toggle3813824. **Build the home page faithful to that screenshot.** Use the exact metric names ("Total Orders Today" not "Revenue"), the exact chart types (radar + area + pie + bar + heatmap, not generic bars), the exact sidebar pattern (vertical 64-72px wide if that's what the screenshot shows). The design tokens are how you style it; the Figma frame is how you compose it.3833845. **State the source explicitly** in the hand-off message: "Home page mirrors the `inspiration-dark-dashboard.png` frame from Figma — sidebar + 4 KPI cards (Orders/Conversion/Clients/Revenue Ratio) + 5 chart types (radar, area, pie, bar, heatmap)."385386#### Why this is mandatory387388When the user provides a Figma URL, they expect the scaffold to look like the Figma. A generic dashboard "themed with the design tokens" misses the point — the design system isn't a paint job, it's a layout vocabulary too. The icons in KPI cards, the progress bars, the way the radar chart relates to the area chart — these aren't decoration. They're the design.389390#### When to fall back to a generic home391392- `.workflow/screenshots/` is empty AND no PROJECT.md describes a specific product (rare).393- The screenshots are all marketing / cover / intro frames with no canonical product layout.394- The user explicitly says "ignore the Figma frames, build a generic dashboard for now".395396In these cases, default to the generic pattern but **state it in the hand-off**: "No canonical product layout found in screenshots/ — generated a generic home with the design tokens. Run `screenshot-to-page` later when you have a target frame."397398#### Anti-patterns399400- ❌ Generating a generic 4-card KPI grid when the Figma shows specific KPI cards with icon badges + progress bars.401- ❌ Using a horizontal pill nav when the Figma shows a vertical icon sidebar.402- ❌ Inventing chart types (the user got "bars" when the Figma had radar + area + pie + bar + heatmap).403- ❌ Skipping the `Read` step on the canonical PNG and writing the layout from imagination.404405### Step 4.5c — Verbatim copy from screenshots, never invent (mandatory)406407A separate failure mode from Step 4.5b: the skill reads the canonical408screenshot, identifies a layout pattern correctly, and then **fills in409the layout with invented copy** — plausible-sounding placeholder text410that sounds like what the project might say but didn't actually appear411in the Figma. The result: a scaffold that visually looks right but412ships marketing copy the user never wrote and didn't approve.413414This step pins the rule.415416#### The rule417418When a screenshot shows visible copy, **transcribe it verbatim**. Never419substitute "plausible-feeling" placeholder text. Specifically:4204211. **Display headlines, eyebrows, button labels, nav items** — these are422 typically large enough to read directly from the rendered PNG. Copy423 them character-for-character. Don't reword. Don't add punctuation424 the source didn't have. Don't translate.4254262. **Body copy in cards / sections** — often smaller and harder to read.427 Crop the PNG at full source resolution (no downscale) for the428 relevant region and re-read. If still illegible, mark as429 `<TBD — body copy in Figma too small to extract verbatim. Replace before launch.>`430 in the generated code, NOT plausible filler.4314323. **Numbered lists / step descriptions** — same treatment as body copy.433 Headings are usually readable; step body often isn't. Copy what you434 can read; mark the rest TBD.4354364. **Footer columns / contact info / hours** — these are the most often437 verbatim-extractable details (large monospace, tabular). Always copy438 verbatim, including formatting (e.g., `MON – FRI` vs `Mon–Fri` is a439 verbatim concern; don't normalize).4404415. **Pages NOT in the Figma at all** — the scaffold may need a sign-in,442 contact, legal, or detail route the source design doesn't cover.443 These are unavoidable for shipping but the content inside them is444 100% invented. Add a `<TbdBanner>` component at the top of these445 pages explaining "Placeholder content — not from Figma source."446 The banner should be visually warning-styled (yellow / orange / brand447 alert color) so the user can't miss that the content is invented.448449#### When to crop higher resolution450451If body copy is unreadable at the typical 1500-wide preview crop:452453```python454# Crop the same region from the SOURCE cover.png (no downscale)455img = Image.open('.workflow/screenshots/cover.png')456section = img.crop((x0, y0, x1, y1)) # Source-resolution coords457section.save('.workflow/screenshots/_<region>.png')458```459460Then `Read` the new crop. Body text that's illegible at 1× becomes461legible at the source resolution; tracking + leading details that462matter typographically also become extractable.463464#### How invented copy poisons the project465466The user trusts the scaffold. They open the running site, see a467"FOR THE COMMITTED" card with a "Train like an athlete with top-tier468equipment and expert programming. Whether you're building muscle or469breaking PRs, we help you push past limits with structured cycles470and a coach who actually knows your name." body — and they think471that's what the brand says. They don't compare back to the Figma472because they trust you did. So the invented copy ships, gets passed473to their copywriter as "the existing copy", gets edited around, and474never gets corrected. That's the harm.475476#### Anti-patterns477478- ❌ Reading "FOR THE COMMITTED" body in Figma and writing479 "Train like an athlete with top-tier equipment AND expert programming."480 when the source said "Train like an athlete WITH top-tier equipment"481 (subtle but verbatim matters).482- ❌ Filling in numbered list bodies with plausib483484…(truncated)