Tailwind Design System
Seth HobsonCommunityCreated by Seth Hobson
- Skill
- Tool integration
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a framework for creating production-ready design systems using Tailwind CSS v4. It focuses on the transition to CSS-first configuration, utilizing the @theme directive for design tokens, implementing responsive patterns, and leveraging modern React 19 component architectures. The documentation covers token hierarchies, dark mode implementation, and advanced animation patterns using native CSS.
What it does
- Configures design tokens using the @theme directive in CSS.
- Implements dark mode via custom CSS variants.
- Standardizes UI patterns using Class Variance Authority (CVA).
- Provides patterns for compound components in React 19.
- Defines native CSS animations and keyframes for UI components.
- Offers guidance for migrating from Tailwind CSS v3 to v4.
When to use it
- When building a new component library from scratch with Tailwind CSS v4.
- When standardizing design tokens and theming across a large codebase.
- When implementing responsive and accessible UI components.
- When upgrading existing Tailwind v3 projects to the v4 architecture.
- When defining complex component variants and states.
Advantages
- Leverages native CSS variables for better performance and maintainability.
- Simplifies configuration by moving from JS-based files to CSS-first patterns.
- Promotes a consistent design token hierarchy.
- Supports modern React 19 patterns like simplified ref handling.
- Enables native CSS animations and transitions for smoother UI interactions.
Usage examples
- “How do I define primary and secondary color tokens in Tailwind v4?”
- The agent provides a CSS snippet using the @theme block with --color-primary and --color-secondary variables defined using OKLCH values.
- “Show me how to create a button component with variants in Tailwind v4.”
- The agent generates a React component using class-variance-authority (CVA) that maps variants like 'destructive' or 'outline' to Tailwind utility classes.
- “How do I implement dark mode in Tailwind v4?”
- The agent explains the use of @custom-variant dark and provides a CSS block to override theme variables within a .dark class.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/wshobson/tailwind-design-system/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip tailwind-design-system.zip -d .agents/skills
Documentation by Seth Hobson
Tailwind Design System (v4)
Build production-ready design systems with Tailwind CSS v4, including CSS-first configuration, design tokens, component variants, responsive patterns, and accessibility.
Note: This skill targets Tailwind CSS v4 (2024+). For v3 projects, refer to the upgrade guide.
When to Use This Skill
Creating a component library with Tailwind v4
Implementing design tokens and theming with CSS-first configuration
Building responsive and accessible components
Standardizing UI patterns across a codebase
Migrating from Tailwind v3 to v4
Setting up dark mode with native CSS features
Key v4 Changes
| v3 Pattern | v4 Pattern |
|---|---|
tailwind.config.ts | @theme in CSS |
@tailwind base/components/utilities | @import "tailwindcss" |
darkMode: "class" | @custom-variant dark (&:where(.dark, .dark *)) |
theme.extend.colors | @theme { --color-*: value } |
require("tailwindcss-animate") | CSS @keyframes in @theme + @starting-style for entry animations |
Quick Start
/* app.css - Tailwind v4 CSS-first configuration */
@import "tailwindcss";
/* Define your theme with @theme */
@theme {
/* Semantic color tokens using OKLCH for better color perception */
--color-background: oklch(100% 0 0);
--color-foreground: oklch(14.5% 0.025 264);
--color-primary: oklch(14.5% 0.025 264);
--color-primary-foreground: oklch(98% 0.01 264);
--color-secondary: oklch(96% 0.01 264);
--color-secondary-foreground: oklch(14.5% 0.025 264);
--color-muted: oklch(96% 0.01 264);
--color-muted-foreground: oklch(46% 0.02 264);
--color-accent: oklch(96% 0.01 264);
--color-accent-foreground: oklch(14.5% 0.025 264);
--color-destructive: oklch(53% 0.22 27);
--color-destructive-foreground: oklch(98% 0.01 264);
--color-border: oklch(91% 0.01 264);
--color-ring: oklch(14.5% 0.025 264);
--color-card: oklch(100% 0 0);
--color-card-foreground: oklch(14.5% 0.025 264);
/* Ring offset for focus states */
--color-ring-offset: oklch(100% 0 0);
/* Radius tokens */
--radius-sm: 0.25rem;
--radius-md: 0.375rem;
--radius-lg: 0.5rem;
--radius-xl: 0.75rem;
/* Animation tokens - keyframes inside @theme are output when referenced by --animate-* variables */
--animate-fade-in: fade-in 0.2s ease-out;
--animate-fade-out: fade-out 0.2s ease-in;
--animate-slide-in: slide-in 0.3s ease-out;
--animate-slide-out: slide-out 0.3s ease-in;
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fade-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes slide-in {
from {
transform: translateY(-0.5rem);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
@keyframes slide-out {
from {
transform: translateY(0);
opacity: 1;
}
to {
transform: translateY(-0.5rem);
opacity: 0;
}
}
}
/* Dark mode variant - use @custom-variant for class-based dark mode */
@custom-variant dark (&:where(.dark, .dark *));
/* Dark mode theme overrides */
.dark {
--color-background: oklch(14.5% 0.025 264);
--color-foreground: oklch(98% 0.01 264);
--color-primary: oklch(98% 0.01 264);
--color-primary-foreground: oklch(14.5% 0.025 264);
--color-secondary: oklch(22% 0.02 264);
--color-secondary-foreground: oklch(98% 0.01 264);
--color-muted: oklch(22% 0.02 264);
--color-muted-foreground: oklch(65% 0.02 264);
--color-accent: oklch(22% 0.02 264);
--color-accent-foreground: oklch(98% 0.01 264);
--color-destructive: oklch(42% 0.15 27);
--color-destructive-foreground: oklch(98% 0.01 264);
--color-border: oklch(22% 0.02 264);
--color-ring: oklch(83% 0.02 264);
--color-card: oklch(14.5% 0.025 264);
--color-card-foreground: oklch(98% 0.01 264);
--color-ring-offset: oklch(14.5% 0.025 264);
}
/* Base styles */
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground antialiased;
}
}Core Concepts
1. Design Token Hierarchy
Brand Tokens (abstract)
└── Semantic Tokens (purpose)
└── Component Tokens (specific)
Example:
oklch(45% 0.2 260) → --color-primary → bg-primary2. Component Architecture
Base styles → Variants → Sizes → States → OverridesDetailed patterns and worked examples
Detailed pattern documentation lives in references/details.md. Read that file when the navigation tier above is insufficient.
Files
- ATTRIBUTION.mdAdded by AutoGineer1020 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md5.2 KiB
- references/advanced-patterns.md7.9 KiB
- references/details.md10.4 KiB
references/details.md
tailwind-design-system — detailed patterns and worked examples
Patterns
Pattern 1: CVA (Class Variance Authority) Components
// components/ui/button.tsx
import { Slot } from '@radix-ui/react-slot'
import { cva, type VariantProps } from 'class-variance-authority'
import { cn } from '@/lib/utils'
const buttonVariants = cva(
// Base styles - v4 uses native CSS variables
'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
outline: 'border border-border bg-background hover:bg-accent hover:text-accent-foreground',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
ghost: 'hover:bg-accent hover:text-accent-foreground',
link: 'text-primary underline-offset-4 hover:underline',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-9 rounded-md px-3',
lg: 'h-11 rounded-md px-8',
icon: 'size-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
asChild?: boolean
}
// React 19: No forwardRef needed
export function Button({
className,
variant,
size,
asChild = false,
ref,
...props
}: ButtonProps & { ref?: React.Ref<HTMLButtonElement> }) {
const Comp = asChild ? Slot : 'button'
return (
<Comp
className={cn(buttonVariants({ variant, size, className }))}
ref={ref}
{...props}
/>
)
}
// Usage
<Button variant="destructive" size="lg">Delete</Button>
<Button variant="outline">Cancel</Button>
<Button asChild><Link href="/home">Home</Link></Button>Pattern 2: Compound Components (React 19)
// components/ui/card.tsx
import { cn } from '@/lib/utils'
// React 19: ref is a regular prop, no forwardRef
export function Card({
className,
ref,
...props
}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) {
return (
<div
ref={ref}
className={cn(
'rounded-lg border border-border bg-card text-card-foreground shadow-sm',
className
)}
{...props}
/>
)
}
export function CardHeader({
className,
ref,
...props
}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) {
return (
<div
ref={ref}
className={cn('flex flex-col space-y-1.5 p-6', className)}
{...props}
/>
)
}
export function CardTitle({
className,
ref,
...props
}: React.HTMLAttributes<HTMLHeadingElement> & { ref?: React.Ref<HTMLHeadingElement> }) {
return (
<h3
ref={ref}
className={cn('text-2xl font-semibold leading-none tracking-tight', className)}
{...props}
/>
)
}
export function CardDescription({
className,
ref,
...props
}: React.HTMLAttributes<HTMLParagraphElement> & { ref?: React.Ref<HTMLParagraphElement> }) {
return (
<p
ref={ref}
className={cn('text-sm text-muted-foreground', className)}
{...props}
/>
)
}
export function CardContent({
className,
ref,
...props
}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) {
return (
<div ref={ref} className={cn('p-6 pt-0', className)} {...props} />
)
}
export function CardFooter({
className,
ref,
...props
}: React.HTMLAttributes<HTMLDivElement> & { ref?: React.Ref<HTMLDivElement> }) {
return (
<div
ref={ref}
className={cn('flex items-center p-6 pt-0', className)}
{...props}
/>
)
}
// Usage
<Card>
<CardHeader>
<CardTitle>Account</CardTitle>
<CardDescription>Manage your account settings</CardDescription>
</CardHeader>
<CardContent>
<form>...</form>
</CardContent>
<CardFooter>
<Button>Save</Button>
</CardFooter>
</Card>Pattern 3: Form Components
// components/ui/input.tsx
import { cn } from '@/lib/utils'
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
error?: string
ref?: React.Ref<HTMLInputElement>
}
export function Input({ className, type, error, ref, ...props }: InputProps) {
return (
<div className="relative">
<input
type={type}
className={cn(
'flex h-10 w-full rounded-md border border-border bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
error && 'border-destructive focus-visible:ring-destructive',
className
)}
ref={ref}
aria-invalid={!!error}
aria-describedby={error ? `${props.id}-error` : undefined}
{...props}
/>
{error && (
<p
id={`${props.id}-error`}
className="mt-1 text-sm text-destructive"
role="alert"
>
{error}
</p>
)}
</div>
)
}
// components/ui/label.tsx
import { cva, type VariantProps } from 'class-variance-authority'
const labelVariants = cva(
'text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70'
)
export function Label({
className,
ref,
...props
}: React.LabelHTMLAttributes<HTMLLabelElement> & { ref?: React.Ref<HTMLLabelElement> }) {
return (
<label ref={ref} className={cn(labelVariants(), className)} {...props} />
)
}
// Usage with React Hook Form + Zod
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
const schema = z.object({
email: z.string().email('Invalid email address'),
password: z.string().min(8, 'Password must be at least 8 characters'),
})
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm({
resolver: zodResolver(schema),
})
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div className="space-y-2">
<Label htmlFor="email">Email</Label>
<Input
id="email"
type="email"
{...register('email')}
error={errors.email?.message}
/>
</div>
<div className="space-y-2">
<Label htmlFor="password">Password</Label>
<Input
id="password"
type="password"
{...register('password')}
error={errors.password?.message}
/>
</div>
<Button type="submit" className="w-full">Sign In</Button>
</form>
)
}Pattern 4: Responsive Grid System
// components/ui/grid.tsx
import { cn } from '@/lib/utils'
import { cva, type VariantProps } from 'class-variance-authority'
const gridVariants = cva('grid', {
variants: {
cols: {
1: 'grid-cols-1',
2: 'grid-cols-1 sm:grid-cols-2',
3: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-3',
4: 'grid-cols-1 sm:grid-cols-2 lg:grid-cols-4',
5: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-5',
6: 'grid-cols-2 sm:grid-cols-3 lg:grid-cols-6',
},
gap: {
none: 'gap-0',
sm: 'gap-2',
md: 'gap-4',
lg: 'gap-6',
xl: 'gap-8',
},
},
defaultVariants: {
cols: 3,
gap: 'md',
},
})
interface GridProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof gridVariants> {}
export function Grid({ className, cols, gap, ...props }: GridProps) {
return (
<div className={cn(gridVariants({ cols, gap, className }))} {...props} />
)
}
// Container component
const containerVariants = cva('mx-auto w-full px-4 sm:px-6 lg:px-8', {
variants: {
size: {
sm: 'max-w-screen-sm',
md: 'max-w-screen-md',
lg: 'max-w-screen-lg',
xl: 'max-w-screen-xl',
'2xl': 'max-w-screen-2xl',
full: 'max-w-full',
},
},
defaultVariants: {
size: 'xl',
},
})
interface ContainerProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof containerVariants> {}
export function Container({ className, size, ...props }: ContainerProps) {
return (
<div className={cn(containerVariants({ size, className }))} {...props} />
)
}
// Usage
<Container>
<Grid cols={4} gap="lg">
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</Grid>
</Container>For advanced animation and dark mode patterns, see references/advanced-patterns.md:
Pattern 5: Native CSS Animations — dialog
@keyframes, native popover API with@starting-style,allow-discretetransitions, and a fullDialogContent/DialogOverlayimplementation using Radix UIPattern 6: Dark Mode —
ThemeProvidercontext withlocalStoragepersistence,prefers-color-schemedetection, metatheme-colorupdate, and aThemeTogglebutton component
Utility Functions
// lib/utils.ts
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
// Focus ring utility
export const focusRing = cn(
"focus-visible:outline-none focus-visible:ring-2",
"focus-visible:ring-ring focus-visible:ring-offset-2",
);
// Disabled utility
export const disabled = "disabled:pointer-events-none disabled:opacity-50";For advanced v4 CSS patterns, the full v3-to-v4 migration checklist, and complete best practices, see references/advanced-patterns.md:
Custom
@utility— reusable CSS utilities for decorative lines and text gradientsTheme modifiers —
@theme inline(reference other CSS vars),@theme static(always output),@import "tailwindcss" theme(static)Namespace overrides — clearing default Tailwind color scales with
--color-*: initialSemi-transparent variants —
color-mix()for alpha scale generationContainer queries —
--container-*token definitionsv3→v4 migration checklist — 10-item checklist covering config, directives, colors, dark mode, animations, React 19 ref changes
Best practices — full Do's and Don'ts list
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Seth Hobson
- Publisher
- Seth Hobson (Community)
- Source
- wshobson/agents · plugins/frontend-mobile-development/skills/tailwind-design-system · commit 4bde316
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Seth Hobson.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download tailwind-design-system.zip