# Design Md To App

> 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).

- Skill: `lukedj78/design-md-to-app` (Agent Skill, multi-file: 33 files)
- Install (CLI): `npx skillmds@latest add lukedj78/design-md-to-app`
- Raw SKILL.md: https://api.skillmd.com/api/skills/lukedj78/design-md-to-app/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: lukedj78 (https://skillmd.com/u/lukedj78)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/lukedj78/design-md-to-app

---


## 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:

1. The chosen UI library (shadcn/ui or MUI) is installed and configured.
2. **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).
3. **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`.
4. A `/showcase` page renders every styled primitive so the user can verify the result.
5. 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).
6. 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.
7. 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`](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:**

1. **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.

2. **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

1. Read the DESIGN.md / screenshot to identify the pattern (sidebar, modal, picker, etc).
2. **Grep `components/ui/` for a matching primitive** (`grep -l Sidebar components/ui/*.tsx`).
3. Check the primitive's exports + usage docs at https://ui.shadcn.com (or the primitive's source).
4. 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):
1. New component → `app/<route>/_components/` (L0).
2. Second use in another page → COPY the file (tolerated duplicate at L0).
3. 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:

1. The **first screenshot** in `.workflow/screenshots/` (typically the dashboard / home) usually shows the primary nav. Identify the items visually.
2. The DESIGN.md `## Components` section, if it documents nav items.
3. The PRD's user stories, which often imply navigation (e.g., "as a user I want to see /clienti, /pratiche, /scadenze").
4. 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

1. **List `.workflow/screenshots/`.** If empty, skip — generate the default home from design tokens (the Constellation marketplace pattern).

2. **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

3. **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

4. **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.

5. **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:

1. **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.

2. **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.

3. **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.

4. **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).

5. **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:

```python
# 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)
