design
Local design mockup workflow for any frontend project. Triggers when the user wants to design, redesign, or mockup a UI component, page, or feature. Discovers the project's actual design system (tokens, fonts, components, framework), generates a standalone HTML mockup that uses those tokens verbatim, asks for approval, then implements the approved design as code in the project's stack. Works with React, Vue, Svelte, Solid, Astro, or plain HTML; with Tailwind v3/v4, CSS modules, styled-components, Panda, vanilla CSS, or any combination. No external service or paid API needed.
design
A six-step workflow that produces high-quality, design-system-faithful UI mockups locally — and then implements them as real components in whatever stack the project is using.
The skill's quality depends entirely on how aggressively it discovers the project's actual design system before generating anything. Do not skip Step 1. Do not invent tokens. Do not assume framework, theme, or styling library.
Invocation
The user will run this skill with a request like:
/design a stats card showing earnings/design --creativity high redesign this modal [screenshot]/design --c low fix the spacing on the sidebar nav
Parse $ARGUMENTS for:
- A creativity level flag (see Step 2)
- An output directory flag:
--out <path>(defaults to.design-mockups/) - The request itself (everything else)
Step 1 — Discover the project's design system
This step is mandatory and must run before any generation. The output of this step is a "design system snapshot" you will use throughout the rest of the workflow. Do not generate a single line of HTML until you have it.
1a. Detect the framework and styling system
Read package.json (or equivalent: Cargo.toml, deno.json, composer.json,
requirements.txt, etc.). Note:
- Framework: React/Next.js/Remix, Vue/Nuxt, Svelte/SvelteKit, SolidJS, Astro, Qwik, Angular, plain HTML, etc.
- Styling system (in priority order):
- Tailwind CSS — check version (v3 vs v4 differ significantly):
tailwind.config.{js,ts,cjs,mjs}→ likely v3@import 'tailwindcss'+@themeblock in a CSS file → v4
- Panda CSS —
panda.config.* - UnoCSS —
uno.config.* - CSS Modules —
*.module.cssfiles - styled-components / emotion — imports in component files
- Vanilla CSS / Sass —
.css/.scssfiles with custom properties - CSS-in-JS framework other than above
- Tailwind CSS — check version (v3 vs v4 differ significantly):
- Component library / primitives:
- Radix UI, Headless UI, Reach UI (unstyled primitives)
- shadcn/ui (often present as
components/ui/) - Material UI, Chakra, Mantine, Ant Design, NextUI
- Component-only libraries (Lucide, Heroicons, Phosphor for icons)
- Class merge utility: search for one of
clsx,cn,classnames,tailwind-merge,cva(class-variance-authority)
1b. Locate design tokens
This is the most important sub-step. Tokens are usually one of:
-
CSS custom properties in a global stylesheet:
# Find candidate files glob: **/{globals,global,theme,tokens,variables,index,app,main,style,styles}.{css,scss,sass,less} glob: **/styles/**/*.{css,scss} glob: **/src/**/{theme,tokens,variables}.*Read the matches and extract every
--*custom property declared in:root,[data-theme],.dark,.light, etc. -
Tailwind config theme (v3): read
tailwind.config.*, extracttheme.colors,theme.extend.colors,theme.fontFamily,theme.borderRadius,theme.spacingif customized. -
Tailwind v4
@themeblock: parse the@theme { ... }block in the global CSS file. The keys (--color-*,--font-*,--radius-*) become Tailwind utility classes. -
JS/TS theme objects: search for files named
theme.{ts,js},tokens.{ts,js},design-tokens.{ts,js}, or any file exporting an object with keys likecolors,fonts,spacing. Common in styled-components, Chakra, Mantine, Tamagui. -
Figma tokens / Style Dictionary output: look for
tokens/ordesign-tokens/directories, JSON files matching W3C token format.
Capture as a snapshot:
[design-system-snapshot]
- framework: <detected>
- styling: <detected>
- token-source: <file path(s) you read tokens from>
- background-tokens: <list with values, e.g. --bg-page: oklch(0.13 0.02 145)>
- text-tokens: <list with values>
- border-tokens: <list with values>
- accent-tokens: <list with values>
- brand-color-scale: <list, e.g. --brand-01 through --brand-10>
- neutral-scale: <list>
- radius: <value(s)>
- font-stack: <list, e.g. Inter, JetBrains Mono>
- icon-library: <detected>
- theme: <light | dark | both>
- class-merge-util: <e.g. cn from @/lib/utils, or none>
- component-pattern: <e.g. CVA + Radix, or plain function components>
1c. Find a baseline component to anchor on
Find ONE existing component to use as a stylistic anchor. Prefer in this order:
- A
Buttoncomponent (button.{tsx,vue,svelte,jsx}incomponents/ui/,src/components/,lib/components/, etc.) - A
Cardcomponent - Any component the user explicitly references in their request
- The most recently modified file under any
components/directory
Read it. Note:
- How props are typed
- How class names are merged
- Whether variants are defined declaratively (CVA/tv) or with conditionals
- File header conventions (
'use client', exports style, etc.)
1d. Detect the theme mode
- If you only find dark token values → dark-only
- If you find both
:rootand.darkselectors → both, light default - If you find
[data-theme="dark"]and[data-theme="light"]→ both, switchable - If only light token values → light-only
The mockup must render in the project's default theme. If both themes exist, generate two side-by-side variants (one per theme) unless the user specifies otherwise.
1e. Show the snapshot to the user
Before proceeding, briefly show the user what you discovered:
Design system detected: Framework: Next.js (React) · Styling: Tailwind v4 · Theme: dark-only · Tokens: 47 CSS vars in
app/globals.css· Radius: 2px · Fonts: Inter, JetBrains Mono · Class merge:cnfrom@/lib/utils· Anchor component:components/ui/button.tsx(CVA + Radix)
If detection fails for any field, say so explicitly and ask the user to point you to the right file.
Step 2 — Resolve creativity level and clarify the request
Creativity level
Parse $ARGUMENTS for a creativity flag. Accept these forms anywhere in the argument string:
--creativity low|medium|high--c low|medium|high- The bare words
low,medium,highif they appear unambiguously
If no creativity level is specified, ask before proceeding:
"What creativity level? low (layout only), medium (layout + tokens + data), or high (full reimagination)?"
What each level means
low — Layout-only refinement
Stay within the existing component's structure. Permitted:
- Reorder, resize, reposition existing elements
- Adjust spacing, padding, gap values
- Fix alignment or visual rhythm
- Correct token misuse (e.g. wrong background level, wrong text token)
- Improve hover / active / focus / disabled states
Not permitted: new UI elements, different data points, color/token shifts beyond correcting misuse, structural changes.
medium — Purposeful redesign
Rethink presentation while keeping the component's purpose identical. Includes everything in low, plus:
- Different layout patterns (horizontal ↔ vertical, grid ↔ list)
- Adjust which data points are shown or how they're labelled
- Use a different part of the existing token scale (e.g. promote a subtle to a brand accent)
- Add or remove supporting UI (icons, labels, dividers)
- Change visual weight hierarchy
Not permitted: changing what the component fundamentally does, inventing data that doesn't exist, introducing new component types not already in the project.
high — Full reimagination
Treat the existing design as context only. Anything goes within the project's design system:
- Completely new layout, structure, visual language
- New information architecture (groupings, hierarchy, progressive disclosure)
- New interaction patterns (replace a slider with input + presets, add toggles)
- New visual motifs (large typographic stats, progress arcs, badges)
- Richer data presentation (inline charts, comparison views)
- Micro-interaction concepts (described in HTML comments)
Must still respect: the project's discovered token system (no raw hex), the discovered radius scale, the discovered theme, the existing component library (no new npm packages in the implementation step).
Additional clarifications
If $ARGUMENTS doesn't already answer these, ask:
- What is being designed? (component, page section, modal, table, form…)
- What data/content should it show?
- Viewport — desktop, mobile, both?
- Interaction states to render — default, hover, loading, empty, error?
- Nearest existing component it should feel consistent with? (optional)
If $ARGUMENTS is clear, proceed without asking — but list your assumptions.
Step 3 — Write a design brief
Show the user a brief before generating any HTML:
- Surface: what is being designed and where it would live
- Goal: primary user action or information communicated
- Creativity level: resolved level + one sentence on what it permits here
- Token mapping: which discovered tokens map to which visual roles
- Constraints: discovered radius, theme, accent color
- Changes planned: a specific bullet list of changes scoped to the creativity level. Be concrete — "move the fee summary above the slider", not "reorder elements"
- States to render: which interaction states will be shown
- Assumptions: anything inferred from incomplete input
Step 4 — Generate the HTML mockup
Generate ONE standalone HTML file. It must use the discovered tokens verbatim. Never invent values. Never use raw hex.
Required scaffold
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>[Component name]</title>
<!-- FONTS: load whatever fonts the project actually uses -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="<google fonts URL for the discovered font stack>" rel="stylesheet">
<!-- ICONS: only if an icon library is in the project -->
<!-- e.g. Phosphor: <script src="https://unpkg.com/@phosphor-icons/web"></script> -->
<!-- e.g. Lucide: <script src="https://unpkg.com/lucide@latest"></script> + lucide.createIcons() -->
<!-- TAILWIND CDN if Tailwind is used in the project -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
/* === DESIGN TOKENS — copied verbatim from the discovered token source === */
:root {
/* paste the actual :root block (or equivalent) from the project here */
}
/* If the project has a .dark or [data-theme="dark"] block, paste it too */
</style>
<script>
/* If Tailwind is used, configure it to alias the token names so utility
classes work the same as in the project. Skip this if not using Tailwind. */
tailwind.config = {
theme: {
extend: {
fontFamily: { /* from discovery */ },
borderRadius: { /* from discovery — match the project exactly */ },
colors: {
/* map every discovered semantic token name to its CSS var */
}
}
}
}
</script>
</head>
<body class="<root background utility>" style="font-family: <discovered sans>;">
<!-- COMPONENT MOCKUP HERE -->
</body>
</html>
Visual rules for the mockup
These rules are universal — they apply regardless of the discovered stack:
- Use only discovered tokens. Never hardcode hex/rgb/oklch values. If a token doesn't exist for what you need, use the closest discovered token and note the gap in the brief.
- Use the discovered radius scale. If the project uses 2px, every corner is 2px. If it uses 12px, every corner is 12px. Don't mix.
- Use the discovered accent color for primary actions, active states, and emphasized values.
- Use the discovered font stack. Use any monospace font found for numbers, addresses, IDs, percentages, monetary values.
- Render multiple states side-by-side when the request involves interaction (default + hover + loading + empty, etc.).
- Prefer
gap-*utilities or flexbox/grid gap over child margins. - No new component primitives. If a component type isn't in the project (e.g. no Slider exists), don't invent one — use existing patterns.
- Render at the discovered theme. If both light and dark exist, render both side-by-side.
- Annotate design decisions in a small footer block at the bottom of
the mockup, listing what changed and why (especially for
mediumandhighcreativity).
Save the artifact
Determine the output directory:
--out <path>flag from$ARGUMENTS, OR.design-mockups/(default — create if missing)
Save to:
{output-dir}/runs/{ISO-timestamp-slug}/design.html
Write {output-dir}/latest.json:
{
"id": "{ISO-timestamp-slug}",
"createdAt": "{ISO timestamp}",
"prompt": "{the description of what was designed}",
"creativity": "{resolved level}",
"designHtmlPath": "{output-dir}/runs/{id}/design.html",
"framework": "{detected framework}",
"stylingSystem": "{detected styling}",
"transport": "local"
}
The slug is the first ~40 chars of the prompt, lowercased, non-alphanumerics
replaced with -, collapsed.
Step 5 — Present and verify (HARD GATE)
Show the user:
- The design brief from Step 3
- The path to the saved HTML file
- The key decisions you made (token choices, layout approach, omissions)
- The verification prompt:
"Open
{path}in your browser to review. Reply with feedback or 'looks good' to proceed to implementation."
You must wait for explicit approval before Step 6. This gate is the entire reason this skill produces good output instead of mediocre output. If the user gives feedback, revise the same HTML file in place, summarize what changed, and ask again. Iterate until approved.
Step 6 — Implement in the project's stack
Once approved, port the design to real code. The implementation must match the discovered framework and conventions exactly.
Universal rules
- Use only the discovered class merge utility. If the project uses
cnfrom@/lib/utils, use that. If it usesclsx, use that. If it uses none, use template literals carefully. - Use only the discovered token names. If the project's Tailwind aliases
tokens as
bg-bg-base, writebg-bg-base. Don't translate tobg-zinc-900. - Match the discovered component pattern. If the anchor component uses CVA + forwardRef + Radix, do the same. If it uses plain function components, do the same. Don't impose patterns the project doesn't use.
- Use only icons from the discovered icon library. If Lucide is installed, use Lucide. If Phosphor is installed, use Phosphor. Don't add a new icon package.
- Don't add new dependencies. If the design needs something not in the project, build it from primitives that already exist.
- Match file placement conventions. Find where similar components live in the project and put new files there.
Framework-specific notes
React / Next.js
- Match the project's file extension (
.tsxvs.jsx) - Match the export style (named vs default)
- If the project uses
'use client'directives, add them where appropriate - Use
forwardRefif other primitives in the project do
Vue / Nuxt
- Match the SFC style (
<script setup>vs Options API) - Match the script lang (
tsvs plain JS) - Match the style block convention (scoped vs unscoped vs
<style module>)
Svelte / SvelteKit
- Match Svelte 4 vs Svelte 5 runes syntax based on what the project uses
- Use the project's prop typing convention
Astro
- Decide between
.astroand the project's framework component flavor - Match slot conventions
After implementing
- Show the diff of files created or modified
- Note any props the caller needs to pass
- Note any assumptions about existing components/utilities
- Suggest a usage example in context
- Don't run the project or commit unless the user asks
Anti-patterns to avoid
| Don't | Do |
|---|---|
| Skip discovery and use generic Tailwind colors | Read the project's actual tokens |
Invent token names like bg-primary-500 | Use exactly what's in :root |
Use rounded-xl because it looks nice | Match the project's actual radius scale |
| Add a new icon package "for variety" | Use only what's already installed |
Hardcode #46E841 because it's the brand | Reference var(--brand-05) |
| Generate React when the project is Vue | Match the discovered framework |
| Implement before the user approves the mockup | Wait for explicit "looks good" |
| Wrap the design in a custom design system | Use the project's existing system |
Add dark: variants when tokens are already dark | Trust the discovered theme |