Vibe Design Skill
Handles all visual styling for a vibe project. Invokes frontend-design first. Commits to a design contract. Re-reads that contract before every component. Creates separate files.
The separation of concerns:
- vibe agent — spec compliance, data flow, logic, tests, docs
- vibe-design — aesthetics, layout, feel, interactions, visual polish
CRITICAL: How this skill works
The reason AI design tools produce generic output is that they commit to a direction once and then forget it during implementation.
This skill works differently:
- frontend-design is invoked first — before reading any project files
- A design contract is written — specific, named, irreversible choices
- The contract is re-read before every single component — not once, every time
- Each page gets its own file — never one monolithic output
If you find yourself about to write a white card with shadow-md — stop. Re-read the design contract. If the contract says "no cards" — no cards.
Step 1 — Invoke frontend-design IMMEDIATELY
Before reading project files. Before understanding the request. Before doing anything else.
Invoke the first-party frontend-design skill now — via the Skill tool
(frontend-design, or the plugin-qualified frontend-design:frontend-design),
not by reading a file path. It ships as a skill/plugin in the current ecosystem;
a hardcoded ~/.claude/skills/... path does not resolve under a plugin install.
This is not optional. This is not "if installed." This is the first action.
Defer aesthetic direction to frontend-design — it owns the typography,
colour, motion, and anti-generic principles. This skill (vibe-design) layers the
vibe-specific mechanics on top: persisting a design contract, re-reading it
before every component, one file per page/component, and grounding in
SPEC/CODEBASE. Do not re-derive aesthetic rules here that frontend-design already
provides; references/ANTI_GENERIC.md and SITE_TYPE_PLAYBOOK.md are
supplementary reminders, not the source of truth.
If the frontend-design skill is genuinely unavailable in the session — proceed
using references/ANTI_GENERIC.md, but note the output quality will be lower.
After reading frontend-design — internalise this:
"I will DERIVE the design from this product's domain × audience × emotion (ANTI_GENERIC.md), not stamp on a house style. I will not produce a SaaS dashboard — and I will not reach for the anti-generic cliché either (warm-cream + terracotta, the Fraunces/DM-Sans reflex, dark-as-premium, a 140px editorial hero on everything). I will pick the archetype that fits THIS domain, derive the palette and typography from it, and be able to justify every choice from the audience. Every component will reflect that derived direction."
Step 2 — Read project context and DESIGN.md
Read in this order:
First — check for DESIGN.md (highest priority):
ls DESIGN.md 2>/dev/null && echo "DESIGN.MD EXISTS" || echo "NO DESIGN.MD"
cat DESIGN.md 2>/dev/null
If DESIGN.md exists — its tokens are the law. Exact hex values. Exact font names. Exact shadow formulas. Do not approximate. Do not substitute. DESIGN.md overrides DESIGN_SYSTEM.md where they conflict.
Then read project files:
vibe/CODEBASE.md— stack, component library, file pathsvibe/SPEC.md— UI specification, screens, componentsvibe/DESIGN_SYSTEM.md— existing tokensCLAUDE.md— code style, naming conventions
Extract:
- Styling approach: Tailwind / CSS Modules / styled-components / vanilla CSS
- Framework: React / Vue / Next.js / vanilla — determines animation library
- Platform: mobile-first or desktop
- Pages and screens that need design work
Step 3 — Write the design contract
This is the most important step. Do not rush it.
Read references/ANTI_GENERIC.md in full — do the domain × audience × emotion
derivation and pick the archetype.
Read references/SITE_TYPE_PLAYBOOK.md — find the matching domain / design world
(software is only a few entries — most products aren't SaaS).
Then write the design contract. This is a concrete, named document.
═══════════════════════════════════════════════════════════
DESIGN CONTRACT — [Project name] — [date]
═══════════════════════════════════════════════════════════
DERIVATION (fill first — everything below traces to this):
Domain: [what world — e.g. children's education, private banking, taqueria]
Audience: [who + what they find credible/delightful]
Emotion: [the one feeling in 3 seconds]
Archetype: [from ANTI_GENERIC.md — e.g. Playful / Swiss / Luxe / Crafted …]
Theme: [light | dark] — with the reason it fits this domain
(do NOT pick dark for "premium"; do NOT default to warm cream)
ONE FITTING BOLD CHOICE: [Name it — bold AND right for this audience]
(bold ≠ random; a private bank being loud is wrong, not brave)
TYPOGRAPHY CONTRACT: (chosen for the archetype/emotion — justify, don't reflex)
Display font: [exact name — for THIS archetype; not the Fraunces/DM-Sans reflex]
Body font: [exact name — NOT Inter/system-ui]
Mono font: [exact name, if used]
Why these: [one line tying the pairing to the emotion]
Display size: [fits the archetype — Luxe may be small+airy, not 140px]
Weight contrast: [display weight vs caption weight — the gap is the design]
COLOUR CONTRACT: (DERIVED via ANTI_GENERIC.md Steps A–D)
Theme + neutrals: [near-white/near-black hexes in the hue's TEMPERATURE —
cool brand → cool neutrals, not warm cream by default]
Brand hue: [one specific, slightly-unexpected shade — NOT the category default
(not SaaS blue, not eco green, not reflex terracotta)]
Brand appears on: [list exactly where] · Brand does NOT appear on: [rest]
(Playful/Maximalist archetypes may use multiple saturated hues — say so.)
MOTION CONTRACT:
Library: [CSS + View Transitions (default) | Framer Motion | GSAP — only if needed]
Energy: [matches archetype — Luxe slow/few · Playful springy · Swiss minimal]
Hero / sections / interactions: [specific approaches] · reduced-motion honoured
FILE STRUCTURE:
[Every file to be created — one page per file, one component family per file]
BANNED FOR THIS PROJECT:
Enemy 1 (SaaS generic): Inter display · blue/indigo/violet primary · white
shadow-md cards · centered hero · 3-col icon grid · gray-100 sections
Enemy 2 (anti-generic cliché): warm-cream + terracotta as default · the
Fraunces/DM-Sans reflex · dark-as-premium · 140px editorial hero on a
non-editorial product · SaaS layout on a non-SaaS domain
[add project-specific bans]
═══════════════════════════════════════════════════════════
Save the contract:
mkdir -p vibe/design
# Write contract to file — this gets re-read before every component
Save as vibe/design/CONTRACT.md.
Present to user:
"Design contract written. Bold choice: [state it clearly] This will look like: [one sentence description] Files to create: [N files — list them] Proceeding."
Wait for approval only if 3+ components. Otherwise proceed immediately.
Step 4 — Establish file structure BEFORE writing any code
Rule: one file per page, one file per component family. Always.
Never put multiple pages in one file. Never create a single wireframe.html or index.html with everything.
If the user asks for a wireframe.html — respond:
"I create separate files per page for maintainability and because vibe-design produces production files, not wireframes. File structure: [list from contract]. Starting with [first page]."
Create the structure:
mkdir -p src/pages src/components src/lib src/styles
# Create animation tokens file FIRST — everything imports from here
# Write src/lib/animations.ts (or CSS keyframes) with motion tokens whose ENERGY
# matches the contract's archetype (Luxe slow/few · Playful springy · Swiss minimal).
# Prefer CSS transitions + View Transitions; use a motion library only if the
# contract's Motion section calls for one. Always gate on prefers-reduced-motion.
# Create CSS tokens file with values from the contract
# Write src/styles/tokens.css with all CSS custom properties
# Announce
echo "Structure created. Building [N] files:"
echo "[list all files from contract]"
echo "Starting with [first file]."
Step 5 — Implement — one file at a time
MANDATORY before each file: Re-read the design contract
cat vibe/design/CONTRACT.md
Then ask: does my plan for this component implement the bold choice? State out loud:
"Building [filename]. Bold choice implementation: [how this component shows it]. Using [display font] at [size]. Brand colour on [what, if anything]."
If you cannot answer how this component implements the bold choice — redesign the approach until you can.
Write the implementation
For each component:
- Implement the design
- Cover all states: default, hover, active, focus, disabled, loading, empty
- Mobile-first if project is mobile-first
Per-component self-check (mandatory before moving to next file):
- Bold choice is visible and intentional in this component
- Display font used for headlines — NOT Inter, NOT system fonts
- Brand colour appears only where the contract specifies
- Animation imported from
src/lib/animations.ts— not inline - No pattern from the BANNED LIST is present
- This component could not be mistaken for a generic SaaS dashboard
If any check fails — fix before moving to the next file.
Stack-specific guidance
React + Tailwind + Framer Motion:
// Tokens in tailwind.config.js, not hardcoded
// All animations from src/lib/animations.ts
// next/font for font loading
// motion.div with variants from animations.ts
React + CSS Modules + Framer Motion:
// CSS custom properties in tokens.css
// BEM class names in .module.css
// Framer Motion for all transitions
Vue 3:
// CSS custom properties globally
// Vue Transition + CSS @keyframes
// IntersectionObserver for scroll reveals
// No Framer Motion (React only)
Vanilla HTML + CSS + JS:
// CSS custom properties at :root
// IntersectionObserver for scroll triggers
// CSS @keyframes with animation-delay for stagger
// No dependencies required
Step 6 — Full consistency check after all files
After all files written:
Screenshot verification (do this first — actually look, don't self-report):
Render the built UI and inspect it visually rather than checking boxes from
memory. Run the app (preview_start / dev server) or open the page in the
browser, take a screenshot of each page at desktop and mobile widths, and judge
the rendered image against the contract and the checks below. This is the
"screenshot test" from references/ANTI_GENERIC.md — run it for real. Iterate on
what the screenshot reveals (spacing, hierarchy, the bold choice actually
landing), not on what the code says it should look like.
Accessibility: if the session has design:accessibility-review, run it
(contrast ratios, keyboard nav, focus states, prefers-reduced-motion); else
spot-check contrast on text/brand-colour pairs and confirm focus-visible states.
Derivation held:
- The design still reads as THIS domain/audience (domain test) — not re-skinnable
- Palette was derived (theme + hue + temperature-matched neutrals), not fallen back to warm cream or dark-for-premium
- Fonts fit the archetype — not the reflex Fraunces/DM-Sans pairing
Typography:
- Display + body fonts are the contract's — everywhere; display ≠ body
- No Inter/system-ui as display unless the contract genuinely chose it
- Weight contrast (heavy display vs light caption) is visible
Colour:
- One brand hue, only where the contract specifies (or the multi-hue set a Playful/Maximalist contract declared)
- Neutrals match the brand's temperature; theme matches the contract
- Brand hue is not the category default (SaaS blue / eco green / reflex terracotta)
Motion:
- Motion energy matches the archetype;
prefers-reduced-motionhandled
Layout:
- The fitting bold choice is visible; structure fits the domain (not a SaaS scroll-journey forced onto a non-SaaS product)
- No unintended SaaS defaults (centered hero / 3-col icon grid / shadow-md cards) unless the contract chose them
Files:
- Every page is a separate file
- No monolithic output
Step 7 — Update docs and commit
Update vibe/DESIGN_SYSTEM.md with the design contract as the direction section.
Update vibe/TASKS.md with what was built.
git add src/ vibe/
git commit -m "design([scope]): [bold choice] — [files created]"
Signal done:
✅ Design complete — [scope]
[One sentence: what it looks like]
Bold choice: [restate]
Files: [N files created — list them]
Contract: vibe/design/CONTRACT.md
Non-negotiable rules
frontend-design is read in Step 1. No exceptions.
The design contract is written before any code. No exceptions.
The contract is re-read before each file. No exceptions.
One file per page. One file per component family. No exceptions.
Generic is failure. A SaaS dashboard means the skill failed. Not played it safe — failed. Retry from Step 3.