AGENTS.md
# Tailwind CSS Best Practices - Agent Guide
This skill provides comprehensive Tailwind CSS patterns and best practices for AI coding agents.
## Skill Overview
**Name:** tailwind-best-practices
**Version:** 1.0.0
**Framework:** Tailwind CSS v3.4+ / v4.0+
**Rule Count:** 29 rules across 8 categories
**License:** MIT
## When to Use This Skill
Activate this skill when:
- Writing or refactoring Tailwind CSS classes
- Implementing responsive designs with breakpoints
- Adding dark mode support to applications
- Creating reusable component patterns
- Configuring Tailwind theme customization (v3 config or v4 @theme)
- Migrating from Tailwind v3 to v4
- Building forms, buttons, cards, tables, navigation
- Optimizing Tailwind for production
- Questions about Tailwind utilities and patterns
## Rule Categories
### 1. Responsive Design (CRITICAL - 6 rules)
Mobile-first responsive patterns are fundamental to every Tailwind project.
**Key Concepts:**
- Mobile-first: Base styles apply to all screens, add breakpoints upward
- Breakpoint order: `sm:` (640px) → `md:` (768px) → `lg:` (1024px) → `xl:` (1280px) → `2xl:` (1536px)
- Container queries for component-scoped responsiveness
- Fluid typography with `clamp()` for smooth scaling
- Responsive grid systems with `grid-cols-{n}`
**Common Patterns:**
```html
<!-- Mobile-first responsive layout -->
<div class="
w-full <!-- Mobile: full width -->
sm:w-1/2 <!-- Tablet: half -->
lg:w-1/3 <!-- Desktop: third -->
px-4 md:px-8 <!-- Responsive spacing -->
">
Content
</div>
```
**Rules:** `resp-mobile-first`, `responsive-breakpoint-order`, `responsive-container-queries`, `responsive-fluid-typography`, `responsive-aspect-ratio`, `responsive-grid-system`
### 2. Dark Mode (CRITICAL - 6 rules)
Modern applications require seamless light/dark theme support.
**Key Concepts:**
- Class strategy (`darkMode: 'class'`) for manual toggle
- Media strategy (`darkMode: 'media'`) for system preference
- Semantic color naming for maintainable themes
- Custom color palettes with full scales (50-950)
- Smooth transitions between themes
**Common Patterns:**
```html
<!-- Dark mode aware component -->
<div class="
bg-white dark:bg-gray-900
text-gray-900 dark:text-white
border border-gray-200 dark:border-gray-700
">
Content adapts to theme
</div>
```
**Rules:** `dark-setup`, `dark-class-strategy`, `dark-media-strategy`, `dark-color-scheme`, `dark-custom-colors`, `dark-transitions`
### 3. Component Patterns (HIGH - 7 rules)
Reusable component patterns for consistent UI.
**Key Concepts:**
- Use `clsx` + `tailwind-merge` (cn utility) for conditional classes
- Component variants with proper type safety
- Consistent button, card, form, table, modal patterns
- Proper accessibility attributes
- Responsive component behavior
**Common Patterns:**
```tsx
// Button with variants using cn utility
import { cn } from '@/lib/utils'
function Button({ variant = 'primary', size = 'md', className, children }) {
return (
<button className={cn(
'inline-flex items-center justify-center rounded-lg font-medium transition-colors',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2',
{
'bg-blue-600 text-white hover:bg-blue-700': variant === 'primary',
'bg-gray-100 text-gray-900 hover:bg-gray-200': variant === 'secondary',
},
{
'px-3 py-1.5 text-sm': size === 'sm',
'px-4 py-2 text-base': size === 'md',
'px-6 py-3 text-lg': size === 'lg',
},
className
)}>
{children}
</button>
)
}
```
**Rules:** `comp-clsx-cn`, `component-btn-variants`, `component-card-patterns`, `component-form-elements`, `component-modals`, `component-navigation`, `component-tables`
### 4. Custom Configuration — v3 (HIGH - 6 rules)
Extending Tailwind's v3 theme via `tailwind.config.js`. For v4, use `@theme {}` instead.
**Key Concepts:**
- Always `extend` theme, don't override (preserves defaults)
- Use full color scales (50-950) for flexibility
- Font families with proper fallbacks
- Custom spacing for layout consistency
- Plugins for extended functionality
- Presets for shared configuration
**Common Patterns:**
```js
// tailwind.config.js - Proper theme extension
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
darkMode: 'class',
theme: {
extend: {
colors: {
brand: {
50: '#eff6ff',
500: '#3b82f6',
950: '#172554',
},
},
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
},
spacing: {
'18': '4.5rem',
'header': '4rem',
},
},
},
plugins: [
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
],
}
```
**Rules:** `config-extend-theme`, `config-custom-colors`, `config-custom-fonts`, `config-custom-spacing`, `config-plugins`, `config-presets`
### 5. V4 & Migration (HIGH - 4 rules)
Tailwind CSS v4 setup, configuration, and migration from v3.
**Key Concepts:**
- CSS-first architecture: `@import "tailwindcss"` replaces `@tailwind` directives
- `@theme {}` replaces `tailwind.config.js` for design tokens
- `@utility` and `@custom-variant` replace JS plugin API
- `@variant` for nesting variants in custom CSS
- `@reference` for importing without emitting CSS
- `@source` / `@source not` for scan control
- New features: `starting:`, `forced-colors:`, `color-mix()`, `transition-discrete`
- Container queries and aspect-ratio built into core (no plugins)
**Common Patterns:**
```css
@import "tailwindcss";
@theme {
--color-brand-500: #3b82f6;
--font-sans: "Inter", sans-serif;
}
@utility scrollbar-hide {
scrollbar-width: none;
}
@custom-variant hocus (&:hover, &:focus);
```
**Rules:** `v4-installation`, `v4-theme-configuration`, `v4-custom-utilities`, `v4-migration`
### 6. Spacing & Typography (MEDIUM - 0 rules)
Consistent spacing and typography systems.
**Key Concepts:**
- Use Tailwind's spacing scale (0-96)
- Custom spacing only when needed
- Typography scale with line heights
- Responsive text sizing
- Vertical rhythm
**Future Rules:** To be added based on common patterns
### 7. Animation (MEDIUM - 0 rules)
Smooth transitions and animations.
**Key Concepts:**
- Transition utilities for state changes
- Custom keyframe animations
- Respect `prefers-reduced-motion`
- Performance considerations
**Future Rules:** To be added based on common patterns
### 8. Performance (LOW - 0 rules)
Build and runtime optimization.
**Key Concepts:**
- Content configuration for tree-shaking
- JIT mode benefits
- Arbitrary value usage
- Bundle size optimization
**Future Rules:** To be added based on common patterns
## Tailwind CSS v4 Quick Reference
When working with Tailwind v4, be aware of these key differences from v3:
### Setup
```css
/* Single import replaces @tailwind directives */
@import "tailwindcss";
```
### Configuration
```css
/* @theme replaces tailwind.config.js */
@theme {
--color-brand-500: #3b82f6;
--font-sans: "Inter", sans-serif;
--breakpoint-3xl: 1920px;
}
```
### Custom Code
```css
/* @utility replaces addUtilities() and @layer components */
@utility scrollbar-hide { scrollbar-width: none; }
/* @custom-variant replaces addVariant() */
@custom-variant hocus (&:hover, &:focus);
/* @variant for nesting in custom CSS */
.card {
background: white;
@variant dark { background: #1e293b; }
}
/* @plugin replaces require() in config */
@plugin "@tailwindcss/forms";
/* @reference for @apply without emitting CSS */
@reference "tailwindcss";
```
### New Variants and Features
```html
<!-- starting: for @starting-style animations -->
<div popover class="transition-discrete starting:open:opacity-0">
<!-- forced-colors: for Windows High Contrast -->
<input class="appearance-none forced-colors:appearance-auto">
<!-- Container query ranges (built-in, no plugin) -->
<div class="@container">
<div class="@min-sm:@max-lg:grid-cols-2">Range query</div>
</div>
<!-- Dynamic values — no config needed -->
<div class="grid-cols-15 px-17">
```
### Plugins No Longer Needed
- `@tailwindcss/aspect-ratio` — native `aspect-*` utilities
- `@tailwindcss/container-queries` — native `@container` with range variants
## Agent Workflow
### 1. Analyze Requirements
- Is this responsive? Use mobile-first approach
- Does it need dark mode? Apply dark: variants
- Is it a reusable component? Use cn utility
- Custom colors/fonts? Extend theme properly
### 2. Write Classes
- Start with base styles (display, sizing)
- Add responsive breakpoints (mobile-first)
- Include dark mode variants
- Add interactive states (hover, focus, active)
- Apply transitions for smooth UX
### 3. Component Pattern
```tsx
// Standard component structure
<ComponentName
className={cn(
// Base
'base styles',
// Responsive
'mobile lg:desktop',
// Colors + Dark Mode
'bg-white dark:bg-gray-900',
'text-gray-900 dark:text-white',
// Interactive
'hover:bg-gray-50',
'focus:ring-2',
// Transitions
'transition-colors duration-200',
// Allow override
className
)}
/>
```
### 4. Verify Patterns
- ✅ Mobile-first breakpoint order
- ✅ Dark mode coverage
- ✅ Accessibility (ARIA, semantic HTML)
- ✅ Interactive states
- ✅ No conflicting utilities
- ✅ Proper transitions
## Essential Utilities Reference
### Layout
```html
<!-- Flexbox -->
<div class="flex items-center justify-between gap-4">
<!-- Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Container -->
<div class="container mx-auto px-4 max-w-7xl">
```
### Spacing
```html
<!-- Padding: p-{size}, px-{size}, py-{size} -->
<div class="p-6 px-4 py-8">
<!-- Margin: m-{size}, mx-{size}, my-{size} -->
<div class="mt-4 mb-8 mx-auto">
<!-- Space between -->
<div class="space-y-4">
```
### Colors
```html
<!-- Background -->
<div class="bg-white dark:bg-gray-900">
<!-- Text -->
<p class="text-gray-900 dark:text-white">
<!-- Border -->
<div class="border border-gray-200 dark:border-gray-700">
```
### Typography
```html
<!-- Size -->
<h1 class="text-4xl md:text-5xl lg:text-6xl">
<!-- Weight -->
<p class="font-medium">
<!-- Line height -->
<p class="leading-relaxed">
```
### Interactive States
```html
<!-- Hover, focus, active -->
<button class="
hover:bg-blue-700
focus:outline-none focus:ring-2 focus:ring-blue-500
active:bg-blue-800
">
```
### Transitions
```html
<!-- Smooth transitions -->
<div class="transition-colors duration-200">
<div class="transition-all duration-300 ease-in-out">
```
## Common Anti-Patterns to Avoid
### ❌ Don't: Desktop-first breakpoints
```html
<div class="w-1/4 lg:w-1/3 md:w-1/2 sm:w-full">
```
### ✅ Do: Mobile-first breakpoints
```html
<div class="w-full sm:w-1/2 md:w-1/3 lg:w-1/4">
```
### ❌ Don't: Conflicting utilities
```html
<div class="px-4 px-8">
```
### ✅ Do: Single utility or use cn
```html
<div class={cn('px-4', condition && 'px-8')}>
```
### ❌ Don't: Override theme without extend (v3)
```js
theme: {
colors: { primary: '#blue' } // Lost all default colors!
}
```
### ✅ Do: Extend theme (v3) or use @theme (v4)
```js
// v3
theme: { extend: { colors: { brand: {...} } } }
```
```css
/* v4 — extends by default */
@theme { --color-brand-500: #3b82f6; }
```
### ❌ Don't: Arbitrary values for standard utilities
```html
<div class="w-[100%]">
```
### ✅ Do: Use built-in utilities
```html
<div class="w-full">
```
## Quick Decision Tree
**Question:** Am I building a new component?
→ YES: Use cn utility, create variants, ensure dark mode support
→ NO: Continue
**Question:** Does it need to be responsive?
→ YES: Mobile-first, add breakpoints upward (sm: md: lg:)
→ NO: Continue
**Question:** Does it have interactive states?
→ YES: Add hover:, focus:, active:, disabled: states
→ NO: Continue
**Question:** Should it transition smoothly?
→ YES: Add transition-* utilities
→ NO: Done
## Resources
- **Documentation:** https://tailwindcss.com/docs
- **Playground:** https://play.tailwindcss.com
- **Component Library:** https://tailwindui.com
- **Icons:** https://heroicons.com
- **Tools:** IntelliSense, Prettier Plugin, tailwind-merge
## Getting Help
1. Check rule files in `rules/` directory for specific patterns
2. Reference `_template.md` for rule structure
3. See `metadata.json` for official documentation links
4. Review `_sections.md` for category descriptions
## Success Metrics
When this skill is applied correctly, you should see:
- ✅ Consistent responsive behavior across devices
- ✅ Seamless dark mode transitions
- ✅ Reusable, maintainable components
- ✅ Accessible, keyboard-navigable interfaces
- ✅ Fast build times and small bundle sizes
- ✅ Clean, readable class names
- ✅ No conflicting utilities
- ✅ Type-safe component APIs
---
**Last Updated:** 2026-03-07
**Skill Version:** 1.0.0
**Maintainer:** Agent Skills Contributors
metadata.json
{
"name": "Tailwind CSS Best Practices",
"version": "1.0.0",
"tailwindVersion": "3.4+ / 4.0+",
"description": "Comprehensive patterns and best practices for Tailwind CSS v3.4+ and v4, covering responsive design, dark mode, component patterns, and configuration.",
"framework": "Tailwind CSS",
"license": "MIT",
"author": {
"name": "Agent Skills Contributors",
"url": "https://github.com/agent-skills/tailwind-best-practices"
},
"repository": {
"type": "git",
"url": "https://github.com/agent-skills/tailwind-best-practices"
},
"keywords": [
"tailwind",
"tailwindcss",
"css",
"utility-first",
"responsive-design",
"dark-mode",
"component-patterns",
"best-practices",
"v4"
],
"categories": [
{
"id": "responsive",
"name": "Responsive Design",
"priority": "CRITICAL",
"description": "Mobile-first responsive design patterns",
"ruleCount": 6
},
{
"id": "dark-mode",
"name": "Dark Mode",
"priority": "CRITICAL",
"description": "Dark mode implementation and theming",
"ruleCount": 6
},
{
"id": "components",
"name": "Component Patterns",
"priority": "HIGH",
"description": "Reusable component patterns and utilities",
"ruleCount": 7
},
{
"id": "configuration",
"name": "Custom Configuration",
"priority": "HIGH",
"description": "Theme extension and configuration",
"ruleCount": 6
},
{
"id": "spacing-typography",
"name": "Spacing & Typography",
"priority": "MEDIUM",
"description": "Spacing and typography systems",
"ruleCount": 0
},
{
"id": "animation",
"name": "Animation",
"priority": "MEDIUM",
"description": "Animations and transitions",
"ruleCount": 0
},
{
"id": "performance",
"name": "Performance",
"priority": "LOW",
"description": "Build and runtime optimization",
"ruleCount": 0
},
{
"id": "v4-migration",
"name": "V4 & Migration",
"priority": "HIGH",
"description": "Tailwind CSS v4 setup, configuration, custom utilities, and v3-to-v4 migration",
"ruleCount": 4
}
],
"references": [
{
"title": "Tailwind CSS Documentation",
"url": "https://tailwindcss.com/docs",
"type": "official",
"description": "Official Tailwind CSS documentation"
},
{
"title": "Utility-First Fundamentals",
"url": "https://tailwindcss.com/docs/utility-first",
"type": "concept",
"description": "Understanding the utility-first approach"
},
{
"title": "Responsive Design",
"url": "https://tailwindcss.com/docs/responsive-design",
"type": "guide",
"description": "Mobile-first responsive design with breakpoints"
},
{
"title": "Dark Mode",
"url": "https://tailwindcss.com/docs/dark-mode",
"type": "guide",
"description": "Implementing dark mode with class or media strategies"
},
{
"title": "Reusing Styles",
"url": "https://tailwindcss.com/docs/reusing-styles",
"type": "guide",
"description": "Extracting components and using @apply"
},
{
"title": "Adding Custom Styles",
"url": "https://tailwindcss.com/docs/adding-custom-styles",
"type": "guide",
"description": "Extending Tailwind with custom utilities and components"
},
{
"title": "Configuration",
"url": "https://tailwindcss.com/docs/configuration",
"type": "guide",
"description": "Customizing Tailwind configuration"
},
{
"title": "Theme Configuration",
"url": "https://tailwindcss.com/docs/theme",
"type": "guide",
"description": "Customizing the default theme"
},
{
"title": "Colors",
"url": "https://tailwindcss.com/docs/customizing-colors",
"type": "reference",
"description": "Default color palette and customization"
},
{
"title": "Spacing",
"url": "https://tailwindcss.com/docs/customizing-spacing",
"type": "reference",
"description": "Default spacing scale and customization"
},
{
"title": "Typography",
"url": "https://tailwindcss.com/docs/font-family",
"type": "reference",
"description": "Font families and typography utilities"
},
{
"title": "Container Queries",
"url": "https://tailwindcss.com/docs/hover-focus-and-other-states#container-queries",
"type": "feature",
"description": "Using container queries for component-scoped responsive design"
},
{
"title": "Arbitrary Values",
"url": "https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values",
"type": "feature",
"description": "Using arbitrary values for one-off customizations"
},
{
"title": "Functions & Directives",
"url": "https://tailwindcss.com/docs/functions-and-directives",
"type": "reference",
"description": "@apply, @layer, theme() and other directives"
},
{
"title": "Content Configuration",
"url": "https://tailwindcss.com/docs/content-configuration",
"type": "optimization",
"description": "Configuring content sources for tree-shaking"
},
{
"title": "Optimizing for Production",
"url": "https://tailwindcss.com/docs/optimizing-for-production",
"type": "optimization",
"description": "Minifying CSS and removing unused styles"
},
{
"title": "Using with Preprocessors",
"url": "https://tailwindcss.com/docs/using-with-preprocessors",
"type": "integration",
"description": "Using Tailwind with Sass, Less, or Stylus"
},
{
"title": "Editor Setup",
"url": "https://tailwindcss.com/docs/editor-setup",
"type": "tooling",
"description": "Setting up IntelliSense and formatting"
},
{
"title": "Tailwind CSS v4 Upgrade Guide",
"url": "https://tailwindcss.com/docs/upgrade-guide",
"type": "guide",
"description": "Official v3 to v4 migration guide"
},
{
"title": "Tailwind UI",
"url": "https://tailwindui.com",
"type": "resource",
"description": "Official component library and templates"
},
{
"title": "Headless UI",
"url": "https://headlessui.com",
"type": "resource",
"description": "Unstyled, accessible UI components"
},
{
"title": "Heroicons",
"url": "https://heroicons.com",
"type": "resource",
"description": "Beautiful hand-crafted SVG icons"
},
{
"title": "Play CDN",
"url": "https://tailwindcss.com/docs/installation/play-cdn",
"type": "tool",
"description": "Quick prototyping with CDN"
}
],
"tools": [
{
"name": "Tailwind CSS IntelliSense",
"description": "VS Code extension for autocomplete and linting",
"url": "https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss"
},
{
"name": "Prettier Plugin",
"description": "Automatic class sorting for consistent ordering",
"url": "https://github.com/tailwindlabs/prettier-plugin-tailwindcss"
},
{
"name": "Tailwind Merge",
"description": "Merge Tailwind classes without conflicts",
"url": "https://github.com/dcastil/tailwind-merge"
},
{
"name": "CLSX",
"description": "Utility for conditionally constructing className strings",
"url": "https://github.com/lukeed/clsx"
},
{
"name": "CVA",
"description": "Class Variance Authority for component variants",
"url": "https://cva.style"
}
],
"ecosystem": {
"frameworks": [
"React",
"Vue",
"Angular",
"Svelte",
"Next.js",
"Nuxt",
"Remix",
"Astro",
"Laravel",
"Rails"
],
"componentLibraries": [
"shadcn/ui",
"Headless UI",
"Radix UI",
"daisyUI",
"Flowbite",
"Preline"
],
"plugins": [
"@tailwindcss/typography",
"@tailwindcss/forms",
"@tailwindcss/aspect-ratio",
"@tailwindcss/container-queries"
]
},
"tags": [
"css-framework",
"utility-first",
"responsive",
"mobile-first",
"dark-mode",
"components",
"design-system",
"frontend",
"ui",
"styling"
],
"lastUpdated": "2026-03-07",
"rulesTotal": 29,
"keyFeatures": [
"Mobile-first responsive design with breakpoint system",
"Dark mode with class and media strategies",
"Component patterns using cn() / clsx for conditional classes",
"Custom theme configuration via tailwind.config.js (v3) and @theme (v4)",
"Container queries for component-scoped responsive design",
"Covers both Tailwind CSS v3.4+ and v4.0+"
]
}
README.md
# Tailwind CSS Best Practices
Patterns and conventions for effective Tailwind CSS usage.
## Overview
This skill provides guidance for:
- Mobile-first responsive design
- Dark mode implementation
- Component styling patterns
- Tailwind configuration (v3 and v4)
- V4 migration from v3
- Animation and transitions
## Categories
### 1. Responsive Design (Critical)
Mobile-first approach, breakpoints, and responsive layouts.
### 2. Dark Mode (Critical)
Setup, styling, and system preference handling.
### 3. Component Patterns (High)
Conditional classes, variants, and reusable patterns.
### 4. Custom Configuration — v3 (High)
Colors, fonts, spacing, and plugins via `tailwind.config.js`.
### 5. V4 & Migration (High)
v4 installation, `@theme` configuration, `@utility`/`@custom-variant`, and v3-to-v4 migration.
### 6. Spacing & Typography (Medium)
Consistent spacing and typography scales.
### 7. Animation (Medium)
Transitions, keyframes, and reduced motion.
### 8. Performance (Low)
Content configuration and optimization.
## Quick Start
```tsx
// cn utility for conditional classes
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
// Usage
<button
className={cn(
'px-4 py-2 rounded-md font-medium',
variant === 'primary' && 'bg-blue-600 text-white',
variant === 'secondary' && 'bg-gray-100 text-gray-900',
)}
>
Click me
</button>
```
## Usage
This skill triggers automatically when:
- Writing Tailwind classes
- Implementing responsive designs
- Setting up dark mode
- Configuring Tailwind
## References
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
- [Tailwind CSS Cheat Sheet](https://nerdcave.com/tailwind-cheat-sheet)
rules/_sections.md
# Rule Sections
## Priority Levels
| Level | Description | When to Apply |
|-------|-------------|---------------|
| CRITICAL | Essential for any Tailwind project | Always |
| HIGH | Important patterns for maintainable code | Most projects |
| MEDIUM | Advanced patterns and optimizations | When scaling |
| LOW | Edge cases and specialized needs | Specific use cases |
## Section Overview
### Responsive Design (CRITICAL)
Rules for mobile-first responsive design patterns. These are fundamental to creating adaptive layouts that work across all device sizes. Includes breakpoint usage, container queries (plugin in v3, built-in in v4 with `@min-*`/`@max-*` range variants), fluid typography, and responsive grid systems.
**Focus:** Mobile-first approach, breakpoint ordering, fluid scaling
**Impact:** User experience on all devices, accessibility, performance
### Dark Mode (CRITICAL)
Rules for implementing dark mode support. Modern applications must support both light and dark themes seamlessly. Covers setup strategies (v3: `tailwind.config.js`, v4: class-based by default or `@custom-variant`), color schemes, transitions, and custom color palettes for dual-theme support. v4 adds `@variant dark {}` for nesting in custom CSS.
**Focus:** Theme strategies, color management, smooth transitions
**Impact:** User preference support, reduced eye strain, modern UX
### Component Patterns (HIGH)
Rules for building reusable, consistent components. Essential patterns for buttons, forms, cards, tables, modals, and navigation. Includes conditional class handling with clsx/cn utility for clean, maintainable component code.
**Focus:** Reusable patterns, consistent styling, component composition
**Impact:** Code maintainability, design consistency, development speed
### Custom Configuration — v3 only (HIGH)
Rules for extending Tailwind's v3 theme via `tailwind.config.js`. Covers extending vs overriding, custom colors with proper scales, font families with fallbacks, custom spacing values, plugin usage, and preset sharing across projects. **For v4 projects, use `@theme {}` instead — see V4 & Migration.**
**Focus:** Theme extension, design system integration, configuration patterns
**Impact:** Design system alignment, team consistency, flexibility
### V4 & Migration (HIGH)
Rules for Tailwind CSS v4 — a CSS-first rewrite that replaces `tailwind.config.js` with `@theme {}` in CSS. Covers installation with the Vite/PostCSS plugin, theme configuration with `@theme`, custom utilities with `@utility` and `@custom-variant`, the `@variant` directive for nesting, `@reference` for non-emitting imports, `@source` for scan control, and the full step-by-step migration path from v3 to v4. Includes new v4 features: `starting:` variant, `forced-colors:` variant, `color-mix()` opacity, container query ranges (`@min-*`/`@max-*`), and `transition-discrete`.
**Focus:** CSS-first configuration, v4 setup, v3→v4 migration path, new CSS features
**Impact:** Modern tooling, zero-JS config, access to new v4 features
**Rules:** `v4-installation`, `v4-theme-configuration`, `v4-custom-utilities`, `v4-migration`
### Spacing & Typography (MEDIUM)
Rules for consistent spacing and typography systems. Includes spacing scale usage, margin/padding patterns, typography hierarchy, line height management, and responsive text sizing.
**Focus:** Vertical rhythm, typography scale, consistent spacing
**Impact:** Visual consistency, readability, design system cohesion
### Animation (MEDIUM)
Rules for adding animations and transitions. Covers transition utilities, custom keyframes, motion preferences (respecting reduced motion), and performance considerations for animations.
**Focus:** Smooth transitions, custom animations, accessibility
**Impact:** User experience, polish, accessibility compliance
### Performance (LOW)
Rules for optimizing build output and runtime performance. Includes content configuration for purging unused styles, JIT mode benefits, and proper usage of arbitrary values.
**Focus:** Bundle size, build time, runtime performance
**Impact:** Load time, user experience, production optimization
rules/_template.md
---
id: rule-id-kebab-case
title: Rule Title
priority: CRITICAL | HIGH | MEDIUM | LOW
category: Responsive Design | Dark Mode | Component Patterns | Custom Configuration | Spacing & Typography | Animation | Performance
---
# Rule Title
Brief description of what this rule covers and why it's important.
## Bad Example
```html
<!-- Anti-pattern: What not to do -->
<div class="w-full p-4 bg-blue-500">
Example showing incorrect usage
</div>
```
```jsx
// Anti-pattern in React/JSX
function BadComponent() {
return (
<div className="inline m-4">
Incorrect pattern
</div>
)
}
```
## Good Example
```html
<!-- Best practice: Correct implementation -->
<div class="
w-full max-w-screen-xl mx-auto
p-6 md:p-8
bg-primary-600 dark:bg-primary-500
rounded-lg shadow-sm
transition-colors duration-200
">
Example showing correct usage with Tailwind v4 patterns
</div>
```
```jsx
// Best practice in React/JSX with TypeScript
interface CardProps {
children: React.ReactNode
variant?: 'primary' | 'secondary'
className?: string
}
function GoodComponent({ children, variant = 'primary', className }: CardProps) {
return (
<div className={cn(
'w-full rounded-lg p-6 transition-colors',
{
'bg-primary-600 text-white dark:bg-primary-500': variant === 'primary',
'bg-gray-100 text-gray-900 dark:bg-gray-800 dark:text-white': variant === 'secondary',
},
className
)}>
{children}
</div>
)
}
```
## Why
1. **Reason 1**: Explanation of first benefit with concrete example.
2. **Reason 2**: How this improves code quality or performance.
3. **Reason 3**: Impact on maintainability or user experience.
4. **Reason 4**: Accessibility or responsiveness consideration.
5. **Reason 5**: Tailwind v4 specific improvements or patterns.
## Usage Examples
### Basic Usage
```html
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
</div>
```
### Advanced Pattern
```jsx
import { cn } from '@/lib/utils'
function AdvancedExample() {
return (
<div className={cn(
// Base styles
'flex items-center justify-between',
'px-4 py-3 rounded-md',
// Responsive
'flex-col sm:flex-row',
'gap-3 sm:gap-4',
// Colors with dark mode
'bg-white dark:bg-gray-900',
'text-gray-900 dark:text-white',
'border border-gray-200 dark:border-gray-700',
// Interactive states
'hover:bg-gray-50 dark:hover:bg-gray-800',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500',
// Transitions
'transition-colors duration-200'
)}>
Content
</div>
)
}
```
### With CSS Variables (Tailwind v4)
```html
<!-- Using @theme directive for custom properties -->
<div class="
bg-[--color-background]
text-[--color-foreground]
border-[--color-border]
">
Dynamic theme support
</div>
```
```css
@theme {
--color-background: oklch(100% 0 0);
--color-foreground: oklch(0% 0 0);
--color-border: oklch(90% 0 0);
@media (prefers-color-scheme: dark) {
--color-background: oklch(20% 0 0);
--color-foreground: oklch(100% 0 0);
--color-border: oklch(30% 0 0);
}
}
```
## Related Patterns
- Related utility classes: `flex`, `grid`, `space-y-*`
- Related rules: Link to other relevant rules
- Documentation: [Tailwind CSS Docs](https://tailwindcss.com/docs/)
## Common Mistakes
### Mistake 1
```html
<!-- Wrong -->
<div class="w-[100%]">Using arbitrary value for standard utility</div>
<!-- Correct -->
<div class="w-full">Using built-in utility</div>
```
### Mistake 2
```html
<!-- Wrong -->
<div class="px-4 px-8">Conflicting utilities</div>
<!-- Correct -->
<div class="px-8">Single utility wins</div>
```
## Tailwind v4 Features
Highlight any Tailwind v4 specific improvements:
- New `@theme` directive for CSS variables
- Improved color spaces (oklch)
- Container queries with `@container`
- New logical properties
- Simplified configuration
## Performance Tips
- Avoid unnecessary arbitrary values
- Use standard utilities when possible
- Leverage JIT mode for custom values
- Consider bundle size impact
rules/comp-clsx-cn.md
---
id: comp-clsx-cn
title: Conditional Classes with clsx/cn
priority: HIGH
category: Component Patterns
---
# Conditional Classes with clsx/cn
## Why It Matters
Conditional class names in React get messy quickly. The `clsx` + `tailwind-merge` pattern (often called `cn`) provides a clean, type-safe way to handle conditional classes while properly merging Tailwind utilities.
## Incorrect
```tsx
// ❌ String concatenation - error prone
<div className={'px-4 ' + (isActive ? 'bg-blue-500' : 'bg-gray-500') + ' ' + className}>
// ❌ Template literals - still messy
<div className={`px-4 ${isActive ? 'bg-blue-500' : 'bg-gray-500'} ${className}`}>
// ❌ Array join - doesn't handle conflicts
<div className={['px-4', isActive && 'bg-blue-500', className].filter(Boolean).join(' ')}>
// ❌ Tailwind conflicts not resolved
// className = "px-8" passed in
<div className={`px-4 ${className}`}> // Both px-4 and px-8 in output!
```
## Correct
### Setup
```bash
npm install clsx tailwind-merge
```
```ts
// lib/utils.ts
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
```
### Basic Usage
```tsx
import { cn } from '@/lib/utils'
// Simple conditional
<div className={cn('px-4 py-2', isActive && 'bg-blue-500')}>
// Multiple conditions
<div className={cn(
'rounded-lg border',
isActive && 'border-blue-500',
isDisabled && 'opacity-50 cursor-not-allowed',
className // Allow override
)}>
// Object syntax
<div className={cn(
'px-4 py-2',
{
'bg-blue-500 text-white': variant === 'primary',
'bg-gray-100 text-gray-900': variant === 'secondary',
'opacity-50': isDisabled,
}
)}>
```
### Button Component Example
```tsx
import { cn } from '@/lib/utils'
import { ButtonHTMLAttributes, forwardRef } from 'react'
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost' | 'danger'
size?: 'sm' | 'md' | 'lg'
isLoading?: boolean
}
const Button = forwardRef<HTMLButtonElement, ButtonProps>(
({ variant = 'primary', size = 'md', isLoading, className, children, disabled, ...props }, ref) => {
return (
<button
ref={ref}
disabled={disabled || isLoading}
className={cn(
// Base styles
'inline-flex items-center justify-center rounded-md font-medium',
'transition-colors focus-visible:outline-none focus-visible:ring-2',
'disabled:pointer-events-none disabled:opacity-50',
// Variants
{
'bg-blue-600 text-white hover:bg-blue-700 focus-visible:ring-blue-500':
variant === 'primary',
'bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:ring-gray-500':
variant === 'secondary',
'border border-gray-300 bg-transparent hover:bg-gray-100 focus-visible:ring-gray-500':
variant === 'outline',
'bg-transparent hover:bg-gray-100 focus-visible:ring-gray-500':
variant === 'ghost',
'bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-500':
variant === 'danger',
},
// Sizes
{
'h-8 px-3 text-sm': size === 'sm',
'h-10 px-4 text-sm': size === 'md',
'h-12 px-6 text-base': size === 'lg',
},
// Allow className override
className
)}
{...props}
>
{isLoading && (
<svg className="mr-2 h-4 w-4 animate-spin" viewBox="0 0 24 24">
{/* spinner */}
</svg>
)}
{children}
</button>
)
}
)
Button.displayName = 'Button'
export { Button }
```
### Why tailwind-merge Matters
```tsx
// Without tailwind-merge - conflicting classes remain
clsx('px-4', 'px-8') // "px-4 px-8" - conflict!
// With tailwind-merge - later class wins
twMerge('px-4', 'px-8') // "px-8"
// cn combines both
cn('px-4 text-red-500', 'px-8') // "px-8 text-red-500"
```
### Input Component Example
```tsx
import { cn } from '@/lib/utils'
import { InputHTMLAttributes, forwardRef } from 'react'
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
error?: string
}
const Input = forwardRef<HTMLInputElement, InputProps>(
({ className, error, ...props }, ref) => {
return (
<div>
<input
ref={ref}
className={cn(
'flex h-10 w-full rounded-md border bg-white px-3 py-2 text-sm',
'placeholder:text-gray-400',
'focus:outline-none focus:ring-2 focus:ring-offset-2',
'disabled:cursor-not-allowed disabled:opacity-50',
error
? 'border-red-500 focus:ring-red-500'
: 'border-gray-300 focus:ring-blue-500',
className
)}
{...props}
/>
{error && <p className="mt-1 text-sm text-red-500">{error}</p>}
</div>
)
}
)
Input.displayName = 'Input'
export { Input }
```
## Benefits
- Clean, readable conditional classes
- Proper Tailwind conflict resolution
- Type-safe with TypeScript
- Allows component className overrides
- Handles undefined/null gracefully
rules/component-btn-variants.md
---
id: component-btn-variants
title: Button Variants
priority: HIGH
category: Component Patterns
---
# Button Variants
Create consistent, reusable button components with proper variants for different contexts and states.
## Bad Example
```html
<!-- Inconsistent button styles across the app -->
<button class="bg-blue-500 text-white px-4 py-2">Submit</button>
<button class="bg-blue-600 text-white px-3 py-1 rounded">Save</button>
<button class="bg-indigo-500 text-white px-6 py-3 rounded-lg">Continue</button>
<!-- Missing interactive states -->
<button class="bg-red-500 text-white px-4 py-2">
No hover, focus, or disabled states
</button>
<!-- Accessibility issues -->
<div class="bg-green-500 text-white px-4 py-2 cursor-pointer" onclick="submit()">
Using div instead of button
</div>
```
## Good Example
```html
<!-- Primary button with all states -->
<button class="
inline-flex items-center justify-center gap-2
px-4 py-2 rounded-lg
bg-primary-600 text-white font-medium
hover:bg-primary-700
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2
active:bg-primary-800
disabled:opacity-50 disabled:cursor-not-allowed
transition-colors duration-150
">
Primary Action
</button>
<!-- Secondary button -->
<button class="
inline-flex items-center justify-center gap-2
px-4 py-2 rounded-lg
bg-gray-100 text-gray-900 font-medium
hover:bg-gray-200
focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2
active:bg-gray-300
disabled:opacity-50 disabled:cursor-not-allowed
dark:bg-gray-800 dark:text-gray-100 dark:hover:bg-gray-700
transition-colors duration-150
">
Secondary Action
</button>
<!-- Outline button -->
<button class="
inline-flex items-center justify-center gap-2
px-4 py-2 rounded-lg
border-2 border-primary-600 text-primary-600 font-medium
hover:bg-primary-50
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2
active:bg-primary-100
disabled:opacity-50 disabled:cursor-not-allowed
dark:border-primary-400 dark:text-primary-400 dark:hover:bg-primary-950
transition-colors duration-150
">
Outline Action
</button>
<!-- Ghost button -->
<button class="
inline-flex items-center justify-center gap-2
px-4 py-2 rounded-lg
text-gray-600 font-medium
hover:bg-gray-100 hover:text-gray-900
focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2
active:bg-gray-200
disabled:opacity-50 disabled:cursor-not-allowed
dark:text-gray-400 dark:hover:bg-gray-800 dark:hover:text-gray-100
transition-colors duration-150
">
Ghost Action
</button>
<!-- Destructive button -->
<button class="
inline-flex items-center justify-center gap-2
px-4 py-2 rounded-lg
bg-red-600 text-white font-medium
hover:bg-red-700
focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2
active:bg-red-800
disabled:opacity-50 disabled:cursor-not-allowed
transition-colors duration-150
">
Delete
</button>
```
## Why
1. **Consistency**: All buttons look and behave the same way across the application.
2. **Accessibility**: Proper focus states, disabled states, and semantic HTML.
3. **User feedback**: Hover, active, and focus states communicate interactivity.
4. **Dark mode support**: Buttons work in both light and dark themes.
5. **Flexibility**: Size and variant options cover all use cases.
## Size Variants
```html
<!-- Extra small -->
<button class="px-2.5 py-1.5 text-xs rounded">XS</button>
<!-- Small -->
<button class="px-3 py-2 text-sm rounded-md">Small</button>
<!-- Medium (default) -->
<button class="px-4 py-2 text-sm rounded-lg">Medium</button>
<!-- Large -->
<button class="px-6 py-3 text-base rounded-lg">Large</button>
<!-- Extra large -->
<button class="px-8 py-4 text-lg rounded-xl">XL</button>
```
## Icon Buttons
```html
<!-- Icon only button -->
<button class="
p-2 rounded-lg
text-gray-500 hover:text-gray-700 hover:bg-gray-100
focus:outline-none focus:ring-2 focus:ring-gray-500
dark:text-gray-400 dark:hover:text-gray-200 dark:hover:bg-gray-800
" aria-label="Close">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
<!-- Button with icon -->
<button class="inline-flex items-center gap-2 px-4 py-2 bg-primary-600 text-white rounded-lg">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
</svg>
Add Item
</button>
```
## Loading State
```html
<button class="
inline-flex items-center justify-center gap-2
px-4 py-2 rounded-lg
bg-primary-600 text-white
disabled:opacity-75 disabled:cursor-wait
" disabled>
<svg class="animate-spin h-4 w-4" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
</svg>
Loading...
</button>
```
## Button Group
```html
<div class="inline-flex rounded-lg shadow-sm">
<button class="px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-l-lg hover:bg-gray-100">
Left
</button>
<button class="px-4 py-2 text-sm font-medium text-gray-900 bg-white border-t border-b border-gray-200 hover:bg-gray-100">
Middle
</button>
<button class="px-4 py-2 text-sm font-medium text-gray-900 bg-white border border-gray-200 rounded-r-lg hover:bg-gray-100">
Right
</button>
</div>
```
## Using @apply for Components
```css
/* In your CSS file */
@layer components {
.btn {
@apply inline-flex items-center justify-center gap-2 px-4 py-2 rounded-lg font-medium transition-colors duration-150 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed;
}
.btn-primary {
@apply bg-primary-600 text-white hover:bg-primary-700 active:bg-primary-800 focus:ring-primary-500;
}
.btn-secondary {
@apply bg-gray-100 text-gray-900 hover:bg-gray-200 active:bg-gray-300 focus:ring-gray-500;
}
}
```
rules/component-card-patterns.md
---
id: component-card-patterns
title: Card Patterns
priority: HIGH
category: Component Patterns
---
# Card Patterns
Create flexible, consistent card components for displaying grouped content with proper structure and styling.
## Bad Example
```html
<!-- Inconsistent card structure -->
<div class="bg-white p-4 shadow">
<img src="image.jpg">
<h3>Title</h3>
<p>Content</p>
</div>
<!-- Missing proper spacing and overflow handling -->
<div class="border p-2">
<img src="wide-image.jpg" class="w-full">
<div>
<h3 class="text-lg">Long title that might overflow the container</h3>
<p>Description text</p>
</div>
</div>
<!-- No interactive states for clickable cards -->
<div onclick="navigate()" class="bg-white shadow p-4">
Click me (no visual feedback)
</div>
```
## Good Example
```html
<!-- Basic card with proper structure -->
<article class="
bg-white dark:bg-gray-800
rounded-xl shadow-sm
border border-gray-200 dark:border-gray-700
overflow-hidden
">
<div class="aspect-video">
<img class="w-full h-full object-cover" src="image.jpg" alt="Description">
</div>
<div class="p-6">
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">
Card Title
</h3>
<p class="mt-2 text-gray-600 dark:text-gray-300">
Card description with supporting text.
</p>
</div>
</article>
<!-- Interactive card with hover state -->
<a href="/article" class="
block
bg-white dark:bg-gray-800
rounded-xl shadow-sm
border border-gray-200 dark:border-gray-700
overflow-hidden
transition-all duration-200
hover:shadow-md hover:border-gray-300 dark:hover:border-gray-600
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2
">
<div class="aspect-[3/2]">
<img class="w-full h-full object-cover" src="image.jpg" alt="">
</div>
<div class="p-6">
<span class="text-xs font-medium text-primary-600 dark:text-primary-400 uppercase tracking-wide">
Category
</span>
<h3 class="mt-2 text-lg font-semibold text-gray-900 dark:text-white line-clamp-2">
Article Title That Might Be Long
</h3>
<p class="mt-2 text-gray-600 dark:text-gray-300 line-clamp-3">
Description text that will be truncated after three lines...
</p>
</div>
</a>
<!-- Horizontal card -->
<article class="
flex flex-col sm:flex-row
bg-white dark:bg-gray-800
rounded-xl shadow-sm
border border-gray-200 dark:border-gray-700
overflow-hidden
">
<div class="sm:w-48 sm:flex-shrink-0">
<img class="w-full h-48 sm:h-full object-cover" src="image.jpg" alt="">
</div>
<div class="p-6 flex flex-col justify-between">
<div>
<h3 class="text-lg font-semibold text-gray-900 dark:text-white">Title</h3>
<p class="mt-2 text-gray-600 dark:text-gray-300">Description</p>
</div>
<div class="mt-4">
<button class="text-primary-600 dark:text-primary-400 font-medium">
Read more
</button>
</div>
</div>
</article>
```
## Why
1. **Consistent structure**: All cards follow the same pattern for predictable layouts.
2. **Proper overflow**: Images and text are contained within card boundaries.
3. **Accessibility**: Semantic HTML and proper focus states for interactive cards.
4. **Dark mode ready**: Colors adapt to both light and dark themes.
5. **Responsive design**: Cards adapt from mobile to desktop layouts.
## Card Variants
### Simple Card
```html
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
<h3 class="font-semibold text-gray-900 dark:text-white">Simple Card</h3>
<p class="mt-2 text-gray-600 dark:text-gray-300">Content goes here.</p>
</div>
```
### Card with Header
```html
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900">
<h3 class="font-semibold text-gray-900 dark:text-white">Card Header</h3>
</div>
<div class="p-6">
<p class="text-gray-600 dark:text-gray-300">Card body content.</p>
</div>
</div>
```
### Card with Footer
```html
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
<div class="p-6">
<h3 class="font-semibold text-gray-900 dark:text-white">Card Title</h3>
<p class="mt-2 text-gray-600 dark:text-gray-300">Card content.</p>
</div>
<div class="px-6 py-4 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900">
<button class="text-primary-600 dark:text-primary-400 font-medium">Action</button>
</div>
</div>
```
### Profile Card
```html
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm border border-gray-200 dark:border-gray-700 p-6 text-center">
<img class="w-24 h-24 rounded-full mx-auto ring-4 ring-gray-100 dark:ring-gray-700" src="avatar.jpg" alt="">
<h3 class="mt-4 font-semibold text-gray-900 dark:text-white">John Doe</h3>
<p class="text-sm text-gray-500 dark:text-gray-400">Software Engineer</p>
<div class="mt-4 flex justify-center gap-3">
<button class="px-4 py-2 bg-primary-600 text-white rounded-lg text-sm">Follow</button>
<button class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-300 rounded-lg text-sm">Message</button>
</div>
</div>
```
### Stats Card
```html
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-6">
<div class="flex items-center justify-between">
<div>
<p class="text-sm text-gray-500 dark:text-gray-400">Total Revenue</p>
<p class="mt-1 text-3xl font-bold text-gray-900 dark:text-white">$45,231</p>
</div>
<div class="p-3 bg-green-100 dark:bg-green-900/30 rounded-full">
<svg class="w-6 h-6 text-green-600 dark:text-green-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7h8m0 0v8m0-8l-8 8-4-4-6 6" />
</svg>
</div>
</div>
<p class="mt-2 text-sm text-green-600 dark:text-green-400">
<span class="font-medium">+12.5%</span> from last month
</p>
</div>
```
## Card Grid Layout
```html
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
<!-- Cards automatically fill grid -->
</div>
<!-- Or auto-fit for flexible columns -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(280px,1fr))] gap-6">
<!-- Cards adapt to available space -->
</div>
```
rules/component-form-elements.md
---
id: component-form-elements
title: Form Elements
priority: HIGH
category: Component Patterns
---
# Form Elements
Create accessible, consistent form components with proper states, labels, and validation styling.
## Bad Example
```html
<!-- Missing labels and accessibility -->
<input type="text" class="border p-2" placeholder="Enter name">
<!-- Inconsistent input styling -->
<input type="email" class="border-2 rounded-lg p-3">
<input type="password" class="border p-2 rounded">
<!-- No error or focus states -->
<input type="text" class="border border-gray-300">
<!-- Using placeholder as label -->
<input type="email" placeholder="Email address">
```
## Good Example
```html
<!-- Text input with label -->
<div>
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Full Name
</label>
<input
type="text"
id="name"
name="name"
class="
w-full px-4 py-2 rounded-lg
border border-gray-300 dark:border-gray-600
bg-white dark:bg-gray-800
text-gray-900 dark:text-white
placeholder-gray-400 dark:placeholder-gray-500
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent
disabled:bg-gray-100 dark:disabled:bg-gray-900 disabled:cursor-not-allowed
transition-colors duration-150
"
placeholder="John Doe"
>
</div>
<!-- Input with error state -->
<div>
<label for="email" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Email Address
</label>
<input
type="email"
id="email"
name="email"
class="
w-full px-4 py-2 rounded-lg
border border-red-500 dark:border-red-400
bg-white dark:bg-gray-800
text-gray-900 dark:text-white
focus:outline-none focus:ring-2 focus:ring-red-500 focus:border-transparent
"
aria-invalid="true"
aria-describedby="email-error"
>
<p id="email-error" class="mt-1 text-sm text-red-600 dark:text-red-400">
Please enter a valid email address.
</p>
</div>
<!-- Select dropdown -->
<div>
<label for="country" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Country
</label>
<select
id="country"
name="country"
class="
w-full px-4 py-2 rounded-lg
border border-gray-300 dark:border-gray-600
bg-white dark:bg-gray-800
text-gray-900 dark:text-white
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent
appearance-none
bg-[url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJNNSA3LjVMMTAgMTIuNUwxNSA3LjUiIHN0cm9rZT0iIzZCNzI4MCIgc3Ryb2tlLXdpZHRoPSIxLjUiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjwvc3ZnPg==')]
bg-no-repeat bg-[right_0.75rem_center]
"
>
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="uk">United Kingdom</option>
</select>
</div>
<!-- Textarea -->
<div>
<label for="message" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Message
</label>
<textarea
id="message"
name="message"
rows="4"
class="
w-full px-4 py-2 rounded-lg
border border-gray-300 dark:border-gray-600
bg-white dark:bg-gray-800
text-gray-900 dark:text-white
placeholder-gray-400 dark:placeholder-gray-500
focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent
resize-none
"
placeholder="Your message..."
></textarea>
</div>
```
## Why
1. **Accessibility**: Proper labels, ARIA attributes, and keyboard navigation.
2. **Consistent styling**: All form elements match the design system.
3. **Clear states**: Visual feedback for focus, error, disabled states.
4. **Dark mode support**: Forms work in both light and dark themes.
5. **User experience**: Proper spacing, sizing, and touch targets.
## Checkbox and Radio
```html
<!-- Checkbox -->
<label class="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
class="
w-5 h-5 rounded
border-gray-300 dark:border-gray-600
text-primary-600
focus:ring-primary-500 focus:ring-offset-0
dark:bg-gray-800 dark:checked:bg-primary-600
"
>
<span class="text-gray-700 dark:text-gray-300">Remember me</span>
</label>
<!-- Radio group -->
<fieldset>
<legend class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Select an option
</legend>
<div class="space-y-2">
<label class="flex items-center gap-3 cursor-pointer">
<input
type="radio"
name="option"
value="1"
class="
w-5 h-5
border-gray-300 dark:border-gray-600
text-primary-600
focus:ring-primary-500 focus:ring-offset-0
dark:bg-gray-800
"
>
<span class="text-gray-700 dark:text-gray-300">Option 1</span>
</label>
<label class="flex items-center gap-3 cursor-pointer">
<input type="radio" name="option" value="2" class="w-5 h-5 border-gray-300 text-primary-600 focus:ring-primary-500">
<span class="text-gray-700 dark:text-gray-300">Option 2</span>
</label>
</div>
</fieldset>
```
## Toggle Switch
```html
<label class="flex items-center cursor-pointer">
<div class="relative">
<input type="checkbox" class="sr-only peer">
<div class="
w-11 h-6 rounded-full
bg-gray-200 dark:bg-gray-700
peer-checked:bg-primary-600
peer-focus:ring-2 peer-focus:ring-primary-500 peer-focus:ring-offset-2
transition-colors duration-200
"></div>
<div class="
absolute left-0.5 top-0.5
w-5 h-5 rounded-full
bg-white shadow-sm
peer-checked:translate-x-5
transition-transform duration-200
"></div>
</div>
<span class="ml-3 text-gray-700 dark:text-gray-300">Enable notifications</span>
</label>
```
## Input with Addon
```html
<!-- Prefix addon -->
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Website
</label>
<div class="flex">
<span class="inline-flex items-center px-3 rounded-l-lg border border-r-0 border-gray-300 dark:border-gray-600 bg-gray-50 dark:bg-gray-700 text-gray-500 dark:text-gray-400 text-sm">
https://
</span>
<input
type="text"
class="flex-1 px-4 py-2 rounded-r-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 focus:ring-2 focus:ring-primary-500 focus:border-transparent"
placeholder="www.example.com"
>
</div>
</div>
<!-- Input with button -->
<div class="flex">
<input
type="email"
class="flex-1 px-4 py-2 rounded-l-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-800 focus:ring-2 focus:ring-primary-500 focus:border-transparent"
placeholder="Enter your email"
>
<button class="px-6 py-2 bg-primary-600 text-white rounded-r-lg hover:bg-primary-700 focus:ring-2 focus:ring-primary-500 focus:ring-offset-2">
Subscribe
</button>
</div>
```
## File Input
```html
<label class="block">
<span class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-1 block">Upload file</span>
<input
type="file"
class="
block w-full text-sm text-gray-500 dark:text-gray-400
file:mr-4 file:py-2 file:px-4
file:rounded-lg file:border-0
file:text-sm file:font-medium
file:bg-primary-50 file:text-primary-700
dark:file:bg-primary-900/50 dark:file:text-primary-300
hover:file:bg-primary-100 dark:hover:file:bg-primary-900
cursor-pointer
"
>
</label>
```
## Form Layout
```html
<form class="space-y-6">
<!-- Two column on larger screens -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-6">
<div>
<label for="firstName">First Name</label>
<input type="text" id="firstName" class="...">
</div>
<div>
<label for="lastName">Last Name</label>
<input type="text" id="lastName" class="...">
</div>
</div>
<!-- Full width -->
<div>
<label for="email">Email</label>
<input type="email" id="email" class="...">
</div>
<!-- Form actions -->
<div class="flex justify-end gap-3">
<button type="button" class="px-4 py-2 text-gray-700 bg-gray-100 rounded-lg">Cancel</button>
<button type="submit" class="px-4 py-2 text-white bg-primary-600 rounded-lg">Submit</button>
</div>
</form>
```
rules/component-modals.md
---
id: component-modals
title: Modal Components
priority: HIGH
category: Component Patterns
---
# Modal Components
Create accessible modal dialogs with proper focus management, backdrop, and animations.
## Bad Example
```html
<!-- Inaccessible modal -->
<div class="fixed inset-0 bg-black/50">
<div class="bg-white p-4 mx-auto mt-20">
<h2>Modal Title</h2>
<p>Content</p>
<button>Close</button>
</div>
</div>
<!-- No focus trap or keyboard handling -->
<div id="modal" class="fixed inset-0 hidden">
<div class="bg-white">
Content without accessibility considerations
</div>
</div>
<!-- Modal without proper centering -->
<div class="fixed top-0 left-0 w-full h-full">
<div class="bg-white w-96">
Not properly centered or positioned
</div>
</div>
```
## Good Example
```html
<!-- Accessible modal with proper structure -->
<div
class="fixed inset-0 z-50 overflow-y-auto"
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<!-- Backdrop -->
<div
class="fixed inset-0 bg-black/50 backdrop-blur-sm transition-opacity"
aria-hidden="true"
></div>
<!-- Modal positioning wrapper -->
<div class="flex min-h-full items-center justify-center p-4">
<!-- Modal panel -->
<div class="
relative w-full max-w-lg
bg-white dark:bg-gray-800
rounded-xl shadow-xl
transform transition-all
">
<!-- Header -->
<div class="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
<h2 id="modal-title" class="text-lg font-semibold text-gray-900 dark:text-white">
Modal Title
</h2>
<button
type="button"
class="p-2 rounded-lg text-gray-400 hover:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors"
aria-label="Close modal"
>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Body -->
<div class="p-6">
<p class="text-gray-600 dark:text-gray-300">
Modal content goes here. This modal is fully accessible with proper ARIA attributes.
</p>
</div>
<!-- Footer -->
<div class="flex justify-end gap-3 p-6 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900 rounded-b-xl">
<button
type="button"
class="px-4 py-2 rounded-lg text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 transition-colors"
>
Cancel
</button>
<button
type="button"
class="px-4 py-2 rounded-lg text-white bg-primary-600 hover:bg-primary-700 transition-colors"
>
Confirm
</button>
</div>
</div>
</div>
</div>
```
## Why
1. **Accessibility**: Proper ARIA roles, labels, and focus management.
2. **Keyboard support**: Escape key closes, Tab traps focus within modal.
3. **Visual feedback**: Backdrop indicates modal context, animation feels natural.
4. **Responsive**: Modal adapts to different screen sizes.
5. **Scroll handling**: Body scroll locked, modal content scrollable.
## Modal Sizes
```html
<!-- Small modal -->
<div class="relative w-full max-w-sm ...">...</div>
<!-- Medium modal (default) -->
<div class="relative w-full max-w-lg ...">...</div>
<!-- Large modal -->
<div class="relative w-full max-w-2xl ...">...</div>
<!-- Extra large modal -->
<div class="relative w-full max-w-4xl ...">...</div>
<!-- Full width modal -->
<div class="relative w-full max-w-full mx-4 ...">...</div>
```
## Confirmation Dialog
```html
<div class="fixed inset-0 z-50 flex items-center justify-center p-4" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-black/50" aria-hidden="true"></div>
<div class="relative w-full max-w-md bg-white dark:bg-gray-800 rounded-xl shadow-xl p-6 text-center">
<!-- Warning icon -->
<div class="mx-auto w-12 h-12 rounded-full bg-red-100 dark:bg-red-900/30 flex items-center justify-center mb-4">
<svg class="w-6 h-6 text-red-600 dark:text-red-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
</div>
<h3 class="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Delete Item?
</h3>
<p class="text-gray-600 dark:text-gray-300 mb-6">
Are you sure you want to delete this item? This action cannot be undone.
</p>
<div class="flex gap-3 justify-center">
<button class="px-4 py-2 rounded-lg text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600">
Cancel
</button>
<button class="px-4 py-2 rounded-lg text-white bg-red-600 hover:bg-red-700">
Delete
</button>
</div>
</div>
</div>
```
## Slide-over Panel
```html
<div class="fixed inset-0 z-50 overflow-hidden" role="dialog" aria-modal="true">
<div class="fixed inset-0 bg-black/50" aria-hidden="true"></div>
<div class="fixed inset-y-0 right-0 flex max-w-full pl-10">
<div class="w-screen max-w-md transform transition-transform duration-300 ease-in-out">
<div class="flex h-full flex-col bg-white dark:bg-gray-800 shadow-xl">
<!-- Header -->
<div class="flex items-center justify-between px-6 py-4 border-b border-gray-200 dark:border-gray-700">
<h2 class="text-lg font-semibold text-gray-900 dark:text-white">
Panel Title
</h2>
<button class="p-2 rounded-lg text-gray-400 hover:text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-700">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<!-- Content (scrollable) -->
<div class="flex-1 overflow-y-auto p-6">
<p class="text-gray-600 dark:text-gray-300">
Slide-over panel content...
</p>
</div>
<!-- Footer -->
<div class="border-t border-gray-200 dark:border-gray-700 px-6 py-4">
<button class="w-full px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700">
Save Changes
</button>
</div>
</div>
</div>
</div>
</div>
```
## Modal Animation Classes
```html
<!-- Opening animation -->
<div class="
transform transition-all duration-300 ease-out
opacity-0 scale-95
data-[open]:opacity-100 data-[open]:scale-100
">
Modal content
</div>
<!-- Closing animation -->
<div class="
transform transition-all duration-200 ease-in
data-[closing]:opacity-0 data-[closing]:scale-95
">
Modal content
</div>
```
## Focus Management (JavaScript)
```javascript
// Trap focus within modal
function trapFocus(element) {
const focusableElements = element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];
element.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstFocusable) {
e.preventDefault();
lastFocusable.focus();
} else if (!e.shiftKey && document.activeElement === lastFocusable) {
e.preventDefault();
firstFocusable.focus();
}
}
if (e.key === 'Escape') {
closeModal();
}
});
firstFocusable.focus();
}
```
rules/component-navigation.md
---
id: component-navigation
title: Navigation Components
priority: HIGH
category: Component Patterns
---
# Navigation Components
Build accessible, responsive navigation patterns that work across all device sizes.
## Bad Example
```html
<!-- Inaccessible navigation -->
<div class="flex gap-4">
<div onclick="navigate('/')">Home</div>
<div onclick="navigate('/about')">About</div>
</div>
<!-- No mobile consideration -->
<nav class="flex gap-6">
<a href="/">Home</a>
<a href="/products">Products</a>
<a href="/services">Services</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
<!-- Links overflow on mobile -->
</nav>
<!-- Missing active states -->
<nav>
<a href="/" class="text-gray-600">Home</a>
<a href="/about" class="text-gray-600">About (currently active but looks same)</a>
</nav>
```
## Good Example
```html
<!-- Responsive navbar with mobile menu -->
<nav class="bg-white dark:bg-gray-900 border-b border-gray-200 dark:border-gray-800">
<div class="container mx-auto px-4">
<div class="flex items-center justify-between h-16">
<!-- Logo -->
<a href="/" class="text-xl font-bold text-gray-900 dark:text-white">
Logo
</a>
<!-- Desktop navigation -->
<div class="hidden md:flex items-center gap-1">
<a href="/" class="px-4 py-2 rounded-lg text-gray-900 dark:text-white bg-gray-100 dark:bg-gray-800 font-medium">
Home
</a>
<a href="/products" class="px-4 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
Products
</a>
<a href="/about" class="px-4 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
About
</a>
<a href="/contact" class="px-4 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors">
Contact
</a>
</div>
<!-- Mobile menu button -->
<button
class="md:hidden p-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800"
aria-label="Toggle menu"
aria-expanded="false"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<!-- Mobile navigation -->
<div class="md:hidden py-4 border-t border-gray-200 dark:border-gray-800">
<div class="flex flex-col gap-1">
<a href="/" class="px-4 py-2 rounded-lg text-gray-900 dark:text-white bg-gray-100 dark:bg-gray-800 font-medium">
Home
</a>
<a href="/products" class="px-4 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800">
Products
</a>
<a href="/about" class="px-4 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800">
About
</a>
<a href="/contact" class="px-4 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800">
Contact
</a>
</div>
</div>
</div>
</nav>
```
## Why
1. **Accessibility**: Semantic HTML with proper ARIA attributes and keyboard navigation.
2. **Responsive design**: Works on mobile with hamburger menu, desktop with full links.
3. **Active states**: Clear indication of current page/section.
4. **Dark mode**: Adapts to both light and dark themes.
5. **Touch-friendly**: Adequate tap targets on mobile devices.
## Sidebar Navigation
```html
<aside class="w-64 bg-white dark:bg-gray-900 border-r border-gray-200 dark:border-gray-800 min-h-screen">
<div class="p-4">
<h2 class="text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider mb-3">
Main Menu
</h2>
<nav class="space-y-1">
<a href="/dashboard" class="
flex items-center gap-3 px-3 py-2 rounded-lg
text-white bg-primary-600
font-medium
">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
Dashboard
</a>
<a href="/analytics" class="
flex items-center gap-3 px-3 py-2 rounded-lg
text-gray-600 dark:text-gray-300
hover:bg-gray-100 dark:hover:bg-gray-800
transition-colors
">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
Analytics
</a>
<a href="/settings" class="
flex items-center gap-3 px-3 py-2 rounded-lg
text-gray-600 dark:text-gray-300
hover:bg-gray-100 dark:hover:bg-gray-800
transition-colors
">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
Settings
</a>
</nav>
</div>
</aside>
```
## Breadcrumb Navigation
```html
<nav aria-label="Breadcrumb" class="text-sm">
<ol class="flex items-center gap-2">
<li>
<a href="/" class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
Home
</a>
</li>
<li class="text-gray-400 dark:text-gray-600">/</li>
<li>
<a href="/products" class="text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200">
Products
</a>
</li>
<li class="text-gray-400 dark:text-gray-600">/</li>
<li>
<span class="text-gray-900 dark:text-white font-medium" aria-current="page">
Product Name
</span>
</li>
</ol>
</nav>
```
## Tab Navigation
```html
<div class="border-b border-gray-200 dark:border-gray-700">
<nav class="flex gap-1" aria-label="Tabs">
<button class="
px-4 py-2 -mb-px
text-primary-600 dark:text-primary-400
border-b-2 border-primary-600 dark:border-primary-400
font-medium
" aria-current="page">
Overview
</button>
<button class="
px-4 py-2 -mb-px
text-gray-500 dark:text-gray-400
border-b-2 border-transparent
hover:text-gray-700 dark:hover:text-gray-300
hover:border-gray-300 dark:hover:border-gray-600
transition-colors
">
Details
</button>
<button class="
px-4 py-2 -mb-px
text-gray-500 dark:text-gray-400
border-b-2 border-transparent
hover:text-gray-700 dark:hover:text-gray-300
hover:border-gray-300 dark:hover:border-gray-600
transition-colors
">
Reviews
</button>
</nav>
</div>
```
## Pagination
```html
<nav aria-label="Pagination" class="flex items-center justify-center gap-1">
<a href="?page=1" class="p-2 rounded-lg text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
</a>
<a href="?page=1" class="px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800">1</a>
<a href="?page=2" class="px-3 py-2 rounded-lg text-white bg-primary-600">2</a>
<a href="?page=3" class="px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800">3</a>
<span class="px-3 py-2 text-gray-400">...</span>
<a href="?page=10" class="px-3 py-2 rounded-lg text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800">10</a>
<a href="?page=3" class="p-2 rounded-lg text-gray-500 hover:bg-gray-100 dark:hover:bg-gray-800">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
</svg>
</a>
</nav>
```
rules/component-tables.md
---
id: component-tables
title: Table Components
priority: HIGH
category: Component Patterns
---
# Table Components
Create responsive, accessible tables with proper styling for data presentation.
## Bad Example
```html
<!-- Unstyled table that breaks on mobile -->
<table>
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Role</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr>
<td>John Doe</td>
<td>john@example.com</td>
<td>Admin</td>
<td>Active</td>
<td><button>Edit</button></td>
</tr>
</tbody>
</table>
<!-- No consideration for long content -->
<table class="w-full">
<tr>
<td>Very long text that will overflow and break the layout on small screens</td>
</tr>
</table>
```
## Good Example
```html
<!-- Responsive table with proper styling -->
<div class="overflow-x-auto rounded-lg border border-gray-200 dark:border-gray-700">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead class="bg-gray-50 dark:bg-gray-800">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Name
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Email
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Role
</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Status
</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wider">
Actions
</th>
</tr>
</thead>
<tbody class="bg-white dark:bg-gray-900 divide-y divide-gray-200 dark:divide-gray-700">
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center gap-3">
<img class="h-10 w-10 rounded-full" src="avatar.jpg" alt="">
<div>
<div class="text-sm font-medium text-gray-900 dark:text-white">John Doe</div>
<div class="text-sm text-gray-500 dark:text-gray-400">john.doe</div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-gray-900 dark:text-white">john@example.com</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-gray-900 dark:text-white">Administrator</span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-400">
Active
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right">
<button class="text-primary-600 dark:text-primary-400 hover:text-primary-800 dark:hover:text-primary-300 text-sm font-medium">
Edit
</button>
</td>
</tr>
</tbody>
</table>
</div>
```
## Why
1. **Responsive**: Horizontal scroll on small screens preserves data integrity.
2. **Accessibility**: Proper `scope` attributes and semantic structure.
3. **Readability**: Consistent spacing, typography, and visual hierarchy.
4. **Dark mode**: Tables work in both light and dark themes.
5. **Interactive**: Hover states and action buttons are clearly styled.
## Table with Sorting
```html
<table class="min-w-full">
<thead>
<tr>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase tracking-wider">
<button class="group inline-flex items-center gap-1">
Name
<svg class="w-4 h-4 text-gray-400 group-hover:text-gray-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4" />
</svg>
</button>
</th>
<!-- Sorted column -->
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-900 dark:text-white uppercase tracking-wider">
<button class="inline-flex items-center gap-1">
Date
<svg class="w-4 h-4 text-gray-900 dark:text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 15l7-7 7 7" />
</svg>
</button>
</th>
</tr>
</thead>
</table>
```
## Table with Selection
```html
<table class="min-w-full">
<thead>
<tr>
<th class="w-12 px-6 py-3">
<input
type="checkbox"
class="w-4 h-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500"
aria-label="Select all"
>
</th>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase">Name</th>
<th class="px-6 py-3 text-left text-xs font-semibold text-gray-500 uppercase">Status</th>
</tr>
</thead>
<tbody>
<tr class="hover:bg-gray-50 dark:hover:bg-gray-800">
<td class="px-6 py-4">
<input
type="checkbox"
class="w-4 h-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500"
>
</td>
<td class="px-6 py-4 text-sm text-gray-900 dark:text-white">John Doe</td>
<td class="px-6 py-4">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
Active
</span>
</td>
</tr>
<!-- Selected row -->
<tr class="bg-primary-50 dark:bg-primary-900/20">
<td class="px-6 py-4">
<input type="checkbox" checked class="w-4 h-4 rounded border-gray-300 text-primary-600 focus:ring-primary-500">
</td>
<td class="px-6 py-4 text-sm text-gray-900 dark:text-white">Jane Smith</td>
<td class="px-6 py-4">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">
Pending
</span>
</td>
</tr>
</tbody>
</table>
```
## Mobile-Friendly Card Table
```html
<!-- Transforms to cards on mobile -->
<div class="hidden md:block overflow-x-auto">
<table class="min-w-full">
<!-- Standard table for desktop -->
</table>
</div>
<!-- Card layout for mobile -->
<div class="md:hidden space-y-4">
<div class="bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700 p-4">
<div class="flex items-center justify-between mb-3">
<div class="flex items-center gap-3">
<img class="w-10 h-10 rounded-full" src="avatar.jpg" alt="">
<div>
<div class="font-medium text-gray-900 dark:text-white">John Doe</div>
<div class="text-sm text-gray-500">john@example.com</div>
</div>
</div>
<span class="px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800">Active</span>
</div>
<dl class="grid grid-cols-2 gap-2 text-sm">
<div>
<dt class="text-gray-500 dark:text-gray-400">Role</dt>
<dd class="text-gray-900 dark:text-white">Administrator</dd>
</div>
<div>
<dt class="text-gray-500 dark:text-gray-400">Joined</dt>
<dd class="text-gray-900 dark:text-white">Jan 15, 2024</dd>
</div>
</dl>
</div>
</div>
```
## Striped Table
```html
<tbody class="bg-white dark:bg-gray-900">
<tr class="even:bg-gray-50 dark:even:bg-gray-800">
<td class="px-6 py-4">Row 1</td>
</tr>
<tr class="even:bg-gray-50 dark:even:bg-gray-800">
<td class="px-6 py-4">Row 2</td>
</tr>
<tr class="even:bg-gray-50 dark:even:bg-gray-800">
<td class="px-6 py-4">Row 3</td>
</tr>
</tbody>
```
## Empty State
```html
<tbody>
<tr>
<td colspan="5" class="px-6 py-12 text-center">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900 dark:text-white">No data found</h3>
<p class="mt-1 text-sm text-gray-500 dark:text-gray-400">Get started by creating a new item.</p>
</td>
</tr>
</tbody>
```
rules/config-custom-colors.md
---
id: config-custom-colors
title: Custom Colors Configuration
priority: HIGH
category: Custom Configuration
---
# Custom Colors Configuration
Define a consistent color system with proper scales and semantic naming.
## Bad Example
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
// Single color without scale
brand: '#3b82f6',
// Arbitrary names
myBlue: '#2563eb',
lightBlue: '#dbeafe',
darkBlue: '#1e40af',
// Inconsistent naming
'btn-primary': '#3b82f6',
'btn-secondary': '#64748b',
headerBg: '#f8fafc',
},
},
},
}
```
## Good Example
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
// Full color scale for brand colors
brand: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
950: '#172554',
},
// Accent color
accent: {
50: '#fdf4ff',
100: '#fae8ff',
200: '#f5d0fe',
300: '#f0abfc',
400: '#e879f9',
500: '#d946ef',
600: '#c026d3',
700: '#a21caf',
800: '#86198f',
900: '#701a75',
950: '#4a044e',
},
// Semantic colors for specific purposes
success: {
light: '#dcfce7',
DEFAULT: '#22c55e',
dark: '#15803d',
},
warning: {
light: '#fef3c7',
DEFAULT: '#f59e0b',
dark: '#b45309',
},
error: {
light: '#fee2e2',
DEFAULT: '#ef4444',
dark: '#b91c1c',
},
},
},
},
}
```
## Why
1. **Consistent scale**: 50-950 scale matches Tailwind's built-in colors.
2. **Design flexibility**: Multiple shades enable hover states, backgrounds, and text.
3. **Semantic naming**: Colors describe purpose, improving code readability.
4. **Dark mode ready**: Full scales make it easy to pick contrasting shades.
5. **Team alignment**: Clear naming conventions prevent color proliferation.
## Usage
```html
<!-- Using brand color scale -->
<button class="bg-brand-600 hover:bg-brand-700 text-white">
Primary Action
</button>
<div class="bg-brand-50 border border-brand-200 text-brand-800">
Brand-themed alert
</div>
<!-- Using semantic colors -->
<div class="bg-success-light text-success-dark">
Success message
</div>
<span class="text-error">Error text</span>
```
## CSS Variables Approach
For dynamic theming:
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
background: 'hsl(var(--color-background) / <alpha-value>)',
foreground: 'hsl(var(--color-foreground) / <alpha-value>)',
primary: {
DEFAULT: 'hsl(var(--color-primary) / <alpha-value>)',
foreground: 'hsl(var(--color-primary-foreground) / <alpha-value>)',
},
secondary: {
DEFAULT: 'hsl(var(--color-secondary) / <alpha-value>)',
foreground: 'hsl(var(--color-secondary-foreground) / <alpha-value>)',
},
muted: {
DEFAULT: 'hsl(var(--color-muted) / <alpha-value>)',
foreground: 'hsl(var(--color-muted-foreground) / <alpha-value>)',
},
card: {
DEFAULT: 'hsl(var(--color-card) / <alpha-value>)',
foreground: 'hsl(var(--color-card-foreground) / <alpha-value>)',
},
border: 'hsl(var(--color-border) / <alpha-value>)',
ring: 'hsl(var(--color-ring) / <alpha-value>)',
},
},
},
}
```
```css
/* globals.css */
@layer base {
:root {
--color-background: 0 0% 100%;
--color-foreground: 222 47% 11%;
--color-primary: 221 83% 53%;
--color-primary-foreground: 210 40% 98%;
--color-secondary: 210 40% 96%;
--color-secondary-foreground: 222 47% 11%;
--color-muted: 210 40% 96%;
--color-muted-foreground: 215 16% 47%;
--color-card: 0 0% 100%;
--color-card-foreground: 222 47% 11%;
--color-border: 214 32% 91%;
--color-ring: 221 83% 53%;
}
.dark {
--color-background: 222 47% 4%;
--color-foreground: 210 40% 98%;
--color-primary: 217 91% 60%;
--color-primary-foreground: 222 47% 11%;
--color-secondary: 217 33% 17%;
--color-secondary-foreground: 210 40% 98%;
--color-muted: 217 33% 17%;
--color-muted-foreground: 215 20% 65%;
--color-card: 222 47% 7%;
--color-card-foreground: 210 40% 98%;
--color-border: 217 33% 17%;
--color-ring: 224 76% 48%;
}
}
```
## Generating Color Scales
Use tools to generate consistent scales:
1. **UI Colors** - https://uicolors.app
2. **Tailwind Ink** - https://tailwind.ink
3. **Palette** - https://palette.app
Or use the `tailwindcss-palette-generator` package:
```bash
npm install tailwindcss-palette-generator
```
```js
const { generatePalette } = require('tailwindcss-palette-generator');
module.exports = {
theme: {
extend: {
colors: {
brand: generatePalette('#3b82f6'),
},
},
},
}
```
## Color Naming Conventions
| Type | Naming | Example |
|------|--------|---------|
| Brand colors | `brand`, `accent` | `brand-500` |
| Semantic | `success`, `warning`, `error`, `info` | `error-light` |
| UI elements | `background`, `foreground`, `border` | `background` |
| Component | `card`, `popover`, `input` | `card-foreground` |
## Opacity Modifiers
```html
<!-- Using opacity with custom colors -->
<div class="bg-brand-500/50">50% opacity background</div>
<div class="text-brand-600/75">75% opacity text</div>
<div class="border-brand-200/30">30% opacity border</div>
```
rules/config-custom-fonts.md
---
id: config-custom-fonts
title: Custom Fonts Configuration
priority: HIGH
category: Custom Configuration
---
# Custom Fonts Configuration
Configure custom font families with proper fallbacks and font feature settings.
## Bad Example
```js
// tailwind.config.js
module.exports = {
theme: {
// DANGER: Replaces ALL font families
fontFamily: {
'custom': ['CustomFont'],
},
},
}
```
```html
<!-- No fallback fonts -->
<p class="font-['CustomFont']">
No fallbacks if custom font fails to load
</p>
<!-- Inconsistent font usage -->
<h1 style="font-family: 'Inter'">Heading</h1>
<p class="font-sans">Body with different font</p>
```
## Good Example
```js
// tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
extend: {
fontFamily: {
// Override sans with custom font + fallbacks
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
// Add display font for headings
display: ['Cal Sans', 'Inter var', ...defaultTheme.fontFamily.sans],
// Add mono font
mono: ['JetBrains Mono', ...defaultTheme.fontFamily.mono],
// Add serif for specific use cases
serif: ['Merriweather', ...defaultTheme.fontFamily.serif],
},
},
},
}
```
## Why
1. **Fallback chain**: System fonts display while custom fonts load.
2. **Consistent typography**: Font families are used consistently across the app.
3. **Performance**: Fallbacks prevent layout shift during font loading.
4. **Flexibility**: Multiple font families for different purposes.
5. **Maintainability**: Change fonts in one place, applies everywhere.
## Usage
```html
<!-- Default sans font (Inter) -->
<p class="font-sans">Body text using Inter</p>
<!-- Display font for headings -->
<h1 class="font-display text-4xl font-bold">
Heading with display font
</h1>
<!-- Mono font for code -->
<code class="font-mono">const x = 42;</code>
<!-- Serif for articles -->
<article class="font-serif prose">
Long-form content in serif font
</article>
```
## Font Loading Strategies
### Using @font-face
```css
/* globals.css */
@font-face {
font-family: 'Inter var';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/Inter-roman.var.woff2') format('woff2');
}
@font-face {
font-family: 'Inter var';
font-style: italic;
font-weight: 100 900;
font-display: swap;
src: url('/fonts/Inter-italic.var.woff2') format('woff2');
}
```
### Using Google Fonts (Next.js)
```jsx
// app/layout.tsx
import { Inter, Merriweather } from 'next/font/google'
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
display: 'swap',
})
const merriweather = Merriweather({
weight: ['400', '700'],
subsets: ['latin'],
variable: '--font-merriweather',
display: 'swap',
})
export default function RootLayout({ children }) {
return (
<html className={`${inter.variable} ${merriweather.variable}`}>
<body>{children}</body>
</html>
)
}
```
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['var(--font-inter)', ...defaultTheme.fontFamily.sans],
serif: ['var(--font-merriweather)', ...defaultTheme.fontFamily.serif],
},
},
},
}
```
## Font Feature Settings
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
},
},
},
}
```
```css
/* Enable OpenType features */
.font-feature-settings {
font-feature-settings:
'cv01' 1, /* Alternate a */
'cv02' 1, /* Alternate g */
'cv03' 1, /* Alternate i */
'cv04' 1, /* Alternate l */
'ss01' 1, /* Open digits */
'ss02' 1, /* Disambiguation */
'case' 1, /* Case-sensitive forms */
'zero' 1; /* Slashed zero */
}
/* Numeric features */
.tabular-nums {
font-variant-numeric: tabular-nums;
}
.oldstyle-nums {
font-variant-numeric: oldstyle-nums;
}
```
## Variable Fonts Configuration
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontWeight: {
// Variable font allows any weight
thin: '100',
extralight: '200',
light: '300',
normal: '400',
medium: '500',
semibold: '600',
bold: '700',
extrabold: '800',
black: '900',
},
},
},
}
```
## Font Size with Line Height
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontSize: {
// [fontSize, lineHeight]
'xs': ['0.75rem', '1rem'],
'sm': ['0.875rem', '1.25rem'],
'base': ['1rem', '1.5rem'],
'lg': ['1.125rem', '1.75rem'],
'xl': ['1.25rem', '1.75rem'],
'2xl': ['1.5rem', '2rem'],
// [fontSize, { lineHeight, letterSpacing, fontWeight }]
'display-lg': ['4.5rem', {
lineHeight: '1.1',
letterSpacing: '-0.02em',
fontWeight: '700',
}],
'display-md': ['3.75rem', {
lineHeight: '1.1',
letterSpacing: '-0.02em',
fontWeight: '700',
}],
},
},
},
}
```
## Complete Typography Setup
```js
// tailwind.config.js
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
extend: {
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
display: ['Cal Sans', ...defaultTheme.fontFamily.sans],
mono: ['JetBrains Mono', ...defaultTheme.fontFamily.mono],
},
fontSize: {
'display-2xl': ['4.5rem', { lineHeight: '1', letterSpacing: '-0.02em' }],
'display-xl': ['3.75rem', { lineHeight: '1.1', letterSpacing: '-0.02em' }],
'display-lg': ['3rem', { lineHeight: '1.1', letterSpacing: '-0.02em' }],
'display-md': ['2.25rem', { lineHeight: '1.2', letterSpacing: '-0.02em' }],
'display-sm': ['1.875rem', { lineHeight: '1.2', letterSpacing: '-0.01em' }],
'display-xs': ['1.5rem', { lineHeight: '1.2' }],
},
letterSpacing: {
tightest: '-0.04em',
tighter: '-0.02em',
tight: '-0.01em',
},
},
},
}
```
## Usage Example
```html
<header>
<h1 class="font-display text-display-xl tracking-tighter">
Welcome to Our Site
</h1>
</header>
<main class="font-sans">
<p class="text-lg leading-relaxed">
Body content with comfortable reading line height.
</p>
<pre class="font-mono text-sm">
<code>console.log('Hello');</code>
</pre>
</main>
```
rules/config-custom-spacing.md
---
id: config-custom-spacing
title: Custom Spacing Configuration
priority: HIGH
category: Custom Configuration
---
# Custom Spacing Configuration
Extend Tailwind's spacing scale with custom values for consistent layouts.
## Bad Example
```js
// tailwind.config.js
module.exports = {
theme: {
// DANGER: Replaces ALL spacing values
spacing: {
'small': '8px',
'medium': '16px',
'large': '32px',
},
},
}
```
```html
<!-- Using arbitrary values instead of config -->
<div class="p-[13px] m-[27px] gap-[19px]">
Magic numbers everywhere
</div>
<!-- Inconsistent spacing across components -->
<div class="p-4">Card 1</div>
<div class="p-[18px]">Card 2</div>
<div class="p-5">Card 3</div>
```
## Good Example
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
spacing: {
// Fill gaps in default scale
'13': '3.25rem', // 52px
'15': '3.75rem', // 60px
'18': '4.5rem', // 72px
'22': '5.5rem', // 88px
// Large spacing for sections
'128': '32rem', // 512px
'144': '36rem', // 576px
// Semantic spacing
'header': '4rem', // 64px - consistent header height
'sidebar': '16rem', // 256px - sidebar width
// Fractional spacing
'4.5': '1.125rem', // 18px
'5.5': '1.375rem', // 22px
},
},
},
}
```
## Why
1. **Consistency**: Predefined values ensure uniform spacing across the app.
2. **Design system alignment**: Spacing values match design specifications.
3. **Maintainability**: Change spacing in one place, update everywhere.
4. **Prevents arbitrary values**: Team uses config values instead of magic numbers.
5. **Preserves defaults**: Tailwind's 0-96 scale remains available.
## Usage
```html
<!-- Using custom spacing values -->
<header class="h-header px-6">
Header with consistent height
</header>
<aside class="w-sidebar">
Sidebar with consistent width
</aside>
<div class="space-y-18">
<section>Section with custom gap</section>
<section>Another section</section>
</div>
<div class="p-4.5">
Card with fractional padding
</div>
```
## Tailwind's Default Spacing Scale
| Class | Value | Pixels |
|-------|-------|--------|
| `0` | 0 | 0px |
| `px` | 1px | 1px |
| `0.5` | 0.125rem | 2px |
| `1` | 0.25rem | 4px |
| `2` | 0.5rem | 8px |
| `3` | 0.75rem | 12px |
| `4` | 1rem | 16px |
| `5` | 1.25rem | 20px |
| `6` | 1.5rem | 24px |
| `8` | 2rem | 32px |
| `10` | 2.5rem | 40px |
| `12` | 3rem | 48px |
| `16` | 4rem | 64px |
| `20` | 5rem | 80px |
| `24` | 6rem | 96px |
| `32` | 8rem | 128px |
| `40` | 10rem | 160px |
| `48` | 12rem | 192px |
| `56` | 14rem | 224px |
| `64` | 16rem | 256px |
| `72` | 18rem | 288px |
| `80` | 20rem | 320px |
| `96` | 24rem | 384px |
## CSS Variables for Dynamic Spacing
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
spacing: {
'header': 'var(--header-height)',
'sidebar': 'var(--sidebar-width)',
'gutter': 'var(--gutter)',
},
},
},
}
```
```css
:root {
--header-height: 4rem;
--sidebar-width: 16rem;
--gutter: 1.5rem;
}
@media (min-width: 1024px) {
:root {
--sidebar-width: 20rem;
--gutter: 2rem;
}
}
```
## Layout-Specific Spacing
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
spacing: {
// Page layout
'page-x': 'clamp(1rem, 5vw, 3rem)',
'page-y': 'clamp(2rem, 8vh, 6rem)',
// Section spacing
'section': 'clamp(4rem, 10vh, 8rem)',
// Component spacing
'card-padding': '1.5rem',
'card-gap': '1rem',
},
},
},
}
```
## Using with Width and Height
Custom spacing works with all spacing-related utilities:
```html
<!-- Padding/Margin -->
<div class="p-18 m-22">Custom spacing</div>
<!-- Width/Height -->
<div class="w-sidebar h-header">Layout element</div>
<!-- Max/Min dimensions -->
<div class="max-w-128 min-h-144">Constrained element</div>
<!-- Gap -->
<div class="grid gap-18">Grid with custom gap</div>
<!-- Inset (positioning) -->
<div class="absolute inset-18">Positioned element</div>
<!-- Space between -->
<div class="space-y-18">Stacked elements</div>
```
## Negative Spacing
Custom spacing values automatically get negative variants:
```html
<div class="-mt-18">Negative margin</div>
<div class="-translate-x-sidebar">Negative transform</div>
```
## Best Practices
1. **Use rem units**: Scales with user font preferences
2. **Follow naming convention**: Numbers for scale, names for semantic values
3. **Document custom values**: Add comments explaining usage
4. **Avoid too many custom values**: Extend only when needed
5. **Consider responsiveness**: Some values may need responsive variants
rules/config-extend-theme.md
---
id: config-extend-theme
title: Extend Theme Configuration
priority: HIGH
category: Custom Configuration
---
# Extend Theme Configuration
Use the `extend` key in your Tailwind config to add custom values while preserving defaults.
## Bad Example
```js
// tailwind.config.js
module.exports = {
theme: {
// DANGER: This replaces ALL colors, losing Tailwind defaults
colors: {
primary: '#3b82f6',
secondary: '#64748b',
},
// DANGER: This replaces ALL spacing values
spacing: {
sm: '0.5rem',
md: '1rem',
lg: '2rem',
},
// DANGER: This replaces ALL font sizes
fontSize: {
heading: '2rem',
body: '1rem',
},
},
}
```
## Good Example
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
// ADD to existing colors
colors: {
primary: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
950: '#172554',
},
secondary: {
50: '#f8fafc',
100: '#f1f5f9',
// ... full scale
},
},
// ADD custom spacing values
spacing: {
'18': '4.5rem',
'88': '22rem',
'128': '32rem',
},
// ADD custom font sizes
fontSize: {
'xxs': '0.625rem',
'display': ['4.5rem', { lineHeight: '1.1', letterSpacing: '-0.02em' }],
},
// ADD custom breakpoints
screens: {
'xs': '475px',
'3xl': '1920px',
},
},
},
}
```
## Why
1. **Preserves defaults**: All built-in Tailwind utilities remain available.
2. **Safer upgrades**: Custom values won't conflict with future Tailwind updates.
3. **Smaller config**: Only specify what you're adding, not everything.
4. **Predictable behavior**: Team members can rely on standard Tailwind values.
5. **Better documentation**: Custom additions are clearly separated from defaults.
## When to Override (Not Extend)
Sometimes you intentionally want to replace defaults:
```js
// tailwind.config.js
module.exports = {
theme: {
// Override: Use custom font stack everywhere
fontFamily: {
sans: ['Inter var', 'system-ui', 'sans-serif'],
serif: ['Merriweather', 'Georgia', 'serif'],
mono: ['JetBrains Mono', 'monospace'],
},
// Override: Use custom breakpoints
screens: {
'sm': '640px',
'md': '768px',
'lg': '1024px',
'xl': '1280px',
// Intentionally removed 2xl
},
extend: {
// Still extend other values
colors: {
brand: '#ff5500',
},
},
},
}
```
## Extending with CSS Variables
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
// Reference CSS variables for dynamic theming
background: 'hsl(var(--background) / <alpha-value>)',
foreground: 'hsl(var(--foreground) / <alpha-value>)',
primary: {
DEFAULT: 'hsl(var(--primary) / <alpha-value>)',
foreground: 'hsl(var(--primary-foreground) / <alpha-value>)',
},
muted: {
DEFAULT: 'hsl(var(--muted) / <alpha-value>)',
foreground: 'hsl(var(--muted-foreground) / <alpha-value>)',
},
},
borderRadius: {
lg: 'var(--radius)',
md: 'calc(var(--radius) - 2px)',
sm: 'calc(var(--radius) - 4px)',
},
},
},
}
```
## Extending Animations
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
animation: {
'fade-in': 'fadeIn 0.5s ease-out',
'slide-up': 'slideUp 0.3s ease-out',
'spin-slow': 'spin 3s linear infinite',
'bounce-slow': 'bounce 2s infinite',
},
keyframes: {
fadeIn: {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
slideUp: {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
},
},
},
}
```
## Extending Typography
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
typography: (theme) => ({
DEFAULT: {
css: {
color: theme('colors.gray.700'),
a: {
color: theme('colors.primary.600'),
'&:hover': {
color: theme('colors.primary.800'),
},
},
'code::before': {
content: '""',
},
'code::after': {
content: '""',
},
},
},
dark: {
css: {
color: theme('colors.gray.300'),
a: {
color: theme('colors.primary.400'),
},
},
},
}),
},
},
}
```
## Checking Available Defaults
View all default values in Tailwind's source:
```bash
npx tailwindcss init --full
```
Or reference the documentation for each utility's default values.
## v4: Use @theme Instead
In Tailwind v4, `tailwind.config.js` is replaced by `@theme {}` in CSS. The `@theme` block extends the default theme by default — no `extend` key needed:
```css
@import "tailwindcss";
@theme {
/* Extends defaults automatically */
--color-brand-500: #3b82f6;
--font-sans: "Inter", sans-serif;
--spacing-18: 4.5rem;
/* To override ALL values in a namespace: */
--color-*: initial;
--color-white: #fff;
--color-brand: #3b82f6;
}
```
See `v4-theme-configuration` for full details.
rules/config-plugins.md
---
id: config-plugins
title: Tailwind Plugins Configuration
priority: HIGH
category: Custom Configuration
---
# Tailwind Plugins Configuration
Use and create plugins to extend Tailwind with custom utilities, components, and variants.
## Bad Example
```js
// tailwind.config.js
module.exports = {
// Not using any plugins, missing useful functionality
plugins: [],
}
```
```css
/* Manually writing repetitive CSS instead of using plugins */
.text-shadow-sm {
text-shadow: 0 1px 2px rgba(0,0,0,0.1);
}
.text-shadow-md {
text-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.text-shadow-lg {
text-shadow: 0 4px 8px rgba(0,0,0,0.1);
}
/* Repeated for each variant... */
```
## Good Example
```js
// tailwind.config.js
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
// Official Tailwind plugins
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
require('@tailwindcss/aspect-ratio'),
require('@tailwindcss/container-queries'),
// Custom plugin for text shadows
plugin(function({ addUtilities, theme, e }) {
const textShadows = {
'.text-shadow-sm': {
textShadow: '0 1px 2px rgba(0, 0, 0, 0.1)',
},
'.text-shadow': {
textShadow: '0 2px 4px rgba(0, 0, 0, 0.1)',
},
'.text-shadow-md': {
textShadow: '0 4px 8px rgba(0, 0, 0, 0.1)',
},
'.text-shadow-lg': {
textShadow: '0 8px 16px rgba(0, 0, 0, 0.1)',
},
'.text-shadow-none': {
textShadow: 'none',
},
}
addUtilities(textShadows)
}),
// Custom plugin for animations
plugin(function({ addUtilities }) {
addUtilities({
'.animate-fade-in': {
animation: 'fadeIn 0.5s ease-out forwards',
},
'.animate-slide-up': {
animation: 'slideUp 0.3s ease-out forwards',
},
'@keyframes fadeIn': {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
'@keyframes slideUp': {
'0%': { transform: 'translateY(10px)', opacity: '0' },
'100%': { transform: 'translateY(0)', opacity: '1' },
},
})
}),
],
}
```
## Why
1. **Code reuse**: Plugins encapsulate common patterns.
2. **Consistency**: Shared plugins ensure uniform styles across projects.
3. **Official support**: Tailwind plugins are well-maintained and documented.
4. **Custom extensions**: Create project-specific utilities without leaving Tailwind.
5. **Composability**: Plugins work with Tailwind's modifiers (hover:, dark:, etc.).
## Official Tailwind Plugins
### Typography Plugin
```bash
npm install @tailwindcss/typography
```
```html
<article class="prose dark:prose-invert lg:prose-xl">
<h1>Article Title</h1>
<p>Beautiful typography defaults for long-form content...</p>
</article>
```
### Forms Plugin
```bash
npm install @tailwindcss/forms
```
```html
<input type="text" class="form-input rounded-lg">
<select class="form-select rounded-lg">
<option>Option 1</option>
</select>
<textarea class="form-textarea rounded-lg"></textarea>
```
### Aspect Ratio Plugin
```bash
npm install @tailwindcss/aspect-ratio
```
```html
<div class="aspect-w-16 aspect-h-9">
<iframe src="..." class="w-full h-full"></iframe>
</div>
```
### Container Queries Plugin
```bash
npm install @tailwindcss/container-queries
```
```html
<div class="@container">
<div class="@lg:flex @lg:gap-8">
Container-based responsive layout
</div>
</div>
```
## Creating Custom Plugins
### Adding Utilities
```js
plugin(function({ addUtilities, theme }) {
addUtilities({
'.scrollbar-hide': {
'-ms-overflow-style': 'none',
'scrollbar-width': 'none',
'&::-webkit-scrollbar': {
display: 'none',
},
},
'.scrollbar-thin': {
'scrollbar-width': 'thin',
},
})
})
```
### Adding Components
```js
plugin(function({ addComponents, theme }) {
addComponents({
'.btn': {
padding: `${theme('spacing.2')} ${theme('spacing.4')}`,
borderRadius: theme('borderRadius.lg'),
fontWeight: theme('fontWeight.medium'),
transition: 'all 150ms ease',
},
'.btn-primary': {
backgroundColor: theme('colors.blue.600'),
color: theme('colors.white'),
'&:hover': {
backgroundColor: theme('colors.blue.700'),
},
},
})
})
```
### Adding Base Styles
```js
plugin(function({ addBase, theme }) {
addBase({
'h1': {
fontSize: theme('fontSize.3xl'),
fontWeight: theme('fontWeight.bold'),
},
'h2': {
fontSize: theme('fontSize.2xl'),
fontWeight: theme('fontWeight.semibold'),
},
})
})
```
### Adding Variants
```js
plugin(function({ addVariant }) {
// Peer checked variant
addVariant('peer-checked', ':merge(.peer):checked ~ &')
// Group hover variant
addVariant('group-hover', ':merge(.group):hover &')
// Supports variant
addVariant('supports-backdrop', '@supports (backdrop-filter: blur(0))')
// Custom selector variant
addVariant('hocus', ['&:hover', '&:focus'])
})
```
## Plugin with Options
```js
// plugins/buttons.js
const plugin = require('tailwindcss/plugin')
module.exports = plugin.withOptions(
function(options = {}) {
return function({ addComponents, theme }) {
const baseRadius = options.radius || theme('borderRadius.lg')
addComponents({
'.btn': {
borderRadius: baseRadius,
padding: `${theme('spacing.2')} ${theme('spacing.4')}`,
},
})
}
},
function(options = {}) {
return {
theme: {
extend: {
// Plugin can extend theme
},
},
}
}
)
```
```js
// tailwind.config.js
module.exports = {
plugins: [
require('./plugins/buttons')({ radius: '0.5rem' }),
],
}
```
## v4: Plugins Move to CSS
In Tailwind v4, plugins use `@plugin` in CSS instead of `require()` in config:
```css
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
```
Custom utilities and variants use CSS directives instead of the JS plugin API:
```css
/* Custom utility — replaces addUtilities() */
@utility scrollbar-hide {
scrollbar-width: none;
}
/* Custom variant — replaces addVariant() */
@custom-variant hocus (&:hover, &:focus);
```
**Plugins no longer needed in v4** (features built into core):
- `@tailwindcss/aspect-ratio` — native `aspect-*` utilities
- `@tailwindcss/container-queries` — native `@container` with `@min-*`/`@max-*` range variants
## Popular Community Plugins
```js
// tailwind.config.js
module.exports = {
plugins: [
// Animations
require('tailwindcss-animate'),
// Scrollbar styling
require('tailwind-scrollbar'),
// Debug screens (shows current breakpoint)
require('tailwindcss-debug-screens'),
// Multi-theme support
require('tailwindcss-themer'),
// Fluid type
require('tailwindcss-fluid-type'),
],
}
```
## Usage Example
```html
<!-- Using typography plugin -->
<article class="prose prose-lg dark:prose-invert">
<h1>Welcome</h1>
<p>This content is beautifully styled.</p>
</article>
<!-- Using custom text-shadow utility -->
<h1 class="text-4xl font-bold text-shadow-lg">
Shadowed Heading
</h1>
<!-- Using custom animation -->
<div class="animate-fade-in">
Fading in content
</div>
```
rules/config-presets.md
---
id: config-presets
title: Tailwind Presets Configuration
priority: MEDIUM
category: Custom Configuration
---
# Tailwind Presets Configuration
Use presets to share common Tailwind configurations across multiple projects or team members.
## Bad Example
```js
// Copying entire config between projects
// project-a/tailwind.config.js
module.exports = {
theme: {
extend: {
colors: { /* 50+ lines of color config */ },
spacing: { /* spacing config */ },
fontFamily: { /* font config */ },
},
},
plugins: [ /* plugins list */ ],
}
// project-b/tailwind.config.js
// Same config copy-pasted, already out of sync...
```
## Good Example
```js
// packages/tailwind-preset/index.js
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
theme: {
extend: {
colors: {
brand: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
950: '#172554',
},
},
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
display: ['Cal Sans', ...defaultTheme.fontFamily.sans],
},
borderRadius: {
DEFAULT: '0.5rem',
},
},
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
],
}
```
```js
// project-a/tailwind.config.js
module.exports = {
presets: [
require('@mycompany/tailwind-preset'),
],
content: ['./src/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {
// Project-specific overrides
colors: {
accent: '#ff5500',
},
},
},
}
```
## Why
1. **Consistency**: Same design tokens across all company projects.
2. **Maintainability**: Update preset once, all projects benefit.
3. **Onboarding**: New projects start with correct configuration.
4. **Version control**: Preset changes are tracked and versioned.
5. **Team alignment**: Design system is encoded in code.
## Creating a Preset Package
### Package Structure
```
packages/tailwind-preset/
├── index.js
├── package.json
├── colors.js
├── typography.js
└── plugins/
└── custom-plugin.js
```
### package.json
```json
{
"name": "@mycompany/tailwind-preset",
"version": "1.0.0",
"main": "index.js",
"peerDependencies": {
"tailwindcss": "^3.0.0"
},
"dependencies": {
"@tailwindcss/forms": "^0.5.0",
"@tailwindcss/typography": "^0.5.0"
}
}
```
### index.js
```js
const colors = require('./colors')
const typography = require('./typography')
const customPlugin = require('./plugins/custom-plugin')
module.exports = {
theme: {
extend: {
colors,
...typography,
},
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
customPlugin,
],
}
```
### colors.js
```js
module.exports = {
brand: {
50: '#eff6ff',
100: '#dbeafe',
500: '#3b82f6',
600: '#2563eb',
900: '#1e3a8a',
},
success: {
light: '#dcfce7',
DEFAULT: '#22c55e',
dark: '#15803d',
},
warning: {
light: '#fef3c7',
DEFAULT: '#f59e0b',
dark: '#b45309',
},
error: {
light: '#fee2e2',
DEFAULT: '#ef4444',
dark: '#b91c1c',
},
}
```
## Using the Preset
### Install
```bash
npm install @mycompany/tailwind-preset
```
### Configure
```js
// tailwind.config.js
module.exports = {
presets: [
require('@mycompany/tailwind-preset'),
],
content: ['./src/**/*.{js,jsx,ts,tsx}'],
// Project-specific additions/overrides
theme: {
extend: {
colors: {
// Add project-specific colors
accent: '#ff5500',
},
},
},
}
```
## Multiple Presets
```js
// tailwind.config.js
module.exports = {
presets: [
require('@mycompany/tailwind-preset'), // Base company preset
require('@mycompany/tailwind-marketing'), // Marketing-specific additions
],
content: ['./src/**/*.{js,jsx,ts,tsx}'],
}
```
Presets are merged in order, later presets override earlier ones.
## Preset with Options
```js
// packages/tailwind-preset/index.js
module.exports = function(options = {}) {
const { enableDarkMode = true, useSerifFont = false } = options
return {
darkMode: enableDarkMode ? 'class' : 'media',
theme: {
extend: {
fontFamily: useSerifFont
? { sans: ['Merriweather', 'serif'] }
: { sans: ['Inter', 'sans-serif'] },
},
},
}
}
```
```js
// tailwind.config.js
module.exports = {
presets: [
require('@mycompany/tailwind-preset')({
enableDarkMode: true,
useSerifFont: false,
}),
],
}
```
## Disabling Default Preset
By default, Tailwind uses its own preset. To start completely fresh:
```js
// tailwind.config.js
module.exports = {
presets: [], // No presets, including Tailwind's default
// You must define everything yourself
theme: {
colors: {
// All colors must be defined
},
spacing: {
// All spacing must be defined
},
},
}
```
## Preset Versioning Strategy
1. **Semantic versioning**: Use semver for breaking changes
2. **Changelog**: Document all changes
3. **Migration guides**: Provide upgrade paths
4. **Lock versions**: Pin preset versions in projects
```json
// package.json
{
"dependencies": {
"@mycompany/tailwind-preset": "^2.0.0"
}
}
```
## Testing Presets
```js
// packages/tailwind-preset/tests/preset.test.js
const preset = require('../index')
describe('Tailwind Preset', () => {
it('should include brand colors', () => {
expect(preset.theme.extend.colors.brand).toBeDefined()
})
it('should include required plugins', () => {
expect(preset.plugins.length).toBeGreaterThan(0)
})
})
```
rules/dark-class-strategy.md
---
id: dark-class-strategy
title: Dark Mode Class Strategy
priority: CRITICAL
category: Dark Mode
---
# Dark Mode Class Strategy
Use the class-based dark mode strategy for manual control over theme switching in your application.
## Bad Example
```html
<!-- Duplicating components for each theme -->
<div class="light-theme-card bg-white text-black">
Content
</div>
<div class="dark-theme-card bg-gray-900 text-white hidden">
Content
</div>
<!-- Using JavaScript to swap all classes -->
<script>
// Anti-pattern: manually toggling every element
document.querySelectorAll('.card').forEach(card => {
card.classList.toggle('bg-white');
card.classList.toggle('bg-gray-900');
});
</script>
<!-- Inline styles for theming -->
<div style="background: var(--theme-bg)">
Content
</div>
```
## Good Example
```html
<!-- Configure dark mode in tailwind.config.js -->
<!-- darkMode: 'class' -->
<!-- Add dark class to html or body -->
<html class="dark">
<body class="bg-white dark:bg-gray-900">
<!-- Components with dark variants -->
<div class="bg-white dark:bg-gray-800 text-gray-900 dark:text-white rounded-lg p-6">
<h2 class="text-gray-900 dark:text-white">Card Title</h2>
<p class="text-gray-600 dark:text-gray-300">Card description</p>
<button class="bg-blue-500 dark:bg-blue-600 hover:bg-blue-600 dark:hover:bg-blue-700">
Action
</button>
</div>
</body>
</html>
<!-- Theme toggle implementation -->
<script>
function toggleDarkMode() {
document.documentElement.classList.toggle('dark');
localStorage.setItem('theme',
document.documentElement.classList.contains('dark') ? 'dark' : 'light'
);
}
// Initialize on page load
if (localStorage.theme === 'dark' ||
(!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark');
}
</script>
```
## Why
1. **User control**: Users can override system preferences and choose their preferred theme.
2. **Persistent preference**: Theme choice can be saved to localStorage and restored on subsequent visits.
3. **Instant switching**: Toggling the `dark` class instantly updates all themed elements.
4. **No flash of wrong theme**: Properly initialized, the correct theme loads before paint.
5. **JavaScript integration**: Easy to connect with React state, Vue refs, or any framework.
## Configuration
```js
// tailwind.config.js
module.exports = {
darkMode: 'class', // Enable class-based dark mode
// ...
}
```
## React Implementation
```jsx
// ThemeProvider.jsx
import { createContext, useContext, useEffect, useState } from 'react';
const ThemeContext = createContext();
export function ThemeProvider({ children }) {
const [theme, setTheme] = useState(() => {
if (typeof window !== 'undefined') {
return localStorage.theme ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
}
return 'light';
});
useEffect(() => {
const root = document.documentElement;
if (theme === 'dark') {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
localStorage.setItem('theme', theme);
}, [theme]);
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
{children}
</ThemeContext.Provider>
);
}
export const useTheme = () => useContext(ThemeContext);
```
## Preventing Flash of Wrong Theme
```html
<!-- Add this script in <head> before CSS -->
<script>
if (localStorage.theme === 'dark' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
document.documentElement.classList.add('dark')
} else {
document.documentElement.classList.remove('dark')
}
</script>
```
## v4: No Config Needed
In Tailwind v4, class-based dark mode is the default. No `tailwind.config.js` required:
```css
@import "tailwindcss";
/* dark: variant works out of the box with .dark class */
```
To use a custom selector (e.g., `data-theme`):
```css
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
```
```html
<html data-theme="dark">
<!-- Works with data attributes in v4 -->
</html>
```
The toggle logic and React implementation remain the same in both v3 and v4.
rules/dark-color-scheme.md
---
id: dark-color-scheme
title: Dark Mode Color Scheme
priority: HIGH
category: Dark Mode
---
# Dark Mode Color Scheme
Use semantic color naming and CSS custom properties for maintainable dark mode color systems.
## Bad Example
```html
<!-- Hardcoded colors without semantic meaning -->
<div class="bg-gray-900 dark:bg-white text-white dark:text-gray-900">
Inverted but confusing
</div>
<!-- Inconsistent color pairs across components -->
<div class="bg-slate-800 dark:bg-slate-100">Header</div>
<div class="bg-gray-900 dark:bg-white">Content</div>
<div class="bg-zinc-800 dark:bg-zinc-50">Footer</div>
<!-- Magic numbers without context -->
<p class="text-gray-700 dark:text-gray-300">
Why these specific shades?
</p>
```
## Good Example
```html
<!-- Semantic color tokens via CSS variables -->
<div class="bg-[--color-surface] text-[--color-text]">
Consistent theming
</div>
<!-- Or use Tailwind's built-in approach with custom colors -->
<div class="bg-surface text-foreground">
<h1 class="text-foreground">Title</h1>
<p class="text-muted">Description</p>
<button class="bg-primary text-primary-foreground">Action</button>
</div>
<!-- Consistent gray scale strategy -->
<body class="bg-white dark:bg-gray-950 text-gray-900 dark:text-gray-50">
<header class="bg-gray-50 dark:bg-gray-900">
<nav class="border-b border-gray-200 dark:border-gray-800">
Navigation
</nav>
</header>
<main class="bg-white dark:bg-gray-950">
<div class="bg-gray-100 dark:bg-gray-900 rounded-lg p-4">
Card content
</div>
</main>
</body>
```
## Why
1. **Semantic meaning**: Color names describe purpose, not appearance.
2. **Consistency**: All surfaces use the same color tokens.
3. **Easy theme changes**: Update colors in one place, apply everywhere.
4. **Accessibility**: Ensures proper contrast ratios are maintained.
5. **Design system alignment**: Matches common design system conventions.
## Color Pairing Strategy
| Light Mode | Dark Mode | Use Case |
|------------|-----------|----------|
| `white` | `gray-950` | Page background |
| `gray-50` | `gray-900` | Elevated surfaces |
| `gray-100` | `gray-800` | Cards, inputs |
| `gray-200` | `gray-700` | Borders, dividers |
| `gray-500` | `gray-400` | Placeholder text |
| `gray-600` | `gray-300` | Secondary text |
| `gray-900` | `gray-50` | Primary text |
## Configuration with Custom Colors
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
// Semantic tokens
background: 'hsl(var(--background))',
foreground: 'hsl(var(--foreground))',
surface: {
DEFAULT: 'hsl(var(--surface))',
elevated: 'hsl(var(--surface-elevated))',
},
muted: {
DEFAULT: 'hsl(var(--muted))',
foreground: 'hsl(var(--muted-foreground))',
},
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
border: 'hsl(var(--border))',
},
},
},
}
```
## CSS Variables Setup
### v3
```css
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222 47% 11%;
--surface: 210 40% 98%;
--surface-elevated: 0 0% 100%;
--muted: 210 40% 96%;
--muted-foreground: 215 16% 47%;
--primary: 221 83% 53%;
--primary-foreground: 210 40% 98%;
--border: 214 32% 91%;
}
.dark {
--background: 222 47% 4%;
--foreground: 210 40% 98%;
--surface: 222 47% 7%;
--surface-elevated: 222 47% 11%;
--muted: 217 33% 17%;
--muted-foreground: 215 20% 65%;
--primary: 217 91% 60%;
--primary-foreground: 222 47% 11%;
--border: 217 33% 17%;
}
}
```
### v4
```css
@import "tailwindcss";
/* Define semantic colors as theme tokens */
@theme {
--color-background: hsl(0 0% 100%);
--color-foreground: hsl(222 47% 11%);
--color-surface: hsl(210 40% 98%);
--color-muted: hsl(210 40% 96%);
--color-primary: hsl(221 83% 53%);
--color-border: hsl(214 32% 91%);
}
/* Dark mode overrides with CSS custom properties */
.dark {
--color-background: hsl(222 47% 4%);
--color-foreground: hsl(210 40% 98%);
--color-surface: hsl(222 47% 7%);
--color-muted: hsl(217 33% 17%);
--color-primary: hsl(217 91% 60%);
--color-border: hsl(217 33% 17%);
}
```
The v4 approach uses `@theme` to register CSS variables as utility classes, so `bg-background`, `text-foreground`, etc. work automatically without configuring `tailwind.config.js`.
## Usage
```html
<div class="bg-background text-foreground min-h-screen">
<header class="bg-surface border-b border-border">
<h1 class="text-foreground">App Name</h1>
</header>
<main class="p-6">
<div class="bg-surface-elevated rounded-lg p-4 border border-border">
<h2 class="text-foreground">Card Title</h2>
<p class="text-muted-foreground">Secondary content</p>
<button class="bg-primary text-primary-foreground px-4 py-2 rounded">
Primary Action
</button>
</div>
</main>
</div>
```
rules/dark-custom-colors.md
---
id: dark-custom-colors
title: Dark Mode Custom Colors
priority: HIGH
category: Dark Mode
---
# Dark Mode Custom Colors
Create custom color palettes that work harmoniously in both light and dark modes.
## Bad Example
```html
<!-- Using colors that clash in dark mode -->
<button class="bg-yellow-400 dark:bg-yellow-400 text-black dark:text-black">
Yellow button (poor contrast in dark mode)
</button>
<!-- Inverted colors that look wrong -->
<div class="bg-blue-500 dark:bg-blue-500">
<p class="text-white dark:text-white">Same colors don't adapt</p>
</div>
<!-- Random dark mode color choices -->
<div class="bg-indigo-600 dark:bg-pink-400">
Unrelated color pairing
</div>
```
## Good Example
```html
<!-- Brand colors with dark mode variants -->
<button class="bg-brand-500 dark:bg-brand-400 text-white dark:text-gray-900">
Brand button
</button>
<!-- Semantic color usage -->
<div class="bg-success-50 dark:bg-success-950 border border-success-200 dark:border-success-800">
<p class="text-success-700 dark:text-success-300">Success message</p>
</div>
<!-- Accent colors that adapt -->
<a href="#" class="text-accent-600 dark:text-accent-400 hover:text-accent-700 dark:hover:text-accent-300">
Accent link
</a>
<!-- Status colors with proper contrast -->
<span class="bg-error-100 dark:bg-error-900/50 text-error-700 dark:text-error-300 px-2 py-1 rounded">
Error
</span>
<span class="bg-warning-100 dark:bg-warning-900/50 text-warning-700 dark:text-warning-300 px-2 py-1 rounded">
Warning
</span>
<span class="bg-info-100 dark:bg-info-900/50 text-info-700 dark:text-info-300 px-2 py-1 rounded">
Info
</span>
```
## Why
1. **Brand consistency**: Maintain brand identity while ensuring readability in dark mode.
2. **Accessibility**: Custom colors are designed with contrast ratios in mind.
3. **Visual harmony**: Light and dark variants feel intentional, not random.
4. **Semantic meaning**: Color names describe usage, making code readable.
5. **Scalability**: Easy to add new status colors or brand variations.
## Configuration
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
// Brand color with full scale
brand: {
50: '#f0f9ff',
100: '#e0f2fe',
200: '#bae6fd',
300: '#7dd3fc',
400: '#38bdf8',
500: '#0ea5e9',
600: '#0284c7',
700: '#0369a1',
800: '#075985',
900: '#0c4a6e',
950: '#082f49',
},
// Accent color
accent: {
50: '#fdf4ff',
100: '#fae8ff',
200: '#f5d0fe',
300: '#f0abfc',
400: '#e879f9',
500: '#d946ef',
600: '#c026d3',
700: '#a21caf',
800: '#86198f',
900: '#701a75',
950: '#4a044e',
},
// Semantic status colors
success: {
50: '#f0fdf4',
100: '#dcfce7',
200: '#bbf7d0',
300: '#86efac',
400: '#4ade80',
500: '#22c55e',
600: '#16a34a',
700: '#15803d',
800: '#166534',
900: '#14532d',
950: '#052e16',
},
warning: {
50: '#fffbeb',
100: '#fef3c7',
200: '#fde68a',
300: '#fcd34d',
400: '#fbbf24',
500: '#f59e0b',
600: '#d97706',
700: '#b45309',
800: '#92400e',
900: '#78350f',
950: '#451a03',
},
error: {
50: '#fef2f2',
100: '#fee2e2',
200: '#fecaca',
300: '#fca5a5',
400: '#f87171',
500: '#ef4444',
600: '#dc2626',
700: '#b91c1c',
800: '#991b1b',
900: '#7f1d1d',
950: '#450a0a',
},
info: {
50: '#eff6ff',
100: '#dbeafe',
200: '#bfdbfe',
300: '#93c5fd',
400: '#60a5fa',
500: '#3b82f6',
600: '#2563eb',
700: '#1d4ed8',
800: '#1e40af',
900: '#1e3a8a',
950: '#172554',
},
},
},
},
}
```
## Color Pairing Guidelines
### Light Mode
- Background: 50-100 shades
- Text: 700-900 shades
- Borders: 200-300 shades
- Primary actions: 500-600 shades
### Dark Mode
- Background: 900-950 shades (or with opacity)
- Text: 200-400 shades
- Borders: 700-800 shades
- Primary actions: 400-500 shades
## Component Example
```html
<!-- Alert component with custom colors -->
<div class="rounded-lg p-4 bg-info-50 dark:bg-info-950 border border-info-200 dark:border-info-800">
<div class="flex items-center gap-3">
<svg class="w-5 h-5 text-info-500 dark:text-info-400" fill="currentColor">...</svg>
<div>
<h4 class="font-medium text-info-800 dark:text-info-200">Information</h4>
<p class="text-info-700 dark:text-info-300 text-sm">This is an informational message.</p>
</div>
</div>
</div>
```
## v4: Custom Colors with @theme
In v4, define color scales directly in CSS:
```css
@import "tailwindcss";
@theme {
--color-brand-50: #eff6ff;
--color-brand-500: #3b82f6;
--color-brand-950: #172554;
--color-success-500: #22c55e;
--color-error-500: #ef4444;
}
```
The same pairing guidelines apply — use lighter shades (50-100) for dark backgrounds and darker shades (700-900) for dark text, with `dark:` variants.
## Generating Color Palettes
Use tools like:
- [UI Colors](https://uicolors.app)
- [Tailwind CSS Color Generator](https://tailwindcolors.com)
- [Palette Generator](https://palette.app)
rules/dark-media-strategy.md
---
id: dark-media-strategy
title: Dark Mode Media Strategy
priority: HIGH
category: Dark Mode
---
# Dark Mode Media Strategy
Use the media-based dark mode strategy to automatically follow the user's system preferences.
## Bad Example
```html
<!-- Manually checking prefers-color-scheme in JavaScript -->
<script>
// Anti-pattern: reimplementing what Tailwind does automatically
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.querySelectorAll('.card').forEach(card => {
card.classList.add('dark-card');
});
}
</script>
<!-- Using separate stylesheets -->
<link rel="stylesheet" href="light.css" media="(prefers-color-scheme: light)">
<link rel="stylesheet" href="dark.css" media="(prefers-color-scheme: dark)">
<!-- Not using dark: variants at all -->
<div class="bg-white text-black">
No dark mode support
</div>
```
## Good Example
```html
<!-- Configure dark mode in tailwind.config.js -->
<!-- darkMode: 'media' (this is the default) -->
<!-- Components automatically respond to system preference -->
<div class="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
<h1 class="text-2xl font-bold text-gray-900 dark:text-white">
Welcome
</h1>
<p class="text-gray-600 dark:text-gray-400">
This content automatically adapts to your system theme preference.
</p>
</div>
<!-- Full page with media-based dark mode -->
<body class="bg-gray-50 dark:bg-gray-950 text-gray-900 dark:text-gray-100 min-h-screen">
<header class="bg-white dark:bg-gray-900 shadow dark:shadow-gray-800">
<nav class="container mx-auto px-4 py-4">
<a href="/" class="text-gray-900 dark:text-white font-bold">Logo</a>
</nav>
</header>
<main class="container mx-auto px-4 py-8">
<article class="prose dark:prose-invert">
<!-- Content styled with prose -->
</article>
</main>
</body>
```
## Why
1. **Zero JavaScript required**: Theme switching happens automatically via CSS media queries.
2. **Respects user preferences**: Honors the user's operating system theme setting.
3. **Automatic updates**: Theme changes instantly when user toggles system preference.
4. **Simpler implementation**: No theme state management or localStorage needed.
5. **Default behavior**: This is Tailwind's default dark mode strategy.
## Configuration
### v3
```js
// tailwind.config.js
module.exports = {
darkMode: 'media', // Default - can be omitted
// ...
}
```
### v4
In v4, dark mode defaults to class-based. To use media (system preference) strategy:
```css
@import "tailwindcss";
/* Override the default dark variant to use system preference */
@custom-variant dark (@media (prefers-color-scheme: dark));
```
## Generated CSS
When you use `dark:bg-gray-900`, Tailwind generates:
```css
@media (prefers-color-scheme: dark) {
.dark\:bg-gray-900 {
background-color: rgb(17 24 39);
}
}
```
## When to Use Media Strategy
- **Content sites**: Blogs, documentation, marketing pages
- **Simple applications**: Where manual theme toggle isn't needed
- **Progressive enhancement**: Starting point before adding manual toggle
- **Accessibility focus**: Automatically respecting user preferences
## When to Use Class Strategy Instead
- **User preference override**: When users should choose regardless of system setting
- **Multiple themes**: Light, dark, and other color schemes
- **Theme persistence**: Saving preference across sessions
- **Admin/dashboard apps**: Where users expect control
## Combining Both Approaches
You can start with media strategy and add optional override:
```js
// tailwind.config.js
module.exports = {
darkMode: 'class', // Use class for control
}
```
```javascript
// Initialize with system preference, allow override
function initTheme() {
const stored = localStorage.getItem('theme');
if (stored === 'dark') {
document.documentElement.classList.add('dark');
} else if (stored === 'light') {
document.documentElement.classList.remove('dark');
} else {
// No stored preference, follow system
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
document.documentElement.classList.add('dark');
}
}
}
```
## Testing Dark Mode
```css
/* Force dark mode in browser DevTools */
@media (prefers-color-scheme: dark) {
/* Styles here */
}
```
Or use browser DevTools:
1. Open DevTools
2. Press Cmd/Ctrl + Shift + P
3. Search "Emulate CSS prefers-color-scheme"
4. Select "dark" or "light"
rules/dark-setup.md
---
id: dark-setup
title: Dark Mode Setup
priority: CRITICAL
category: Dark Mode
---
# Dark Mode Setup
## Why It Matters
Dark mode is expected in modern applications. Tailwind provides first-class dark mode support with the `dark:` variant. Proper setup ensures consistent dark mode across your application.
## Configuration Options
| Mode | v3 | v4 | Use Case |
|------|----|----|----------|
| Class-based | `darkMode: 'class'` in config | Default, no config needed | Manual toggle, user preference |
| Media (system) | `darkMode: 'media'` in config | `@variant dark (&:where(.dark, .dark *));` override | Simple, automatic |
| Custom selector | `darkMode: ['selector', '...']` | `@custom-variant dark (...)` | Advanced custom logic |
## Configuration
### v3 — `tailwind.config.js`
```js
// tailwind.config.js
module.exports = {
// Option 1: Class-based (recommended for most apps)
darkMode: 'class',
// Option 2: System preference
darkMode: 'media',
// Option 3: Custom selector (Tailwind 3.4.1+)
darkMode: ['selector', '[data-theme="dark"]'],
theme: {
extend: {
colors: {
background: {
light: '#ffffff',
dark: '#0f172a',
},
},
},
},
}
```
### v4 — CSS only, no config file
```css
@import "tailwindcss";
/* Dark mode is class-based by default in v4 — no config needed */
/* The dark: variant works out of the box */
/* To use a custom selector instead of .dark class: */
@custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));
/* To use media (system preference) strategy: */
/* @custom-variant dark (@media (prefers-color-scheme: dark)); */
```
## Basic Usage
```tsx
// Apply dark mode classes
<div className="bg-white dark:bg-gray-900">
<h1 className="text-gray-900 dark:text-white">
Title
</h1>
<p className="text-gray-600 dark:text-gray-400">
Description text
</p>
<button className="
bg-blue-600 hover:bg-blue-700
dark:bg-blue-500 dark:hover:bg-blue-600
text-white
">
Button
</button>
</div>
```
## Dark Mode Toggle (Class Strategy)
```tsx
// hooks/useDarkMode.ts
import { useEffect, useState } from 'react'
export function useDarkMode() {
const [isDark, setIsDark] = useState(() => {
if (typeof window === 'undefined') return false
// Check localStorage first
const stored = localStorage.getItem('theme')
if (stored) return stored === 'dark'
// Fall back to system preference
return window.matchMedia('(prefers-color-scheme: dark)').matches
})
useEffect(() => {
const root = document.documentElement
if (isDark) {
root.classList.add('dark')
localStorage.setItem('theme', 'dark')
} else {
root.classList.remove('dark')
localStorage.setItem('theme', 'light')
}
}, [isDark])
return { isDark, toggle: () => setIsDark(!isDark) }
}
// Component
function DarkModeToggle() {
const { isDark, toggle } = useDarkMode()
return (
<button
onClick={toggle}
className="p-2 rounded-lg bg-gray-100 dark:bg-gray-800"
aria-label="Toggle dark mode"
>
{isDark ? 'Light' : 'Dark'}
</button>
)
}
```
## Prevent Flash on Load
```html
<!-- Add to <head> before any stylesheets -->
<script>
// Prevent flash of wrong theme
(function() {
const theme = localStorage.getItem('theme')
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
if (theme === 'dark' || (!theme && prefersDark)) {
document.documentElement.classList.add('dark')
}
})()
</script>
```
## System Preference with Override
```tsx
type Theme = 'light' | 'dark' | 'system'
function useTheme() {
const [theme, setTheme] = useState<Theme>(() => {
return (localStorage.getItem('theme') as Theme) || 'system'
})
useEffect(() => {
const root = document.documentElement
const systemDark = window.matchMedia('(prefers-color-scheme: dark)')
const applyTheme = () => {
const isDark =
theme === 'dark' || (theme === 'system' && systemDark.matches)
root.classList.toggle('dark', isDark)
}
applyTheme()
// Listen for system changes when using 'system'
if (theme === 'system') {
systemDark.addEventListener('change', applyTheme)
return () => systemDark.removeEventListener('change', applyTheme)
}
}, [theme])
const setAndStore = (newTheme: Theme) => {
setTheme(newTheme)
localStorage.setItem('theme', newTheme)
}
return { theme, setTheme: setAndStore }
}
```
## Theme Selector UI
```tsx
function ThemeSelector() {
const { theme, setTheme } = useTheme()
return (
<select
value={theme}
onChange={(e) => setTheme(e.target.value as Theme)}
className="rounded border p-2 bg-white dark:bg-gray-800"
>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="system">System</option>
</select>
)
}
```
## Common Dark Mode Patterns
```tsx
// Borders
<div className="border border-gray-200 dark:border-gray-700">
// Shadows (less prominent in dark mode)
<div className="shadow-md dark:shadow-gray-900/50">
// Hover states
<button className="
hover:bg-gray-100 dark:hover:bg-gray-800
">
// Focus rings
<input className="
focus:ring-blue-500 dark:focus:ring-blue-400
focus:ring-offset-2 dark:focus:ring-offset-gray-900
">
```
## v4: Using @variant in Custom CSS
In v4, nest dark mode inside custom CSS with `@variant`:
```css
.card {
background: white;
color: #1e293b;
@variant dark {
background: #1e293b;
color: #f1f5f9;
}
}
```
## Impact
- Better user experience
- Reduces eye strain in dark environments
- Professional, modern look
- Accessibility improvement
rules/dark-transitions.md
---
id: dark-transitions
title: Dark Mode Transitions
priority: MEDIUM
category: Dark Mode
---
# Dark Mode Transitions
Add smooth transitions when switching between light and dark modes for a polished user experience.
## Bad Example
```html
<!-- No transition - jarring instant change -->
<div class="bg-white dark:bg-gray-900 text-black dark:text-white">
Content flashes when theme changes
</div>
<!-- Transitioning everything (performance issue) -->
<div class="transition-all duration-500 bg-white dark:bg-gray-900">
Transitioning all properties is expensive
</div>
<!-- Inconsistent transition timing -->
<div class="transition duration-100 bg-white dark:bg-gray-900">
<p class="transition duration-500 text-black dark:text-white">
Different speeds feel disjointed
</p>
</div>
```
## Good Example
```html
<!-- Targeted transitions for theme changes -->
<div class="transition-colors duration-200 bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
Smooth color transition
</div>
<!-- Global theme transition on body -->
<body class="transition-colors duration-300 bg-white dark:bg-gray-950">
<!-- All children inherit the smooth background change -->
</body>
<!-- Component with multiple color transitions -->
<div class="
transition-colors duration-200
bg-white dark:bg-gray-800
border border-gray-200 dark:border-gray-700
shadow-sm dark:shadow-gray-900/20
">
<h2 class="transition-colors duration-200 text-gray-900 dark:text-white">
Title
</h2>
<p class="transition-colors duration-200 text-gray-600 dark:text-gray-400">
Description
</p>
</div>
<!-- Using CSS custom property for consistent timing -->
<style>
:root {
--theme-transition: 200ms;
}
</style>
<div class="transition-colors duration-[--theme-transition] bg-white dark:bg-gray-900">
Consistent timing via CSS variable
</div>
```
## Why
1. **Polished UX**: Smooth transitions feel more professional and intentional.
2. **Reduced visual jarring**: Users don't experience harsh color flashing.
3. **Consistent behavior**: All elements change at the same rate.
4. **Performance optimized**: Transitioning only colors is GPU-efficient.
5. **Accessibility**: Gradual changes are easier on the eyes.
## Best Practices
### 1. Use `transition-colors` Not `transition-all`
```html
<!-- Good: specific property -->
<div class="transition-colors duration-200">...</div>
<!-- Avoid: all properties -->
<div class="transition-all duration-200">...</div>
```
### 2. Consistent Duration
```html
<!-- Recommended: 150-300ms for theme transitions -->
<div class="transition-colors duration-200">...</div>
```
### 3. Apply at the Component Level
```html
<!-- Apply transition to the component root -->
<article class="transition-colors duration-200 bg-white dark:bg-gray-800 rounded-lg p-6">
<h3 class="text-gray-900 dark:text-white">Title</h3>
<p class="text-gray-600 dark:text-gray-300">Content</p>
</article>
```
## Global Theme Transition
```css
/* globals.css */
@layer base {
* {
@apply transition-colors duration-200;
}
}
```
Or more selectively:
```css
@layer base {
body,
header,
main,
footer,
nav,
article,
section,
aside,
.card,
.btn {
@apply transition-colors duration-200;
}
}
```
## Disabling Transitions During Load
Prevent flash of animated content on page load:
```html
<script>
// Add no-transition class before paint
document.documentElement.classList.add('no-transition');
// Remove after a tick
window.addEventListener('load', () => {
requestAnimationFrame(() => {
document.documentElement.classList.remove('no-transition');
});
});
</script>
```
```css
.no-transition,
.no-transition * {
transition: none !important;
}
```
## React Implementation
```jsx
// useThemeTransition.js
import { useEffect, useLayoutEffect } from 'react';
export function useThemeTransition() {
useLayoutEffect(() => {
// Disable transitions on initial load
document.documentElement.classList.add('no-transition');
// Re-enable after paint
const timer = requestAnimationFrame(() => {
document.documentElement.classList.remove('no-transition');
});
return () => cancelAnimationFrame(timer);
}, []);
}
```
## Excluding Elements from Transition
```html
<!-- Some elements shouldn't transition (e.g., images) -->
<img class="transition-none" src="photo.jpg" alt="">
<!-- Code blocks shouldn't animate -->
<pre class="transition-none">
<code>...</code>
</pre>
```
## Accessibility Considerations
Respect reduced motion preferences:
```css
@media (prefers-reduced-motion: reduce) {
* {
transition-duration: 0.01ms !important;
}
}
```
Or in Tailwind:
```html
<div class="transition-colors duration-200 motion-reduce:transition-none">
Respects reduced motion preference
</div>
```
rules/resp-mobile-first.md
---
id: resp-mobile-first
title: Mobile-First Responsive Design
priority: CRITICAL
category: Responsive Design
---
# Mobile-First Responsive Design
## Why It Matters
Tailwind is mobile-first by default. Unprefixed utilities apply to all screen sizes, and prefixed utilities (sm:, md:, lg:) apply at that breakpoint and up. Understanding this prevents confusion and produces cleaner code.
## Breakpoints
| Prefix | Min-Width | CSS |
|--------|-----------|-----|
| (none) | 0px | Default (mobile) |
| sm: | 640px | @media (min-width: 640px) |
| md: | 768px | @media (min-width: 768px) |
| lg: | 1024px | @media (min-width: 1024px) |
| xl: | 1280px | @media (min-width: 1280px) |
| 2xl: | 1536px | @media (min-width: 1536px) |
## Incorrect
```tsx
// ❌ Desktop-first thinking (confusing)
<div className="w-1/4 lg:w-1/3 md:w-1/2 sm:w-full">
// Hard to understand, order matters differently than expected
</div>
// ❌ Overriding mobile styles at every breakpoint
<div className="hidden sm:block md:block lg:block xl:block">
// Redundant - sm:block already applies to all larger screens
</div>
// ❌ Not starting with mobile
<div className="md:flex md:items-center">
// What happens on mobile? (default: display: block)
</div>
```
## Correct
```tsx
// ✅ Mobile-first: base styles first, then add breakpoints
<div className="
w-full // Mobile: full width
sm:w-1/2 // Small+: half width
md:w-1/3 // Medium+: third width
lg:w-1/4 // Large+: quarter width
">
Content
</div>
// ✅ Clear progression
<div className="
flex flex-col // Mobile: stack vertically
md:flex-row // Medium+: row layout
">
<div>Item 1</div>
<div>Item 2</div>
</div>
// ✅ Only specify changes at breakpoints
<div className="block sm:hidden">
Mobile only
</div>
<div className="hidden sm:block">
Tablet and up
</div>
```
## Common Patterns
### Responsive Grid
```tsx
// Cards: 1 col → 2 col → 3 col → 4 col
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
{items.map(item => <Card key={item.id} />)}
</div>
```
### Responsive Typography
```tsx
<h1 className="
text-2xl // Mobile
md:text-3xl // Tablet
lg:text-4xl // Desktop
xl:text-5xl // Large desktop
">
Heading
</h1>
```
### Responsive Spacing
```tsx
<section className="
px-4 py-8 // Mobile: smaller padding
md:px-8 md:py-12 // Tablet: medium padding
lg:px-16 lg:py-16 // Desktop: larger padding
">
Content
</section>
```
### Responsive Layout
```tsx
// Stack on mobile, side-by-side on larger screens
<div className="flex flex-col lg:flex-row gap-8">
<aside className="w-full lg:w-64 lg:flex-shrink-0">
Sidebar
</aside>
<main className="flex-1">
Main content
</main>
</div>
```
### Responsive Hide/Show
```tsx
// Mobile navigation (hamburger menu)
<nav className="md:hidden">
<button>Menu</button>
</nav>
// Desktop navigation
<nav className="hidden md:flex space-x-4">
<a href="/">Home</a>
<a href="/about">About</a>
</nav>
```
## Thinking Process
1. **Start with mobile design** - What should this look like on a phone?
2. **Add tablet changes** - At md:, what needs to change?
3. **Add desktop changes** - At lg:, what needs to change?
4. **Only add what changes** - Don't repeat styles that stay the same
## Impact
- Cleaner, more maintainable code
- Smaller CSS output
- Better mental model for responsive design
- Easier to debug responsive issues
rules/responsive-aspect-ratio.md
---
id: responsive-aspect-ratio
title: Responsive Aspect Ratios
priority: CRITICAL
category: Responsive Design
---
# Responsive Aspect Ratios
Use Tailwind's aspect ratio utilities to maintain consistent proportions for images, videos, and containers across different screen sizes.
## Bad Example
```html
<!-- Hardcoded padding hack (outdated approach) -->
<div class="relative h-0 pb-[56.25%]">
<img class="absolute inset-0 w-full h-full object-cover" src="image.jpg">
</div>
<!-- Fixed dimensions that break responsiveness -->
<video class="w-640 h-360" src="video.mp4"></video>
<!-- Inconsistent aspect ratios at different breakpoints -->
<div class="h-48 md:h-64 lg:h-96 w-full">
<img class="w-full h-full object-cover" src="image.jpg">
</div>
```
## Good Example
```html
<!-- Native aspect ratio utility -->
<div class="aspect-video">
<img class="w-full h-full object-cover" src="image.jpg">
</div>
<!-- Responsive aspect ratios -->
<div class="aspect-square md:aspect-video lg:aspect-[21/9]">
<video class="w-full h-full object-cover" src="video.mp4"></video>
</div>
<!-- Image with automatic aspect ratio -->
<img class="aspect-[4/3] w-full object-cover" src="image.jpg" alt="">
<!-- Card with consistent thumbnail ratio -->
<article class="overflow-hidden rounded-lg">
<div class="aspect-[3/2]">
<img class="w-full h-full object-cover" src="thumbnail.jpg" alt="">
</div>
<div class="p-4">
<h3>Card Title</h3>
</div>
</article>
<!-- Iframe embed with aspect ratio -->
<div class="aspect-video w-full">
<iframe class="w-full h-full" src="https://youtube.com/embed/..." allowfullscreen></iframe>
</div>
```
## Why
1. **Prevents layout shift**: Content reserves space before loading, improving CLS (Cumulative Layout Shift).
2. **Consistent design**: Maintains proportions across different content and screen sizes.
3. **Simpler markup**: Native `aspect-ratio` CSS property is cleaner than padding hacks.
4. **Responsive flexibility**: Easily change ratios at different breakpoints.
5. **Better performance**: Browser can allocate space before images load.
## Built-in Aspect Ratios
| Class | Ratio | Use Case |
|-------|-------|----------|
| `aspect-auto` | auto | Native image/video ratio |
| `aspect-square` | 1/1 | Profile pictures, icons |
| `aspect-video` | 16/9 | Videos, presentations |
## Custom Aspect Ratios
```html
<!-- Common aspect ratios -->
<div class="aspect-[4/3]">Standard photo</div>
<div class="aspect-[3/2]">Classic photo</div>
<div class="aspect-[21/9]">Ultrawide/cinema</div>
<div class="aspect-[9/16]">Mobile/portrait video</div>
<div class="aspect-[1/1.414]">A4 paper ratio</div>
<!-- Decimal ratios -->
<div class="aspect-[1.618/1]">Golden ratio</div>
```
## Configuration
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
aspectRatio: {
'4/3': '4 / 3',
'3/2': '3 / 2',
'21/9': '21 / 9',
'golden': '1.618 / 1',
},
},
},
}
```
## Responsive Pattern
```html
<!-- Portrait on mobile, landscape on desktop -->
<div class="aspect-[9/16] sm:aspect-square md:aspect-video lg:aspect-[21/9]">
<img class="w-full h-full object-cover" src="hero.jpg" alt="">
</div>
```
rules/responsive-breakpoint-order.md
---
id: responsive-breakpoint-order
title: Responsive Breakpoint Order
priority: CRITICAL
category: Responsive Design
---
# Responsive Breakpoint Order
Maintain consistent breakpoint ordering in class names for readability and predictability.
## Bad Example
```html
<!-- Random breakpoint order (hard to read) -->
<div class="lg:px-8 px-4 xl:px-12 md:px-6 sm:px-5">
Content
</div>
<!-- Mixed ordering makes it hard to understand responsive behavior -->
<h1 class="xl:text-5xl text-2xl md:text-3xl sm:text-2xl lg:text-4xl">
Heading
</h1>
<!-- Inconsistent grouping -->
<div class="w-full md:w-1/2 p-4 lg:w-1/3 md:p-6 lg:p-8">
Card
</div>
```
## Good Example
```html
<!-- Ascending breakpoint order (mobile to desktop) -->
<div class="px-4 sm:px-5 md:px-6 lg:px-8 xl:px-12">
Content
</div>
<!-- Clear progression from smallest to largest -->
<h1 class="text-2xl sm:text-2xl md:text-3xl lg:text-4xl xl:text-5xl">
Heading
</h1>
<!-- Group by property, then order by breakpoint -->
<div class="w-full md:w-1/2 lg:w-1/3 p-4 md:p-6 lg:p-8">
Card
</div>
```
## Why
1. **Predictable scanning**: Developers can quickly scan left-to-right to understand how styles change across breakpoints.
2. **Easier debugging**: When breakpoints are ordered, it's clear which style applies at each screen size.
3. **Consistent team conventions**: A standard order ensures all team members write classes the same way.
4. **Mirrors CSS cascade**: The left-to-right order matches how CSS media queries would naturally be written.
5. **Automated tooling support**: Tools like Prettier with tailwind plugin automatically sort classes this way.
## Recommended Class Order
```html
<div class="
[base styles]
[sm: styles]
[md: styles]
[lg: styles]
[xl: styles]
[2xl: styles]
">
```
## Using Prettier Plugin
Install `prettier-plugin-tailwindcss` to automatically sort classes:
```bash
npm install -D prettier prettier-plugin-tailwindcss
```
```json
// .prettierrc
{
"plugins": ["prettier-plugin-tailwindcss"]
}
```
rules/responsive-container-queries.md
---
id: responsive-container-queries
title: Container Queries
priority: CRITICAL
category: Responsive Design
---
# Container Queries
Use container queries to create components that respond to their parent container's size rather than the viewport.
## Bad Example
```html
<!-- Using viewport-based breakpoints for component layout -->
<div class="card">
<div class="flex flex-col md:flex-row">
<img class="w-full md:w-1/3" src="image.jpg" alt="">
<div class="p-4 md:p-6">
<h2 class="text-lg md:text-xl">Title</h2>
<p class="text-sm md:text-base">Description</p>
</div>
</div>
</div>
<!-- Component breaks when placed in narrow sidebar -->
<aside class="w-64">
<!-- Card still uses md: breakpoint based on viewport, not container -->
<div class="card">...</div>
</aside>
```
## Good Example
```html
<!-- Define a container -->
<div class="@container">
<div class="flex flex-col @md:flex-row">
<img class="w-full @md:w-1/3" src="image.jpg" alt="">
<div class="p-4 @lg:p-6">
<h2 class="text-lg @md:text-xl">Title</h2>
<p class="text-sm @md:text-base">Description</p>
</div>
</div>
</div>
<!-- Named containers for nested queries -->
<div class="@container/sidebar">
<div class="@container/card">
<div class="@lg/card:flex-row @xl/sidebar:gap-8">
Content adapts to both containers
</div>
</div>
</div>
<!-- Inline size container (width only) -->
<div class="@container/main inline-size">
<article class="@sm/main:columns-2 @lg/main:columns-3">
Multi-column text
</article>
</div>
```
## Why
1. **True component reusability**: Components adapt to their container regardless of where they're placed in the layout.
2. **Sidebar-aware components**: Cards and widgets behave correctly in narrow sidebars without viewport hacks.
3. **Design system flexibility**: Build components once that work in any context.
4. **Better composition**: Nested components can each respond to their own container.
5. **Future-proof**: Container queries are the modern approach to responsive components.
## Container Query Breakpoints
| Prefix | Min-width |
|--------|-----------|
| `@xs` | 20rem (320px) |
| `@sm` | 24rem (384px) |
| `@md` | 28rem (448px) |
| `@lg` | 32rem (512px) |
| `@xl` | 36rem (576px) |
| `@2xl` | 42rem (672px) |
| `@3xl` | 48rem (768px) |
| `@4xl` | 56rem (896px) |
| `@5xl` | 64rem (1024px) |
| `@6xl` | 72rem (1152px) |
| `@7xl` | 80rem (1280px) |
## Configuration
### v3 — requires plugin
```bash
npm install @tailwindcss/container-queries
```
```js
// tailwind.config.js
module.exports = {
plugins: [require('@tailwindcss/container-queries')],
theme: {
containers: {
'xs': '20rem',
'sm': '24rem',
'md': '28rem',
'lg': '32rem',
'xl': '36rem',
// Add custom sizes
'prose': '65ch',
},
},
}
```
### v4 — built into core, no plugin needed
Container queries are native in v4. No plugin to install:
```css
@import "tailwindcss";
/* Custom container sizes via @theme */
@theme {
--container-prose: 65ch;
}
```
v4 also adds range variants for container queries:
```html
<!-- Min-width container query (same as v3) -->
<div class="@container">
<div class="@md:flex-row flex-col">...</div>
</div>
<!-- Max-width container query (v4 only) -->
<div class="@container">
<div class="@max-sm:flex-col">Only stacks below @sm</div>
</div>
<!-- Range: between two sizes (v4 only) -->
<div class="@container">
<div class="@min-sm:@max-lg:grid-cols-2">2 cols between sm and lg</div>
</div>
```
rules/responsive-fluid-typography.md
---
id: responsive-fluid-typography
title: Fluid Typography
priority: CRITICAL
category: Responsive Design
---
# Fluid Typography
Use fluid typography that scales smoothly between breakpoints rather than jumping between fixed sizes.
## Bad Example
```html
<!-- Abrupt size jumps at breakpoints -->
<h1 class="text-2xl md:text-4xl lg:text-6xl">
Heading with jarring size changes
</h1>
<!-- Fixed sizes that don't adapt smoothly -->
<p class="text-sm md:text-base lg:text-lg">
Body text with noticeable jumps
</p>
<!-- Inconsistent scaling ratios -->
<article>
<h1 class="text-3xl md:text-5xl">Title</h1> <!-- 1.67x jump -->
<h2 class="text-xl md:text-2xl">Subtitle</h2> <!-- 1.2x jump -->
<p class="text-base md:text-lg">Content</p> <!-- 1.125x jump -->
</article>
```
## Good Example
```html
<!-- Using clamp() for fluid typography -->
<h1 class="text-[clamp(1.5rem,4vw,3rem)]">
Smoothly scaling heading
</h1>
<!-- Tailwind v4 fluid type scale -->
<h1 class="text-4xl/fluid">
Fluid heading
</h1>
<!-- Custom fluid sizes with arbitrary values -->
<p class="text-[clamp(1rem,2.5vw,1.25rem)]">
Body text that scales smoothly
</p>
<!-- Fluid typography with consistent scale -->
<article>
<h1 class="text-[clamp(2rem,5vw+1rem,4rem)]">Title</h1>
<h2 class="text-[clamp(1.5rem,3vw+0.5rem,2.5rem)]">Subtitle</h2>
<p class="text-[clamp(1rem,1vw+0.75rem,1.25rem)]">Content</p>
</article>
<!-- Using CSS custom properties for maintainability -->
<div class="[--fluid-min:1rem] [--fluid-max:1.5rem]">
<p class="text-[clamp(var(--fluid-min),3vw,var(--fluid-max))]">
Configurable fluid text
</p>
</div>
```
## Why
1. **Smooth user experience**: No jarring size changes when resizing the browser or rotating devices.
2. **Better readability**: Text size adjusts naturally to available space.
3. **Reduced breakpoints**: One fluid declaration replaces multiple breakpoint-specific sizes.
4. **Accessibility**: Users with various screen sizes get appropriately sized text.
5. **Cleaner code**: Less class clutter from multiple responsive variants.
## Clamp() Formula
```
clamp(minimum, preferred, maximum)
```
- **minimum**: Smallest the text should be
- **preferred**: Ideal size (usually viewport-relative)
- **maximum**: Largest the text should be
## Common Fluid Typography Scale
```html
<!-- Headings -->
<h1 class="text-[clamp(2.25rem,6vw,4.5rem)]">Display</h1>
<h2 class="text-[clamp(1.875rem,5vw,3rem)]">H1</h2>
<h3 class="text-[clamp(1.5rem,4vw,2.25rem)]">H2</h3>
<h4 class="text-[clamp(1.25rem,3vw,1.875rem)]">H3</h4>
<h5 class="text-[clamp(1.125rem,2.5vw,1.5rem)]">H4</h5>
<!-- Body -->
<p class="text-[clamp(1rem,1.5vw,1.125rem)]">Body large</p>
<p class="text-[clamp(0.875rem,1.25vw,1rem)]">Body</p>
<p class="text-[clamp(0.75rem,1vw,0.875rem)]">Small</p>
```
## Configuration Extension
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
fontSize: {
'fluid-sm': 'clamp(0.875rem, 1.5vw, 1rem)',
'fluid-base': 'clamp(1rem, 2vw, 1.25rem)',
'fluid-lg': 'clamp(1.25rem, 3vw, 1.875rem)',
'fluid-xl': 'clamp(1.5rem, 4vw, 2.5rem)',
'fluid-2xl': 'clamp(2rem, 5vw, 3.5rem)',
'fluid-3xl': 'clamp(2.5rem, 6vw, 4.5rem)',
},
},
},
}
```
rules/responsive-grid-system.md
---
id: responsive-grid-system
title: Responsive Grid System
priority: CRITICAL
category: Responsive Design
---
# Responsive Grid System
Use CSS Grid with Tailwind for flexible, responsive layouts that adapt to different screen sizes and content needs.
## Bad Example
```html
<!-- Using floats (outdated) -->
<div class="clearfix">
<div class="float-left w-1/3">Column 1</div>
<div class="float-left w-1/3">Column 2</div>
<div class="float-left w-1/3">Column 3</div>
</div>
<!-- Fixed column widths that don't adapt -->
<div class="flex">
<div class="w-64">Sidebar</div>
<div class="w-[calc(100%-16rem)]">Content</div>
</div>
<!-- Too many breakpoint-specific classes -->
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5 2xl:grid-cols-6">
<!-- Items -->
</div>
```
## Good Example
```html
<!-- Auto-fit grid that adapts to available space -->
<div class="grid grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-6">
<div>Card 1</div>
<div>Card 2</div>
<div>Card 3</div>
<div>Card 4</div>
</div>
<!-- Responsive 12-column grid -->
<div class="grid grid-cols-12 gap-4">
<aside class="col-span-12 md:col-span-3 lg:col-span-2">
Sidebar
</aside>
<main class="col-span-12 md:col-span-9 lg:col-span-7">
Main content
</main>
<aside class="col-span-12 lg:col-span-3">
Secondary sidebar
</aside>
</div>
<!-- Named grid areas for complex layouts -->
<div class="grid grid-cols-1 md:grid-cols-[200px_1fr_200px] grid-rows-[auto_1fr_auto] min-h-screen gap-4">
<header class="md:col-span-3 bg-gray-100">Header</header>
<nav class="bg-gray-200">Navigation</nav>
<main class="bg-white">Main Content</main>
<aside class="bg-gray-200">Sidebar</aside>
<footer class="md:col-span-3 bg-gray-100">Footer</footer>
</div>
<!-- Fluid grid with minimum item size -->
<div class="grid grid-cols-[repeat(auto-fill,minmax(min(100%,300px),1fr))] gap-6">
<!-- Cards that never break on small screens -->
</div>
```
## Why
1. **True two-dimensional layout**: Grid handles both rows and columns simultaneously.
2. **Content-aware sizing**: `auto-fit` and `auto-fill` create responsive grids without breakpoints.
3. **Gap management**: `gap` utilities handle spacing consistently without margin hacks.
4. **Alignment control**: Easy vertical and horizontal alignment with `place-items` and `place-content`.
5. **Named lines and areas**: Complex layouts become readable and maintainable.
## Grid Patterns
### Auto-Responsive Cards
```html
<div class="grid grid-cols-[repeat(auto-fit,minmax(280px,1fr))] gap-4">
<!-- Cards automatically wrap and resize -->
</div>
```
### Holy Grail Layout
```html
<div class="grid grid-cols-1 md:grid-cols-[250px_1fr_250px] grid-rows-[auto_1fr_auto] min-h-screen">
<header class="md:col-span-3">Header</header>
<nav>Nav</nav>
<main>Content</main>
<aside>Sidebar</aside>
<footer class="md:col-span-3">Footer</footer>
</div>
```
### Masonry-like Grid
```html
<div class="columns-1 sm:columns-2 lg:columns-3 xl:columns-4 gap-4 space-y-4">
<div class="break-inside-avoid">Item 1</div>
<div class="break-inside-avoid">Item 2</div>
<!-- Items of varying heights -->
</div>
```
### Feature Grid
```html
<div class="grid grid-cols-2 md:grid-cols-4 gap-4">
<div class="col-span-2 row-span-2">Featured</div>
<div>Item 1</div>
<div>Item 2</div>
<div>Item 3</div>
<div>Item 4</div>
</div>
```
## Configuration
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
gridTemplateColumns: {
'sidebar': '250px 1fr',
'sidebar-right': '1fr 300px',
'footer': '200px minmax(900px, 1fr) 200px',
},
gridTemplateRows: {
'layout': 'auto 1fr auto',
},
},
},
}
```
rules/v4-custom-utilities.md
---
id: v4-custom-utilities
title: V4 Custom Utilities and Variants
priority: HIGH
category: V4 & Migration
---
## Why It Matters
Tailwind v4 replaces JavaScript plugins with CSS-native directives: `@utility` for custom utility classes and `@custom-variant` for custom variants. No `plugin()` API, no JavaScript — everything lives in your CSS file.
## Incorrect
```js
// ❌ v3 plugin API — does not work in v4
const plugin = require('tailwindcss/plugin')
module.exports = {
plugins: [
plugin(function ({ addUtilities, addVariant }) {
addUtilities({
'.scrollbar-hide': { '-ms-overflow-style': 'none', 'scrollbar-width': 'none' },
})
addVariant('hocus', ['&:hover', '&:focus'])
}),
],
}
```
## Correct
### Custom utilities with @utility
```css
@import "tailwindcss";
/* Simple custom utility */
@utility scrollbar-hide {
-ms-overflow-style: none;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
/* Utility with variant support */
@utility text-balance {
text-wrap: balance;
}
```
```html
<!-- Use like any built-in utility -->
<div class="scrollbar-hide overflow-y-auto">
<p class="text-balance">
```
### Custom variants with @custom-variant
```css
@import "tailwindcss";
/* Combine hover + focus into one variant */
@custom-variant hocus (&:hover, &:focus);
/* Target elements inside a specific parent */
@custom-variant sidebar-open (.sidebar-open &);
/* Dark mode scoped to a class (if you need custom selector) */
@custom-variant dark (&:where(.dark, .dark *));
/* RTL support */
@custom-variant rtl ([dir="rtl"] &);
```
```html
<!-- hocus: applies on both hover and focus -->
<button class="hocus:bg-blue-600 hocus:text-white">
<!-- sidebar-open: applies when ancestor has .sidebar-open -->
<nav class="sidebar-open:translate-x-0 -translate-x-full">
```
### Using official plugins in v4
```bash
npm install @tailwindcss/typography @tailwindcss/forms
```
```css
/* app.css — import plugins directly in CSS */
@import "tailwindcss";
@plugin "@tailwindcss/typography";
@plugin "@tailwindcss/forms";
```
### Nesting variants in custom CSS with @variant
Use `@variant` to apply Tailwind variants inside custom CSS blocks:
```css
.my-element {
background: white;
/* Nest multiple variants */
@variant dark {
background: #1e293b;
@variant hover {
background: #334155;
}
}
/* Single variant */
@variant focus {
outline: 2px solid var(--color-blue-500);
}
}
```
### Replacing @layer components with @utility
In v3, custom component classes used `@layer components`. In v4, use `@utility` instead — it sorts based on property count so utilities can override it:
```css
/* ❌ v3 */
@layer components {
.btn {
border-radius: 0.5rem;
padding: 0.5rem 1rem;
background-color: ButtonFace;
}
}
/* ✅ v4 */
@utility btn {
border-radius: 0.5rem;
padding: 0.5rem 1rem;
background-color: ButtonFace;
}
```
### Importing without emitting CSS with @reference
Use `@reference` to access theme values and utilities for `@apply` without duplicating Tailwind's output. Useful in CSS files that are not your main entry point:
```css
/* components/card.css */
@reference "tailwindcss";
.card {
@apply rounded-lg bg-white shadow-sm p-6;
}
```
## Recommended Patterns
| v3 (JS plugin API) | v4 (CSS directive) |
|--------------------|--------------------|
| `addUtilities({ '.foo': {...} })` | `@utility foo { ... }` |
| `addVariant('bar', '...')` | `@custom-variant bar (...)` |
| `@layer components { .foo {...} }` | `@utility foo { ... }` |
| Nesting variants in JS | `@variant dark { @variant hover { ... } }` |
| `require('@tailwindcss/forms')` | `@plugin "@tailwindcss/forms"` |
| `require('@tailwindcss/typography')` | `@plugin "@tailwindcss/typography"` |
| `@import` for `@apply` access | `@reference "tailwindcss"` (no CSS output) |
Reference: https://tailwindcss.com/docs/adding-custom-styles
rules/v4-installation.md
---
id: v4-installation
title: V4 Installation & Setup
priority: HIGH
category: V4 & Migration
---
## Why It Matters
Tailwind CSS v4 ships as a dedicated Vite plugin (`@tailwindcss/vite`) or PostCSS plugin (`@tailwindcss/postcss`). The old `tailwindcss` + `postcss` + `autoprefixer` setup no longer applies. Configuration moves entirely out of JavaScript and into CSS via `@import "tailwindcss"`.
## Incorrect
```bash
# ❌ v3 install — wrong for v4
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
```
```js
// ❌ v3 postcss.config.js — not needed in v4 with Vite
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
```
```css
/* ❌ v3 directives — do not use in v4 */
@tailwind base;
@tailwind components;
@tailwind utilities;
```
## Correct
### With Vite (recommended)
```bash
npm install tailwindcss @tailwindcss/vite
```
```ts
// vite.config.ts
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})
```
```css
/* app.css — single import replaces all three @tailwind directives */
@import "tailwindcss";
```
### With PostCSS (Laravel Mix, Webpack, etc.)
```bash
npm install tailwindcss @tailwindcss/postcss
```
```js
// postcss.config.js
module.exports = {
plugins: {
'@tailwindcss/postcss': {},
},
}
```
```css
/* app.css */
@import "tailwindcss";
```
### Limiting scan scope with @source
By default, v4 auto-detects your template files. Override with `@source` if needed:
```css
@import "tailwindcss";
/* Only scan specific paths */
@source "./resources/js/**/*.{ts,tsx}";
@source "./resources/views/**/*.blade.php";
/* Exclude paths from scanning */
@source not "./resources/js/legacy/**";
```
### Importing without emitting CSS with @reference
Use `@reference` when you need access to theme values or `@apply` in a secondary CSS file without duplicating Tailwind's output:
```css
/* components/card.css — not the main entry point */
@reference "tailwindcss";
.card {
@apply rounded-lg bg-white shadow-sm dark:bg-gray-800;
}
```
## Recommended Patterns
| Scenario | Tool | Install |
|----------|------|---------|
| React + Vite | `@tailwindcss/vite` | `npm i tailwindcss @tailwindcss/vite` |
| Laravel + Vite | `@tailwindcss/vite` | `npm i tailwindcss @tailwindcss/vite` |
| PostCSS-based | `@tailwindcss/postcss` | `npm i tailwindcss @tailwindcss/postcss` |
| CLI only | `@tailwindcss/cli` | `npm i tailwindcss @tailwindcss/cli` |
Reference: https://tailwindcss.com/docs/installation
rules/v4-migration.md
---
id: v4-migration
title: Migrating from Tailwind v3 to v4
priority: HIGH
category: V4 & Migration
---
## Why It Matters
Tailwind v4 is a full rewrite with a CSS-first architecture. Most v3 projects can be upgraded automatically using the official upgrade tool, but understanding the breaking changes prevents surprises and helps when the tool cannot auto-migrate everything.
## Step 1 — Run the Upgrade Tool
Always start here. It handles most of the migration automatically:
```bash
npx @tailwindcss/upgrade
```
This will:
- Update dependencies (`tailwindcss`, add `@tailwindcss/vite` or `@tailwindcss/postcss`)
- Convert `tailwind.config.js` → `@theme {}` in CSS
- Rename deprecated utilities in template files
- Replace `@tailwind` directives with `@import "tailwindcss"`
> Requires Node.js 20+.
## Step 2 — Manual Changes
The upgrade tool handles most cases, but verify these manually:
### 1. Remove `tailwind.config.js` — use `@theme {}` in CSS
```js
// ❌ Before (v3)
module.exports = {
content: ['./resources/**/*.{blade.php,tsx}'],
darkMode: 'class',
theme: {
extend: {
colors: { brand: '#3b82f6' },
fontFamily: { display: ['Satoshi', 'sans-serif'] },
},
},
plugins: [require('@tailwindcss/forms')],
}
```
```css
/* ✅ After (v4) — app.css */
@import "tailwindcss";
@plugin "@tailwindcss/forms";
@theme {
--color-brand: #3b82f6;
--font-display: "Satoshi", sans-serif;
}
```
### 2. Replace `@tailwind` directives
```css
/* ❌ v3 */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* ✅ v4 */
@import "tailwindcss";
```
### 3. Renamed utilities — update in all template files
| v3 class | v4 class | Change |
|----------|----------|--------|
| `shadow-sm` | `shadow-xs` | Scale shifted down |
| `shadow` | `shadow-sm` | Bare → named |
| `shadow-md` | `shadow-md` | No change |
| `blur-sm` | `blur-xs` | Scale shifted down |
| `blur` | `blur-sm` | Bare → named |
| `rounded-sm` | `rounded-xs` | Scale shifted down |
| `rounded` | `rounded-sm` | Bare → named |
| `outline-none` | `outline-hidden` | Semantic rename |
| `ring` | `ring-3` | Bare → explicit width |
| `transform-none` | `scale-none` / `rotate-none` | Individual properties |
### 4. Transform utilities — individual properties
```html
<!-- ❌ v3 -->
<button class="scale-150 focus:transform-none">
<button class="transition-[opacity,transform] hover:scale-150">
<!-- ✅ v4 -->
<button class="scale-150 focus:scale-none">
<button class="transition-[opacity,scale] hover:scale-150">
```
### 5. Grid arbitrary values — underscores instead of commas
```html
<!-- ❌ v3 -->
<div class="grid-cols-[max-content,auto]">
<!-- ✅ v4 -->
<div class="grid-cols-[max-content_auto]">
```
### 6. Replace JS plugins with CSS directives
```js
// ❌ v3 — tailwind.config.js
plugins: [
plugin(({ addUtilities, addVariant }) => {
addUtilities({ '.scrollbar-hide': { 'scrollbar-width': 'none' } })
addVariant('hocus', ['&:hover', '&:focus'])
}),
]
```
```css
/* ✅ v4 — app.css */
@utility scrollbar-hide {
scrollbar-width: none;
}
@custom-variant hocus (&:hover, &:focus);
```
### 7. Replace `@layer components` with `@utility`
```css
/* ❌ v3 */
@layer components {
.btn { border-radius: 0.5rem; padding: 0.5rem 1rem; }
}
/* ✅ v4 — @utility sorts by property count for correct specificity */
@utility btn {
border-radius: 0.5rem;
padding: 0.5rem 1rem;
}
```
### 8. Plugins removed in v4 (built into core)
These v3 plugins are no longer needed — their features are native in v4:
| v3 Plugin | v4 Status |
|-----------|-----------|
| `@tailwindcss/aspect-ratio` | Native `aspect-*` utilities |
| `@tailwindcss/container-queries` | Native `@container` support with `@min-*`/`@max-*` range variants |
## Step 3 — New v4 Features to Adopt
These are new in v4 — not breaking changes, but worth adopting:
```html
<!-- Dynamic values — no config needed -->
<div class="grid-cols-15 px-17 w-23">
<!-- 3D transforms -->
<div class="perspective-distant rotate-x-12 transform-3d">
<!-- Composable variants -->
<div class="group-has-focus:opacity-100">
<!-- not-* variant -->
<div class="not-hover:opacity-50">
<!-- inert variant -->
<div class="inert:opacity-30">
<!-- field-sizing for auto-growing inputs -->
<textarea class="field-sizing-content">
<!-- starting: variant for CSS @starting-style (animate initial appearance) -->
<div popover class="transition-discrete starting:open:opacity-0 open:opacity-100">
<!-- forced-colors: variant for Windows High Contrast accessibility -->
<input type="checkbox" class="appearance-none forced-colors:appearance-auto">
<!-- color-mix for opacity modifiers (works with CSS variables) -->
<div class="bg-blue-500/50">
<!-- v4 uses color-mix(in oklab, ...) under the hood -->
<!-- @variant directive for nesting variants in custom CSS -->
```
```css
/* Nest variants in custom CSS */
.my-card {
background: white;
@variant dark {
background: #1e293b;
@variant hover {
background: #334155;
}
}
}
```
## Checklist
- [ ] Run `npx @tailwindcss/upgrade`
- [ ] Remove `tailwind.config.js` (or keep for remaining v3 projects)
- [ ] Replace `@tailwind` directives with `@import "tailwindcss"`
- [ ] Verify renamed utilities: shadow, blur, rounded, outline, ring
- [ ] Fix transform utilities: `transform-none` → individual resets
- [ ] Fix grid arbitrary values: commas → underscores
- [ ] Convert JS plugins to `@utility` / `@custom-variant` in CSS
- [ ] Switch official plugins to `@plugin` imports in CSS
- [ ] Remove `@tailwindcss/aspect-ratio` and `@tailwindcss/container-queries` plugins (now in core)
- [ ] Replace `@layer components` with `@utility` for custom component classes
- [ ] Run `npm run build` and check for warnings
Reference: https://tailwindcss.com/docs/upgrade-guide
rules/v4-theme-configuration.md
---
id: v4-theme-configuration
title: V4 Theme Configuration with @theme
priority: HIGH
category: V4 & Migration
---
## Why It Matters
In Tailwind v4, `tailwind.config.js` is gone. All theme customisation happens in CSS using the `@theme` directive. Values defined in `@theme` become both CSS custom properties and Tailwind utility classes automatically — no JavaScript configuration required.
## Incorrect
```js
// ❌ tailwind.config.js does not exist in v4
module.exports = {
theme: {
extend: {
colors: { brand: '#3b82f6' },
fontFamily: { display: ['Satoshi', 'sans-serif'] },
},
},
}
```
## Correct
### Extend the default theme
```css
/* app.css */
@import "tailwindcss";
@theme {
/* Colors — generates bg-brand-*, text-brand-*, border-brand-* etc. */
--color-brand-500: #3b82f6;
--color-brand-600: #2563eb;
/* Fonts — generates font-display, font-mono etc. */
--font-display: "Satoshi", sans-serif;
--font-sans: "Inter", sans-serif;
/* Breakpoints — generates 3xl:* variant */
--breakpoint-3xl: 1920px;
/* Spacing — generates p-18, m-18, w-18 etc. */
--spacing-18: 4.5rem;
/* Shadows — generates shadow-soft */
--shadow-soft: 0 4px 20px rgba(0, 0, 0, 0.08);
/* Custom animations */
--animate-fade-in: fade-in 0.3s ease-out;
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
}
```
### Override an entire namespace (remove defaults)
```css
@import "tailwindcss";
@theme {
/* Reset all default colors, then define only yours */
--color-*: initial;
--color-white: #fff;
--color-black: #000;
--color-primary: #3f3cbb;
--color-secondary: #121063;
}
```
### Use theme values in arbitrary utilities
```html
<!-- Generated utility class -->
<div class="bg-brand-500 font-display shadow-soft">
<!-- Or reference the CSS variable directly -->
<div class="bg-[var(--color-brand-500)]">
```
### Reference theme variables in custom CSS
```css
.my-component {
color: var(--color-brand-500);
font-family: var(--font-display);
padding: var(--spacing-18);
}
```
## Recommended Patterns
| v3 config | v4 @theme equivalent |
|-----------|---------------------|
| `colors.brand` | `--color-brand-*` |
| `fontFamily.display` | `--font-display` |
| `screens['3xl']` | `--breakpoint-3xl` |
| `spacing['18']` | `--spacing-18` |
| `boxShadow.soft` | `--shadow-soft` |
| `keyframes` + `animation` | `--animate-*` + `@keyframes` inside `@theme` |
Reference: https://tailwindcss.com/docs/theme
SKILL.md
---
name: tailwind-best-practices
description: Tailwind CSS patterns and conventions. Use when writing responsive designs, implementing dark mode, creating reusable component styles, configuring Tailwind, or migrating from v3 to v4. Triggers on tasks involving Tailwind classes, responsive design, dark mode, CSS styling, or "migrate to Tailwind v4".
license: MIT
metadata:
author: agent-skills
version: "1.0.0"
tailwindVersion: "3.4+ / 4.0+"
---
# Tailwind CSS Best Practices
Comprehensive patterns for building consistent, maintainable interfaces with Tailwind CSS v3.4+ and v4. Contains 29 rules covering responsive design, dark mode, component patterns, configuration, and v4 migration.
## Metadata
- **Version:** 1.0.0
- **Framework:** Tailwind CSS v3.4+ / v4.0+
- **Rule Count:** 29 rules across 8 categories
- **License:** MIT
- **Documentation:** [tailwindcss.com/docs](https://tailwindcss.com/docs)
## Step 1: Detect Tailwind Version
**Always check the version before giving any advice.** v3 and v4 are fundamentally different.
Check `package.json` for the installed version:
```json
{ "tailwindcss": "^3.x" } // → v3 rules apply
{ "tailwindcss": "^4.x" } // → v4 rules apply
```
Also check for these signals:
| Signal | Version |
|--------|---------|
| `tailwind.config.js` exists | v3 |
| `@import "tailwindcss"` in CSS | v4 |
| `@tailwindcss/vite` in dependencies | v4 |
| `@tailwindcss/postcss` in dependencies | v4 |
| `@theme {}` block in CSS | v4 |
**If v3**: Apply `resp-`, `dark-`, `comp-`, `config-` rules. Note that v4 is available.
**If v4**: Apply `v4-` rules. `tailwind.config.js` patterns do NOT apply — use `@theme {}` instead.
**If migrating v3 → v4**: Follow `v4-migration` rules directly.
## When to Apply
Reference these guidelines when:
- Writing responsive layouts
- Implementing dark mode
- Creating reusable component styles
- Configuring Tailwind (v3 or v4)
- Migrating a project from v3 to v4
- Setting up a new project with v4
## Rule Categories by Priority
| Priority | Category | Impact | Prefix | Version |
|----------|----------|--------|--------|---------|
| 1 | Responsive Design | CRITICAL | `resp-` | v3 / v4 |
| 2 | Dark Mode | CRITICAL | `dark-` | v3 / v4 |
| 3 | Component Patterns | HIGH | `comp-` | v3 / v4 |
| 4 | Custom Configuration | HIGH | `config-` | v3 |
| 5 | V4 & Migration | HIGH | `v4-` | v4 only |
| 6 | Spacing & Typography | MEDIUM | `space-` | v3 / v4 |
| 7 | Animation | MEDIUM | `anim-` | v3 / v4 |
| 8 | Performance | LOW | `perf-` | v3 / v4 |
## Quick Reference
### 1. Responsive Design (CRITICAL)
- `resp-mobile-first` - Mobile-first approach
- `resp-breakpoints` - Use breakpoints correctly
- `resp-container` - Container patterns
- `resp-grid-flex` - Grid vs Flexbox decisions
- `resp-hidden-shown` - Conditional display
### 2. Dark Mode (CRITICAL)
- `dark-setup` - Configure dark mode
- `dark-classes` - Apply dark mode classes
- `dark-toggle` - Implement dark mode toggle
- `dark-system-preference` - Respect system preference
- `dark-colors` - Design for both modes
### 3. Component Patterns (HIGH)
- `comp-clsx-cn` - Conditional classes utility
- `comp-variants` - Component variants pattern
- `comp-slots` - Slot-based components
- `comp-composition` - Composing utilities
### 4. Custom Configuration — v3 only (HIGH)
- `config-extend` - Extend vs override theme
- `config-colors` - Custom color palette
- `config-fonts` - Custom fonts
- `config-screens` - Custom breakpoints
- `config-plugins` - Using plugins
### 5. V4 & Migration (HIGH)
- `v4-installation` - Install v4 with Vite or PostCSS, `@source`, `@reference`
- `v4-theme-configuration` - Replace `tailwind.config.js` with `@theme {}` in CSS
- `v4-custom-utilities` - `@utility`, `@custom-variant`, `@variant`, `@plugin`
- `v4-migration` - Step-by-step v3 → v4 migration with renamed utilities, `starting:`, `forced-colors:`
### 6. Spacing & Typography (MEDIUM)
- `space-consistent` - Consistent spacing scale
- `space-margins` - Margin patterns
- `space-padding` - Padding patterns
- `typo-scale` - Typography scale
- `typo-line-height` - Line height
### 7. Animation (MEDIUM)
- `anim-transitions` - Transition utilities
- `anim-keyframes` - Custom keyframes
- `anim-reduced-motion` - Respect motion preferences
### 8. Performance (LOW)
- `perf-purge` - Content configuration
- `perf-jit` - JIT mode benefits
- `perf-arbitrary` - Arbitrary values usage
## Essential Patterns
### Mobile-First Responsive Design
```tsx
// ✅ Mobile-first: start with mobile, add larger breakpoints
<div className="
w-full // Mobile: full width
md:w-1/2 // Tablet: half width
lg:w-1/3 // Desktop: third width
">
<p className="
text-sm // Mobile: small text
md:text-base // Tablet: base text
lg:text-lg // Desktop: large text
">
Content
</p>
</div>
// ❌ Don't think desktop-first
<div className="w-1/3 md:w-1/2 sm:w-full"> // Confusing
```
### Dark Mode Implementation
**v3 — `tailwind.config.js`:**
```js
module.exports = { darkMode: 'class' }
```
**v4 — CSS only, no config file:**
```css
@import "tailwindcss";
/* dark mode is class-based by default in v4 — no config needed */
```
**Component — identical in both versions:**
```tsx
<div className="bg-white dark:bg-gray-900 text-gray-900 dark:text-white">
<h2 className="text-gray-900 dark:text-white">Title</h2>
<p className="text-gray-600 dark:text-gray-400">Description</p>
</div>
function toggleDarkMode() {
document.documentElement.classList.toggle('dark')
}
```
### Conditional Classes with clsx/cn
```tsx
import { clsx, type ClassValue } from 'clsx'
import { twMerge } from 'tailwind-merge'
// cn utility - merges Tailwind classes intelligently
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
// Usage
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'danger'
size?: 'sm' | 'md' | 'lg'
className?: string
children: React.ReactNode
}
function Button({ variant = 'primary', size = 'md', className, children }: ButtonProps) {
return (
<button
className={cn(
// Base styles
'inline-flex items-center justify-center rounded-md font-medium transition-colors',
'focus:outline-none focus:ring-2 focus:ring-offset-2',
// Variants
{
'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500':
variant === 'primary',
'bg-gray-100 text-gray-900 hover:bg-gray-200 focus:ring-gray-500':
variant === 'secondary',
'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500':
variant === 'danger',
},
// Sizes
{
'px-3 py-1.5 text-sm': size === 'sm',
'px-4 py-2 text-base': size === 'md',
'px-6 py-3 text-lg': size === 'lg',
},
// Allow override
className
)}
>
{children}
</button>
)
}
```
### Theme Configuration — v3 vs v4
**v3 — `tailwind.config.js`:**
```js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ['./resources/**/*.{blade.php,js,ts,jsx,tsx}'],
darkMode: 'class',
theme: {
extend: {
colors: { primary: { 500: '#0ea5e9', 600: '#0284c7' } },
fontFamily: { sans: ['Inter', 'sans-serif'] },
spacing: { '18': '4.5rem' },
},
},
plugins: [require('@tailwindcss/forms')],
}
```
**v4 — `app.css` only, no JS config:**
```css
@import "tailwindcss";
@theme {
--color-primary-500: #0ea5e9;
--color-primary-600: #0284c7;
--font-sans: Inter, sans-serif;
--spacing-18: 4.5rem;
--breakpoint-3xl: 1920px;
}
```
> See `v4-theme-configuration` and `v4-migration` rules for full details.
### Responsive Grid Layout
```tsx
// Product grid - responsive columns
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
// Dashboard layout - sidebar + main
<div className="flex flex-col lg:flex-row min-h-screen">
<aside className="
w-full lg:w-64
bg-gray-900
lg:min-h-screen
">
<nav>...</nav>
</aside>
<main className="flex-1 p-4 lg:p-8">
<div className="max-w-7xl mx-auto">
{children}
</div>
</main>
</div>
```
### Form Styling
```tsx
<form className="space-y-6">
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 dark:text-gray-300">
Email
</label>
<input
type="email"
id="email"
className="
mt-1 block w-full rounded-md
border-gray-300 dark:border-gray-600
bg-white dark:bg-gray-800
text-gray-900 dark:text-white
shadow-sm
focus:border-blue-500 focus:ring-blue-500
disabled:bg-gray-100 disabled:cursor-not-allowed
"
/>
</div>
<button
type="submit"
className="
w-full flex justify-center
py-2 px-4
border border-transparent rounded-md
shadow-sm text-sm font-medium
text-white bg-blue-600
hover:bg-blue-700
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500
disabled:opacity-50 disabled:cursor-not-allowed
"
>
Submit
</button>
</form>
```
### Animations with Reduced Motion
```tsx
// Respect user's motion preferences
<div className="
transition-transform duration-300
hover:scale-105
motion-reduce:transition-none
motion-reduce:hover:transform-none
">
Card content
</div>
// Custom animation
<div className="animate-fade-in motion-reduce:animate-none">
Content
</div>
```
```js
// tailwind.config.js
module.exports = {
theme: {
extend: {
keyframes: {
'fade-in': {
'0%': { opacity: '0' },
'100%': { opacity: '1' },
},
},
animation: {
'fade-in': 'fade-in 0.3s ease-out',
},
},
},
}
```
## How to Use
Always run Step 1 (version detection) first, then read the relevant rule files:
**v3 projects:**
```
rules/config-extend-theme.md
rules/dark-setup.md
rules/comp-clsx-cn.md
rules/resp-mobile-first.md
```
**v4 projects:**
```
rules/v4-installation.md
rules/v4-theme-configuration.md
rules/v4-custom-utilities.md
```
**Migrating v3 → v4:**
```
rules/v4-migration.md
```
## References
- [Tailwind CSS Documentation](https://tailwindcss.com/docs) - Official documentation
- [Responsive Design Guide](https://tailwindcss.com/docs/responsive-design) - Mobile-first patterns
- [Dark Mode Guide](https://tailwindcss.com/docs/dark-mode) - Theme implementation
- [Configuration Guide](https://tailwindcss.com/docs/configuration) - Customization
- [Tailwind UI](https://tailwindui.com) - Official component library
- [Headless UI](https://headlessui.com) - Accessible components
- [Heroicons](https://heroicons.com) - Icon library
## Ecosystem Tools
- **Tailwind CSS IntelliSense** - VS Code autocomplete and linting
- **Prettier Plugin** - Automatic class sorting
- **tailwind-merge** - Conflict-free class merging
- **clsx** - Conditional class utility
- **CVA** - Component variant system
## License
MIT License - See repository for full license text.
This skill is part of the Agent Skills collection, providing AI-powered development assistance with industry best practices.