--- name: tailwind-config description: Update Tailwind CSS configuration, custom themes, and design tokens across packages. Use when adding new colors, spacing scales, or customizing the design system. allowed-tools: Read, Edit, Grep, Glob --- # Tailwind CSS Configuration Skill This skill helps you configure and customize Tailwind CSS across the monorepo. ## When to Use This Skill - Adding custom colors to the design system - Configuring typography scales - Customizing spacing and sizing - Setting up design tokens - Configuring Tailwind plugins - Debugging Tailwind configuration issues - Creating reusable theme presets ## Tailwind Configuration Structure ``` ├── packages/ui/ │ ├── tailwind.config.ts # UI package Tailwind config │ └── src/styles/globals.css # Global styles and CSS variables └── apps/web/ ├── tailwind.config.ts # Web app Tailwind config └── src/app/globals.css # App-specific global styles ``` ## Base Tailwind Configuration ### UI Package Configuration ```typescript // packages/ui/tailwind.config.ts import type { Config } from "tailwindcss"; const config: Config = { darkMode: ["class"], content: [ "./src/**/*.{ts,tsx}", ], theme: { container: { center: true, padding: "2rem", screens: { "2xl": "1400px", }, }, extend: { colors: { border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))", }, destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))", }, muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))", }, accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))", }, popover: { DEFAULT: "hsl(var(--popover))", foreground: "hsl(var(--popover-foreground))", }, card: { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, keyframes: { "accordion-down": { from: { height: "0" }, to: { height: "var(--radix-accordion-content-height)" }, }, "accordion-up": { from: { height: "var(--radix-accordion-content-height)" }, to: { height: "0" }, }, }, animation: { "accordion-down": "accordion-down 0.2s ease-out", "accordion-up": "accordion-up 0.2s ease-out", }, }, }, plugins: [require("tailwindcss-animate")], }; export default config; ``` ### App Configuration (Extending UI) ```typescript // apps/web/tailwind.config.ts import type { Config } from "tailwindcss"; import baseConfig from "@sgcarstrends/ui/tailwind.config"; const config: Config = { ...baseConfig, content: [ "./src/**/*.{ts,tsx}", "../../packages/ui/src/**/*.{ts,tsx}", // Include UI package components ], theme: { ...baseConfig.theme, extend: { ...(baseConfig.theme?.extend || {}), // App-specific extensions colors: { ...(baseConfig.theme?.extend?.colors || {}), brand: { 50: "#f0f9ff", 100: "#e0f2fe", 200: "#bae6fd", 300: "#7dd3fc", 400: "#38bdf8", 500: "#0ea5e9", 600: "#0284c7", 700: "#0369a1", 800: "#075985", 900: "#0c4a6e", }, }, }, }, }; export default config; ``` ## CSS Variables ### Global Styles with CSS Variables ```css /* packages/ui/src/styles/globals.css */ @tailwind base; @tailwind components; @tailwind utilities; @layer base { :root { --background: 0 0% 100%; --foreground: 222.2 84% 4.9%; --card: 0 0% 100%; --card-foreground: 222.2 84% 4.9%; --popover: 0 0% 100%; --popover-foreground: 222.2 84% 4.9%; --primary: 222.2 47.4% 11.2%; --primary-foreground: 210 40% 98%; --secondary: 210 40% 96.1%; --secondary-foreground: 222.2 47.4% 11.2%; --muted: 210 40% 96.1%; --muted-foreground: 215.4 16.3% 46.9%; --accent: 210 40% 96.1%; --accent-foreground: 222.2 47.4% 11.2%; --destructive: 0 84.2% 60.2%; --destructive-foreground: 210 40% 98%; --border: 214.3 31.8% 91.4%; --input: 214.3 31.8% 91.4%; --ring: 222.2 84% 4.9%; --radius: 0.5rem; } .dark { --background: 222.2 84% 4.9%; --foreground: 210 40% 98%; --card: 222.2 84% 4.9%; --card-foreground: 210 40% 98%; --popover: 222.2 84% 4.9%; --popover-foreground: 210 40% 98%; --primary: 210 40% 98%; --primary-foreground: 222.2 47.4% 11.2%; --secondary: 217.2 32.6% 17.5%; --secondary-foreground: 210 40% 98%; --muted: 217.2 32.6% 17.5%; --muted-foreground: 215 20.2% 65.1%; --accent: 217.2 32.6% 17.5%; --accent-foreground: 210 40% 98%; --destructive: 0 62.8% 30.6%; --destructive-foreground: 210 40% 98%; --border: 217.2 32.6% 17.5%; --input: 217.2 32.6% 17.5%; --ring: 212.7 26.8% 83.9%; } } @layer base { * { @apply border-border; } body { @apply bg-background text-foreground; } } ``` ## Common Customizations ### Adding Brand Colors ```typescript // tailwind.config.ts export default { theme: { extend: { colors: { brand: { primary: "#0070F3", secondary: "#7928CA", accent: "#FF0080", }, // Singapore-themed colors sg: { red: "#ED2939", white: "#FFFFFF", }, }, }, }, }; ``` Usage: ```tsx

SG Cars Trends

``` ### Custom Typography ```typescript // tailwind.config.ts export default { theme: { extend: { fontFamily: { sans: ["Inter", "system-ui", "sans-serif"], mono: ["Fira Code", "monospace"], }, fontSize: { "2xs": "0.625rem", // 10px xs: "0.75rem", // 12px sm: "0.875rem", // 14px base: "1rem", // 16px lg: "1.125rem", // 18px xl: "1.25rem", // 20px "2xl": "1.5rem", // 24px "3xl": "1.875rem", // 30px "4xl": "2.25rem", // 36px "5xl": "3rem", // 48px }, lineHeight: { tight: "1.2", normal: "1.5", relaxed: "1.75", }, }, }, }; ``` ### Custom Spacing ```typescript // tailwind.config.ts export default { theme: { extend: { spacing: { "4.5": "1.125rem", // 18px "13": "3.25rem", // 52px "15": "3.75rem", // 60px "128": "32rem", // 512px "144": "36rem", // 576px }, }, }, }; ``` ### Custom Breakpoints ```typescript // tailwind.config.ts export default { theme: { screens: { xs: "475px", sm: "640px", md: "768px", lg: "1024px", xl: "1280px", "2xl": "1536px", "3xl": "1920px", }, }, }; ``` ### Custom Animations ```typescript // tailwind.config.ts export default { theme: { extend: { keyframes: { shimmer: { "0%": { backgroundPosition: "-200% 0" }, "100%": { backgroundPosition: "200% 0" }, }, fadeIn: { "0%": { opacity: "0" }, "100%": { opacity: "1" }, }, slideUp: { "0%": { transform: "translateY(10px)", opacity: "0" }, "100%": { transform: "translateY(0)", opacity: "1" }, }, }, animation: { shimmer: "shimmer 2s linear infinite", fadeIn: "fadeIn 0.3s ease-out", slideUp: "slideUp 0.4s ease-out", }, }, }, }; ``` Usage: ```tsx
Loading...
``` ## Tailwind Plugins ### Typography Plugin ```bash pnpm add -D @tailwindcss/typography ``` ```typescript // tailwind.config.ts export default { plugins: [ require("@tailwindcss/typography"), ], }; ``` Usage: ```tsx

Blog Post Title

Content goes here...

``` ### Forms Plugin ```bash pnpm add -D @tailwindcss/forms ``` ```typescript // tailwind.config.ts export default { plugins: [ require("@tailwindcss/forms"), ], }; ``` ### Container Queries Plugin ```bash pnpm add -D @tailwindcss/container-queries ``` ```typescript // tailwind.config.ts export default { plugins: [ require("@tailwindcss/container-queries"), ], }; ``` Usage: ```tsx
{/* Content */}
``` ## Custom Utilities ### Adding Custom Utilities ```css /* packages/ui/src/styles/globals.css */ @layer utilities { .text-balance { text-wrap: balance; } .scrollbar-hide { -ms-overflow-style: none; scrollbar-width: none; } .scrollbar-hide::-webkit-scrollbar { display: none; } .glass { background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.2); } } ``` ### Plugin-Based Custom Utilities ```typescript // tailwind.config.ts import plugin from "tailwindcss/plugin"; export default { plugins: [ plugin(function ({ addUtilities }) { addUtilities({ ".no-scrollbar": { "-ms-overflow-style": "none", "scrollbar-width": "none", "&::-webkit-scrollbar": { display: "none", }, }, ".scrollbar-thin": { "scrollbar-width": "thin", }, }); }), ], }; ``` ## Design Tokens ### Creating a Shared Preset ```typescript // packages/ui/tailwind-preset.ts import type { Config } from "tailwindcss"; const preset: Partial = { darkMode: ["class"], theme: { extend: { colors: { border: "hsl(var(--border))", input: "hsl(var(--input))", ring: "hsl(var(--ring))", background: "hsl(var(--background))", foreground: "hsl(var(--foreground))", primary: { DEFAULT: "hsl(var(--primary))", foreground: "hsl(var(--primary-foreground))", }, secondary: { DEFAULT: "hsl(var(--secondary))", foreground: "hsl(var(--secondary-foreground))", }, destructive: { DEFAULT: "hsl(var(--destructive))", foreground: "hsl(var(--destructive-foreground))", }, muted: { DEFAULT: "hsl(var(--muted))", foreground: "hsl(var(--muted-foreground))", }, accent: { DEFAULT: "hsl(var(--accent))", foreground: "hsl(var(--accent-foreground))", }, popover: { DEFAULT: "hsl(var(--popover))", foreground: "hsl(var(--popover-foreground))", }, card: { DEFAULT: "hsl(var(--card))", foreground: "hsl(var(--card-foreground))", }, }, borderRadius: { lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", }, }, }, }; export default preset; ``` Use in apps: ```typescript // apps/web/tailwind.config.ts import preset from "@sgcarstrends/ui/tailwind-preset"; export default { presets: [preset], content: ["./src/**/*.{ts,tsx}"], theme: { extend: { // App-specific extensions }, }, }; ``` ## Dark Mode Configuration ### Class-Based Dark Mode ```typescript // tailwind.config.ts export default { darkMode: ["class"], }; ``` ### Theme Toggle Implementation ```tsx "use client"; import { useTheme } from "next-themes"; import { Button } from "@sgcarstrends/ui"; export function ThemeToggle() { const { theme, setTheme } = useTheme(); return ( ); } ``` ### Provider Setup ```bash pnpm add next-themes ``` ```tsx // app/providers.tsx "use client"; import { ThemeProvider } from "next-themes"; export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` ## Responsive Design Patterns ### Mobile-First Approach ```tsx // Default (mobile)
{/* Mobile styles */}
// Tablet and up
{/* Tablet styles */}
// Desktop and up
{/* Desktop styles */}
``` ### Container Queries ```tsx
{/* Responsive grid based on container */}
``` ## Performance Optimization ### Content Configuration ```typescript // tailwind.config.ts export default { content: [ "./src/**/*.{ts,tsx}", // Only include what's needed "../../packages/ui/src/**/*.{ts,tsx}", ], // Safelist classes that are generated dynamically safelist: [ "bg-red-500", "bg-green-500", "bg-blue-500", ], }; ``` ### Purge Unused Styles Tailwind automatically purges unused styles in production. For dynamic classes: ```typescript // ❌ Bad - Won't be detected
// ✅ Good - Use safelist or full class names
``` ## Debugging ### Enable JIT Debug Mode ```bash # See which classes are being generated TAILWIND_MODE=watch npx tailwindcss -i ./src/styles/globals.css -o ./dist/output.css --watch ``` ### Check Configuration ```bash # Inspect resolved config npx tailwindcss config ``` ### Common Issues **Classes not applying:** 1. Check content paths include all component files 2. Verify CSS is imported in app 3. Check for typos in class names 4. Ensure build process is running **Dark mode not working:** 1. Verify `darkMode: ["class"]` in config 2. Check ThemeProvider is set up 3. Ensure dark mode classes are in CSS **Purge removing needed classes:** 1. Add to safelist 2. Use complete class names (not dynamic) 3. Check content paths ## Testing Tailwind Classes ```typescript // __tests__/components/button.test.tsx import { render } from "@testing-library/react"; import { Button } from "@sgcarstrends/ui"; describe("Button Tailwind Classes", () => { it("applies variant classes correctly", () => { const { container } = render(); const button = container.querySelector("button"); // Check if destructive variant classes are applied expect(button?.className).toContain("bg-destructive"); }); it("applies size classes correctly", () => { const { container } = render(); const button = container.querySelector("button"); expect(button?.className).toContain("h-11"); }); }); ``` ## References - Tailwind CSS Documentation: https://tailwindcss.com - Related files: - `packages/ui/tailwind.config.ts` - UI package config - `apps/web/tailwind.config.ts` - Web app config - `packages/ui/src/styles/globals.css` - Global styles - `packages/ui/CLAUDE.md` - UI package documentation ## Best Practices 1. **Mobile-First**: Design for mobile, then add larger breakpoints 2. **CSS Variables**: Use CSS variables for theming 3. **Reusable Presets**: Share configuration via presets 4. **Content Paths**: Include all component paths 5. **Safelist Sparingly**: Only safelist when absolutely necessary 6. **Semantic Names**: Use meaningful color names (brand, accent, not blue-500) 7. **Dark Mode**: Support dark mode from the start 8. **Performance**: Minimize custom utilities, use Tailwind defaults 9. **Size Utility**: Use `size-*` instead of `h-* w-*` when dimensions are equal (Tailwind v3.4+) ### Size Utility Convention When an element needs equal height and width, use the `size-*` utility instead of separate `h-*` and `w-*` classes: ```tsx // ✅ Good - Use size-* for equal dimensions
Icon
Avatar
// ❌ Avoid - Redundant h-* and w-*
Icon
Avatar
// ✅ Good - Different dimensions still use h-* and w-*
Rectangle
Sidebar
``` **Common Use Cases:** - Icons: `size-4`, `size-5`, `size-6` - Avatars: `size-8`, `size-10`, `size-12` - Icon buttons: `size-8`, `size-10`, `size-12` - Loading spinners: `size-4`, `size-6`, `size-8` - Square containers: `size-16`, `size-24`, `size-32`