--- name: tailwindcss-advanced-components description: Tailwind CSS advanced component patterns with CVA integration and variant management --- # Tailwind CSS Advanced Component Patterns ## Component Variants with CVA ### Class Variance Authority Integration ```bash npm install class-variance-authority ``` ```typescript // components/Button.tsx import { cva, type VariantProps } from 'class-variance-authority'; import { cn } from '@/lib/utils'; const buttonVariants = cva( // Base styles '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 disabled:pointer-events-none disabled:opacity-50', { variants: { variant: { default: 'bg-brand-500 text-white hover:bg-brand-600 focus-visible:ring-brand-500', secondary: 'bg-gray-100 text-gray-900 hover:bg-gray-200 focus-visible:ring-gray-500', outline: 'border border-gray-300 bg-transparent hover:bg-gray-100 focus-visible:ring-gray-500', ghost: 'hover:bg-gray-100 focus-visible:ring-gray-500', destructive: 'bg-red-500 text-white hover:bg-red-600 focus-visible:ring-red-500', link: 'text-brand-500 underline-offset-4 hover:underline', }, size: { sm: 'h-8 px-3 text-sm', md: 'h-10 px-4 text-sm', lg: 'h-12 px-6 text-base', xl: 'h-14 px-8 text-lg', icon: 'h-10 w-10', }, fullWidth: { true: 'w-full', }, }, compoundVariants: [ { variant: 'outline', size: 'sm', className: 'border', }, { variant: 'outline', size: ['md', 'lg', 'xl'], className: 'border-2', }, ], defaultVariants: { variant: 'default', size: 'md', }, } ); export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; } export function Button({ className, variant, size, fullWidth, asChild = false, ...props }: ButtonProps) { return ( ``` ## Compound Components Pattern ### Context-Based Component System ```tsx // components/Card/index.tsx import { createContext, useContext, forwardRef } from 'react'; import { cn } from '@/lib/utils'; // Context for shared state const CardContext = createContext<{ variant?: 'default' | 'elevated' | 'outline' }>({}); // Root component interface CardProps extends React.HTMLAttributes { variant?: 'default' | 'elevated' | 'outline'; } const Card = forwardRef( ({ className, variant = 'default', children, ...props }, ref) => { const variants = { default: 'bg-white border border-gray-200', elevated: 'bg-white shadow-lg', outline: 'bg-transparent border-2 border-gray-300', }; return (
{children}
); } ); // Sub-components const CardHeader = forwardRef>( ({ className, ...props }, ref) => (
) ); const CardTitle = forwardRef>( ({ className, ...props }, ref) => (

) ); const CardDescription = forwardRef>( ({ className, ...props }, ref) => (

) ); const CardContent = forwardRef>( ({ className, ...props }, ref) => (

) ); const CardFooter = forwardRef>( ({ className, ...props }, ref) => (
) ); // Named exports Card.displayName = 'Card'; CardHeader.displayName = 'CardHeader'; CardTitle.displayName = 'CardTitle'; CardDescription.displayName = 'CardDescription'; CardContent.displayName = 'CardContent'; CardFooter.displayName = 'CardFooter'; export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter }; ``` ### Usage ```tsx Account Settings Manage your account preferences
...
``` ## Data Attribute Variants ### CSS-Based State Management ```css /* Define data attribute variants */ @custom-variant data-state-open (&[data-state="open"]); @custom-variant data-state-closed (&[data-state="closed"]); @custom-variant data-side-top (&[data-side="top"]); @custom-variant data-side-bottom (&[data-side="bottom"]); @custom-variant data-side-left (&[data-side="left"]); @custom-variant data-side-right (&[data-side="right"]); @custom-variant data-highlighted (&[data-highlighted]); @custom-variant data-disabled (&[data-disabled]); ``` ```tsx // Dropdown component using data attributes function DropdownContent({ children, ...props }) { return (
{children}
); } function DropdownItem({ children, disabled, ...props }) { return (
{children}
); } ``` ## Group and Peer Patterns ### Complex State Propagation ```html

Title

Description

Card content
``` ## Slot Pattern with @apply ### Reusable Component Slots ```css @layer components { /* Base dialog structure */ .dialog { @apply fixed inset-0 z-50 flex items-center justify-center; } .dialog-overlay { @apply fixed inset-0 bg-black/50 backdrop-blur-sm; @apply data-state-open:animate-in data-state-open:fade-in-0; @apply data-state-closed:animate-out data-state-closed:fade-out-0; } .dialog-content { @apply relative z-50 w-full max-w-lg rounded-xl bg-white p-6 shadow-xl; @apply data-state-open:animate-in data-state-open:fade-in-0 data-state-open:zoom-in-95; @apply data-state-closed:animate-out data-state-closed:fade-out-0 data-state-closed:zoom-out-95; } .dialog-header { @apply flex flex-col gap-1.5 text-center sm:text-left; } .dialog-title { @apply text-lg font-semibold leading-none tracking-tight; } .dialog-description { @apply text-sm text-gray-500; } .dialog-footer { @apply flex flex-col-reverse gap-2 sm:flex-row sm:justify-end; } .dialog-close { @apply absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100; @apply focus:outline-none focus:ring-2 focus:ring-offset-2; } } ``` ## Polymorphic Components ### "as" Prop Pattern ```tsx import { forwardRef, ElementType, ComponentPropsWithoutRef } from 'react'; import { cn } from '@/lib/utils'; type PolymorphicRef = ComponentPropsWithoutRef['ref']; type PolymorphicComponentProps = Props & { as?: C; className?: string; children?: React.ReactNode; } & Omit, 'as' | 'className' | keyof Props>; // Text component that can be any element interface TextProps { size?: 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl'; weight?: 'normal' | 'medium' | 'semibold' | 'bold'; color?: 'default' | 'muted' | 'accent'; } type TextComponent = ( props: PolymorphicComponentProps & { ref?: PolymorphicRef } ) => React.ReactElement | null; export const Text: TextComponent = forwardRef( ( { as, size = 'base', weight = 'normal', color = 'default', className, children, ...props }: PolymorphicComponentProps, ref?: PolymorphicRef ) => { const Component = as || 'span'; const sizes = { xs: 'text-xs', sm: 'text-sm', base: 'text-base', lg: 'text-lg', xl: 'text-xl', '2xl': 'text-2xl', }; const weights = { normal: 'font-normal', medium: 'font-medium', semibold: 'font-semibold', bold: 'font-bold', }; const colors = { default: 'text-gray-900', muted: 'text-gray-500', accent: 'text-brand-500', }; return ( {children} ); } ); ``` ### Usage ```tsx Default span Large muted paragraph Bold heading Accent link ``` ## Headless Component Integration ### Headless UI with Tailwind ```tsx import { Dialog, Transition } from '@headlessui/react'; import { Fragment } from 'react'; function Modal({ isOpen, onClose, title, children }) { return ( {/* Backdrop */}
{title}
{children}
); } ``` ### Radix UI with Tailwind ```tsx import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; function Dropdown() { return ( Options Profile Delete ); } ``` ## Animation Patterns ### Staggered Animations ```tsx function StaggeredList({ items }) { return (
    {items.map((item, index) => (
  • {item.content}
  • ))}
); } ``` ### Skeleton Loading ```tsx function Skeleton({ className, ...props }) { return (
); } function CardSkeleton() { return (
); } ``` ## Best Practices ### 1. Use cn() Utility for Class Merging ```typescript // lib/utils.ts import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } ``` ### 2. Extract Common Patterns ```css @layer components { /* Consistent focus ring */ .focus-ring { @apply focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2; } /* Consistent disabled state */ .disabled-state { @apply disabled:pointer-events-none disabled:opacity-50; } /* Truncate text */ .truncate-lines-2 { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; } } ``` ### 3. Document Component APIs ```tsx /** * Button component with multiple variants * * @example * * * @example * */ export function Button({ ... }) { ... } ``` ### 4. Test Component Variants ```tsx // Button.test.tsx describe('Button', () => { it('renders all variants correctly', () => { const variants = ['default', 'secondary', 'outline', 'ghost', 'destructive']; variants.forEach(variant => { render(); // Assert classes are applied correctly }); }); }); ```