Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Skill for creating and managing a Design System using Tailwind CSS and shadcn/ui. Use when defining design tokens, setting up theming with CSS variables, building a consistent UI component library, initializing a design system configuration, or wrapping shadcn/ui components into design system primitives.
.claude/skills/giuseppe-trisciuoglio-tailwind-design-system/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 4% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 74% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 90% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 158% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 70% | 0% |
Expert guide for creating and managing a centralized Design System using Tailwind CSS (v4.1+) and shadcn/ui. This skill provides structured workflows for defining design tokens, configuring themes with CSS variables, and building a consistent UI component library based on shadcn/ui primitives.
Relationship with other skills:
globals.css with a centralized theming system (light/dark mode)Run these commands to set up the project:
bash# Check if Tailwind is installed npx tailwindcss --version # For Tailwind v4 (recommended) npx @tailwindcss/vite@latest init # or: npm install -D tailwindcss @tailwindcss/vite # Initialize shadcn/ui CLI npx shadcn@latest init # Install core shadcn/ui components npx shadcn@latest add button card input -y
Validation checkpoint: After setup, verify with:
bashls src/components/ui/ # Should list installed components cat src/app/globals.css # Should contain @tailwind directives
Create src/app/globals.css with your design tokens:
css@tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { /* Brand Colors */ --primary: oklch(0.55 0.18 250); --primary-foreground: oklch(0.985 0 0); /* Semantic Colors */ --background: oklch(0.99 0 0); --foreground: oklch(0.15 0 0); --secondary: oklch(0.96 0.01 250); --secondary-foreground: oklch(0.20 0 0); /* Validation: all colors must have foreground pair */ --destructive: oklch(0.55 0.22 25); --destructive-foreground: oklch(0.985 0 0); } .dark { --primary: oklch(0.65 0.20 250); --background: oklch(0.14 0 0); --foreground: oklch(0.97 0 0); --secondary: oklch(0.25 0.02 250); } }
Validation checkpoint: Verify tokens are valid CSS:
bashgrep -E "^[[:space:]]*--[a-z-]+:" src/app/globals.css | wc -l # Should return count of defined tokens (e.g., 10+)
Bridge CSS variables to Tailwind utilities (Tailwind v4.1+):
css@theme inline { --color-primary: var(--primary); --color-primary-foreground: var(--primary-foreground); --color-background: var(--background); --color-foreground: var(--foreground); }
Add dark mode class toggle in components/providers/theme-provider.tsx:
tsximport { useEffect } from "react"; export function ThemeProvider({ children }: { children: React.ReactNode }) { useEffect(() => { const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches; document.documentElement.classList.toggle("dark", isDark); }, []); return <>{children}</>; }
Validation checkpoint: Test dark mode:
bashdocument.documentElement.classList.contains("dark") // in browser console
Create src/components/ds/Button.tsx:
tsximport { Button as ShadcnButton } from "@/components/ui/button"; type DSVariant = "primary" | "secondary" | "destructive" | "ghost"; const variantMap: Record<DSVariant, "default" | "secondary" | "destructive" | "ghost"> = { primary: "default", secondary: "secondary", destructive: "destructive", ghost: "ghost", }; export function Button({ variant = "primary", ...props }: { variant?: DSVariant } & React.ComponentProps<typeof ShadcnButton>) { return <ShadcnButton variant={variantMap[variant]} {...props} />; }
Validation checkpoint: Verify build passes:
bashnpx tsc --noEmit src/components/ds/Button.tsx
Run the token validation script:
bashREQUIRED=("primary" "primary-foreground" "background" "foreground" "secondary" "secondary-foreground") for token in "${REQUIRED[@]}"; do grep -q "$token:" src/app/globals.css || echo "MISSING: --$token" done
Validation checkpoint: Ensure all shadcn components use DS tokens:
bashgrep -r "bg-primary\|text-primary\|bg-background" src/components/ds/
Extend the base tokens in globals.css:
css:root { --warning: oklch(0.84 0.16 84); --warning-foreground: oklch(0.28 0.07 46); } .dark { --warning: oklch(0.41 0.11 46); --warning-foreground: oklch(0.99 0.02 95); } @theme inline { --color-warning: var(--warning); --color-warning-foreground: var(--warning-foreground); }
Usage: <div className="bg-warning text-warning-foreground">Warning</div>
See references/component-wrapping.md for complete examples including Button, Text, and Stack primitives with full TypeScript types.
Create constrained design system components that enforce token usage. Inline example:
tsximport { Button as ShadcnButton } from "@/components/ui/button"; export function Button({ variant = "primary", size = "md", ...props }) { const variantMap = { primary: "default", secondary: "secondary" }; const sizeMap = { sm: "sm", md: "default", lg: "lg" }; return ( <ShadcnButton variant={variantMap[variant]} size={sizeMap[size]} {...props} /> ); }
For applications requiring multiple brand themes beyond light/dark:
css[data-theme="ocean"] { --primary: oklch(0.55 0.18 230); --primary-foreground: oklch(0.985 0 0); } [data-theme="forest"] { --primary: oklch(0.50 0.15 145); --primary-foreground: oklch(0.985 0 0); }
tsxconst [theme, setTheme] = useState("light"); useEffect(() => { document.documentElement.setAttribute("data-theme", theme); }, [theme]);
Verify all required tokens are defined:
bash#!/bin/bash REQUIRED=("--background" "--foreground" "--primary" "--primary-foreground") for token in "${REQUIRED[@]}"; do grep -q "$token:" src/styles/globals.css || echo "Missing: $token" done
--primary, --primary-foreground) for seamless integration@theme inline vs @theme: Use @theme inline when bridging CSS variables to Tailwind utilities; use @theme for direct token definition:root. Missing dark tokens cause visual regressions:root are global. Use [data-theme] selectors for multi-theme without conflictsvar() lookup adds minimal but non-zero overhead@theme directive and @theme inline are v4.1+ features. For v3 projects, use tailwind.config.js with theme.extendglobals.css. Never hardcode color values in components--primary, --destructive) not appearance-based (--blue-500, --red-600)-foreground token for contrast complianceindex.ts for clean importsgap-2, gap-4, gap-6) through DS components rather than arbitrary values| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-02 | pass→pass | 8,392 | 8,911 | +6% | 1 | 1 | 0% | 1,703 | 4,402 | +158% | 0 | 0 | — |
case-01 | fail→pass | 25,035 | 12,844 | -49% | 1 | 1 | 0% | 5,170 | 5,402 | +4% | 0 | 0 | — |
case-03 | pass→pass | 12,679 | 8,658 | -32% | 1 | 1 | 0% | 2,513 | 4,268 | +70% | 0 | 0 | — |
case-04 | pass→pass | 10,374 | 8,662 | -17% | 1 | 1 | 0% | 1,977 | 4,327 | +119% | 0 | 0 | — |
case-05 | pass→pass | 13,497 | 10,010 | -26% | 1 | 1 | 0% | 2,288 | 4,591 | +101% | 0 | 0 | — |
case-06 | pass→pass | 7,892 | 4,744 | -40% | 1 | 1 | 0% | 1,393 | 3,426 | +146% | 0 | 0 | — |
case-07 | pass→pass | 11,489 | 7,524 | -35% | 1 | 1 | 0% | 1,917 | 4,014 | +109% | 0 | 0 | — |
case-08 | pass→pass | 15,919 | 7,720 | -52% | 1 | 1 | 0% | 2,866 | 3,941 | +38% | 0 | 0 | — |
case-09 | fail→fail | 18,621 | 15,634 | -16% | 1 | 1 | 0% | 3,110 | 5,659 | +82% | 0 | 0 | — |
case-10 | pass→pass | 13,248 | 10,131 | -24% | 1 | 1 | 0% | 2,623 | 4,462 | +70% | 0 | 0 | — |
case-11 | pass→pass | 6,496 | 6,585 | +1% | 1 | 1 | 0% | 1,224 | 3,764 | +208% | 0 | 0 | — |
case-12 | pass→pass | 14,262 | 9,811 | -31% | 1 | 1 | 0% | 2,444 | 4,403 | +80% | 0 | 0 | — |
case-13 | pass→pass | 5,203 | 1,931 | -63% | 1 | 1 | 0% | 914 | 2,860 | +213% | 0 | 0 | — |
case-14 | pass→pass | 15,090 | 10,994 | -27% | 1 | 1 | 0% | 2,676 | 4,620 | +73% | 0 | 0 | — |
case-15 | pass→pass | 9,086 | 9,355 | +3% | 1 | 1 | 0% | 1,452 | 4,177 | +188% | 0 | 0 | — |
case-16 | fail→pass | 15,542 | 10,775 | -31% | 1 | 1 | 0% | 2,479 | 4,306 | +74% | 0 | 0 | — |
case-17 | pass→pass | 8,078 | 3,821 | -53% | 1 | 1 | 0% | 1,335 | 3,257 | +144% | 0 | 0 | — |
case-18 | pass→pass | 14,640 | 9,842 | -33% | 1 | 1 | 0% | 2,321 | 4,310 | +86% | 0 | 0 | — |
case-19 | pass→pass | 10,224 | 8,680 | -15% | 1 | 1 | 0% | 1,559 | 3,934 | +152% | 0 | 0 | — |
case-20 | pass→pass | 12,113 | 11,481 | -5% | 1 | 1 | 0% | 1,950 | 4,376 | +124% | 0 | 0 | — |
case-21 | pass→pass | 5,014 | 3,079 | -39% | 1 | 1 | 0% | 851 | 3,058 | +259% | 0 | 0 | — |
case-22 | fail→pass | 17,088 | 12,573 | -26% | 1 | 1 | 0% | 2,533 | 4,809 | +90% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +14 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.