AgentSkillsCN

building-ui-with-shadcn

遵循严格的设计系统规范,使用 shadcn/ui 与 Tailwind v4 构建无障碍、风格统一的 UI 组件。适用于前端开发、组件创建、设计落地,以及跨项目保持 UI 一致性。

SKILL.md
--- frontmatter
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

bash
npx create-next-app@latest my-app
cd my-app
npx shadcn-ui@latest init

Configuration

tailwind.config.js (Tailwind v4):

javascript
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):

css
@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:

tsx
// Heading 1 (Size 1 - largest)
<h1 className="text-4xl font-semibold">Main Title</h1>

// Heading 2 (Size 2)
<h2 className="text-2xl font-semibold">Section Title</h2>

// Body (Size 3 - default)
<p className="text-base font-regular">Body text content here</p>

// Small/Label (Size 4 - smallest)
<label className="text-sm font-regular">Label Text</label>

Typography Hierarchy

Always maintain clear hierarchy with limited options:

Good hierarchy:

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

tsx
// Too many different sizes and weights
<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:

tsx
// Correct spacing
<div className="p-6 space-y-4">
  <div className="flex gap-2">Content</div>
  <div className="m-4">Properly spaced</div>
</div>

// Incorrect spacing
<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

PurposeTailwindPixelsWhen to Use
Tight spacinggap-2, p-28pxSmall component padding
Standard spacinggap-4, p-416pxDefault padding/gaps
Loose spacinggap-6, p-624pxSection padding
Very loosegap-8, p-832pxLarge section separation
Extra loosegap-12, p-1248pxMajor section breaks

Responsive Grid

Grid system applies to all breakpoints:

tsx
<div className="flex gap-4 md:gap-6 lg:gap-8">
  {/* Gap increases from 16px → 24px → 32px */}
</div>

Color Implementation

Color Variables

Use CSS variables following OKLCH color format:

css
/* Light mode */
:root {
  --background: oklch(1 0 0);           /* White */
  --foreground: oklch(0.145 0 0);       /* Dark gray/black */
  --primary: oklch(0.205 0 0);          /* Brand color */
  --secondary: oklch(0.15 0 0);         /* Secondary */
  --muted: oklch(0.96 0 0);             /* Light gray bg */
  --muted-foreground: oklch(0.5 0 0);   /* Medium gray text */
  --destructive: oklch(0.62 0.22 29.23); /* Red */
}

/* Dark mode */
@dark {
  --background: oklch(0.08 0 0);        /* Very dark */
  --foreground: oklch(0.98 0 0);        /* Light text */
  --primary: oklch(0.85 0.1 60);        /* Bright brand */
  --muted: oklch(0.15 0 0);             /* Dark gray bg */
  --muted-foreground: oklch(0.7 0 0);   /* Light gray text */
}

60/30/10 in Practice

tsx
// Correct color distribution
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:

tsx
// Too many accent colors
<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:

  1. Behavior Layer: Radix UI primitives (accessibility, keyboard nav)
  2. Style Layer: Tailwind CSS + CVA (variants)

Component with data-slot

tsx
import { Button } from '@/components/ui/button'

// shadcn/ui v4 components use data-slot for styling
export function MyComponent() {
  return (
    <Button 
      variant="default"
      size="lg"
      className="w-full"
    >
      Click me
    </Button>
  )
}

Creating Custom Components

tsx
// components/ui/card.tsx
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>
  )
}

// Usage with 8pt grid
<Card className="space-y-4">
  <h2 className="text-2xl font-semibold">Title</h2>
  <p className="text-base font-regular">Content</p>
</Card>

Installing Components

bash
# Install individual 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

  • Headings use Size 1 or 2 with semibold weight
  • Body text uses Size 3 with regular weight
  • Labels use Size 4 with regular weight
  • Related elements grouped with consistent gap-4 or gap-6
  • Important elements use accent color (10% max)
  • Sufficient contrast between text and background
  • Spacing follows 8pt grid throughout

Layout Example

tsx
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

  1. Get a Gemini API key:

  2. Set environment variable:

    bash
    export GEMINI_API_KEY="your-api-key-here"
    
  3. Install Gemini CLI:

    bash
    # Install globally (recommended)
    npm install -g @google/generative-ai-cli
    
    # Or use with npx (no installation)
    npx @google/generative-ai-cli
    

Prompting Gemini for UI Code

Use specific, design-system-aware prompts to generate compliant code:

Good prompt:

code
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):

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

bash
gemini
# Select "Use Gemini API key"
# Paste your prompt

Single query:

bash
gemini "Create a button component with text-base font-semibold, p-4, bg-primary text-primary-foreground"

Workflow: Generate → Review → Integrate

  1. Generate code with Gemini CLI:

    bash
    gemini "Create a login form with email and password inputs, using 4 font sizes, 8pt grid spacing, and shadcn/ui components"
    
  2. Copy the generated code to your component file

  3. Review against checklist:

    • Only 4 font sizes (text-4xl, text-2xl, text-base, text-sm)
    • Only 2 weights (font-semibold, font-regular)
    • Spacing divisible by 4 or 8 (8px, 16px, 24px, 32px, etc.)
    • Colors follow 60/30/10 rule
    • Uses shadcn/ui components
    • No custom CSS
  4. 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
  5. Integrate into your project:

    tsx
    // components/features/LoginForm.tsx
    import { Button } from '@/components/ui/button'
    import { Input } from '@/components/ui/input'
    
    // [Paste generated code here after review]
    

Example: Generating a Dashboard Widget

Prompt:

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

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

tsx
// In your main agent or build process
import { MetricWidget } from '@/components/features/MetricWidget'

// Use in your application
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

tsx
// Automatic dark mode detection
<html className="dark">
  {/* Dark mode CSS variables apply */}
</html>

Testing Dark Mode

bash
# Toggle dark mode in browser DevTools
# Or use Tailwind's class-based approach
<html className={theme === 'dark' ? 'dark' : ''}>

Code Review Checklist

Design Principles

  • Typography: Exactly 4 font sizes, only semibold/regular weights
  • Spacing: All values divisible by 8 or 4 (no arbitrary values)
  • Colors: 60% neutral, 30% complementary, 10% accent
  • Structure: Elements logically grouped with consistent gaps

Technical

  • Uses OKLCH color variables
  • Leverages @theme directive for variables
  • Components use proper data-slot attributes
  • Class Variance Authority for variants
  • Dark mode implemented consistently
  • Accessibility: Good contrast, keyboard nav, semantic HTML

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