On this page

Component Design System

Build reusable UI components with Tailwind CSS and class-variance-authority

I use shadcn/ui as the foundation for my component library. Unlike traditional component libraries, shadcn/ui gives you the source code — meaning full control over styling and behavior. Components are built with Radix UI primitives (for accessibility), styled with Tailwind CSS, and use class-variance-authority (CVA) for variant management. This pattern lets me create consistent components with variants (size, color, state) while keeping the API simple and type-safe.

I use shadcn/ui + Radix UI + CVA for accessible, customizable components. You could achieve similar results with Material UI, Chakra UI, Ant Design, or building from scratch with Headless UI. The variant pattern shown here (using CVA) can also be implemented with plain Tailwind classes, CSS Modules, or styled-components.

How it works

components/
├── ui/
│   ├── button.tsx      # Button component with variants
│   ├── input.tsx       # Input component
│   ├── card.tsx        # Card components (Card, CardHeader, etc.)
│   ├── dialog.tsx      # Dialog/Modal component
│   └── separator.tsx   # Separator component
└── common/
    ├── Container.tsx   # Layout container
    ├── Logo.tsx        # Logo component
    └── ThemeToggle.tsx # Theme switcher

lib/
└── cn.ts               # Utility for merging class names

styles/
└── globals.css         # Global styles & CSS variables

Design Tokens

CSS variables for consistent colors and theming.

Composable

Build complex UIs from small reusable components.

CVA Variants

Type-safe component variants with class-variance-authority.

Radix Primitives

Accessible, unstyled components from Radix UI.

Key Features

  • Variant-based component API
  • Composable and reusable patterns
  • Accessible components (Radix UI)
  • Type-safe props with TypeScript
  • Consistent design tokens

Live Example

Buttons

<Button>Default</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="outline">Outline</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Destructive</Button>
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>

Input Fields

<Input placeholder="Default input" />
<Input placeholder="Disabled input" disabled />
<div className="flex gap-2">
  <Input placeholder="Search..." className="flex-1" />
  <Button>Search</Button>
</div>

Cards

Simple Card

A basic card component with header and content.

Highlighted Card

A card with primary border highlight.

<Card>
  <CardHeader>
    <CardTitle>Simple Card</CardTitle>
  </CardHeader>
  <CardContent>
    <p>A basic card component with header and content.</p>
  </CardContent>
</Card>

<Card className="border-primary">
  <CardHeader>
    <CardTitle className="text-primary">Highlighted Card</CardTitle>
  </CardHeader>
  <CardContent>
    <p>A card with primary border highlight.</p>
  </CardContent>
</Card>