Claude Cursor Skill

tailwind-design-system

Build scalable design systems with Tailwind CSS v4, design tokens, component libraries, and responsive patterns. Use when creating component libraries, implementing design systems, or standardizing UI patterns.

LLM Mart · 0 points · 15 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download wshobson-agents-plugins_frontend-mobile-development_skills_tailwind-design-system-554237f.zip · 8 KB
Part of wshobson/agents — 170 skills

Install

skills CLI npx skills add https://github.com/wshobson/agents/tree/main/plugins/frontend-mobile-development/skills/tailwind-design-system
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install wshobson-agents@llmmart
Git git clone https://github.com/wshobson/agents.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole wshobson/agents collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

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-primary

2. Component Architecture

Base styles → Variants → Sizes → States → Overrides

Detailed patterns and worked examples

Detailed pattern documentation lives in references/details.md. Read that file when the navigation tier above is insufficient.

Files (agents)
  • references
    • advanced-patterns.md 7.9 KB
      # Tailwind Design System: Advanced Patterns
      
      Advanced Tailwind CSS v4 patterns including animations, dark mode theming, custom utilities, theme modifiers, namespace overrides, and the v3-to-v4 migration checklist.
      
      ## Pattern 5: Native CSS Animations (v4)
      
      ```css
      /* In your CSS file - native @starting-style for entry animations */
      @theme {
        --animate-dialog-in: dialog-fade-in 0.2s ease-out;
        --animate-dialog-out: dialog-fade-out 0.15s ease-in;
      }
      
      @keyframes dialog-fade-in {
        from {
          opacity: 0;
          transform: scale(0.95) translateY(-0.5rem);
        }
        to {
          opacity: 1;
          transform: scale(1) translateY(0);
        }
      }
      
      @keyframes dialog-fade-out {
        from {
          opacity: 1;
          transform: scale(1) translateY(0);
        }
        to {
          opacity: 0;
          transform: scale(0.95) translateY(-0.5rem);
        }
      }
      
      /* Native popover animations using @starting-style */
      [popover] {
        transition:
          opacity 0.2s,
          transform 0.2s,
          display 0.2s allow-discrete;
        opacity: 0;
        transform: scale(0.95);
      }
      
      [popover]:popover-open {
        opacity: 1;
        transform: scale(1);
      }
      
      @starting-style {
        [popover]:popover-open {
          opacity: 0;
          transform: scale(0.95);
        }
      }
      ```
      
      ```typescript
      // components/ui/dialog.tsx - Using native popover API
      import * as DialogPrimitive from '@radix-ui/react-dialog'
      import { cn } from '@/lib/utils'
      
      const DialogPortal = DialogPrimitive.Portal
      
      export function DialogOverlay({
        className,
        ref,
        ...props
      }: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Overlay> & {
        ref?: React.Ref<HTMLDivElement>
      }) {
        return (
          <DialogPrimitive.Overlay
            ref={ref}
            className={cn(
              'fixed inset-0 z-50 bg-black/80',
              'data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out',
              className
            )}
            {...props}
          />
        )
      }
      
      export function DialogContent({
        className,
        children,
        ref,
        ...props
      }: React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
        ref?: React.Ref<HTMLDivElement>
      }) {
        return (
          <DialogPortal>
            <DialogOverlay />
            <DialogPrimitive.Content
              ref={ref}
              className={cn(
                'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border border-border bg-background p-6 shadow-lg sm:rounded-lg',
                'data-[state=open]:animate-dialog-in data-[state=closed]:animate-dialog-out',
                className
              )}
              {...props}
            >
              {children}
            </DialogPrimitive.Content>
          </DialogPortal>
        )
      }
      ```
      
      ## Pattern 6: Dark Mode with CSS (v4)
      
      ```typescript
      // providers/ThemeProvider.tsx - Simplified for v4
      'use client'
      
      import { createContext, useContext, useEffect, useState } from 'react'
      
      type Theme = 'dark' | 'light' | 'system'
      
      interface ThemeContextType {
        theme: Theme
        setTheme: (theme: Theme) => void
        resolvedTheme: 'dark' | 'light'
      }
      
      const ThemeContext = createContext<ThemeContextType | undefined>(undefined)
      
      export function ThemeProvider({
        children,
        defaultTheme = 'system',
        storageKey = 'theme',
      }: {
        children: React.ReactNode
        defaultTheme?: Theme
        storageKey?: string
      }) {
        const [theme, setTheme] = useState<Theme>(defaultTheme)
        const [resolvedTheme, setResolvedTheme] = useState<'dark' | 'light'>('light')
      
        useEffect(() => {
          const stored = localStorage.getItem(storageKey) as Theme | null
          if (stored) setTheme(stored)
        }, [storageKey])
      
        useEffect(() => {
          const root = document.documentElement
          root.classList.remove('light', 'dark')
      
          const resolved = theme === 'system'
            ? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
            : theme
      
          root.classList.add(resolved)
          setResolvedTheme(resolved)
      
          // Update meta theme-color for mobile browsers
          const metaThemeColor = document.querySelector('meta[name="theme-color"]')
          if (metaThemeColor) {
            metaThemeColor.setAttribute('content', resolved === 'dark' ? '#09090b' : '#ffffff')
          }
        }, [theme])
      
        return (
          <ThemeContext.Provider value={{
            theme,
            setTheme: (newTheme) => {
              localStorage.setItem(storageKey, newTheme)
              setTheme(newTheme)
            },
            resolvedTheme,
          }}>
            {children}
          </ThemeContext.Provider>
        )
      }
      
      export const useTheme = () => {
        const context = useContext(ThemeContext)
        if (!context) throw new Error('useTheme must be used within ThemeProvider')
        return context
      }
      
      // components/ThemeToggle.tsx
      import { Moon, Sun } from 'lucide-react'
      import { useTheme } from '@/providers/ThemeProvider'
      
      export function ThemeToggle() {
        const { resolvedTheme, setTheme } = useTheme()
      
        return (
          <Button
            variant="ghost"
            size="icon"
            onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}
          >
            <Sun className="size-5 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
            <Moon className="absolute size-5 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
            <span className="sr-only">Toggle theme</span>
          </Button>
        )
      }
      ```
      
      ## Advanced v4 Patterns
      
      ### Custom Utilities with `@utility`
      
      Define reusable custom utilities:
      
      ```css
      /* Custom utility for decorative lines */
      @utility line-t {
        @apply relative before:absolute before:top-0 before:-left-[100vw] before:h-px before:w-[200vw] before:bg-gray-950/5 dark:before:bg-white/10;
      }
      
      /* Custom utility for text gradients */
      @utility text-gradient {
        @apply bg-gradient-to-r from-primary to-accent bg-clip-text text-transparent;
      }
      ```
      
      ### Theme Modifiers
      
      ```css
      /* Use @theme inline when referencing other CSS variables */
      @theme inline {
        --font-sans: var(--font-inter), system-ui;
      }
      
      /* Use @theme static to always generate CSS variables (even when unused) */
      @theme static {
        --color-brand: oklch(65% 0.15 240);
      }
      
      /* Import with theme options */
      @import "tailwindcss" theme(static);
      ```
      
      ### Namespace Overrides
      
      ```css
      @theme {
        /* Clear all default colors and define your own */
        --color-*: initial;
        --color-white: #fff;
        --color-black: #000;
        --color-primary: oklch(45% 0.2 260);
        --color-secondary: oklch(65% 0.15 200);
      
        /* Clear ALL defaults for a minimal setup */
        /* --*: initial; */
      }
      ```
      
      ### Semi-transparent Color Variants
      
      ```css
      @theme {
        /* Use color-mix() for alpha variants */
        --color-primary-50: color-mix(in oklab, var(--color-primary) 5%, transparent);
        --color-primary-100: color-mix(
          in oklab,
          var(--color-primary) 10%,
          transparent
        );
        --color-primary-200: color-mix(
          in oklab,
          var(--color-primary) 20%,
          transparent
        );
      }
      ```
      
      ### Container Queries
      
      ```css
      @theme {
        --container-xs: 20rem;
        --container-sm: 24rem;
        --container-md: 28rem;
        --container-lg: 32rem;
      }
      ```
      
      ## v3 to v4 Migration Checklist
      
      - [ ] Replace `tailwind.config.ts` with CSS `@theme` block
      - [ ] Change `@tailwind base/components/utilities` to `@import "tailwindcss"`
      - [ ] Move color definitions to `@theme { --color-*: value }`
      - [ ] Replace `darkMode: "class"` with `@custom-variant dark`
      - [ ] Move `@keyframes` inside `@theme` blocks (ensures keyframes output with theme)
      - [ ] Replace `require("tailwindcss-animate")` with native CSS animations
      - [ ] Update `h-10 w-10` to `size-10` (new utility)
      - [ ] Remove `forwardRef` (React 19 passes ref as prop)
      - [ ] Consider OKLCH colors for better color perception
      - [ ] Replace custom plugins with `@utility` directives
      
      ## Best Practices
      
      ### Do's
      
      - **Use `@theme` blocks** - CSS-first configuration is v4's core pattern
      - **Use OKLCH colors** - Better perceptual uniformity than HSL
      - **Compose with CVA** - Type-safe variants
      - **Use semantic tokens** - `bg-primary` not `bg-blue-500`
      - **Use `size-*`** - New shorthand for `w-* h-*`
      - **Add accessibility** - ARIA attributes, focus states
      
      ### Don'ts
      
      - **Don't use `tailwind.config.ts`** - Use CSS `@theme` instead
      - **Don't use `@tailwind` directives** - Use `@import "tailwindcss"`
      - **Don't use `forwardRef`** - React 19 passes ref as prop
      - **Don't use arbitrary values** - Extend `@theme` instead
      - **Don't hardcode colors** - Use semantic tokens
      - **Don't forget dark mode** - Test both themes
      
    • details.md 10.4 KB
      # tailwind-design-system — detailed patterns and worked examples
      
      ## Patterns
      
      ### Pattern 1: CVA (Class Variance Authority) Components
      
      ```typescript
      // 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)
      
      ```typescript
      // 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
      
      ```typescript
      // 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
      
      ```typescript
      // 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](references/advanced-patterns.md):
      
      - **Pattern 5: Native CSS Animations** — dialog `@keyframes`, native popover API with `@starting-style`, `allow-discrete` transitions, and a full `DialogContent`/`DialogOverlay` implementation using Radix UI
      - **Pattern 6: Dark Mode** — `ThemeProvider` context with `localStorage` persistence, `prefers-color-scheme` detection, meta `theme-color` update, and a `ThemeToggle` button component
      
      ## Utility Functions
      
      ```typescript
      // 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](references/advanced-patterns.md):
      
      - **Custom `@utility`** — reusable CSS utilities for decorative lines and text gradients
      - **Theme modifiers** — `@theme inline` (reference other CSS vars), `@theme static` (always output), `@import "tailwindcss" theme(static)`
      - **Namespace overrides** — clearing default Tailwind color scales with `--color-*: initial`
      - **Semi-transparent variants** — `color-mix()` for alpha scale generation
      - **Container queries** — `--container-*` token definitions
      - **v3→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
      
  • SKILL.md 5.2 KB
    ---
    name: tailwind-design-system
    description: Build scalable design systems with Tailwind CSS v4, design tokens, component libraries, and responsive patterns. Use when creating component libraries, implementing design systems, or standardizing UI patterns.
    ---
    
    # 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](https://tailwindcss.com/docs/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
    
    ```css
    /* 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-primary
    ```
    
    ### 2. Component Architecture
    
    ```
    Base styles → Variants → Sizes → States → Overrides
    ```
    
    ## Detailed patterns and worked examples
    
    Detailed pattern documentation lives in `references/details.md`. Read that file when the navigation tier above is insufficient.
    
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related