- Removed neon, glass, premium, and link variants from Button component - Replaced variant="link" in PostCard with variant="ghost" (with underline) - Replaced variant="premium" in LibraryPage and FAB with variant="default" - Updated COMPONENT_USAGE.md to reflect removed variants - Remaining variants: default, destructive, outline, secondary, ghost - Action 9.3.1.2 complete
9.5 KiB
Component Usage Guide
Last Updated: 2025-01-27
Purpose: Guide for when and how to use design system components
Table of Contents
Button
Location: @/components/ui/button
Import: import { Button } from '@/components/ui/button'
When to Use
✅ DO use Button component for:
- All interactive buttons (primary actions, secondary actions, navigation)
- Icon buttons
- Link-style buttons
- Destructive actions (delete, remove)
❌ DON'T use:
- Native
<button>elements (ESLint will warn) - Custom button implementations with inline styles
Variants
| Variant | Use Case | Example |
|---|---|---|
default |
Primary actions, main CTAs | "Save", "Submit", "Upload" |
outline |
Secondary actions, outlined style | "Cancel", "Back", inactive states |
ghost |
Tertiary actions, minimal style | Icon buttons, menu items, subtle actions |
destructive |
Destructive actions | "Delete", "Remove", "Clear" |
secondary |
Secondary actions with background | Alternative to outline |
link |
ghost with underline) |
Note: Variants neon, glass, premium, and link have been removed. Use default, outline, or ghost instead.
Sizes
| Size | Use Case |
|---|---|
default |
Standard buttons (h-10) |
sm |
Compact buttons, inline actions (h-8) |
lg |
Prominent CTAs (h-12) |
icon |
Icon-only buttons (h-10 w-10) |
Examples
// Primary action
<Button variant="default" onClick={handleSave}>
Save Changes
</Button>
// Secondary action
<Button variant="outline" onClick={handleCancel}>
Cancel
</Button>
// Icon button
<Button variant="ghost" size="icon" onClick={handleEdit}>
<Edit className="w-4 h-4" />
</Button>
// Destructive action
<Button variant="destructive" onClick={handleDelete}>
Delete Track
</Button>
// Link-style (use ghost with underline)
<Button variant="ghost" className="text-kodo-cyan hover:underline" onClick={handleViewAll}>
View all tracks
</Button>
Common Patterns
Button Groups:
<div className="flex gap-2">
<Button variant="outline">Cancel</Button>
<Button variant="default">Save</Button>
</div>
Icon with Text:
<Button variant="ghost" size="sm">
<Heart className="w-4 h-4" />
<span>Like</span>
</Button>
Card
Location: @/components/ui/card
Import: import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@/components/ui/card'
When to Use
✅ DO use Card component for:
- Content containers
- Dashboard widgets
- Feature cards
- Data display sections
❌ DON'T use:
- Custom card implementations
- Legacy Card component from
@/components/base/Card - Invalid
variantprop (Card doesn't support variants)
Structure
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
<CardDescription>Optional description</CardDescription>
</CardHeader>
<CardContent>
{/* Main content */}
</CardContent>
<CardFooter>
{/* Footer actions */}
</CardFooter>
</Card>
Examples
// Simple card
<Card>
<CardHeader>
<CardTitle>Track Statistics</CardTitle>
</CardHeader>
<CardContent>
<p>Total plays: 1,234</p>
</CardContent>
</Card>
// Card with footer actions
<Card>
<CardHeader>
<CardTitle>Settings</CardTitle>
<CardDescription>Manage your preferences</CardDescription>
</CardHeader>
<CardContent>
{/* Settings form */}
</CardContent>
<CardFooter>
<Button variant="default">Save</Button>
</CardFooter>
</Card>
Styling
- Card uses className-based styling (no variants)
- Use
classNameprop for custom styling - Default styles: rounded-2xl, border, background, shadow, hover effects
Input
Location: @/components/ui/input
Import: import { Input } from '@/components/ui/input'
When to Use
✅ DO use Input component for:
- Text inputs
- Number inputs
- Email inputs
- Password inputs
- Search inputs
❌ DON'T use:
- Native
<input>elements (for consistency) - Custom input implementations (unless specialized like AuthInput, ChatInput)
Examples
// Basic input
<Input
type="text"
placeholder="Enter track title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
// With label
<div className="space-y-2">
<Label htmlFor="title">Track Title</Label>
<Input
id="title"
type="text"
placeholder="Enter track title"
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</div>
// Disabled state
<Input
type="text"
placeholder="Disabled input"
disabled
/>
Styling
- Input uses consistent styling: rounded-lg, border, background, focus states
- Focus state: border-kodo-cyan
- Disabled state: opacity-50, cursor-not-allowed
Select
Location: @/components/ui/select
Import: import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select'
When to Use
✅ DO use Select component for:
- Dropdown selections
- Filter options
- Sort options
- Choice selections
❌ DON'T use:
- Native
<select>elements (for consistency) - Custom select implementations
Examples
// Basic select
<Select value={value} onValueChange={setValue}>
<SelectTrigger>
<SelectValue placeholder="Select an option" />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2">Option 2</SelectItem>
<SelectItem value="option3">Option 3</SelectItem>
</SelectContent>
</Select>
Dialog
Location: @/components/ui/dialog
Import: import { Dialog } from '@/components/ui/dialog'
When to Use
✅ DO use Dialog component for:
- Confirmation dialogs
- Alert dialogs
- Information dialogs
- Modal forms
Variants
| Variant | Use Case |
|---|---|
default |
Standard dialog |
alert |
Error/warning messages (red icon) |
confirm |
Confirmation dialogs (cyan icon) |
info |
Information dialogs (cyan icon) |
Examples
// Confirmation dialog
<Dialog
open={isOpen}
onClose={() => setIsOpen(false)}
title="Delete Track"
variant="alert"
onConfirm={handleDelete}
confirmLabel="Delete"
cancelLabel="Cancel"
>
<p>Are you sure you want to delete this track? This action cannot be undone.</p>
</Dialog>
Alert
Location: @/components/ui/alert
Import: import { Alert, AlertDescription } from '@/components/ui/alert'
When to Use
✅ DO use Alert component for:
- Success messages
- Warning messages
- Error messages (non-critical)
- Information messages
Examples
// Success alert
<Alert variant="success">
<AlertDescription>Track uploaded successfully!</AlertDescription>
</Alert>
// Error alert
<Alert variant="error">
<AlertDescription>Failed to upload track. Please try again.</AlertDescription>
</Alert>
Badge
Location: @/components/ui/badge
Import: import { Badge } from '@/components/ui/badge'
When to Use
✅ DO use Badge component for:
- Status indicators
- Tags
- Counts
- Labels
Examples
// Status badge
<Badge variant="success">Active</Badge>
<Badge variant="error">Inactive</Badge>
// Count badge
<Badge variant="secondary">42</Badge>
Best Practices
1. Always Use Design System Components
- ✅ Use
Buttoninstead of<button> - ✅ Use
Inputinstead of<input> - ✅ Use
Cardinstead of custom card implementations - ✅ Use
Selectinstead of native<select>
2. Component Composition
- Compose components using sub-components (e.g.,
CardHeader,CardContent) - Use className for custom styling when needed
- Don't override design system styles unnecessarily
3. Accessibility
- All components include proper ARIA attributes
- Focus states are handled automatically
- Keyboard navigation is supported
4. Consistency
- Use consistent variants across similar actions
- Follow the variant guidelines (default for primary, outline for secondary, ghost for tertiary)
- Use appropriate sizes (sm for compact, default for standard, lg for prominent)
5. Performance
- Components are optimized with React.forwardRef
- Use
asChildprop when composing with other components (Button only)
6. Testing
- All components have test files
- Test components before using in production
- Follow existing test patterns
Migration Guide
Migrating from Custom Buttons
Before:
<button className="px-4 py-2 bg-kodo-cyan text-white rounded">
Click me
</button>
After:
<Button variant="default">Click me</Button>
Migrating from Custom Cards
Before:
<div className="rounded-lg border bg-kodo-ink p-4">
<h3>Title</h3>
<p>Content</p>
</div>
After:
<Card>
<CardHeader>
<CardTitle>Title</CardTitle>
</CardHeader>
<CardContent>
<p>Content</p>
</CardContent>
</Card>
Resources
- Design System Colors: See
apps/web/src/styles/COLOR_USAGE.md - Component Tests: See
apps/web/src/components/ui/*.test.tsx - Component Source: See
apps/web/src/components/ui/*.tsx
Questions?
If you're unsure which component to use:
- Check this guide first
- Look at similar components in the codebase
- Review component test files for usage examples
- When in doubt, use the most common variant (default for Button, standard for others)