tailwindcss-advanced-components

Tailwind CSS Advanced Component Patterns

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "tailwindcss-advanced-components" with this command: npx skills add josiahsiegel/claude-plugin-marketplace/josiahsiegel-claude-plugin-marketplace-tailwindcss-advanced-components

Tailwind CSS Advanced Component Patterns

Component Variants with CVA

Class Variance Authority Integration

npm install class-variance-authority

// 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<HTMLButtonElement>, VariantProps<typeof buttonVariants> { asChild?: boolean; }

export function Button({ className, variant, size, fullWidth, asChild = false, ...props }: ButtonProps) { return ( <button className={cn(buttonVariants({ variant, size, fullWidth, className }))} {...props} /> ); }

Usage

<Button>Default</Button> <Button variant="secondary" size="lg">Large Secondary</Button> <Button variant="destructive" fullWidth>Delete</Button> <Button variant="ghost" size="icon"><IconMenu /></Button>

Compound Components Pattern

Context-Based Component System

// 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<HTMLDivElement> { variant?: 'default' | 'elevated' | 'outline'; }

const Card = forwardRef<HTMLDivElement, CardProps>( ({ 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 (
  &#x3C;CardContext.Provider value={{ variant }}>
    &#x3C;div
      ref={ref}
      className={cn(
        'rounded-xl',
        variants[variant],
        className
      )}
      {...props}
    >
      {children}
    &#x3C;/div>
  &#x3C;/CardContext.Provider>
);

} );

// Sub-components const CardHeader = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( ({ className, ...props }, ref) => ( <div ref={ref} className={cn('flex flex-col gap-1.5 p-6 pb-0', className)} {...props} /> ) );

const CardTitle = forwardRef<HTMLHeadingElement, React.HTMLAttributes<HTMLHeadingElement>>( ({ className, ...props }, ref) => ( <h3 ref={ref} className={cn('text-xl font-semibold leading-none tracking-tight', className)} {...props} /> ) );

const CardDescription = forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>( ({ className, ...props }, ref) => ( <p ref={ref} className={cn('text-sm text-gray-500', className)} {...props} /> ) );

const CardContent = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( ({ className, ...props }, ref) => ( <div ref={ref} className={cn('p-6', className)} {...props} /> ) );

const CardFooter = forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>( ({ className, ...props }, ref) => ( <div ref={ref} className={cn('flex items-center p-6 pt-0', className)} {...props} /> ) );

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

<Card variant="elevated"> <CardHeader> <CardTitle>Account Settings</CardTitle> <CardDescription>Manage your account preferences</CardDescription> </CardHeader> <CardContent> <form>...</form> </CardContent> <CardFooter className="justify-between"> <Button variant="ghost">Cancel</Button> <Button>Save Changes</Button> </CardFooter> </Card>

Data Attribute Variants

CSS-Based State Management

/* 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]);

// Dropdown component using data attributes function DropdownContent({ children, ...props }) { return ( <div data-state={isOpen ? 'open' : 'closed'} data-side={side} className=" absolute z-50 min-w-[8rem] overflow-hidden rounded-md border border-gray-200 bg-white p-1 shadow-lg

    data-state-open:animate-in
    data-state-open:fade-in-0
    data-state-open:zoom-in-95

    data-state-closed:animate-out
    data-state-closed:fade-out-0
    data-state-closed:zoom-out-95

    data-side-top:slide-in-from-bottom-2
    data-side-bottom:slide-in-from-top-2
    data-side-left:slide-in-from-right-2
    data-side-right:slide-in-from-left-2
  "
  {...props}
>
  {children}
&#x3C;/div>

); }

function DropdownItem({ children, disabled, ...props }) { return ( <div data-highlighted={isHighlighted || undefined} data-disabled={disabled || undefined} className=" relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none

    data-highlighted:bg-gray-100
    data-disabled:pointer-events-none
    data-disabled:opacity-50
  "
  {...props}
>
  {children}
&#x3C;/div>

); }

Group and Peer Patterns

Complex State Propagation

<!-- Group pattern: Parent hover affects children --> <div class="group relative overflow-hidden rounded-xl"> <img src="image.jpg" class="transition-transform duration-300 group-hover:scale-110" /> <div class=" absolute inset-0 bg-gradient-to-t from-black/80 to-transparent opacity-0 transition-opacity group-hover:opacity-100 "> <div class=" absolute bottom-0 left-0 right-0 p-6 translate-y-4 transition-transform group-hover:translate-y-0 "> <h3 class="text-xl font-bold text-white">Title</h3> <p class="text-gray-200">Description</p> </div> </div> </div>

<!-- Named groups for nested components --> <div class="group/card"> <div class="group/header"> <button class="group-hover/header:text-blue-500"> Header Action </button> </div> <div class="group-hover/card:bg-gray-50"> Card content </div> </div>

<!-- Peer pattern: Sibling state affects elements --> <div class="relative"> <input type="email" class="peer w-full border rounded-lg px-4 py-2 placeholder-transparent" placeholder="Email" /> <label class=" absolute left-4 top-2 text-gray-500 transition-all peer-placeholder-shown:top-2 peer-placeholder-shown:text-base peer-focus:-top-6 peer-focus:text-sm peer-focus:text-blue-500 peer-[:not(:placeholder-shown)]:-top-6 peer-[:not(:placeholder-shown)]:text-sm "> Email address </label> </div>

<!-- Peer for form validation --> <div> <input type="email" class="peer" required /> <p class="hidden text-red-500 peer-invalid:block"> Please enter a valid email </p> </div>

Slot Pattern with @apply

Reusable Component Slots

@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

import { forwardRef, ElementType, ComponentPropsWithoutRef } from 'react'; import { cn } from '@/lib/utils';

type PolymorphicRef<C extends ElementType> = ComponentPropsWithoutRef<C>['ref'];

type PolymorphicComponentProps<C extends ElementType, Props = {}> = Props & { as?: C; className?: string; children?: React.ReactNode; } & Omit<ComponentPropsWithoutRef<C>, '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 = <C extends ElementType = 'span'>( props: PolymorphicComponentProps<C, TextProps> & { ref?: PolymorphicRef<C> } ) => React.ReactElement | null;

export const Text: TextComponent = forwardRef( <C extends ElementType = 'span'>( { as, size = 'base', weight = 'normal', color = 'default', className, children, ...props }: PolymorphicComponentProps<C, TextProps>, ref?: PolymorphicRef<C> ) => { 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 (
  &#x3C;Component
    ref={ref}
    className={cn(sizes[size], weights[weight], colors[color], className)}
    {...props}
  >
    {children}
  &#x3C;/Component>
);

} );

Usage

<Text>Default span</Text> <Text as="p" size="lg" color="muted">Large muted paragraph</Text> <Text as="h1" size="2xl" weight="bold">Bold heading</Text> <Text as="a" href="/link" color="accent">Accent link</Text>

Headless Component Integration

Headless UI with Tailwind

import { Dialog, Transition } from '@headlessui/react'; import { Fragment } from 'react';

function Modal({ isOpen, onClose, title, children }) { return ( <Transition appear show={isOpen} as={Fragment}> <Dialog as="div" className="relative z-50" onClose={onClose}> {/* Backdrop */} <Transition.Child as={Fragment} enter="ease-out duration-300" enterFrom="opacity-0" enterTo="opacity-100" leave="ease-in duration-200" leaveFrom="opacity-100" leaveTo="opacity-0" > <div className="fixed inset-0 bg-black/50 backdrop-blur-sm" /> </Transition.Child>

    &#x3C;div className="fixed inset-0 overflow-y-auto">
      &#x3C;div className="flex min-h-full items-center justify-center p-4">
        &#x3C;Transition.Child
          as={Fragment}
          enter="ease-out duration-300"
          enterFrom="opacity-0 scale-95"
          enterTo="opacity-100 scale-100"
          leave="ease-in duration-200"
          leaveFrom="opacity-100 scale-100"
          leaveTo="opacity-0 scale-95"
        >
          &#x3C;Dialog.Panel className="
            w-full max-w-md transform overflow-hidden rounded-2xl
            bg-white p-6 text-left align-middle shadow-xl transition-all
          ">
            &#x3C;Dialog.Title className="text-lg font-medium leading-6 text-gray-900">
              {title}
            &#x3C;/Dialog.Title>
            &#x3C;div className="mt-2">
              {children}
            &#x3C;/div>
          &#x3C;/Dialog.Panel>
        &#x3C;/Transition.Child>
      &#x3C;/div>
    &#x3C;/div>
  &#x3C;/Dialog>
&#x3C;/Transition>

); }

Radix UI with Tailwind

import * as DropdownMenu from '@radix-ui/react-dropdown-menu';

function Dropdown() { return ( <DropdownMenu.Root> <DropdownMenu.Trigger className=" inline-flex items-center justify-center rounded-md bg-white px-4 py-2 text-sm font-medium border border-gray-300 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-brand-500 "> Options <ChevronDownIcon className="ml-2 h-4 w-4" /> </DropdownMenu.Trigger>

  &#x3C;DropdownMenu.Portal>
    &#x3C;DropdownMenu.Content
      className="
        min-w-[200px] rounded-md bg-white p-1 shadow-lg
        border border-gray-200
        animate-in fade-in-0 zoom-in-95
        data-[side=bottom]:slide-in-from-top-2
        data-[side=top]:slide-in-from-bottom-2
      "
      sideOffset={5}
    >
      &#x3C;DropdownMenu.Item className="
        relative flex cursor-pointer select-none items-center
        rounded-sm px-2 py-2 text-sm outline-none
        data-[highlighted]:bg-gray-100
      ">
        Profile
      &#x3C;/DropdownMenu.Item>

      &#x3C;DropdownMenu.Separator className="my-1 h-px bg-gray-200" />

      &#x3C;DropdownMenu.Item className="
        relative flex cursor-pointer select-none items-center
        rounded-sm px-2 py-2 text-sm text-red-600 outline-none
        data-[highlighted]:bg-red-50
      ">
        Delete
      &#x3C;/DropdownMenu.Item>
    &#x3C;/DropdownMenu.Content>
  &#x3C;/DropdownMenu.Portal>
&#x3C;/DropdownMenu.Root>

); }

Animation Patterns

Staggered Animations

function StaggeredList({ items }) { return ( <ul className="space-y-2"> {items.map((item, index) => ( <li key={item.id} className="animate-in fade-in-0 slide-in-from-left-4" style={{ animationDelay: ${index * 100}ms }} > {item.content} </li> ))} </ul> ); }

Skeleton Loading

function Skeleton({ className, ...props }) { return ( <div className={cn( 'animate-pulse rounded-md bg-gray-200', className )} {...props} /> ); }

function CardSkeleton() { return ( <div className="rounded-xl border border-gray-200 p-6"> <div className="flex items-center gap-4"> <Skeleton className="h-12 w-12 rounded-full" /> <div className="space-y-2"> <Skeleton className="h-4 w-[200px]" /> <Skeleton className="h-3 w-[150px]" /> </div> </div> <div className="mt-4 space-y-2"> <Skeleton className="h-3 w-full" /> <Skeleton className="h-3 w-full" /> <Skeleton className="h-3 w-3/4" /> </div> </div> ); }

Best Practices

  1. Use cn() Utility for Class Merging

// lib/utils.ts import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge';

export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); }

  1. Extract Common Patterns

@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; } }

  1. Document Component APIs

/**

  • Button component with multiple variants
  • @example
  • <Button variant="primary" size="lg">Click me</Button>
  • @example
  • <Button variant="ghost" size="icon">
  • <IconMenu />
  • </Button> */ export function Button({ ... }) { ... }
  1. Test Component Variants

// Button.test.tsx describe('Button', () => { it('renders all variants correctly', () => { const variants = ['default', 'secondary', 'outline', 'ghost', 'destructive'];

variants.forEach(variant => {
  render(&#x3C;Button variant={variant}>Test&#x3C;/Button>);
  // Assert classes are applied correctly
});

}); });

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

tailwindcss-advanced-layouts

No summary provided by upstream source.

Repository SourceNeeds Review
General

tailwindcss-animations

No summary provided by upstream source.

Repository SourceNeeds Review
General

tailwindcss-mobile-first

No summary provided by upstream source.

Repository SourceNeeds Review
General

docker-best-practices

No summary provided by upstream source.

Repository SourceNeeds Review