| name | building-ui-with-shadcn |
| description | Builds accessible, consistent UI components using shadcn/ui with Tailwind v4 following strict design system guidelines. Use for frontend development, component creation, design implementation, and UI consistency across projects. |
Building UI with shadcn/ui and Tailwind v4
Implements components using shadcn/ui and Tailwind v4 while adhering to a strict design system based on 4-size typography, 8pt grid spacing, and 60/30/10 color distribution.
Core Design Principles
1. Typography System: 4 Sizes, 2 Weights Only
Font Sizes:
- Size 1: Large headings (largest)
- Size 2: Subheadings and important content
- Size 3: Body text (default)
- Size 4: Small text and labels (smallest)
Font Weights:
- Semibold: Headings and emphasis only
- Regular: Body text and general UI elements
Never introduce additional font sizes or weights—maintain visual hierarchy through consistent sizing patterns.
2. 8pt Grid System
All spacing values MUST be divisible by 8 or 4:
✅ Correct:
- p-4 (16px), p-6 (24px), p-8 (32px)
- m-2 (8px), m-4 (16px), m-6 (24px)
- gap-2 (8px), gap-4 (16px), gap-8 (32px)
- Custom: 8, 16, 24, 32, 40, 48, 56, 64px
❌ Incorrect:
- 25px padding → Use 24px instead
- 11px margin → Use 12px instead
- 15px gap → Use 16px instead
- 7px, 13px, arbitrary values
Why: Creates visual harmony, simplifies decisions, establishes predictable patterns.
3. 60/30/10 Color Rule
Color Distribution:
-
60%: Neutral color (bg-background)
- Light mode: White or light gray
- Dark mode: Dark gray or black
- Usage: Primary backgrounds, cards, containers
-
30%: Complementary color (text-foreground)
- Light mode: Dark gray or black
- Dark mode: Light gray or white
- Usage: Text, icons, subtle UI elements
-
10%: Accent color (brand color)
- Your primary brand color (red, blue, etc.)
- Usage: Call-to-action buttons, highlights, important indicators
- ⚠️ Avoid overuse—reserve for elements needing attention
4. Clean Visual Structure
- Logical Grouping: Related elements visually connected
- Deliberate Spacing: Follow 8pt grid system
- Proper Alignment: Elements aligned within containers
- Simplicity First: Clarity and function before flashiness
Foundation
Tailwind v4 Setup
npx create-next-app@latest my-app
cd my-app
npx shadcn-ui@latest init
Configuration
tailwind.config.js (Tailwind v4):
import defaultConfig from 'tailwindcss/defaultConfig'
export default {
content: [
'./app/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
],
theme: {
extend: {
colors: {
background: 'var(--background)',
foreground: 'var(--foreground)',
primary: 'var(--primary)',
'primary-foreground': 'var(--primary-foreground)',
},
},
},
}
CSS Variables
app/globals.css (Tailwind v4):
@import "tailwindcss";
@theme {
--color-background: oklch(1 0 0);
--color-foreground: oklch(0.145 0 0);
--color-primary: oklch(0.205 0 0);
--color-primary-foreground: oklch(0.985 0 0);
--color-secondary: oklch(0.15 0 0);
--color-secondary-foreground: oklch(0.98 0 0);
--color-muted: oklch(0.96 0 0);
--color-muted-foreground: oklch(0.5 0 0);
--color-destructive: oklch(0.62 0.22 29.23);
--color-destructive-foreground: oklch(0.985 0 0);
}
@dark {
--color-background: oklch(0.08 0 0);
--color-foreground: oklch(0.98 0 0);
--color-primary: oklch(0.85 0.1 60);
--color-primary-foreground: oklch(0.12 0 0);
}
Typography System
Font Sizes Implementation
Use consistent sizing throughout your app:
<h1 className="text-4xl font-semibold">Main Title</h1>
<h2 className="text-2xl font-semibold">Section Title</h2>
<p className="text-base font-regular">Body text content here</p>
<label className="text-sm font-regular">Label Text</label>
Typography Hierarchy
Always maintain clear hierarchy with limited options:
✅ Good hierarchy:
<div className="space-y-6">
<h1 className="text-4xl font-semibold">Page Title</h1>
<p className="text-base font-regular text-muted-foreground">Intro text</p>
<h2 className="text-2xl font-semibold mt-8">Section</h2>
<p className="text-base font-regular">Content</p>
<label className="text-sm font-regular">Input label</label>
</div>
❌ Inconsistent hierarchy:
<h1 className="text-5xl font-bold">Title</h1>
<h2 className="text-xl font-medium">Subtitle</h2>
<p className="text-lg font-semibold">Body</p>
8pt Grid System
Spacing Implementation
Always use multiples of 4 or 8:
<div className="p-6 space-y-4">
<div className="flex gap-2">Content</div>
<div className="m-4">Properly spaced</div>
</div>
<div className="p-5 space-y-3"> {/* ❌ 5 and 3 not divisible */}
<div className="flex gap-1.5">Content</div>
<div className="m-3">Inconsistent</div>
</div>
Common Spacing Values
| Purpose | Tailwind | Pixels | When to Use |
|---|
| Tight spacing | gap-2, p-2 | 8px | Small component padding |
| Standard spacing | gap-4, p-4 | 16px | Default padding/gaps |
| Loose spacing | gap-6, p-6 | 24px | Section padding |
| Very loose | gap-8, p-8 | 32px | Large section separation |
| Extra loose | gap-12, p-12 | 48px | Major section breaks |
Responsive Grid
Grid system applies to all breakpoints:
<div className="flex gap-4 md:gap-6 lg:gap-8">
{}
</div>
Color Implementation
Color Variables
Use CSS variables following OKLCH color format:
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--secondary: oklch(0.15 0 0);
--muted: oklch(0.96 0 0);
--muted-foreground: oklch(0.5 0 0);
--destructive: oklch(0.62 0.22 29.23);
}
@dark {
--background: oklch(0.08 0 0);
--foreground: oklch(0.98 0 0);
--primary: oklch(0.85 0.1 60);
--muted: oklch(0.15 0 0);
--muted-foreground: oklch(0.7 0 0);
}
60/30/10 in Practice
export function Card() {
return (
<div className="bg-background p-6"> {/* 60% - neutral background */}
<h2 className="text-foreground font-semibold">Title</h2> {/* 30% - text */}
<button className="bg-primary text-primary-foreground"> {/* 10% - accent */}
Action
</button>
</div>
);
}
❌ Overusing accent:
<div className="bg-primary p-4">
<h2 className="text-primary font-semibold">Title</h2>
<button className="bg-primary">Button</button>
<div className="bg-secondary">More accents</div>
</div>
Component Architecture
shadcn/ui Structure
2-layered architecture:
- Behavior Layer: Radix UI primitives (accessibility, keyboard nav)
- Style Layer: Tailwind CSS + CVA (variants)
Component with data-slot
import { Button } from '@/components/ui/button'
export function MyComponent() {
return (
<Button
variant="default"
size="lg"
className="w-full"
>
Click me
</Button>
)
}
Creating Custom Components
import { ReactNode } from 'react'
interface CardProps {
children: ReactNode
className?: string
}
export function Card({ children, className }: CardProps) {
return (
<div className={`bg-background border border-border rounded-lg p-6 ${className}`}>
{children}
</div>
)
}
<Card className="space-y-4">
<h2 className="text-2xl font-semibold">Title</h2>
<p className="text-base font-regular">Content</p>
</Card>
Installing Components
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card
npx shadcn-ui@latest add dialog
npx shadcn-ui@latest add input
Visual Hierarchy
Implementation Checklist
Layout Example
export function Section() {
return (
<div className="space-y-6"> {/* Major spacing: 24px */}
<div>
<h2 className="text-2xl font-semibold">Section Title</h2>
<p className="text-sm text-muted-foreground mt-2">Subtitle</p>
</div>
<div className="space-y-4"> {/* Content spacing: 16px */}
<div className="flex gap-4"> {/* Item spacing: 16px */}
<img src="..." className="w-16 h-16 rounded" />
<div>
<h3 className="text-base font-semibold">Item</h3>
<p className="text-sm text-foreground">Description</p>
</div>
</div>
</div>
<button className="bg-primary text-primary-foreground px-6 py-3">
Action {/* Accent color: 10% */}
</button>
</div>
)
}
AI-Assisted Code Generation with Gemini CLI
Generate UI code using Google's Gemini AI while maintaining design system compliance.
Prerequisites
-
Get a Gemini API key:
-
Set environment variable:
export GEMINI_API_KEY="your-api-key-here"
-
Install Gemini CLI:
npm install -g @google/generative-ai-cli
npx @google/generative-ai-cli
Prompting Gemini for UI Code
Use specific, design-system-aware prompts to generate compliant code:
Good prompt:
Create a React shadcn/ui component for a user profile card with:
- Title using text-2xl font-semibold
- Subtitle using text-sm font-regular
- Spacing following 8pt grid (use only gap-4, gap-6, gap-8, p-4, p-6, p-8)
- Colors: 60% neutral background, 30% text-foreground, 10% primary accent
- Only 4 font sizes and 2 weights (semibold, regular)
- Use Tailwind v4 utilities
Better prompt (with constraints):
Create a React component using shadcn/ui and Tailwind v4.
DESIGN CONSTRAINTS:
- Typography: Use ONLY text-4xl, text-2xl, text-base, text-sm with font-semibold or font-regular
- Spacing: Use ONLY gap-2, gap-4, gap-6, gap-8, p-2, p-4, p-6, p-8, m-2, m-4, m-6, m-8
- Colors: Follow 60/30/10 rule (bg-background 60%, text-foreground 30%, bg-primary 10%)
- Use className with Tailwind utilities, no custom CSS
Component: [Your specific component request here]
Running Gemini CLI
Interactive mode:
gemini
Single query:
gemini "Create a button component with text-base font-semibold, p-4, bg-primary text-primary-foreground"
Workflow: Generate → Review → Integrate
-
Generate code with Gemini CLI:
gemini "Create a login form with email and password inputs, using 4 font sizes, 8pt grid spacing, and shadcn/ui components"
-
Copy the generated code to your component file
-
Review against checklist:
-
Fix any violations:
- Replace non-compliant spacing: 5px → 4px, 11px → 12px
- Remove extra font weights: font-medium → use text size instead
- Adjust color distribution if needed
-
Integrate into your project:
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
Example: Generating a Dashboard Widget
Prompt:
Create a React component for a dashboard metric widget using shadcn/ui and Tailwind v4.
REQUIREMENTS:
- Display a number metric with label
- Show trend indicator (up/down)
- Typography: Use only text-2xl (metric), text-base (label), text-sm (description)
- All spacing values must be 8, 16, 24, 32, 48 pixels (p-2, p-4, p-6, p-8, p-12)
- Colors: bg-background, text-foreground, accent with bg-primary only on one element (trend indicator)
- Include border and rounded corners using Tailwind
Component name: MetricWidget
What you get back:
export function MetricWidget({ metric, label, trend }) {
return (
<div className="bg-background border border-border rounded-lg p-6 space-y-4">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-regular text-muted-foreground">
{label}
</p>
<p className="text-2xl font-semibold text-foreground mt-2">
{metric}
</p>
</div>
<div className={`px-4 py-2 rounded ${trend > 0 ? 'bg-primary' : 'bg-destructive'}`}>
<span className="text-sm font-regular text-primary-foreground">
{trend > 0 ? '↑' : '↓'} {Math.abs(trend)}%
</span>
</div>
</div>
<p className="text-sm font-regular text-muted-foreground">
Last 30 days
</p>
</div>
)
}
After review: This code already follows all guidelines! Copy it directly into your project.
Bypassing Generated Code to Main Agent
Once generated and validated:
import { MetricWidget } from '@/components/features/MetricWidget'
export default function Dashboard() {
return (
<div className="space-y-6">
<MetricWidget metric="2,345" label="Total Users" trend={12} />
<MetricWidget metric="$45,231" label="Revenue" trend={8} />
</div>
)
}
The main agent can:
- Import and use the component directly
- Test it against the design system
- Request modifications if needed
- Integrate into larger layouts
Dark Mode
Configuration
<html className="dark">
{}
</html>
Testing Dark Mode
<html className={theme === 'dark' ? 'dark' : ''}>
Code Review Checklist
Design Principles
Technical
Common Issues to Flag
- ❌ More than 4 font sizes
- ❌ Extra font weights (medium, bold, etc.)
- ❌ Spacing not divisible by 4 or 8 (5px, 7px, 11px, etc.)
- ❌ Random padding/margins (inconsistent grid)
- ❌ Overused accent colors (exceeding 10%)
- ❌ Poor contrast between text and background
- ❌ Unnecessary custom CSS (when Tailwind utilities exist)
Resources