FoundryKit

Styling

How to style and customize FoundryKit primitives

Styling

FoundryKit primitives are built with Tailwind CSS and class-variance-authority (CVA) to provide flexible and consistent styling options.

Variants

Most primitives support multiple variants that change their visual appearance:

Button Variants

import { Button } from '@foundrykit/primitives'

function ButtonVariants() {
  return (
    <div className="space-x-2">
      <Button variant="default">Default</Button>
      <Button variant="destructive">Destructive</Button>
      <Button variant="outline">Outline</Button>
      <Button variant="secondary">Secondary</Button>
      <Button variant="ghost">Ghost</Button>
      <Button variant="link">Link</Button>
    </div>
  )
}

Badge Variants

import { Badge } from '@foundrykit/primitives'

function BadgeVariants() {
  return (
    <div className="space-x-2">
      <Badge>Default</Badge>
      <Badge variant="secondary">Secondary</Badge>
      <Badge variant="destructive">Destructive</Badge>
      <Badge variant="outline">Outline</Badge>
    </div>
  )
}

Sizes

Many components support different sizes:

Button Sizes

import { Button } from '@foundrykit/primitives'

function ButtonSizes() {
  return (
    <div className="space-x-2">
      <Button size="sm">Small</Button>
      <Button size="md">Medium</Button>
      <Button size="lg">Large</Button>
    </div>
  )
}

Input Sizes

import { Input } from '@foundrykit/primitives'

function InputSizes() {
  return (
    <div className="space-y-2">
      <Input size="sm" placeholder="Small input" />
      <Input size="md" placeholder="Medium input" />
      <Input size="lg" placeholder="Large input" />
    </div>
  )
}

Custom Styling

Using Tailwind Classes

You can apply custom Tailwind classes to override or extend component styles:

import { Button, Card } from '@foundrykit/primitives'

function CustomStyling() {
  return (
    <div className="space-y-4">
      {/* Custom button styling */}
      <Button className="bg-gradient-to-r from-blue-500 to-purple-500 text-white hover:from-blue-600 hover:to-purple-600">
        Gradient Button
      </Button>
      
      {/* Custom card styling */}
      <Card className="border-2 border-dashed border-gray-300 bg-gray-50">
        <div className="p-4">
          <p>Custom styled card</p>
        </div>
      </Card>
    </div>
  )
}

Conditional Styling

Use conditional classes for dynamic styling:

import { Button } from '@foundrykit/primitives'
import { cn } from '@foundrykit/utils'

function ConditionalStyling({ isActive, isDisabled }) {
  return (
    <Button
      className={cn(
        "transition-all duration-200",
        isActive && "ring-2 ring-blue-500 ring-offset-2",
        isDisabled && "opacity-50 cursor-not-allowed"
      )}
      disabled={isDisabled}
    >
      Conditional Button
    </Button>
  )
}

Theme Integration

CSS Variables

Primitives use CSS variables for consistent theming. You can customize these in your CSS:

:root {
  /* Color variables */
  --color-primary: oklch(0.55 0.15 250);
  --color-primary-foreground: oklch(0.98 0.005 250);
  --color-secondary: oklch(0.95 0.01 250);
  --color-secondary-foreground: oklch(0.15 0.08 250);
  
  /* Border radius */
  --radius: 0.5rem;
  
  /* Spacing */
  --spacing-1: 0.25rem;
  --spacing-2: 0.5rem;
  --spacing-3: 0.75rem;
  --spacing-4: 1rem;
}

Dark Mode

Primitives automatically support dark mode when you configure Tailwind CSS:

@import "tailwindcss";

/* Dark mode variables */
@media (prefers-color-scheme: dark) {
  :root {
    --color-primary: oklch(0.65 0.15 250);
    --color-primary-foreground: oklch(0.05 0.005 250);
    --color-secondary: oklch(0.05 0.01 250);
    --color-secondary-foreground: oklch(0.95 0.08 250);
  }
}

Responsive Design

Responsive Variants

Use Tailwind's responsive prefixes to apply different styles at different breakpoints:

import { Button, Card } from '@foundrykit/primitives'

function ResponsiveDesign() {
  return (
    <div className="space-y-4">
      <Button className="w-full sm:w-auto md:w-32 lg:w-48">
        Responsive Button
      </Button>
      
      <Card className="p-2 sm:p-4 md:p-6 lg:p-8">
        <p>Responsive padding</p>
      </Card>
    </div>
  )
}

Mobile-First Approach

Design for mobile first, then enhance for larger screens:

import { Input, Label } from '@foundrykit/primitives'

function MobileFirstForm() {
  return (
    <div className="space-y-4">
      <div className="space-y-2">
        <Label htmlFor="email" className="text-sm sm:text-base">Email</Label>
        <Input 
          id="email" 
          type="email"
          className="text-sm sm:text-base h-8 sm:h-10"
          placeholder="Enter your email"
        />
      </div>
    </div>
  )
}

Animation and Transitions

Smooth Transitions

Add smooth transitions to interactive elements:

import { Button, Card } from '@foundrykit/primitives'

function AnimatedComponents() {
  return (
    <div className="space-y-4">
      <Button className="transition-all duration-200 hover:scale-105 active:scale-95">
        Animated Button
      </Button>
      
      <Card className="transition-all duration-300 hover:shadow-lg hover:-translate-y-1">
        <div className="p-4">
          <p>Hover to animate</p>
        </div>
      </Card>
    </div>
  )
}

Loading States

Style components for loading states:

import { Button, Skeleton } from '@foundrykit/primitives'

function LoadingStates({ isLoading }) {
  if (isLoading) {
    return (
      <div className="space-y-2">
        <Skeleton className="h-4 w-[200px]" />
        <Skeleton className="h-10 w-[100px]" />
      </div>
    )
  }
  
  return (
    <Button className="transition-opacity duration-200">
      Loaded Button
    </Button>
  )
}

Best Practices

1. Use Semantic Colors

Prefer semantic color classes over hardcoded colors:

// ✅ Good - Uses semantic colors
<Button className="bg-primary text-primary-foreground hover:bg-primary/90">

// ❌ Avoid - Hardcoded colors
<Button className="bg-blue-500 text-white hover:bg-blue-600">

2. Maintain Consistency

Use consistent spacing and sizing throughout your application:

// ✅ Good - Consistent spacing
<div className="space-y-4">
  <Input className="h-10" />
  <Button className="h-10" />
</div>

// ❌ Avoid - Inconsistent sizing
<div className="space-y-4">
  <Input className="h-8" />
  <Button className="h-12" />
</div>

3. Leverage Design Tokens

Use design tokens for consistent theming:

// ✅ Good - Uses design tokens
<Card className="rounded-lg border border-border bg-card text-card-foreground">

// ❌ Avoid - Hardcoded values
<Card className="rounded-md border border-gray-200 bg-white text-gray-900">

4. Progressive Enhancement

Start with basic styles and enhance progressively:

function ProgressiveEnhancement() {
  return (
    <Button 
      className={cn(
        "base-styles", // Always applied
        "enhanced-styles", // Applied when supported
        "advanced-styles" // Applied for advanced features
      )}
    >
      Enhanced Button
    </Button>
  )
}

Next Steps