Theme System Guide
Overview
The theme system is built on:
- Tailwind CSS for utility classes
- shadcn/ui for component primitives
- CSS Variables for dynamic values
- CSS Modules for component styles
Token System
Our token system is designed to be fully compatible with shadcn/ui while providing additional semantic tokens for layout and custom components.
When to Use What
-
shadcn/ui Base Tokens - Use for core UI components:
// Direct Tailwind classes for UI primitives <Button className="px-4 py-2" /> <Badge className="px-2 py-1.5" /> <Input className="h-10 px-3" />
-
Theme Semantic Tokens - Use for layout and custom components:
// Layout spacing <Container className="px-viewport-edge" /> <main className="mt-layout-top" /> // Form layouts <FormStack className="gap-form-stack" /> <FormField className="space-y-form-label" /> // Custom composite components <ArticleCard className="p-card-md" /> <DashboardWidget className="gap-widget" />
Decision Tree
To quickly decide which approach to use:
-
Is this a shadcn/ui component or UI primitive?
- Yes → Use direct Tailwind classes
- No → Continue to 2
-
Is this related to:
- Layout spacing?
- Form layout?
- Custom composite component?
- Yes to any → Use semantic tokens
- No → Use direct Tailwind classes
✅ Correct Usage:
// UI primitive - Direct Tailwind
<Button className="px-4 py-2 gap-2">
<Icon className="w-4 h-4" />
Click Me
</Button>
// Layout - Semantic tokens
<Container className="px-viewport-edge">
<main className="mt-layout-top mb-layout-bottom">
<FormStack className="gap-form-stack">
<Input className="h-10 px-3" /> {/* UI primitive */}
</FormStack>
</main>
</Container>
❌ Incorrect Usage:
// Don't use semantic tokens for UI primitives
<Button className="px-button-md"> // ❌
// Don't use direct values for layout
<Container className="px-6"> // ❌
Color System
Base colors follow shadcn/ui’s HSL pattern:
:root {
--primary: 220.9 39.3% 11%;
--primary-foreground: 210 20% 98%;
/* ... other shadcn/ui colors ... */
}
Typography
We use shadcn/ui’s typography scale for consistency:
text-sm // 14px
text-base // 16px
text-lg // 18px
text-xl // 20px
// ... etc
Font families:
- Sans: Inter Variable (shadcn/ui compatible)
- Mono: System monospace stack
Spacing System
Our spacing system combines shadcn/ui compatibility with semantic tokens:
-
Component Spacing - Maps to Tailwind’s scale:
// shadcn/ui pattern <Button className="h-10 px-4 py-2" /> // Our semantic equivalent <Button className="h-button-md px-button-md" />
-
Layout Spacing - Our custom tokens:
<Container className="px-viewport-edge"> <header className="mb-layout-top" /> <main className="mb-layout-bottom" /> <footer className="mt-layout-footer-top mb-layout-footer-bottom" /> </Container>
Opacity System
-
Interactive States - shadcn/ui compatible:
hover:bg-primary/90 disabled:opacity-50
-
Semantic States - Our extensions:
bg-primary/subtle border-border/border text-muted-foreground/form-text
Component Development
Using shadcn/ui Components
-
Install components using the CLI:
bun run ui:add button
-
Use with default styling:
<Button>Default styling</Button>
-
Extend with our tokens:
<Button className="px-button-lg">Extended</Button>
Creating Custom Components
-
Follow shadcn/ui patterns:
const customVariants = cva( // Base styles "rounded-md transition-colors", { variants: { // Your variants } } )
-
Use our semantic tokens:
<div className="p-card-md space-y-form-stack"> {/* Content */} </div>
Best Practices
-
Consistency First
- Use shadcn/ui patterns for UI components
- Use our semantic tokens for layout and custom components
- Don’t mix patterns unnecessarily
-
Token Usage
- Prefer semantic tokens over raw values
- Use shadcn/ui’s color system
- Use our spacing system for layout
-
Maintainability
- Document component variants
- Test in both themes
- Consider responsive design
- Follow accessibility guidelines
-
Performance
- Reuse existing tokens
- Avoid custom CSS where possible
- Leverage Tailwind’s utilities
Migration Guide
When converting existing components:
-
Colors
// Before bg-gray-100 dark:bg-gray-800 // After bg-muted text-muted-foreground
-
Spacing
// Before p-4 gap-8 // After - UI Components p-4 gap-8 // Keep shadcn/ui patterns // After - Layout p-card-md gap-form-stack // Use semantic tokens
-
Typography
// Keep shadcn/ui patterns text-sm font-medium
-
Opacity
// Before opacity-50 // After opacity-disabled // For UI states opacity-theme-inactive // For theme elements
Remember: The goal is to maintain compatibility with shadcn/ui while providing semantic tokens for layout and custom components.
shadcn/ui Integration
Our theme system is fully compatible with shadcn/ui components while providing additional semantic tokens for layout and custom components. See the Component Variants Guide for detailed examples.
Token Alignment
-
Base Tokens - shadcn/ui’s core tokens:
: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; }
-
Semantic Tokens - Our extensions:
:root { /* Layout */ --viewport-edge: 1rem; --layout-top: 2rem; /* Component Spacing */ --button-sm: 0.75rem; --button-md: 1rem; /* Form Elements */ --form-input: 2.5rem; --form-padding-x: 0.75rem; /* Interactive States */ --opacity-hover: 0.9; --opacity-active: 0.7; }
Usage Guidelines
-
shadcn/ui Components
// Use shadcn/ui's base tokens <Button variant="default"> <Card className="bg-card text-card-foreground">
-
Layout & Custom Components
// Use our semantic tokens <Container className="px-viewport-edge"> <FormStack className="gap-form-stack">
See the Component Variants Guide for more examples.