
Frontend God Mode
- 85 installs
- 14 repo stars
- Updated May 10, 2026
- shawnchee/frontend-god-mode
Helps with frontend development tasks during AI-assisted development.
About
frontend-god-mode is a Claude Code skill for frontend development. It helps solo builders move faster with AI-assisted coding.
- frontend-god-mode
- Frontend Development
- AI-coding skill
Frontend God Mode by the numbers
- 85 all-time installs (skills.sh)
- +8 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #1,096 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/shawnchee/frontend-god-mode --skill frontend-god-modeAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 85 |
|---|---|
| repo stars | ★ 14 |
| Last updated | May 10, 2026 |
| Repository | shawnchee/frontend-god-mode ↗ |
What it does
Helps with frontend development tasks during AI-assisted development.
Files
Frontend God Mode
You are designing for someone who paid for a senior designer + frontend engineer. Do not produce generic AI output. Every choice — typography, color, layout, motion, copy — must be intentional and demonstrably better than the median ChatGPT/Claude default.
Active Baseline (Do Not Ask The User To Edit)
These are your global dials. Adapt them dynamically when the user requests differently in chat, but do not ask them to configure these — apply them as the standard.
DESIGN_VARIANCE: 8 (1 = perfect symmetry, 10 = artsy chaos)
MOTION_INTENSITY: 6 (1 = static, 10 = cinematic physics)
VISUAL_DENSITY: 4 (1 = airy gallery, 10 = cockpit)Workflow — Run This Every Time
1. Decide tone before writing code
Pick one clear aesthetic direction. Bold maximalism and refined minimalism both work — the failure mode is being in between.
Ask yourself: brutalist, maximalist chaos, retro-futuristic, organic, luxury/refined, playful/toy, editorial/magazine, art deco, soft pastel, industrial/utilitarian — or some specific blend? Commit to one in your head before opening a file.
2. Check setup state
If you see no package.json or no design dependencies, run the setup walkthrough at references/setup-walkthrough.md first.
Then check for `DESIGN.md` at the project root. If it exists, read it BEFORE picking aesthetic — your choices must align with what's already there. If it doesn't exist and you're about to do meaningful UI work, plan to create it (see references/design-doc.md and step 5 below).
3. Route to the right reference
Load only what's relevant for the task — don't dump every reference into context.
| Task | Load |
|---|---|
| Picking fonts, sizing, hierarchy | references/typography.md |
| Picking palette, accents, dark mode | references/color.md |
| Adding animations, transitions, micro-physics | references/motion.md |
| Spacing, grids, hero structure | references/layout.md |
| Pulling pre-built components | references/components.md |
| Marketing landing feature row (SaaS bento) | references/bento-engine.md |
| Analytics / data-dense dashboard | references/layout.md (Dashboard Hardening) — NOT bento-engine |
| Accessibility, contrast, focus, keyboard | references/accessibility.md |
| Headlines, button labels, empty states, errors | references/copy.md |
| Generating / updating the project's DESIGN.md | references/design-doc.md |
| Final pass before "done" | references/anti-slop.md (always — pre-flight) |
Important distinction: "SaaS bento" (marketing feature row) and "data dashboard" are different problems. Bento-engine.md applies only to the marketing context. Real analytics dashboards delete the cards and use divide-y rows — see Dashboard Hardening in layout.md.
4. Enforce the pre-flight checklist
Before reporting work as done, read `references/anti-slop.md` and verify every banned pattern is absent. This is non-negotiable.
5. Write or update DESIGN.md at the project root
After finishing a meaningful build, write ./DESIGN.md (create if missing, append to "Last updated" if exists) using the template in references/design-doc.md. This is the project's persistent design memory — without it, future sessions drift.
Skip this step only when: the user is making a tiny, isolated tweak (e.g., "fix this button label") that doesn't change tokens or aesthetic.
The Five Hard Rules (Memorize These)
These override everything. If you catch yourself doing any of them, stop and rework.
1. No regular Inter. Banned. Use Geist, Satoshi, Cabinet Grotesk, Outfit, Instrument Serif, or PP Editorial New. Match the font to the aesthetic — never default. (Inter Tight and Inter Display are allowed as body cuts only — see references/typography.md.)
2. No purple-to-blue gradients on white. This is the #1 AI tell. If you want color drama, pick one saturated accent (emerald, electric blue, deep rose, terracotta) under 80% saturation against a tinted neutral. Maximalist exception: for posters, editorial layouts, music/festival/event design, zines, brutalist/avant-garde briefs, or any DESIGN_VARIANCE ≥ 8 context, you may use a curated 3-color palette instead of one accent — but the palette must be intentional (e.g., terracotta + mustard + cream, NOT random Tailwind defaults). Still no purple→blue gradient. When in doubt, default to ONE accent.
3. No `h-screen` on heroes. iOS Safari ruins it. Always min-h-[100dvh].
4. No three equal cards in a row as the "features" pattern. Use 2-col zig-zag, asymmetric bento, horizontal scroll, or kill the cards entirely and use divide-y.
5. No generic data. "John Doe", "Acme", "Nexus", "$99.99", "+1 (555) 123-4567" → all banned. Invent realistic, slightly messy data: Mira Okonkwo, Lattice & Co., $47.20, +1 (312) 847-1928.
6. DESIGN.md is mandatory. On any meaningful UI build, read ./DESIGN.md first if it exists, or create it after the build using references/design-doc.md as the template. Skip ONLY for tiny isolated tweaks (single button label, one className change). Never skip on first-time builds.
Architecture Defaults (Don't Ask, Just Apply)
Unless the user specifies otherwise:
- Framework: React or Next.js. Default to Server Components; isolate motion/state into leaf Client Components with
"use client"at the top. - Styling: Tailwind. Check
package.jsonfor v3 vs v4 — never mix syntaxes. For v4, do NOT usetailwindcssplugin inpostcss.config.js; use@tailwindcss/postcss. - Icons:
@phosphor-icons/reactor@radix-ui/react-icons. Standardize stroke width globally (1.5 or 2.0). Never emojis. - Layout containers:
max-w-[1400px] mx-autoormax-w-7xl. CSS Grid over flex math. Mobile collapses to single column withpx-4belowmd:. - Dependencies: Before importing any 3rd party lib, check
package.json. If missing, output the install command first. Never assume. - Shadcn: Allowed, but never default. Customize radii, colors, shadows.
- Images:
https://picsum.photos/seed/{string}/800/600for placeholders. Never broken Unsplash URLs.
When User Asks "Just Build Me X"
Don't ask 10 questions. Make 90% of decisions yourself based on the brief and these rules. Only ask if a single missing piece would derail the design (e.g., "is this for a B2B SaaS or a creative agency?" when it's truly ambiguous).
After building: state the aesthetic direction you picked in one sentence so they can redirect if wrong.
When User Asks To "Add Animation" / "Make It Feel Alive"
Load references/motion.md and references/bento-engine.md. Apply spring physics, perpetual micro-interactions, staggered orchestration. Never linear easing. Never animate width/height — only transform and opacity.
When User Asks To "Polish" / "Make It Better"
Run a pre-flight pass against references/anti-slop.md. Look for: gray-on-color text, nested cards, cramped padding, skipped heading levels, unstaggered list reveals, untinted shadows, missing empty/error states.
Setup Walkthrough Trigger
If the user says any of: "set up website builder", "install design tools", "/website-builder-setup", "/frontend-god-mode setup" — run the full walkthrough at references/setup-walkthrough.md. Otherwise assume tools are already installed and proceed directly to building.
Final Pre-Flight (Before Saying "Done")
Run the full pre-flight from references/anti-slop.md plus the items below. The full anti-slop checklist is the source of truth — this is the must-not-skip subset.
Typography
- [ ] No regular Inter / Roboto / Arial / system-ui as primary
- [ ] At most TWO type families (display + body, optional mono)
- [ ] Body line-height ≥ 1.5
- [ ] Body width capped at
max-w-[65ch]
Color
- [ ] No purple-blue gradient on white
- [ ] No
#000/#FFFdirectly (tinted neutrals only) - [ ] One accent (or curated 3-color palette in maximalist mode)
- [ ] Tinted shadows (no pure black)
Layout
- [ ] No centered hero (when DESIGN_VARIANCE > 4)
- [ ] No 3 equal cards in a row as features
- [ ] No nested cards beyond depth 1
- [ ] All full-height sections use
min-h-[100dvh](neverh-screen) - [ ] Mobile collapses to single column with
px-4minimum
Motion
- [ ] Spring physics on interactive motion (no linear easing for UI motion)
- [ ] No bounce / elastic
- [ ] Only
transform/opacityanimated - [ ] Perpetual loops isolated in memoized leaf Client Components
- [ ]
useEffectanimations have cleanup - [ ]
prefers-reduced-motionrespected
Accessibility
- [ ] Body text contrast ≥ 4.5:1 (WCAG AA)
- [ ] Focus-visible rings on every interactive element
- [ ] Touch targets ≥ 44px on mobile
- [ ] Form inputs have real labels (not just placeholders)
- [ ] Heading levels sequential (no skipping)
Content & state
- [ ] Realistic data (no John Doe / Acme / 99.99%)
- [ ] No filler copy (Elevate / Unleash / Seamless / Next-Gen)
- [ ] No emojis anywhere (Phosphor / Radix icons only)
- [ ] Loading skeletons match layout (not generic spinners)
- [ ] Empty + error states designed and inline
Skill protocol
- [ ] Stated the aesthetic direction in one line for the user
- [ ] DESIGN.md written/updated at the project root (mandatory unless tiny isolated tweak)
- [ ] DESIGN.md was read FIRST if it already existed
If any item fails, fix before reporting done.
After You're Done — Recommend Next Steps
Once the build passes the pre-flight, suggest the user invoke companion skills to close the loop. Only mention skills the user actually has installed (you can check with npx skills list if uncertain — don't fabricate). Otherwise tell them which to install.
If the user has a dev server running or could start one:
"Want me to take screenshots and verify it renders correctly? Installanthropics/skills@webapp-testing—npx skills add anthropics/skills@webapp-testing -a claude-code— and I can boot the dev server, screenshot each section, and check for browser console errors."
For a UX quality pass:
"For a quantitative UX critique, installnpx skills add pbakaus/impeccable@critique -a claude-codeand prompt/critique <area>."
For technical / a11y audit:
"For a deeper technical audit (WCAG violations beyondaccessibility.md, perf, responsive), installnpx skills add pbakaus/impeccable@audit -a claude-codeand prompt/audit <area>."
For mobile-heavy projects:
"For mobile-first refinement (touch targets, gesture patterns, iOS/Android conventions), install npx skills add sleekdotdesign/agent-skills@sleek-design-mobile-apps -a claude-code."Make the recommendation contextual — don't dump all four at the user. Pick the ONE that most likely catches what your build might have missed.
Accessibility Reference
Production-grade work means accessible work. This isn't optional. Generic AI output skips a11y entirely — don't.
The Hard Floor
- WCAG 2.2 AA contrast ratios:
- Body text on background: ≥ 4.5:1
- Large text (≥ 18pt or 14pt bold): ≥ 3:1
- UI components, focus indicators, graphical elements: ≥ 3:1
- All interactive elements must be keyboard-reachable in a logical tab order.
- Focus rings must be visible. Never
outline: nonewithout a replacement. - Reduced motion must be respected (see motion.md).
Contrast Calibration
When using OKLCH neutrals:
| Pair | Likely contrast | Verdict |
|---|---|---|
oklch(0.15 0.01 H) on oklch(0.99 0.005 H) | ~17:1 | ✅ AAA |
oklch(0.55 0.01 H) on oklch(0.99 0.005 H) | ~4.6:1 | ✅ AA body |
oklch(0.65 0.01 H) on oklch(0.99 0.005 H) | ~3.4:1 | ⚠️ large text only |
oklch(0.75 0.01 H) on oklch(0.99 0.005 H) | ~2.5:1 | ❌ FAIL |
Test with real values: use https://oklch.com/ or https://www.tpgi.com/color-contrast-checker/ before shipping.
Focus Rings (Critical)
shadcn defaults to ring-sky-500 or ring-blue-500 — which collides with our color rules. Override globally:
:root {
--ring: oklch(0.55 0.15 250 / 0.5); /* matches your accent at 50% */
}
*:focus-visible {
outline: 2px solid var(--ring);
outline-offset: 2px;
}Use :focus-visible (not :focus) so mouse clicks don't show rings, but keyboard tab does.
For motion-friendly focus, use a 200ms ease transition on the outline width — don't animate the outline color.
Keyboard Navigation Checklist
- [ ] Every clickable element is
<button>,<a>, or hasrole="button"+tabIndex={0}+ key handler - [ ] Tab order matches visual order
- [ ] Modal traps focus inside until dismissed (use Radix or Headless UI)
- [ ] Esc closes modals, popovers, dropdowns
- [ ] Arrow keys navigate within radio groups, menus, tabs
- [ ] No keyboard trap (you can always Tab out)
Semantic HTML
Don't use <div> for everything.
| Element | Use for |
|---|---|
<button> | Triggers actions in-page |
<a href> | Navigation, opens new view/page |
<nav> | Site navigation |
<main> | Primary page content (one per page) |
<article> | Self-contained piece (blog post, product) |
<section> | Thematic grouping |
<aside> | Tangential content (sidebar) |
<header> | Page or section intro |
<footer> | Page or section outro |
<h1>–<h6> | Sequential heading hierarchy (no skipping) |
<dialog> | Modals (or Radix Dialog with proper roles) |
Screen Reader Hygiene
- Images:
alttext describes purpose, not appearance. Decorative images getalt="". - Icons in buttons: wrap in
<span className="sr-only">Add to cart</span>or usearia-label. - Form inputs: ALWAYS associate with
<label htmlFor>oraria-labelledby. Placeholders are NOT labels. - Loading states:
aria-busy="true"+aria-live="polite"on the parent. - Toasts / notifications:
role="status"+aria-live="polite"for non-urgent,role="alert"for urgent.
Form Accessibility
- Label above input (mandatory — see layout.md)
- Helper text under input, linked via
aria-describedby - Error text under input, linked via
aria-describedbyANDaria-invalid="true" - Required fields marked with
aria-required="true"(and a visual indicator, not just color) - Don't use color alone to indicate state — pair with icon or text
Touch Targets
- Mobile interactive elements: minimum 44×44px tap area
- Pad small buttons with extra
paddingto reach 44px without growing visible size - Spacing between adjacent tap targets: at least 8px
Reduced Motion
Already covered in motion.md, but double-check:
import { useReducedMotion } from 'framer-motion';
const reduced = useReducedMotion();
// Disable or shorten animations when reduced is trueCommon AI Accessibility Failures
- ❌
<div onClick>instead of<button> - ❌ Placeholder used as the only label
- ❌ Skipped heading levels (
<h1>then<h3>, no<h2>) - ❌
outline: noneon focus, no replacement - ❌ Color-only error indication (red text, no icon, no label)
- ❌
<a>tags with nohref(orhref="#") - ❌ Modal that doesn't trap focus
- ❌ Animations that don't respect
prefers-reduced-motion - ❌ Insufficient contrast on muted text (
text-gray-400on white = ~3:1, fails body) - ❌ Touch targets under 44px on mobile
Quick Audit Tools
Recommend the user run:
- axe DevTools browser extension — flags WCAG violations
- Lighthouse in Chrome DevTools — Accessibility score
- WAVE browser extension — visual feedback layer
Pre-Flight Accessibility Check
- [ ] All body text contrast ≥ 4.5:1
- [ ] All interactive elements keyboard-reachable
- [ ] Focus rings visible (and not the default sky-blue)
- [ ] Heading levels sequential (no skipping)
- [ ] Form inputs have proper labels (not just placeholders)
- [ ] Errors aren't color-only
- [ ] Touch targets ≥ 44px
- [ ] Modals trap focus + Esc dismisses
- [ ]
prefers-reduced-motionrespected - [ ] Decorative images have
alt="", meaningful images have descriptive alt - [ ] Icon-only buttons have
aria-labelorsr-onlytext
Anti-Slop Reference (Pre-Flight Mandatory)
This is the final filter. Run the project through every item before reporting "done." If any check fails, fix it. No exceptions.
The 24 AI Tells
If your output has any of these, it screams "AI generated."
Visual / CSS
1. Inter font for premium / creative work (use Geist, Satoshi, Cabinet Grotesk, Outfit, PP Editorial New) 2. Purple-to-blue gradient on white backgrounds (the #1 tell — banned outright) 3. Pure `#000` / `#FFF` without tint (use oklch(0.12 0.005 H) / oklch(0.99 0.005 H)) 4. Default neon shadow like box-shadow: 0 0 20px rgba(120,80,255,0.5) (banned) 5. Untinted shadows with pure black (rgba(0,0,0,0.1)) — must tint to bg hue 6. Gradient text fill on large H1s (cliché, use solid color + tracking) 7. Custom mouse cursors (outdated, breaks accessibility — banned) 8. Side-tab borders (border-l-4 border-purple-500 on alerts) — banned 9. Bounce / elastic easing (feels dated, use spring with damping: 20) 10. Generic `rounded-md` everywhere — vary based on aesthetic (rounded-[2.5rem], rounded-[14px], sharp rounded-none)
Typography
11. Oversized H1 that screams — control with weight + color, not just text-9xl 12. Serif fonts on dashboards — banned for software UIs 13. All-caps body copy (only labels < 4 words) 14. Centered long paragraphs — left-align everything past 2 lines 15. Body width over 65 characters — caps reading comfort 16. `font-sans` defaulting to system-ui — must explicitly load a non-system font
Layout
17. Centered hero with H1 + subtitle + 2 CTAs (banned when DESIGN_VARIANCE > 4) 18. 3 equal cards in a row as the features pattern (banned) 19. Cards inside cards — flatten the depth 20. `h-screen` on heroes — min-h-[100dvh] always 21. Flex percentage math (w-[calc(33%-1rem)]) — use Grid
Content / Data
22. Generic names — "John Doe", "Jane Smith", "Sarah Chen", "Jack Su" (banned). Use specific, realistic names: Mira Okonkwo, Theo Vasquez, Lin Park-Aboagye 23. Round-number fake data — 99.99%, $50.00, 1,000 users, +1 (555) 123-4567 (banned). Use messy: 47.2%, $47.20, 1,128 users, +1 (312) 847-1928 24. Startup slop names — "Acme", "Nexus", "SmartFlow", "Apex" (banned). Invent contextual: Lattice & Co., Provenance Studio, Tessellate Labs
Copy
- Filler words: "Elevate", "Seamless", "Unleash", "Next-Gen", "Revolutionary", "Game-Changing" — all banned. Use concrete verbs.
- "Lorem ipsum" in shipped output — banned. Write actual placeholder copy that matches the aesthetic.
- AI-tone phrases: "In the realm of...", "Whether you're...", "From X to Y" — banned.
Images
- Broken Unsplash links — banned. Use
https://picsum.photos/seed/{string}/800/600for deterministic placeholders, or specific stock URLs you've verified. - Generic SVG "egg" avatars or Lucide user icons as user profile pics — banned. Use named, varied placeholder portraits or stylized initials.
- Emojis anywhere — banned. Use Phosphor Icons or Radix Icons.
Forbidden CSS Patterns
/* ❌ All banned */
.bad-1 { font-family: Inter, sans-serif; }
.bad-2 { background: linear-gradient(135deg, #a855f7, #3b82f6); }
.bad-3 { color: #000; background: #fff; }
.bad-4 { box-shadow: 0 0 30px rgba(168, 85, 247, 0.4); }
.bad-5 { box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
.bad-6 { animation: bounce 1s infinite; }
.bad-7 { cursor: url('custom.png'), auto; }
.bad-8 { border-left: 4px solid purple; }
.bad-9 { transition: all 0.3s ease; } /* use cubic-bezier */
.bad-10 { height: 100vh; } /* use 100dvh */Forbidden React Patterns
/* ❌ Banned */
const [mouseX, setMouseX] = useState(0); // re-renders kill mobile — use useMotionValue
<div className="grid grid-cols-3 gap-4"> // banned for "features" sections
<Card />
<Card />
<Card />
</div>
<motion.div animate={{ width: '100%' }} /> // animate transform/opacity only
window.addEventListener('scroll', ...) // use useScrollState Coverage Required
Every data-displaying component MUST have:
- ✅ Loading state — skeleton matching layout sizes (NOT generic spinners)
- ✅ Empty state — beautifully composed, indicates how to populate
- ✅ Error state — clear, inline, actionable
- ✅ Success state — feedback after mutations
Generic AI output skips empty/error states. Don't.
Architecture Verifications
- [ ] No
'use client'on Server Components that could be RSC - [ ] All Framer Motion components have
'use client'at top - [ ] Tailwind v3 vs v4 syntax matches
package.json - [ ]
package.jsonchecked before importing any 3rd party lib - [ ] No
useEffectwithout cleanup - [ ] No animations on
width/height - [ ] Memoized perpetual-motion components
- [ ] No mixing GSAP + Framer in same component tree
The Final Pre-Flight (Read Before Saying "Done")
TYPOGRAPHY
[ ] No Inter / Roboto / Arial / system-ui as primary
[ ] Two type families maximum (display + body, optional mono)
[ ] Body line-height ≥ 1.5
[ ] Body width capped at 65ch
[ ] Display tracking tightened
COLOR
[ ] No purple-blue gradient on white
[ ] No #000 / #FFF directly (tinted only)
[ ] One accent color, saturation ≤ 80%
[ ] Tinted shadows (no pure black)
[ ] Status colors from approved palette
LAYOUT
[ ] No centered hero (variance > 4)
[ ] No 3 equal cards row
[ ] No nested cards beyond depth 1
[ ] No h-screen
[ ] CSS Grid (not flex math)
[ ] Mobile collapses to single column
MOTION
[ ] Spring physics (no linear easing)
[ ] No bounce / elastic
[ ] Only transform/opacity animated
[ ] List reveals staggered
[ ] Reduced-motion respected
[ ] useEffect cleanup present
CONTENT
[ ] Realistic names (no John Doe)
[ ] Messy data (no 99.99%)
[ ] No "Acme" / "Nexus" / "SmartFlow"
[ ] No "Elevate / Seamless / Unleash" copy
[ ] No emojis (Phosphor / Radix only)
[ ] No broken Unsplash URLs
STATE
[ ] Loading skeletons (not spinners)
[ ] Empty states designed
[ ] Error states inline + actionable
[ ] Success feedback present
ARCHITECTURE
[ ] 'use client' only where needed
[ ] No state for hover position
[ ] No window.scroll listeners
[ ] Tailwind version syntax correct
[ ] Dependencies verified in package.jsonIf you can't tick every box, don't say done. Fix and re-check.
Bento 2.0 — The Motion-Engine Paradigm
For modern SaaS dashboards and feature sections, this is your default architecture. Vercel-core meets Dribbble-clean. Heavy on perpetual physics.
When to Use This
- Modern SaaS landing page "feature row"
- Product page showcasing 4-6 capabilities
- Dashboard hero showcasing "what the product does"
- Anywhere the brief says: "show off the product features in a modern, animated way"
The Aesthetic
Use the project's tokens from `DESIGN.md` — never hardcode Zinc/Slate/Stone. Values below are illustrative; replace with the project's actual neutral family.
- Background: the project's
--bg-subtle(e.g.,oklch(0.985 0.005 H)where H matches the chosen hue family) - Card surface: the project's
--surface(e.g., off-white in light mode,oklch(0.18 0.005 H)in dark) - Card border:
border border-[var(--border)]/60— token, not literalborder-zinc-200 - Card radius:
rounded-[2.5rem]— distinctive, not defaultrounded-2xl - Diffusion shadow: wide + low-opacity + tinted toward bg hue (see
color.md). NOTrgba(0,0,0,...). - Internal padding:
p-8 md:p-10 - Title placement: OUTSIDE and BELOW the cards (gallery-style), not inside
The Grid
<section className="bg-zinc-50 py-24">
<div className="max-w-7xl mx-auto px-6 md:px-10">
{/* Eyebrow + heading */}
<div className="mb-16 max-w-2xl">
<p className="text-sm tracking-wide uppercase text-zinc-500 mb-3">What's inside</p>
<h2 className="text-4xl md:text-5xl tracking-tighter">
The motion engine, fully realized.
</h2>
</div>
{/* Bento grid — 3 cols, 2 rows, asymmetric spans */}
<div className="grid grid-cols-1 md:grid-cols-3 md:grid-rows-2 gap-4">
<Card span="md:col-span-2 md:row-span-2">
<IntelligentList />
</Card>
<Card>
<CommandInput />
</Card>
<Card>
<LiveStatus />
</Card>
</div>
</div>
</section>The 5 Card Archetypes
Each card MUST have a perpetual micro-animation. These are the proven five:
1. The Intelligent List
A vertical stack of items with infinite auto-sorting. Items swap positions using layoutId. Simulates an AI prioritizing tasks.
'use client';
import { motion, LayoutGroup } from 'framer-motion';
import { useEffect, useState } from 'react';
const tasks = [
{ id: 1, label: 'Draft Q3 launch plan', priority: 'high' },
{ id: 2, label: 'Review onboarding flow', priority: 'med' },
{ id: 3, label: 'Sync with marketing', priority: 'low' },
{ id: 4, label: 'Update docs site', priority: 'med' },
];
export function IntelligentList() {
const [items, setItems] = useState(tasks);
useEffect(() => {
const t = setInterval(() => {
setItems((prev) => [...prev].sort(() => Math.random() - 0.5));
}, 3000);
return () => clearInterval(t);
}, []);
return (
<LayoutGroup>
<ul className="space-y-3">
{items.map((task) => (
<motion.li
key={task.id}
layout
layoutId={`task-${task.id}`}
transition={{ type: 'spring', stiffness: 100, damping: 20 }}
className="rounded-2xl bg-zinc-50 p-4 flex items-center gap-3"
>
<span className={`w-2 h-2 rounded-full bg-emerald-500`} />
<span className="text-sm">{task.label}</span>
</motion.li>
))}
</ul>
</LayoutGroup>
);
}2. The Command Input
Search/AI bar with multi-step typewriter cycling through realistic prompts. Includes blinking cursor and shimmer "processing" state.
Loops through 3-4 prompts:
- "Draft a launch announcement..."
- "Find churn signals from last quarter..."
- "Suggest pricing for the enterprise tier..."
Each prompt: 2.5s typing → 1s pause → shimmer for 0.8s → fade out → next prompt.
3. The Live Status
Scheduling / calendar interface with breathing status indicators. Notification badge pops in with overshoot spring (damping: 12), holds for 3s, vanishes.
<motion.div
initial={{ scale: 0, opacity: 0 }}
animate={{ scale: 1, opacity: 1 }}
exit={{ scale: 0, opacity: 0 }}
transition={{ type: 'spring', stiffness: 300, damping: 12 }}
className="absolute top-4 right-4 px-3 py-1 rounded-full bg-rose-500 text-white text-xs"
>
3 new
</motion.div>4. The Wide Data Stream
Horizontal infinite carousel of metric cards. Seamless loop using x: ['0%', '-100%'] with duplicated content.
<motion.div
animate={{ x: ['0%', '-50%'] }}
transition={{ repeat: Infinity, duration: 30, ease: 'linear' }}
className="flex gap-4 w-max"
>
{[...metrics, ...metrics].map((m, i) => (
<MetricCard key={i} {...m} />
))}
</motion.div>5. The Contextual UI (Focus Mode)
A document/text view that animates a staggered highlight sweeping across a paragraph, followed by a floating action toolbar that "Float-ins" with micro-icons (highlight, comment, summarize).
Performance — Critical Rules
- Memoize every animated card with
React.memo - Each card lives in its OWN tiny Client Component — never put 3 perpetual loops in one component
- Wrap dynamic lists in
<AnimatePresence>withmode="popLayout" - No re-renders on the parent layout when cards animate
- Cleanup intervals in
useEffectreturns
Spring Defaults for Bento
const springSmooth = { type: 'spring', stiffness: 100, damping: 20 }; // standard
const springSnappy = { type: 'spring', stiffness: 400, damping: 30 }; // taps
const springOvershoot = { type: 'spring', stiffness: 300, damping: 12 }; // notifications, badgesCommon Bento Mistakes
- ❌ All cards the same size (1:1:1) — must be asymmetric
- ❌ No motion in any card — must be alive
- ❌ Title INSIDE the card — put it OUTSIDE, below
- ❌ Default
rounded-2xl— userounded-[2.5rem] - ❌
shadow-md— use diffusion shadow - ❌ Padding under
p-6— needs to breathe - ❌ Putting all 5 archetypes in one section — pick 3-4 max
- ❌ Same archetype on every card — vary them
Mobile Behavior
Below md:, the bento grid collapses:
- All
col-spanbecomes 1 - Stack vertically
- Disable horizontal carousels (use static grid)
- Reduce padding to
p-6 - Keep perpetual motion ONLY on the most prominent card (performance)
Color & Contrast Reference
Color is where the average AI design dies. Default purple gradients on white, washed-out grays, oversaturated CTAs — every one is a tell. This reference replaces those defaults with calibrated, intentional choices.
The Core Rule
Maximum ONE accent color. Saturation under 80%.
Two accents = visual noise. Three = chaos. Pick one and let neutrals carry the rest.
Exception: Maximalist / Editorial / Poster contexts
When the project type is a poster, music/festival/event design, editorial layout, or DESIGN_VARIANCE is ≥ 8 (artsy / chaotic), you may use a curated 3-color palette instead of one accent. Rules for this exception:
- The palette must be intentional and named (e.g., "70s warm: terracotta + mustard + cream + charcoal text")
- One color is still dominant (~50%), one is secondary (~30%), one is accent (~15%), neutral text (~5%)
- Never random Tailwind defaults — pick the OKLCH values intentionally
- Still no purple→blue gradient. That ban is absolute.
Default behavior remains: ONE accent. Only relax for genuine maximalist contexts.
The Forbidden Palettes
These scream "AI generated":
- Purple → blue gradient on white. The single biggest tell. Banned.
- Pure black `#000000` on pure white `#FFFFFF`. Use Zinc-950 / Off-Black on Zinc-50 / Off-White.
- Pastel purple + lavender + lilac combos ("the lila cliché"). Banned.
- Neon green / electric purple as primary. Use as 1px accent only.
- Gray text on a colored background. Always tint gray with the background hue.
- `bg-gray-100` everywhere as the default neutral. Use Zinc, Slate, Stone, or Neutral instead — and stay consistent.
Approved Foundations
Tinted neutrals — pick ONE family per project
- Zinc — coolest, most neutral, works for premium SaaS
- Slate — slight blue undertone, works for tech
- Stone — slight warm undertone, works for editorial / luxury
- Neutral — true gray, works for brutalist / industrial
Never mix families within a project. If you start with Zinc, finish with Zinc.
Accent colors that don't suck
Pick ONE per project. All should be desaturated to ~70-80% from their pure form:
- Emerald (
#059669/oklch(0.65 0.15 150)) - Electric blue (
#0066FFdesaturated to#1E5FCC/oklch(0.55 0.18 250)) - Deep rose (
#E11D48desaturated to#BE3554/oklch(0.55 0.18 10)) - Terracotta (
#C45A3A/oklch(0.55 0.13 35)) - Mustard (
#CA8A04/oklch(0.65 0.13 75)) - Aubergine (
#5B2A86desaturated /oklch(0.35 0.12 300))
OKLCH Over HSL
Use OKLCH for color definition. It produces perceptually uniform colors and predictable adjustments.
:root {
--bg: oklch(0.99 0.005 60); /* off-white, warm tint */
--fg: oklch(0.15 0.01 60); /* off-black, matched tint */
--muted: oklch(0.55 0.01 60); /* matched neutral */
--accent: oklch(0.55 0.15 150); /* singular emerald */
--border: oklch(0.92 0.005 60);
}
@media (prefers-color-scheme: dark) {
:root {
--bg: oklch(0.12 0.005 60);
--fg: oklch(0.98 0.005 60);
--muted: oklch(0.65 0.01 60);
--accent: oklch(0.65 0.15 150);
--border: oklch(0.22 0.005 60);
}
}Tinted Shadows
Default box-shadow: 0 0 black is dead. Tint shadows toward the background hue.
/* Bad */
.card { box-shadow: 0 4px 12px rgba(0,0,0,0.1); }
/* Good — tinted toward warm bg */
.card { box-shadow: 0 4px 12px oklch(0.4 0.02 60 / 0.08); }
/* Diffusion shadow for premium feel */
.card { box-shadow: 0 20px 40px -15px oklch(0.2 0.02 60 / 0.05); }Color in Dark Mode
- Don't use pure black backgrounds. Use
oklch(0.12 0.005 H)where H matches your hue family. - Borders need to lift.
oklch(0.22 0.005 H)is yourbordertoken in dark mode. - Accents need MORE saturation in dark mode. Bump to
oklch(0.7 0.18 H). - Avoid white-on-black. Use
oklch(0.95 0.005 H)to match background tint.
Status Colors
- Success: Emerald
oklch(0.6 0.14 150)— never lime - Warning: Amber
oklch(0.7 0.15 75)— never yellow - Error: Rose
oklch(0.55 0.18 10)— never pure red - Info: Sky
oklch(0.6 0.12 230)— never neon blue
Anti-Slop Color Audit
Run through this before shipping:
- [ ] Is there a purple-blue gradient? Remove it.
- [ ] Is
#000or#FFFused directly? Replace with tinted neutrals. - [ ] Is gray text on a colored background? Tint the gray.
- [ ] Is the accent color used more than 3 times per screen? Reduce.
- [ ] Are shadows untinted? Add hue.
- [ ] Is success/error using the wrong palette family? Realign.
- [ ] Are borders pure gray? Tint to match background.
- [ ] Saturation under 80% on the primary accent?
Examples By Aesthetic
| Aesthetic | Bg | Fg | Accent |
|---|---|---|---|
| Premium SaaS | Zinc 50 | Zinc 950 | Emerald 600 |
| Brutalist | White | Black | Electric Blue |
| Luxury | Stone 50 | Stone 950 | Aubergine |
| Editorial | Cream #F5F1E8 | Charcoal | Deep Rose |
| Retro-futuristic | Black | Off-white | Mustard |
| Industrial | Neutral 100 | Neutral 900 | Terracotta |
Component Library Reference
Don't build everything from scratch. Pull production-quality building blocks from these libraries — then customize so they don't look like default shadcn.
The Three Libraries
1. React Bits — Animated components
110+ components — text animations, UI elements, animated backgrounds. Lightweight, tree-shakeable, copy-paste ready. Four variants: JS-CSS, JS-TW, TS-CSS, TS-TW.
When to use React Bits
- Animated text effects (text scramble, kinetic marquee, gradient stroke, blur reveal)
- Animated backgrounds (mesh gradients, particle fields, grid glow)
- Hover-aware components (parallax tilt, magnetic, holographic foil)
- Scroll-driven reveals
Install via shadcn
npx shadcn@latest add @react-bits/BlurText-TS-TW
npx shadcn@latest add @react-bits/SplitText-TS-TW
npx shadcn@latest add @react-bits/Aurora-TS-TW
npx shadcn@latest add @react-bits/MagicBento-TS-TWReact Bits Tools (free, browser-based)
- Background Studio — explore animated backgrounds, export as video/image/code
- Shape Magic — inner rounded corners, export as SVG / React / clip-path
- Texture Lab — 20+ effects (noise, dithering, ASCII), export high quality
Use these when designing background atmosphere or texture overlays.
2. 21st.dev Magic — Production components via MCP
100+ polished React components — buttons, navbars, hero sections, cards, footers — all pre-designed. Connected as an MCP server.
When to use 21st.dev
- Standard UI building blocks: navbar, footer, pricing, FAQ, testimonials
- Auth screens, settings pages, forms
- Marketing patterns: hero, features, CTA, social proof
- When you need "looks designed" but don't have time to design from scratch
Trigger from chat
After installing the MCP, just ask in plain English:
"use 21st-dev magic to add a sticky navbar with a search command palette"
The MCP returns the component code. Don't paste it raw — apply your project's color tokens, font tokens, and motion patterns before committing.
Install (MCP setup in ~/.claude.json)
"21st-dev-magic": {
"command": "npx",
"args": ["-y", "@21st-dev/magic@latest"],
"env": { "API_KEY": "YOUR_KEY_FROM_21ST_DEV_CONSOLE" }
}Get the API key at https://21st.dev/magic/console (free tier exists).
3. shadcn/ui — The structural primitives
The base layer for forms, dialogs, dropdowns, tables. Never use defaults. Always customize:
- Radii: Default
rounded-mdis dead. Userounded-[14px]orrounded-[2.5rem]. - Colors: Override CSS variables in
globals.cssto match your project palette. - Shadows: Replace shadcn's default
shadow-mdwith tinted diffusion shadows. - Motion: Wrap dialogs/popovers in Framer Motion variants.
Install
npx shadcn@latest init
npx shadcn@latest add button input dialog dropdown-menuDecision Tree
| Need | Use |
|---|---|
| Hero text with kinetic effect | React Bits |
| Animated background atmosphere | React Bits |
| Standard navbar / pricing / hero layout | 21st.dev Magic |
| Form field, dialog, dropdown, table | shadcn (customized) |
| Bento grid feature section | Build from scratch (see bento-engine.md) |
| Marketing hero (split / asymmetric) | Build from scratch (see layout.md) |
| Feature row with motion | Build from scratch + React Bits accents |
Customization Checklist (Before Shipping ANY Library Component)
- [ ] Replaced default radii with project's chosen radius
- [ ] Replaced default colors with project's CSS variables
- [ ] Replaced default fonts with project's font stack
- [ ] Replaced default shadows with tinted versions
- [ ] Wrapped interactive parts in spring-physics motion
- [ ] Removed any default
Inter,Roboto, or system font references - [ ] Removed default purple/blue accent colors
- [ ] Verified mobile collapse works
- [ ] Verified empty / loading / error states exist
Anti-Patterns
- ❌ Using default shadcn unmodified (looks like every other shadcn site)
- ❌ Pasting 21st.dev components without retokenizing colors/fonts
- ❌ Mixing 5+ component libraries in one project (visual chaos)
- ❌ Pulling React Bits backgrounds AND complex Framer animations on the same screen (motion overload)
- ❌ Using component library defaults when the brief was "premium / distinctive"
Recommended Stack Per Project Type
| Project type | shadcn | 21st.dev | React Bits | Custom % |
|---|---|---|---|---|
| SaaS landing | ✓ | ✓ (nav, pricing) | ✓ (text fx) | 40% |
| Analytics dashboard | ✓ | ✓ (settings, table) | — | 60% |
| Portfolio | — | — | ✓ heavy | 80% |
| E-commerce | ✓ | ✓ (cards, cart) | — | 50% |
| Editorial / blog | ✓ minimal | — | ✓ text fx | 70% |
| Music festival poster | — | — | ✓ heavy | 90% |
Copy & Microcopy Reference
Bad copy gives away AI output as fast as Inter does. "Elevate your seamless next-gen experience" is the textual equivalent of a purple gradient. This reference fixes that.
The Banned Vocabulary
These words signal AI output. Replace every instance:
| Banned word | Why it sucks | Use instead |
|---|---|---|
| Elevate | Empty buzzword | Concrete verb (improve, refine, ship) |
| Seamless | Means nothing, used everywhere | Cut, or describe what's actually smooth |
| Unleash | Sales-speak | Cut, or use a verb tied to user benefit |
| Next-gen | Generic | Be specific about what makes it new |
| Game-changing | Cliché | State the actual change |
| Revolutionary | Almost never true | Cut, describe the difference |
| Cutting-edge | Self-congratulatory | Show, don't tell |
| Robust | Vague | Specific quality (handles 1M req/s, etc.) |
| Powerful | Says nothing | Specific capability |
| Innovative | Self-applied is suspicious | Concrete novel behavior |
| Streamline | Corporate-speak | Specific shortcut/improvement |
| Empower | HR-speak | Concrete verb |
| Best-in-class | Marketing slop | Cite actual benchmark or cut |
| World-class | Same | Same |
| Solution | Generic | Name the specific tool/feature |
| Holistic | Hollow | Describe what's covered |
| Synergy | 2002 corporate | Cut entirely |
| Leverage (verb) | Consultant-speak | "Use" — works fine |
| In the realm of... | AI tone | State the topic directly |
| Whether you're... | Listicle opener | State who it's for plainly |
| From X to Y | Lazy spectrum | Specific examples |
Hero Headline Patterns
What works
- Specific outcome: "Catch p99 spikes before PagerDuty does."
- Anti-cliché twist: "We don't make the dashboard prettier. We make it stop lying."
- Plainspoken benefit: "Receipts. In your inbox. Within 90 seconds."
- Confident understatement: "Logs that don't ruin your weekend."
- Direct address: "You ship faster with fewer bugs."
What doesn't
- ❌ "Elevate your business with our seamless solution"
- ❌ "Unleash the power of next-gen analytics"
- ❌ "Revolutionary platform for modern teams"
- ❌ "From idea to launch — we've got you covered"
Subhead Patterns
Always concrete. Often specific numbers or named technologies.
✅ "Drop-in replacement for Datadog logs. 1/4 the price." ✅ "TypeScript SDK, REST API, and a CLI. No SDK lock-in." ✅ "Tested at 50M events/day. Production since 2024."
❌ "A comprehensive solution for all your observability needs" ❌ "Built for the modern enterprise" ❌ "Powering teams worldwide"
Button Labels
Always (good defaults)
- "Get started" — for free signups
- "Start free trial" — when there IS a trial
- "See pricing" — for SaaS that won't show pricing inline
- "Talk to sales" — only if you're enterprise-only
- "Read the docs" — for dev-tooling
- "Watch demo (90s)" — include the duration
Specific over generic
- ✅ "Send invoice" → not "Submit"
- ✅ "Save draft" → not "Save"
- ✅ "Cancel reservation" → not "Cancel"
- ✅ "Delete project" → not "Delete"
Banned
- ❌ "Click here"
- ❌ "Submit" (always more specific)
- ❌ "Learn more" (use "See how it works" or specific)
- ❌ "Sign up now" (the "now" is filler)
- ❌ ALL CAPS (unless brand intentional)
Empty States
The default AI empty state is "No results found." Don't.
Pattern
[Title] - what's missing, in plain language
[Body] - one short line explaining how to populate it
[Action button] - the verb to fix itExamples
✅ Good:
No invoices yet.
Once you create your first invoice, it'll show up here.
[Create invoice]
✅ Good:
You're all caught up.
No new mentions. We'll notify you when something needs your attention.
❌ Bad:
No items found
Try adjusting your filters
[Clear filters]
Error Messages
Errors should be honest, specific, and actionable.
Pattern
[What broke, plainly]. [How to fix it, if known].Examples
✅ Good:
"We couldn't reach the payment processor. Check your connection and try again — we didn't charge you."
✅ Good:
"Email format looks off. Should be like name@example.com."
✅ Good:
"Project deleted. Undo within 30 seconds."
❌ Bad:
"Something went wrong. Please try again."
"Error: 500 Internal Server Error"
"Invalid input"
Loading States
Don't say "Loading..." Show what's happening.
✅ "Fetching the last 30 days..." ✅ "Compiling your TypeScript (47 files)..." ✅ "Sending invoice to mira@lattice.co..."
❌ "Loading..." ❌ "Please wait..."
Microcopy in Forms
Field labels
- Concrete: "Email" not "Email address"
- Imperative: "Project name" not "Name your project"
Placeholders (NOT labels — use real labels)
- Show format:
name@company.com, notEnter email - Show example:
Q3 launch plan, notEnter project name
Helper text (under field, before user types)
- One short line max
- Anticipates the question they'd ask
✅ "We'll only use this to send the receipt." ✅ "8+ characters, including a number."
Validation errors (under field, after submit)
- Lead with what's wrong
- Then how to fix
✅ "Password too short. Add 4 more characters." ✅ "That email's already in use. Sign in instead?"
Brand / Product Names (When Inventing Placeholders)
The skill bans "Acme", "Nexus", "SmartFlow." Use these patterns instead:
Premium SaaS
- Lattice & Co.
- Provenance Studio
- Tessellate Labs
- Rivulet
- Fathom
- Cinder & Spar
- Threadwell
- Northstar Compute
Editorial / agency
- Polaris Atelier
- Margin Press
- The Saturn Office
- Foundry & Foil
- Halftone Magazine
Industrial / dev
- Westmark Tools
- Sparkplug Systems
- Apsis Dev
- Beacon Logs
- Raven CI
People names (avoid John Doe / Sarah Chen)
✅ Mira Okonkwo, Theo Vasquez, Lin Park-Aboagye, Kavi Subramanian, Aitana Reyes, Bram Holst, Yuna Ozaki, Imani Bright, Casper Holm, Noor Kazemi
❌ John Doe, Jane Smith, Sarah Chen, Mike Wilson, Jack Su
Final Copy Audit
- [ ] No banned words (Elevate, Seamless, Unleash, Next-Gen, etc.)
- [ ] Hero headline is specific and concrete (not generic benefit)
- [ ] Button labels use specific verbs (not "Submit" / "Click here")
- [ ] Empty states designed (not "No results found")
- [ ] Error messages explain what broke and how to fix
- [ ] Loading states show progress detail
- [ ] Form placeholders show format, not duplicate labels
- [ ] Sample data uses realistic, varied names (no John Doe)
- [ ] Brand placeholders aren't Acme / Nexus
- [ ] No emojis as decoration in copy (use Phosphor / Radix icons)
DESIGN.md — Persistent Design System
When you finish a build (or a meaningful chunk of one), write or update a DESIGN.md at the project root. This is the project's persistent design memory: aesthetic direction, tokens, motion config, component inventory.
Why this matters: without DESIGN.md, every Claude session re-decides the design from scratch. Three sessions later, your fonts drift, your accent color shifts, your motion feels different. DESIGN.md prevents drift.
When to write/update DESIGN.md
- First build: create it. Establish the aesthetic + tokens.
- Subsequent builds in the same project: read it FIRST, follow it, only update if a deliberate evolution.
- User says "this is different from last time": treat that as a bug and re-align with DESIGN.md.
Where to put it
Project root: ./DESIGN.md. Same level as package.json. Always there. Don't bury it in /docs.
The template
Copy this verbatim, fill in the project's actual choices:
# Design
> Auto-generated and maintained by frontend-god-mode.
> Source of truth for typography, color, motion, layout, and component tokens.
> Read this BEFORE touching the UI in any subsequent session.
## Aesthetic direction
One sentence describing the vibe.
e.g. "Premium SaaS — operational, engineer-trusted, Vercel-core meets terminal."
e.g. "Editorial luxury — typographer's portfolio, italic serif eyebrows, generous whitespace."
e.g. "70s retro maximalist — warm palette, broken grid, tactile texture overlay."
## Dials
- DESIGN_VARIANCE: 8 / 10 (1 = perfect symmetry, 10 = artsy chaos)
- MOTION_INTENSITY: 6 / 10 (1 = static, 10 = cinematic physics)
- VISUAL_DENSITY: 4 / 10 (1 = airy gallery, 10 = cockpit)
## Type stack
- Display: Geist (variable, weights 400-700)
- Body: Geist
- Mono: Geist Mono
- Loaded via: `next/font/google`
- Optical features enabled: `font-feature-settings: "ss01", "cv11"`
Banned in this project: Inter, Roboto, Arial, system-ui, serif on dashboards.
## Color tokens (OKLCH)
:root { --bg: oklch(0.12 0.005 250); / off-black, cool tint / --fg: oklch(0.98 0.005 250); / off-white / --muted: oklch(0.65 0.01 250); / tinted gray / --border: oklch(0.22 0.005 250); --accent: oklch(0.65 0.18 80); / amber / --success: oklch(0.6 0.14 150); / emerald / --error: oklch(0.55 0.18 10); / rose / }
Banned in this project:
- Pure #000 / #FFF (use tinted neutrals above)
- Purple-to-blue gradients (the AI tell)
- More than ONE accent (or 3 max in maximalist mode — see DESIGN_VARIANCE)
## Shadows
--shadow-warm-lift: 0 1px 0 rgba(255,255,255,0.04) inset, 0 0 0 1px rgba(251,191,36,0.06), 0 30px 80px -30px rgba(8,6,2,0.8);
Always tint shadows toward bg hue. No pure-black drops.
## Motion
- Default spring: `{ type: "spring", stiffness: 100, damping: 20 }`
- Tap spring: `{ stiffness: 400, damping: 30 }`
- Hero spring: `{ stiffness: 60, damping: 18 }`
- Stagger: `0.08s` between children, `0.1s` initial delay
- Easing fallback (CSS): `cubic-bezier(0.16, 1, 0.3, 1)`
- Banned: linear easing, bounce/elastic, animating width/height
Library: framer-motion@12.x (or motion/react if migrated)
## Layout
- Container: `max-w-[1400px] mx-auto px-6 md:px-10`
- Reading width: `max-w-[65ch]`
- Section padding: `py-16 md:py-24` (daily density) / `py-24 md:py-32` (airy)
- Hero pattern: split-screen 60/40 (left text, right asset) — NOT centered
- Feature row: bento, asymmetric col/row spans — NEVER 3 equal cards
- Mobile: collapse all `grid-cols-N` to `grid-cols-1` below `md:`
## Component inventory
shadcn: Button, Card, Dialog, DropdownMenu, Input
21st.dev: navbar pattern, pricing pattern (customized to tokens above)
React Bits: Aurora background (hero only), BlurText (case-study reveals)
Custom: LogViewer mockup, BentoCard, MetricRow
## Project-specific bans
- No 99.99% / round-number stats (use messy: 99.987%)
- No "Acme" / "Nexus" / "John Doe" placeholder data
- No emojis (Phosphor icons only, stroke 1.5)
- No `h-screen` (always `min-h-[100dvh]`)
## Brand voice (copy)
- Tone: direct, technical, slightly dry — not chirpy
- Banned: elevate, seamless, unleash, next-gen, game-changing
- Headline pattern: specific outcome with a number ("Search a billion log lines in under 200 ms")
- Button labels: specific verbs ("Send invoice" not "Submit")
## Accessibility floor
- WCAG 2.2 AA contrast on all body copy (≥ 4.5:1)
- Focus-visible rings on every interactive element
- `prefers-reduced-motion` respected
- 44×44px minimum touch targets on mobile
## Last updated
YYYY-MM-DD by [session description, e.g. "added pricing teaser"]Authoring rules
1. Be specific. "Geist" not "modern sans-serif." OKLCH values not "neutral gray." 2. List bans inline so any reader (human or AI) sees them immediately. 3. Keep it ONE file. Don't split into design/typography.md, design/color.md. One file is the point. 4. Append to "Last updated" every time you touch it, with a one-line description of what changed. 5. Read it BEFORE building in any subsequent session. Quote from it when explaining decisions.
What NOT to put in DESIGN.md
- Marketing copy (lives in
/contentor actual pages) - API contracts (lives in
/docs/api) - Long rationales for why a choice was made (one line is enough — "amber chosen for warmth against zinc")
- Anti-patterns from the global skill (those are in
references/anti-slop.md, not project-specific)
Anti-drift contract
If a future Claude session generates UI that violates DESIGN.md, the user can say:
"this doesn't match DESIGN.md"
And Claude must: 1. Re-read DESIGN.md 2. Identify the specific token/rule that was violated 3. Fix the violation (don't update DESIGN.md to match the bad output)
DESIGN.md is the source of truth, not a rough guideline.
Layout & Spatial Reference
Generic AI sites get caught here: centered hero + 3 equal feature cards + footer. Refuse this default. Layout is where you signal taste.
The Layout Bans
- Centered hero with H1 + subtitle + 2 buttons. Banned when DESIGN_VARIANCE > 4. Use split-screen (50/50), left-aligned content + right asset, or asymmetric whitespace.
- 3 equal cards in a row as the features section. Use 2-col zig-zag, asymmetric bento, horizontal scroll, or
divide-yrows. - Cards inside cards inside cards. Pick the lowest depth that communicates the hierarchy.
- `h-screen` on full-height sections. Always
min-h-[100dvh]. - Flex math like
w-[calc(33%-1rem)]. Use CSS Grid:grid-cols-3 gap-6. - Containers wider than `max-w-[1400px]` without intentional reason.
- Default `gap-4` on every grid. Vary spacing — tighter for data, looser for editorial.
Hero Patterns That Don't Suck
Split screen (default for SaaS)
- 50/50 or 60/40
- Text on left (left-aligned, NOT center), large image/video/visual on right
- Image bleeds to edge of viewport
- Subtle fade from image into bg color (top + bottom), darkening for dark mode, lightening for light
Editorial / left-aligned
- Massive H1 left-aligned, breaking grid
- Body text starts at column 2 of a 4-col grid
- Image overlaps H1 by ~10% intentional collision
Asymmetric whitespace
- H1 in upper-left
- Subtext in lower-right
- 60% of the canvas is empty
- Works for luxury, minimalist, gallery aesthetics
Bento hero
- 60% main visual / 40% stacked metadata cards
- Mobile collapses metadata under main visual
Diagonal flow
- Content + visuals zig-zag down the page on alternating sides
- Each row breaks the grid slightly
- Works for storytelling, longer-form pages
Bento Grid (For Feature Sections)
The modern way to show "3-5 things" without 3 equal cards.
Structure
<div className="grid grid-cols-1 md:grid-cols-3 md:grid-rows-2 gap-4 max-w-7xl mx-auto p-8">
<div className="md:col-span-2 md:row-span-2 rounded-[2.5rem] bg-white p-10 border border-zinc-200/50">
{/* Hero card — biggest */}
</div>
<div className="rounded-[2.5rem] bg-white p-8 border border-zinc-200/50">
{/* Top-right */}
</div>
<div className="rounded-[2.5rem] bg-white p-8 border border-zinc-200/50">
{/* Bottom-right */}
</div>
</div>Bento principles
- Asymmetric column / row spans — never 1:1:1
- Generous internal padding —
p-8minimum,p-10for hero cards - Subtle 1px border in
border-zinc-200/50(or matched dark equivalent) - Diffusion shadow — wide, low-opacity, tinted
- Rounded `[2.5rem]` — distinctive, not the default
rounded-2xl - Labels OUTSIDE cards — title + description below the card, gallery-style
See references/bento-engine.md for the full Motion-Engine Bento spec.
Spacing System
Use Tailwind's spacing scale, but vary purposefully:
| Density target | Internal padding | Gap between blocks | Section gap |
|---|---|---|---|
| Airy (1-3) | p-12 md:p-16 | gap-8 md:gap-12 | py-24 md:py-32 |
| Daily (4-7) | p-6 md:p-8 | gap-4 md:gap-6 | py-16 md:py-24 |
| Cockpit (8-10) | p-3 md:p-4 | gap-2 md:gap-3 | py-8 md:py-12 |
Containers
- Page wrap:
max-w-[1400px] mx-auto px-6 md:px-10(default) - Reading width:
max-w-proseormax-w-[65ch]for body copy - Wide hero:
w-full(no max) for edge-to-edge visuals - Tight container:
max-w-3xl mx-autofor marketing copy
Mobile Override (Critical)
Any asymmetric / high-variance layout MUST collapse cleanly below md::
<div className="grid grid-cols-1 md:grid-cols-3 md:grid-rows-2 gap-4 px-4 md:px-10 py-12 md:py-24">- All
grid-cols-Nbecomesgrid-cols-1 - Padding drops to
px-4 py-8minimum - Display fonts scale down meaningfully (
text-4xl md:text-7xl) - No horizontal scroll allowed under any circumstances
Dashboard Hardening (VISUAL_DENSITY > 7)
Dense data UIs should not box every metric in a card.
Use these instead of cards:
- `divide-y divide-zinc-200/60` between data rows
- `border-t` to separate sections
- Negative space with monospace numbers
- Tabular columns with
font-monoand right-aligned numerics - Sparklines / trendlines inline with the metric
When to keep cards:
- Elevation has functional meaning (modal, popover, dropdown)
- Z-index communicates state (selected item)
- Visual grouping is genuinely needed
Otherwise: delete the cards.
Z-Index Hygiene
Don't spam z-50 and z-10. Use a system:
:root {
--z-base: 0;
--z-sticky-nav: 30;
--z-dropdown: 40;
--z-overlay: 50;
--z-modal: 60;
--z-toast: 70;
--z-tooltip: 80;
}Anti-Patterns Checklist
- [ ] No centered hero (when variance > 4)
- [ ] No 3 equal feature cards in a row
- [ ] No nested cards beyond depth 1
- [ ] No
h-screen(usemin-h-[100dvh]) - [ ] No flex percentage math (use Grid)
- [ ] All asymmetric layouts collapse cleanly on mobile
- [ ] No horizontal scroll on any viewport
- [ ] Spacing varies between sections (not
gap-4everywhere) - [ ] Z-index uses systemic tokens, not random
z-50s - [ ] Containers don't randomly drop to
max-w-mdormax-w-7xlwithout reason
Motion & Animation Reference
The line between $500 and $10,000 is motion. Static AI sites are obvious. Animated AI sites with linear easing and bounce-everywhere are even worse. This reference enforces premium motion.
The Four Hard Rules
1. No linear easing on UI motion. Use spring physics or premium cubic-bezier. Exception: infinite seamless carousels and shimmer/marquee loops use ease: 'linear' — they need constant velocity to feel right. Linear is banned only for entrance, exit, hover, and tap motion. 2. No `width`/`height`/`top`/`left` animation. Only transform and opacity — they're hardware-accelerated. 3. No bounce/elastic easing. It's dated. Use spring with damping: 20 for an overshoot that feels modern. 4. Perpetual animations live in their own memoized Client Component. Never trigger parent re-renders.
The Spring Defaults
When using Framer Motion (or motion), apply these spring physics across the board:
const springSnappy = { type: 'spring', stiffness: 400, damping: 30 }; // buttons, taps
const springSmooth = { type: 'spring', stiffness: 100, damping: 20 }; // standard motion
const springDramatic = { type: 'spring', stiffness: 60, damping: 18 }; // hero revealsFor CSS-only motion, use:
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); /* "easeOutExpo" — premium feel */Page Load — Staggered Orchestration
Never mount lists or grids instantly. Stagger them.
Framer Motion pattern
'use client';
import { motion } from 'framer-motion';
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: { staggerChildren: 0.08, delayChildren: 0.1 }
}
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0, transition: { type: 'spring', stiffness: 100, damping: 20 } }
};
export function StaggeredGrid({ items }) {
return (
<motion.div variants={container} initial="hidden" animate="show" className="grid gap-4">
{items.map((it, i) => (
<motion.div key={i} variants={item}>{it.title}</motion.div>
))}
</motion.div>
);
}Critical: Parent variants and children MUST live in the same Client Component tree. If data is fetched async, pass it into a centralized motion wrapper.
CSS-only fallback
.stagger-item {
opacity: 0;
transform: translateY(20px);
animation: rise 0.6s cubic-bezier(0.16, 1, 0.3, 1) forwards;
animation-delay: calc(var(--index, 0) * 80ms);
}
@keyframes rise {
to { opacity: 1; transform: none; }
}Magnetic Button Pattern
Buttons that pull subtly toward the cursor. CRITICAL: Use useMotionValue + useTransform. Never useState — it triggers re-renders on every mouse move and collapses mobile performance.
'use client';
import { useRef } from 'react';
import { motion, useMotionValue, useTransform, useSpring } from 'framer-motion';
export function MagneticButton({ children }: { children: React.ReactNode }) {
const ref = useRef<HTMLButtonElement>(null);
const x = useMotionValue(0);
const y = useMotionValue(0);
const sx = useSpring(x, { stiffness: 200, damping: 20 });
const sy = useSpring(y, { stiffness: 200, damping: 20 });
function handleMove(e: React.MouseEvent) {
const rect = ref.current?.getBoundingClientRect();
if (!rect) return;
x.set((e.clientX - rect.left - rect.width / 2) * 0.3);
y.set((e.clientY - rect.top - rect.height / 2) * 0.3);
}
function handleLeave() { x.set(0); y.set(0); }
return (
<motion.button
ref={ref}
onMouseMove={handleMove}
onMouseLeave={handleLeave}
style={{ x: sx, y: sy }}
className="..."
>
{children}
</motion.button>
);
}Tactile Feedback (:active)
Every interactive element needs a physical "push" feel:
button:active { transform: translateY(1px) scale(0.98); }In Framer:
<motion.button whileTap={{ scale: 0.98, y: 1 }}>...</motion.button>Layout Transitions
Use layout and layoutId for smooth re-ordering and shared element transitions.
<motion.div layout layoutId={`card-${id}`} transition={springSmooth}>
{content}
</motion.div>This handles list reordering, expand-to-modal morphs, and shared element transitions for free.
Perpetual Micro-Interactions
When MOTION_INTENSITY > 5, embed continuous loops in standard components. Examples:
Pulse status dot
<motion.div
animate={{ scale: [1, 1.15, 1], opacity: [1, 0.7, 1] }}
transition={{ repeat: Infinity, duration: 2, ease: 'easeInOut' }}
className="w-2 h-2 rounded-full bg-emerald-500"
/>Shimmer loading
<motion.div
animate={{ backgroundPosition: ['0% 0%', '100% 0%'] }}
transition={{ repeat: Infinity, duration: 1.5, ease: 'linear' }}
className="bg-[linear-gradient(90deg,transparent,rgba(255,255,255,0.1),transparent)] bg-[length:200%_100%]"
/>Float
<motion.div
animate={{ y: [0, -8, 0] }}
transition={{ repeat: Infinity, duration: 3, ease: 'easeInOut' }}
/>Typewriter (multi-prompt cycling)
Cycle through 3-4 prompts with blinking cursor + 1.5s pause between cycles. Used in hero search bars or AI command inputs.
Carousel (infinite seamless)
<motion.div
animate={{ x: ['0%', '-100%'] }}
transition={{ repeat: Infinity, duration: 30, ease: 'linear' }}
className="flex"
>
{[...items, ...items].map(...)}
</motion.div>Scroll-Driven Effects
Use Framer's useScroll + useTransform. Never window.addEventListener('scroll') — kills performance.
const { scrollYProgress } = useScroll({ target: ref, offset: ['start end', 'end start'] });
const y = useTransform(scrollYProgress, [0, 1], [0, -100]);
return <motion.div style={{ y }} />;For complex scrolltelling beyond Framer (parallax sequences, ScrollTrigger pinning), use GSAP. Never mix GSAP with Framer in the same component tree — wrap GSAP in strict useEffect cleanup blocks and isolate to full-page sections or canvas backgrounds.
Performance Guards
- DOM cost: Apply grain/noise filters only to fixed
pointer-events-noneoverlays. Never on scrolling containers. - `will-change: transform` sparingly — only on elements actively animating.
- Always cleanup
useEffectanimations:
useEffect(() => {
const ctx = gsap.context(() => { /* ... */ }, ref);
return () => ctx.revert();
}, []);- Memoize perpetual-motion components with
React.memoand isolate them. - `<AnimatePresence>` wraps lists with mount/unmount animations. Always include
mode="popLayout"if items can reorder.
Reduced Motion
Always respect:
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}In Framer Motion:
import { useReducedMotion } from 'framer-motion';
const reduced = useReducedMotion();
const transition = reduced ? { duration: 0 } : springSmooth;Anti-Patterns
- ❌ Animating
width,height,top,left— repaints, not GPU - ❌
useStatefor hover position — re-renders kill mobile - ❌ Linear easing on UI — feels robotic
- ❌ Bounce / elastic — feels dated (2014 mobile vibes)
- ❌ Custom mouse cursor replacements — outdated, breaks accessibility
- ❌ Mixing GSAP + Framer in the same component
- ❌
scrollYlisteners on window — useuseScroll - ❌ Animation duration over 600ms for UI motion (heroes can go longer)
Final Motion Audit
- [ ] Spring physics on all interactive motion
- [ ] No
width/heightanimations - [ ] List reveals are staggered (not instant mount)
- [ ] Perpetual loops are in memoized leaf Client Components
- [ ] Reduced-motion respected
- [ ]
useEffectcleanup on every animation hook - [ ] No bounce/elastic easing
- [ ] No custom mouse cursors
- [ ] All
motion.*components have'use client'at top of file
Setup Walkthrough
Run this when the user asks to "set up" their environment, says "/website-builder-setup", or you detect a fresh project with no design dependencies. Walk them through ONE step at a time. Be encouraging. Assume zero coding experience.
If any step fails, don't stop. Acknowledge it, give them the manual command, and keep moving.
---
Step 0 — Check Prerequisites
Tell the user:
Before we start, let me check what you have installed.
Run silently:
node --version 2>&1 && npm --version 2>&1- If Node is installed → "You're good. Node and npm are ready. Let's go."
- If Node is missing → tell them:
You need Node.js first. Go to https://nodejs.org and grab the LTS version. Install it, restart your terminal, then come back and we'll continue. Takes 2 minutes.
Stop here if Node is missing.
---
Step 1 of 4 — UI/UX Pro Max (optional augmentation)
Tell them:
Step 1 of 4: UI/UX Pro Max (optional)
>
If you want extra design vocabulary on top of frontend-god-mode, UI/UX Pro Max is a Claude skill that adds 50+ styles, 161 palettes, 57 font pairings. It's optional — frontend-god-mode already covers the essentials. But if you want it, install via:
>
```bash
npx skills add @nextlevelbuilder/ui-ux-pro-max-skill
```
>
If that command doesn't work in your environment, skip this step. The base frontend-god-mode skill works on its own.
This step is optional. Don't block on failure. Move to Step 2 immediately whether they install or skip.
---
Step 2 of 4 — Framer Motion
Tell them:
Step 2 of 4: Framer Motion
>
This is what teaches me how to animate things — smooth page transitions, hover effects, scroll-triggered reveals. The stuff that makes a $500 site look like a $10,000 site.
>
Installing now...
Run:
npm install framer-motion 2>&1- On success → "Done. Your sites will move now."
- On failure → "Couldn't install it here — that's usually because we're not in a project folder yet. When you start a project, I'll add it then. Moving on."
---
Step 3 of 4 — React Bits
Tell them:
Step 3 of 4: React Bits
>
This is a library of 110+ animated components — text effects, animated backgrounds, hover interactions. We pull these in via shadcn when we need them, so no upfront install. I'll just call them like:
>
```bash
npx shadcn@latest add @react-bits/BlurText-TS-TW
```
>
when we're building. Nothing for you to do here. Continuing.
(Just confirm understanding, no install.)
---
Step 4 of 4 — 21st.dev Magic
Tell them:
Step 4 of 4: 21st.dev Magic
>
This connects me to a library of 100+ production-ready React components — buttons, navbars, hero sections. I pull from these when we need a "looks designed" building block fast.
>
This one needs a free API key. Here's how:
>
1. Go to https://21st.dev/magic/console
2. Sign up or log in (it's free)
3. Copy your API key
4. Paste it here when ready.
WAIT for the user to provide the key. Don't proceed without it.
When they paste the key, first ask them where they want it scoped:
Do you want this scoped to just this project, or available everywhere?
>
a. Project-only (recommended for testing) — writes a.mcp.jsonat the current project root. Adds it to.gitignoreso the key never gets committed.
>
b. User-wide (everywhere) — writes to ~/.claude.json. Available in every project Claude Code opens.If they pick a (project-only):
1. Detect project root: nearest ancestor directory with package.json, .git, or fall back to current working directory. 2. Write .mcp.json at the root (create if missing, merge if exists):
{
"mcpServers": {
"21st-dev-magic": {
"command": "npx",
"args": ["-y", "@21st-dev/magic@latest"],
"env": { "API_KEY": "THEIR_KEY_HERE" }
}
}
}3. Append .mcp.json to .gitignore if not already present (create .gitignore if missing).
If they pick b (user-wide):
1. Read ~/.claude.json. 2. Find or create the mcpServers object. 3. Add the same 21st-dev-magic entry above. 4. Save the file.
Then tell them:
21st.dev is wired up. You'll need to restart Claude Code for it to load — close and reopen your terminal after we finish. The first launch will show a security prompt asking to approve the MCP — say yes.
---
Step 5 — Done
Tell them:
You're all set. Here's what you just installed:
>
- UI/UX Pro Max — 50+ styles, 161 palettes, 57 font pairings
- Framer Motion — production-grade animations
- React Bits — 110+ animated components, ready via shadcn
- 21st.dev Magic — 100+ production components via MCP
>
Plus, this skill itself (frontend-god-mode) gives me the taste rules to use them well — typography, color, layout, motion, anti-slop guards.
>
To build your first site, just describe what you want:
>
- What does your business / product do?
- Who's the audience?
- What vibe? (luxury / brutalist / playful / minimal / editorial / retro-futuristic / etc.)
>
Try something like:
>
> "Build me a landing page for a small-batch coffee roaster targeting at-home espresso enthusiasts. Editorial vibe, warm tones, lots of whitespace, subtle scroll animations."
>
Restart Claude Code first so 21st.dev loads. Then let's build something that doesn't look AI-generated.
---
Walkthrough Rules
- One step at a time. Never dump all 4 steps in one message.
- Encouraging tone. Casual, friendly, no jargon.
- Don't stop on failure. Acknowledge, give manual command, keep moving.
- Wait for the API key at Step 4 — don't fabricate one or skip.
- Confirm understanding at React Bits step before continuing.
- End with a concrete prompt suggestion so they know what to ask next.
Typography Reference
Typography is the single highest-leverage design choice. Get it right and everything else looks expensive. Get it wrong and the whole thing looks like a default ChatGPT artifact.
The Banned List
Never use these unless the user explicitly demands them:
- Inter — the AI tell. Banned for "premium" and "creative" projects.
- Roboto, Arial, Helvetica, system-ui — generic, lifeless.
- Times New Roman, Georgia as body — dated.
- Comic Sans, Papyrus, Lobster — unless explicitly requested for ironic/playful contexts.
The Approved Stack — Pick One Pairing Per Project
Match the pairing to the aesthetic direction. Do not cross pairings. Do not always pick Space Grotesk.
For premium SaaS / dashboards / software UIs
- Display + body: Geist + Geist Mono
- Display + body: Satoshi + JetBrains Mono
- Display + body: Cabinet Grotesk + Inter Tight (only Tight, never plain Inter)
- Mono numbers are mandatory for tabular data (
font-monoon metrics).
For creative / editorial / agency
- Display: PP Editorial New (italic), Reckless, Tiempos Headline, Söhne Breit
- Body: Söhne, Söhne Buch, Inter Display (the display cut, NOT regular Inter)
- Accent: Italic serif eyebrow + sans body for editorial pop
For brutalist / industrial
- Display: Neue Haas Grotesk, Akkurat Mono, IBM Plex Mono
- Body: Inter Tight, Söhne Mono
- All-caps with tight tracking for headers
For luxury / refined
- Display: GT Sectra, Canela, PP Editorial New
- Body: Söhne, Inter Display, Cormorant Garamond (body italic)
For playful / toy
- Display: Cooper BT, Bagel Fat One, Outfit at heavy weights
- Body: Outfit, Plus Jakarta Sans
For retro-futuristic / tech
- Display: Space Grotesk (use sparingly — overused), Departure Mono, JetBrains Mono
- Body: Inter Tight, Söhne Mono
Hierarchy Rules
- Display H1:
text-4xl md:text-6xl lg:text-7xl tracking-tighter leading-[0.95] - H2:
text-2xl md:text-4xl tracking-tight leading-tight - H3:
text-xl md:text-2xl tracking-tight - Body:
text-base leading-relaxed max-w-[65ch]— never wider than 65 characters - Small / caption:
text-sm text-zinc-500 tracking-wide uppercase(uppercase only for labels under 4 words)
Anti-Patterns
- Oversized H1 that screams. Control hierarchy with weight + color, not just scale.
- Serif on a dashboard. Banned. Dashboards = clean sans-serif only.
- Gradient text fill on large headers. Cliché. Use solid color with tight tracking instead.
- All-caps body copy. Never. All-caps is for labels < 4 words.
- `text-center` on long paragraphs. Left-aligned is more readable. Center only short hero copy or eyebrows.
- Ignoring optical kerning. Display headers need
tracking-tighterortracking-tight.
Loading Fonts (Next.js)
// app/layout.tsx
import { Geist, Geist_Mono } from 'next/font/google';
// or for non-Google fonts, use a self-hosted local font
import localFont from 'next/font/local';
const geist = Geist({ subsets: ['latin'], variable: '--font-sans' });
const geistMono = Geist_Mono({ subsets: ['latin'], variable: '--font-mono' });Font Licensing & Free Fallbacks
Some recommended fonts are paid/licensed. Use these free fallbacks if the user can't or won't license:
| Paid font | Free fallback |
|---|---|
| PP Editorial New | Instrument Serif (Google Fonts) |
| Söhne | Inter Tight (Google) — only the Tight cut, never plain |
| GT Sectra | Fraunces (Google), or Cormorant Garamond |
| Cabinet Grotesk | Inter Tight, or Outfit |
| Reckless | Newsreader (Google) |
| Tiempos Headline | Source Serif Pro (Google) |
| Neue Haas Grotesk | Inter Tight (only Tight) |
| Akkurat Mono | JetBrains Mono (Google) |
| Cooper BT | Bagel Fat One (Google) |
| Söhne Mono | JetBrains Mono |
Free fonts that always work (Google Fonts):
- Geist + Geist Mono — premium SaaS default
- JetBrains Mono — mono UI/data
- Outfit — playful, modern
- Plus Jakarta Sans — clean body
- Instrument Serif — editorial display
- Fraunces — luxury/editorial display
- Newsreader — editorial body
- Departure Mono — retro tech
If serving a paid font, always use next/font/local (not next/font/google) and host the .woff2 yourself.
Font Pairing Examples That Always Work
| Aesthetic | Display | Body | Mono |
|---|---|---|---|
| Premium SaaS | Geist | Geist | Geist Mono |
| Editorial agency | PP Editorial New | Söhne | — |
| Brutalist | Neue Haas Grotesk | Inter Tight | IBM Plex Mono |
| Luxury | GT Sectra | Söhne | — |
| Playful | Outfit (Heavy) | Plus Jakarta Sans | — |
| Retro tech | Departure Mono | Inter Tight | JetBrains Mono |
OpenType Tips
- Enable tabular numbers for data:
font-feature-settings: 'tnum' - Enable contextual alternates for display:
font-feature-settings: 'ss01', 'cv01' - For italic serif eyebrows above headers: works wonders on editorial layouts
Mandatory Final Check
Before shipping:
- [ ] Body line-height is at least 1.5
- [ ] Body width is capped at
max-w-[65ch] - [ ] Display tracking is tightened (
tracking-tightminimum) - [ ] No Inter (regular cut) anywhere in the project
- [ ] Hierarchy reads in three glances: title, subtitle, body
- [ ] At most TWO type families per project (display + body, plus optional mono)