Back to docs
08Week 1

Generating Components

The complete vibe coding loop — describe, generate, review, refine

The Component Generation Loop

Every component you build with Claude follows the same 5-step loop. Master this loop and you can build anything:

  • 1. Describe — Write a detailed prompt describing the component you want. Include its purpose, variants, props, states, and visual behavior.
  • 2. Generate — Let Claude write the code. Don’t interrupt — let it finish the full component before you start reviewing.
  • 3. Review — Read the output carefully. Does it match your intent? Does the structure make sense? Are the styles correct?
  • 4. Refine — Ask Claude to fix specific issues. Be precise about what’s wrong and what you want instead.
  • 5. Save — Once the component looks right, save it to your project. Test it in the browser to make sure it works.

Anatomy of a Good Component Prompt

A good prompt has these parts:

Create a [COMPONENT NAME] component at [FILE PATH].

Purpose: [What does this component do?]
Variants: [List all visual variants]
Props: [List all props with types]
States: [Hover, focus, disabled, loading, error, etc.]
Content: [What goes inside? Text, icons, children?]
Behavior: [Click handlers, animations, transitions]
Constraints: [Tailwind only, no inline styles, follow SKILL.md, etc.]

Example: Card Component

Create a Card component at src/components/ui/Card.tsx.

Purpose: A container for grouping related content with optional header and footer.
Variants: default (white bg, subtle border), elevated (white bg, shadow-md), outlined (transparent bg, strong border)
Props:
  - variant: "default" | "elevated" | "outlined" (default: "default")
  - padding: "sm" | "md" | "lg" (default: "md")
  - children: ReactNode
  - className?: string (for overrides)
States: Hover — elevated variant gets shadow-lg on hover
Content: Children slot for any content
Behavior: No click handlers — purely presentational
Constraints: Tailwind only, use design tokens from SKILL.md, TypeScript strict

The 3 Components You Build First

Every design system starts with three foundational components. Build these first to establish your patterns, then everything else follows the same approach.

1. Button

Create a Button component at src/components/ui/Button.tsx.

Purpose: The primary interactive element across the application.
Variants:
  - primary: dark background (#1A1A1A), white text
  - secondary: white background, dark border, dark text
  - ghost: transparent background, no border, dark text
  - danger: red background (#DC2626), white text
Sizes: sm (h-8, text-sm, px-3), md (h-10, text-sm, px-4), lg (h-12, text-base, px-6)
Props:
  - label: string
  - variant: "primary" | "secondary" | "ghost" | "danger" (default: "primary")
  - size: "sm" | "md" | "lg" (default: "md")
  - onClick?: () => void
  - disabled?: boolean
  - loading?: boolean
  - icon?: ReactNode (optional, renders before label)
States:
  - Hover: slight opacity change or color shift
  - Focus: ring-2 ring-offset-2 ring-primary
  - Disabled: opacity-50, cursor-not-allowed, pointer-events-none
  - Loading: show a spinner, disable clicks
Constraints: Tailwind only, use cn() for conditional classes, TypeScript strict, default export

2. Input

Create an Input component at src/components/ui/Input.tsx.

Purpose: Text input field with label, helper text, and error states.
Props:
  - label: string
  - placeholder?: string
  - value: string
  - onChange: (value: string) => void
  - type: "text" | "email" | "password" | "number" (default: "text")
  - error?: string (shows error message below input)
  - helperText?: string (shows helper text below input, hidden when error is present)
  - disabled?: boolean
  - required?: boolean
States:
  - Default: gray border
  - Focus: ring-2 ring-primary, border-primary
  - Error: red border, red ring, error text in red below
  - Disabled: bg-gray-50, opacity-50, cursor-not-allowed
Layout:
  - Label above the input
  - Input field full width
  - Helper text or error text below
Constraints: Tailwind only, TypeScript strict, default export, use forwardRef

3. Card

Create a Card component at src/components/ui/Card.tsx.

Purpose: Content container with optional header, body, and footer sections.
Variants:
  - default: white bg, border border-gray-200, rounded-xl
  - elevated: white bg, shadow-md, rounded-xl
  - outlined: transparent bg, border-2 border-gray-300, rounded-xl
Props:
  - variant: "default" | "elevated" | "outlined" (default: "default")
  - padding: "none" | "sm" | "md" | "lg" (default: "md")
  - children: ReactNode
  - className?: string
Sub-components (compound pattern):
  - Card.Header — top section with bottom border
  - Card.Body — main content area
  - Card.Footer — bottom section with top border
Constraints: Tailwind only, TypeScript strict, use cn() utility

Reviewing Claude’s Output

When Claude generates a component, run through this checklist before accepting it:

  • Does it match the prompt? — Check that all variants, props, and states you asked for are actually implemented.
  • Are the types correct? — Make sure the props interface is complete and uses proper TypeScript types (no any).
  • Is it using Tailwind correctly? — No inline styles, no arbitrary values when tokens exist, using cn() for conditionals.
  • Does it follow your SKILL.md? — File naming, export style, design tokens, directory structure.
  • Is it accessible? — Does it have proper ARIA attributes? Can you tab to it? Does it work with screen readers?
  • Is it self-contained? — Does the component work on its own, or does it have hidden dependencies?

Refining — Asking Claude to Fix Things

When something isn’t right, be specific about what needs to change. Vague refinement prompts lead to vague results.

Bad Refinement Prompts

"Make it look better"
"Fix the styling"
"It doesn't look right"
"Can you improve it?"

Good Refinement Prompts

"The hover state on the primary variant should change the background to #333333, not opacity"

"The disabled state is missing cursor-not-allowed — add it to all variants"

"The padding on the sm size is too tight — change from px-2 to px-3"

"Add a focus-visible ring using ring-2 ring-offset-2 ring-blue-500 to all interactive variants"

"The error state border should be red-500 not red-600, and the error text should be text-sm not text-xs"

Fix One Thing at a Time

Don’t ask Claude to fix 5 things at once. Fix one thing, verify it’s correct, then move to the next. This gives you more control and makes it easier to catch if Claude introduces a new bug while fixing the first one.

Tip: If Claude keeps getting something wrong after 2-3 attempts, try rephrasing your prompt completely. Sometimes a different description of the same thing clicks better.

Compound Components

Once you have your foundational components, you can compose them into larger, more complex components. These are called compound components or pattern components.

Example: LoginForm

Create a LoginForm component at src/components/patterns/LoginForm.tsx.

Purpose: A complete login form with email, password, and submit button.

Compose these existing components:
  - Input (from src/components/ui/Input.tsx)
  - Button (from src/components/ui/Button.tsx)
  - Card (from src/components/ui/Card.tsx)

Layout:
  - Wrapped in a Card (elevated variant, md padding)
  - Card.Header with "Sign In" title and "Welcome back" subtitle
  - Card.Body with:
    - Email Input (type="email", required)
    - Password Input (type="password", required)
    - "Forgot password?" link aligned right
  - Card.Footer with:
    - Primary Button (label="Sign In", full width)
    - Text: "Don't have an account? Sign up" with link

Behavior:
  - Form validates on submit
  - Show error states on invalid inputs
  - Button shows loading state during submission
  - onSubmit prop: (email: string, password: string) => Promise<void>

Constraints: Import existing components, don't recreate them. Tailwind only. TypeScript strict.

Tip: Always tell Claude which existing components to import. If you don’t, it will create everything from scratch and you’ll end up with duplicated code that doesn’t match your design system.