From Design to Production
The capstone: take a design concept through to production-ready code
You have the skills. Prompting patterns from Chapter 4, CLAUDE.md from Chapter 5, design systems from Chapter 6, automation from Chapter 7, tool integrations from Chapter 8. This chapter puts them all together. You will follow a single design project — a SaaS pricing page — from blank folder to deployed site. Every technique in the book shows up here, in sequence, for real.
Production readiness contract
Do not ship because the page looks good in one browser. Ship only after build, tests, responsive screenshots, keyboard navigation, accessibility checks, performance budget, data/privacy review, security review, owner approval, and rollback path are all accounted for. Claude Code can help run these checks, but a human remains responsible for the release decision.
Think of this pipeline like the design process you already know: research, wireframe, visual design, refine with feedback, hand off for build, launch. Each stage maps to specific Claude Code techniques. You can pause between any stage and resume later.
9.1 Design-to-Production Pipeline
Design-to-Production Pipeline
The full pipeline has six stages. Each has a goal, a Claude Code technique, and a chapter in this book that teaches it in depth.
| Stage | Goal | Claude Code Technique | Learned In |
|---|---|---|---|
| 1. Define | State the design problem clearly | Natural-language design brief in plan mode | Ch 3 — Design intent |
| 2. Explore | Understand constraints and options | Plan mode file exploration + reference analysis | Ch 4 — Prompting |
| 3. Prototype | First working version | Implementation from plan with CLAUDE.md | Ch 3 + Ch 5 |
| 4. Refine | Match design intent | Chrome extension visual verification + iteration | Ch 8 — Browser testing |
| 5. Harden | Production quality | Accessibility audit, responsive fixes, hooks | Ch 6 + Ch 7 |
| 6. Ship | Deploy to users | Tests, commit, PR, deploy pipeline | Ch 7 — CI integration |
This pipeline works for any size project. A single landing page might move through all six stages in one session. A full product might take weeks, with separate sessions for each stage. The structure stays the same.
Break between stages
Long sessions accumulate context that degrades output quality. I recommend running /clear between stages and resuming with --continue. The plan and CLAUDE.md carry forward; the accumulated noise does not.
9.2 Choosing Your Tech Stack
Choosing Your Tech Stack
Claude Code is stack-agnostic. It works with whatever frameworks your project uses. For designers moving to production, three stacks cover most cases.
| Stack | Best For | Deploy Target | Claude Knowledge |
|---|---|---|---|
| Next.js + Tailwind | Production sites, marketing pages, dashboards | Vercel (zero-config) | Deep — conventions, App Router, SSR |
| Vite + React + Tailwind | SPAs, component libraries, rapid prototyping | Any static host | Strong — standard patterns |
| Static HTML + CSS | Landing pages, email templates, simple sites | Anywhere | Excellent — no framework conventions |
For this capstone, I am using Next.js with Tailwind CSS. It is the most common production stack, and Claude Code has deep knowledge of Next.js conventions — the App Router, file-based routing, server components, and image optimization.
Scaffolding the project
Start Claude Code in the folder where you want the project to live. Ask it to scaffold everything.
Create a new Next.js 15 project with TypeScript and Tailwind CSS v4.
Use the App Router. Set up:
- src/app/ for pages
- src/components/ui/ for design system primitives
- src/components/ for composed components
- src/lib/ for utilities
Configure a CLAUDE.md with:
- Inter as the primary font
- Primary color: #6366f1 (indigo)
- Secondary color: #ec4899 (pink)
- Neutral: slate gray scale
- 2-space indentation, PascalCase components, kebab-case files
- Run typecheck and lint before committing
Claude generates the full project structure, configures Tailwind, writes the CLAUDE.md, and sets up the initial page. The CLAUDE.md ensures every future session starts with the same design language — the tokens, naming, and conventions from Chapter 5.
# CLAUDE.md (generated)
## Tech Stack
- Next.js 15 with App Router, TypeScript strict, Tailwind CSS v4
## Design Tokens
- Font: Inter (headings 700, body 400)
- Colors:
- primary: #6366f1
- secondary: #ec4899
- neutral: slate-50 through slate-900
- Spacing: Tailwind default scale (4px base unit)
- Border radius: 8px cards, 6px buttons, full for avatars
## Code Style
- 2-space indentation
- Components: PascalCase, kebab-case files
- Use CVA for variant management
- Forward refs on all primitives
- ARIA attributes on interactive elements
## Workflow
- Run `npm run typecheck` after changes
- Run `npm run lint` before committing
- Start dev server: `npm run dev` (port 3000)
My Take
Plan mode is the most underused feature for design projects. Spending 10 minutes exploring before coding saves hours of iteration. Without exploration, Claude builds the first thing that comes to mind. With exploration, Claude builds the right thing. For small, well-defined tasks, skip planning and go straight to implementation. For anything with more than two components, plan first.
9.3 Building the Component Library
Building the Component Library
With the project scaffolded and CLAUDE.md in place, you build the design system primitives first. These are the building blocks every page section will use.
Three approaches to component generation
Claude Code can build components from a screenshot, from design tokens, or by following existing patterns in your codebase. The pattern-based approach produces the most consistent, production-ready output.
Pattern-based (recommended)
Look at @src/components/ui/Button.tsx to understand our
component patterns. Follow that pattern to build a Card
component with:
- Variant prop: elevated, outlined, filled
- Padding prop: sm, md, lg
- Slots for header, body, and footer
- Support for loading state
Screenshot-only (less consistent)
[paste screenshot]
Build this card component.
The pattern-based approach references an existing component so Claude matches your conventions — file structure, export style, prop naming, and TypeScript patterns. The screenshot approach works for one-off components but produces inconsistent code across a library.
Generating the full primitive set
Ask Claude to build all the primitives your pricing page needs in one prompt.
Build the following primitives in src/components/ui/.
Follow the Button.tsx pattern for structure, naming, and exports.
1. Card — composed with CardHeader, CardBody, CardFooter
- Variants: elevated, outlined, filled
- Padding: sm (12px), md (16px), lg (24px)
2. Badge — color variants (primary, secondary, neutral, success, warning)
- Optional dot indicator
- Sizes: sm, md
3. Avatar — image with fallback initials
- Sizes: sm (32px), md (40px), lg (56px)
- Group layout support (overlapping avatars)
4. PriceCard — specialized card for pricing tiers
- Props: tier name, price, period, features list, CTA text, highlighted
- Highlighted variant gets a ring and "Recommended" badge
Each component must:
- Accept className for overrides
- Forward refs with forwardRef
- Include TypeScript types
- Be fully accessible (ARIA, keyboard navigation)
Claude generates all four components. Because CLAUDE.md specifies design tokens and the Button.tsx reference establishes conventions, the output is consistent without you re-explaining anything.
A production component: the accessible Dialog
As an example of what production-ready looks like, here is the Dialog component Claude generates when you specify accessibility requirements.
import { useRef, useEffect } from "react";
import { createPortal } from "react-dom";
interface DialogProps {
isOpen: boolean;
onClose: () => void;
title: string;
children: React.ReactNode;
}
export function Dialog({ isOpen, onClose, title, children }: DialogProps) {
const dialogRef = useRef<HTMLDivElement>(null);
const previousFocusRef = useRef<HTMLElement | null>(null);
useEffect(() => {
if (isOpen) {
previousFocusRef.current = document.activeElement as HTMLElement;
dialogRef.current?.focus();
} else {
previousFocusRef.current?.focus();
}
}, [isOpen]);
useEffect(() => {
if (!isOpen) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === "Escape") onClose();
if (e.key === "Tab") {
const focusable = dialogRef.current?.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (!focusable?.length) return;
const first = focusable[0] as HTMLElement;
const last = focusable[focusable.length - 1] as HTMLElement;
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
};
document.addEventListener("keydown", handleKeyDown);
return () => document.removeEventListener("keydown", handleKeyDown);
}, [isOpen, onClose]);
if (!isOpen) return null;
return createPortal(
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50"
onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
>
<div
ref={dialogRef}
tabIndex={-1}
className="bg-white rounded-lg p-6 max-w-md w-full mx-4 shadow-xl"
>
<h2 id="dialog-title" className="text-lg font-semibold mb-4">
{title}
</h2>
{children}
</div>
</div>,
document.body
);
}
This component traps focus inside the dialog, returns focus to the trigger when closed, responds to the Escape key, and uses proper ARIA attributes. You did not write any of this. You described what you needed, and Claude built it.
Tip
When you specify accessibility requirements in your component prompt, Claude includes them by default. When you do not specify them, Claude sometimes skips them. Always include accessibility requirements in your component briefs.
9.4 Responsive Design and Accessibility
Responsive Design and Accessibility
The prototype works on your screen. Now make it work on every screen and for every user.
Responsive design with specific breakpoints
Claude generates responsive layouts when you give it concrete breakpoint requirements. Vague instructions produce generic results. Specific instructions produce layouts that match your design intent.
Build the pricing page layout with these breakpoints:
- Mobile (<640px): single column, stacked cards, full-width CTA
- Tablet (640-1024px): 2-column grid for pricing cards
- Desktop (>1024px): 3-column grid with sidebar for FAQ
The navigation should collapse to a hamburger on mobile.
The feature comparison table should scroll horizontally on mobile.
Use Tailwind responsive prefixes (sm:, md:, lg:).
Claude applies these breakpoints across all components. Because the design tokens are defined in CLAUDE.md, the spacing, font sizes, and colors stay consistent across every breakpoint.
Accessibility as a prompt requirement
The same specificity principle applies to accessibility. Telling Claude to "make it accessible" produces partial results. Listing specific requirements produces thorough ones.
Review the pricing page for accessibility:
1. All images have alt text
2. Form labels are associated with inputs via htmlFor
3. Color contrast meets WCAG 2.1 AA (4.5:1 for body text)
4. All interactive elements are reachable via Tab
5. The pricing cards have role="group" with aria-label
6. The FAQ accordion uses aria-expanded and aria-controls
7. Focus indicators are visible (not outline:none)
8. The page has a logical heading hierarchy (h1, h2, h3)
Fix any issues you find.
Chrome extension for visual verification
The Chrome integration closes the feedback loop. You build something, Claude opens it in the browser, compares it to your design reference, and tells you what needs fixing.
claude --chrome
Open localhost:3000 in Chrome and verify:
1. The page matches the original design mockup I pasted
2. All sections render correctly on mobile (375px) and desktop (1440px)
3. No console errors
4. Tab navigation works through all interactive elements
5. Color contrast meets WCAG AA standards
Take a screenshot and tell me what needs fixing.
Claude reads the console, inspects the DOM, takes a screenshot, and compares it to your reference. It returns a list of visual differences — wrong spacing, misaligned elements, missing styles — and fixes them.
Warning
Code that looks correct in your editor can render differently in the browser. CSS specificity conflicts, missing imports, and responsive breakpoints do not show up in the code. They show up when you open the page. Always verify visually. The Chrome extension exists for this reason.
9.5 Testing and QA
Testing and QA
The prototype looks right. Now verify it behaves right — across states, screen sizes, and interactions.
Test generation that follows your patterns
Claude examines your existing test files to match the framework, assertion style, and structure your project uses. You do not need to specify which testing library to use — Claude reads your package.json and follows what is already there.
Write tests for the PriceCard component. Cover:
- Rendering with default props
- Rendering in highlighted state
- CTA button click fires the onClick handler
- Feature list renders all items
- Badge appears on highlighted card
- Responsive layout on mobile viewport
Run the tests and fix any failures.
Claude generates the test file, runs it, and fixes any failures. Here is what the output looks like when the project uses vitest.
import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { PriceCard } from "./PriceCard";
describe("PriceCard", () => {
const defaultProps = {
tier: "Pro",
price: "$29",
period: "/month",
features: ["Unlimited projects", "Priority support", "API access"],
ctaText: "Get Started",
highlighted: false,
};
it("renders tier name and price", () => {
render(<PriceCard {...defaultProps} />);
expect(screen.getByText("Pro")).toBeInTheDocument();
expect(screen.getByText("$29")).toBeInTheDocument();
});
it("shows badge when highlighted", () => {
render(<PriceCard {...defaultProps} highlighted />);
expect(screen.getByText("Recommended")).toBeInTheDocument();
});
it("fires onClick when CTA is clicked", () => {
const onClick = vi.fn();
render(<PriceCard {...defaultProps} onCtaClick={onClick} />);
fireEvent.click(screen.getByText("Get Started"));
expect(onClick).toHaveBeenCalledOnce();
});
it("renders all features", () => {
render(<PriceCard {...defaultProps} />);
expect(screen.getByText("Unlimited projects")).toBeInTheDocument();
expect(screen.getByText("Priority support")).toBeInTheDocument();
expect(screen.getByText("API access")).toBeInTheDocument();
});
});
Verification-first prompting
The single highest-leverage technique for quality is giving Claude a way to verify its own work. The best practices documentation emphasizes this repeatedly. Compare two approaches.
Verification-first prompt
Implement the FAQ accordion component.
After implementing:
1. Run the component tests
2. Open localhost:3000 in Chrome
3. Verify keyboard navigation works
(Tab between items, Enter to expand)
4. Check that aria-expanded toggles correctly
5. Screenshot the result
Fix anything that fails.
Implementation-only prompt
Implement the FAQ accordion component.
The verification-first prompt produces fewer bugs because Claude checks its own work immediately. When it finds a problem, it fixes it before showing you the result.
Hooks for automatic quality gates
Hooks guarantee that quality checks run after every change. Unlike CLAUDE.md instructions (advisory — Claude might ignore them), hooks are deterministic — they run every time without exception.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx eslint ${tool_input.file_path} --fix"
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "npm run typecheck && npm run lint && npm test"
}
]
}
]
}
}
This hook configuration runs ESLint auto-fix after every file write or edit, and runs typecheck + lint + tests before completing any session. If any check fails, the session does not end clean — Claude keeps working until everything passes.
9.6 Deploying
Deploying
The code works locally. The tests pass. Time to put it on the internet.
The full deploy checklist
Before deploying, run through this checklist. Each item is a prompt you give Claude.
| Category | Check | How to Verify |
|---|---|---|
| Tests | All tests pass | npm test |
| Types | No TypeScript errors | npm run typecheck |
| Lint | No lint errors | npm run lint |
| Build | Production build succeeds | npm run build |
| Responsive | Works on mobile, tablet, desktop | Chrome extension at 375px, 768px, 1440px |
| Accessibility | Tab navigation, contrast, ARIA | Manual Tab-through + Chrome DevTools audit |
| Console | No errors or warnings | Chrome extension reads console |
| Visual | Matches design mockup | Screenshot comparison with reference |
Claude can run this entire checklist in a single prompt.
Run the full pre-deploy checklist:
1. Run all tests — fix failures
2. Run typecheck — fix errors
3. Run lint — fix issues
4. Run production build — fix errors
5. Open localhost:3000 in Chrome
6. Check responsive at 375px, 768px, 1440px
7. Tab through all interactive elements
8. Check console for errors
9. Take a screenshot and compare to the design mockup
Fix anything that fails. Then commit with:
"feat: pricing page — three tiers, comparison table, FAQ accordion"
Deploying to Vercel
For a Next.js project, Vercel is the default deployment target. The connection is native — push to main, the site updates.
Commit all changes and push to the main branch.
Vercel is connected to this repo and will auto-deploy.
After deployment completes, open the live URL in Chrome and:
1. Check for console errors
2. Verify all images load
3. Test the responsive layout on mobile
4. Take a screenshot and compare to the design mockup
Deploying as a static demo
For sharing a design without a server — a client review, a design critique, a portfolio piece — ask Claude to build a self-contained HTML file.
Build the pricing page as a single self-contained HTML file
that works offline when opened by double-clicking. Bundle all
CSS and JS inline. The file should work with no server.
GitHub Actions for continuous quality
For team projects, every pull request gets an automated review. Claude Code runs as a GitHub Action that checks accessibility, responsive design, and component consistency.
name: Design Review
on:
pull_request:
types: [opened, synchronize]
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
prompt: |
Review this PR for:
- Accessibility issues (missing ARIA, poor contrast)
- Responsive design problems
- Component consistency with the design system
- Performance concerns (large bundles, unnecessary re-renders)
Every PR now gets a design-oriented review before merge. This catches regressions that manual review misses — a color token swapped for a hardcoded value, a missing aria-label, a responsive breakpoint that broke during refactoring.
My Take
The biggest mistake designers make with AI coding tools is stopping at the prototype stage. The jump from prototype to production is where the real value is. Anyone can generate a prototype. Production-quality code that handles edge cases, accessibility, and responsive design is what separates experiments from shipped products. Not every project needs production quality — prototypes for user testing are valuable on their own. But if you are putting something on the internet with your name on it, take the time to harden it.
You started with a blank folder. You now have a pricing page built on a design system, tested, accessible, responsive, and deployed. Every technique came from a previous chapter. The pipeline is repeatable — define, explore, prototype, refine, harden, ship — for any project, any size, any stack. This is the workflow the book has been building toward. Everything else is practice.