---
name: tailwind-shadcn-ui-builder
description: Builds polished, accessible UI components and pages with Tailwind CSS v4 and shadcn/ui, including design tokens, responsive layout, dark mode and states. Use when asked to create, restyle or "make it look good" for any React/Next.js interface, dashboard, form, landing section or component library piece.
license: MIT
compatibility: React 18+, Tailwind CSS v3.4 or v4, shadcn/ui CLI available (npx shadcn@latest). Works in Next.js, Vite and Remix projects.
metadata:
  category: design
  version: "1.0.0"
---

# Tailwind + shadcn/ui Builder

You are producing production UI, not a mockup. The result must be responsive, keyboard accessible, themed through tokens (never hard-coded hex values in components), and consistent with anything that already exists in the project.

## Step 1 — Read the project before writing a line

1. Find the token source: `app/globals.css` or `tailwind.config.*`. Note the color variables (`--background`, `--primary`, `--radius`), fonts and breakpoints.
2. Check `components.json` to learn the shadcn style (`default` or `new-york`), base color and alias paths (`@/components/ui`).
3. List installed primitives with `ls components/ui`. Reuse them. Only run `npx shadcn@latest add <component>` for primitives that are missing.
4. Open one existing page and one existing component to match spacing scale, heading sizes and how variants are expressed (`cva` or plain classes).

If none of these exist, initialise shadcn (`npx shadcn@latest init`) and propose a small token set before building.

## Step 2 — Plan the composition

Write a five-line plan before coding:

- Layout skeleton (grid/flex, max width, gutters).
- Primitives used (Card, Button, Dialog, Table, Tabs, Sheet, Form).
- States to cover: default, hover, focus-visible, disabled, loading, empty, error.
- Breakpoints: mobile first, then `md:` and `lg:` overrides only where the design changes.
- Data shape the component receives (typed props; no fetching inside presentational components).

## Step 3 — Build with these rules

- **Tokens over values.** Use `bg-background text-foreground border-border`, `text-muted-foreground`, `rounded-lg` (which maps to `--radius`). Never `#fff`, `bg-gray-900`, or arbitrary `[13px]` unless a token cannot express it.
- **Spacing rhythm.** Pick one scale per component (`gap-2/4/6`, `p-4/6`) and stay on it. Avoid mixing `p-3` and `p-5`.
- **Typography.** At most three sizes per view: heading (`text-2xl font-semibold tracking-tight`), body (`text-sm`), meta (`text-xs text-muted-foreground`).
- **Variants with `cva`.** For any component with more than one look, define `variant` and `size` through `class-variance-authority` and merge classes with `cn()`.
- **Accessibility.** Every interactive element is a `<button>` or `<a>`, has a visible `focus-visible:ring-2 ring-ring ring-offset-2`, icons-only buttons get `aria-label` and an `sr-only` text child. Forms use shadcn `Form` with `FormLabel`, `FormMessage` and `aria-invalid` from the validation state.
- **Dark mode.** Rely on the tokens; test by toggling the `dark` class on `<html>`. If something looks wrong in dark mode, fix the token, not the component.
- **Motion.** Use `transition-colors duration-150` on interactive elements and `animate-in fade-in` (tailwindcss-animate) for overlays. No decorative animation on data-heavy views.
- **Empty and loading states.** Ship a `Skeleton` layout that mirrors the final shape, and an empty state with one primary action.
- **Icons.** `lucide-react`, sized with `className="size-4"`; never inline SVG paths for common icons.

## Step 4 — Verify

Run the checklist and fix failures before reporting:

- [ ] `pnpm typecheck` (or `tsc --noEmit`) passes; no `any` in props.
- [ ] Renders at 375px, 768px and 1280px without horizontal scroll.
- [ ] Tab through the component: focus order is logical and every control shows a ring.
- [ ] Contrast of text on backgrounds meets 4.5:1 (use the tokens; do not lighten `muted-foreground`).
- [ ] Dark mode viewed at least once.
- [ ] No unused imports, no leftover `console.log`, no `className` strings longer than one line without a `cn()` split.

## Output format

Return: (1) the file list created or changed, (2) the component code, (3) a short "usage" snippet showing props, (4) any `npx shadcn@latest add` commands you ran, (5) known limitations. Keep prose to a minimum; the code is the deliverable.

## Pitfalls

- Adding a second design language (raw Tailwind colors next to shadcn tokens) is the most common way a codebase drifts. Do not do it.
- Do not wrap every element in a `div`; use semantic `section`, `nav`, `header`, `ul` and let Tailwind style them.
- `w-screen` and `h-screen` cause overflow on mobile browsers; use `w-full` and `min-h-dvh`.
- Avoid `!important` (`!bg-red-500`). If specificity fights you, the abstraction is wrong.
- Dialogs and Sheets must trap focus and close on Escape — shadcn primitives do this already, so do not rebuild them from Radix by hand.
- Do not fetch data inside a UI component; accept data as props so the component can be reused in Storybook and tests.
