Component Usage Guide¶
Design System Version: 1.0.0 Last Updated: November 18, 2025
Overview¶
This guide provides practical examples for using the SetTimes Design System components. All components are accessible, responsive, and follow WCAG 2.1 AA guidelines.
Import Components¶
import { Button, Input, Card, Badge, Alert, Modal, Loading } from '@/components/ui'
Button¶
Primary Actions¶
<Button variant="primary" onClick={createEvent}>
Create Event
</Button>
Secondary Actions¶
<Button variant="secondary" onClick={cancel}>
Cancel
</Button>
Destructive Actions¶
<Button variant="danger" onClick={deleteEvent}>
Delete Event
</Button>
With Loading State¶
<Button
variant="primary"
loading={isSubmitting}
onClick={handleSubmit}
>
{isSubmitting ? 'Saving...' : 'Save Event'}
</Button>
Sizes¶
<Button size="sm">Small</Button>
<Button size="md">Medium</Button>
<Button size="lg">Large</Button>
Input¶
Basic Text Input¶
<Input
label="Event Name"
type="text"
value={eventName}
onChange={(e) => setEventName(e.target.value)}
placeholder="Long Weekend Band Crawl Vol. 6"
required
/>
With Error State¶
<Input
label="Email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
error={errors.email}
helperText="We'll never share your email"
/>
With Icon¶
import { faSearch } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
<Input
placeholder="Search events..."
value={search}
onChange={(e) => setSearch(e.target.value)}
icon={<FontAwesomeIcon icon={faSearch} />}
iconPosition="left"
/>
Card¶
Basic Card¶
<Card>
<h3 className="text-xl font-bold mb-2">Event Title</h3>
<p className="text-text-secondary">Event description...</p>
</Card>
Hoverable Card (Interactive)¶
<Card hoverable onClick={() => navigate(`/events/${event.id}`)}>
<h3 className="text-xl font-bold">{event.name}</h3>
<p className="text-text-tertiary">{event.date}</p>
</Card>
Custom Padding¶
<Card padding="lg">
<h2 className="text-2xl font-bold">Large Padding</h2>
</Card>
<Card padding="none">
<img src={banner} alt="Event banner" className="w-full rounded-t-xl" />
<div className="p-6">
<h3>Custom Content</h3>
</div>
</Card>
Badge¶
Status Indicators¶
<Badge variant="success">Published</Badge>
<Badge variant="warning">Draft</Badge>
<Badge variant="error">Archived</Badge>
<Badge variant="info">Scheduled</Badge>
Sizes¶
<Badge size="sm" variant="success">Small</Badge>
<Badge size="md" variant="success">Medium</Badge>
<Badge size="lg" variant="success">Large</Badge>
Alert¶
Success Notification¶
<Alert variant="success" dismissible onClose={() => setShowAlert(false)}>
Event published successfully!
</Alert>
Error Notification¶
<Alert variant="error">
Failed to save event. Please try again.
</Alert>
Warning with Custom Content¶
<Alert variant="warning" dismissible onClose={handleDismiss}>
<h4 className="font-bold mb-1">Unsaved Changes</h4>
<p>You have unsaved changes. Are you sure you want to leave?</p>
</Alert>
Modal¶
Basic Modal¶
const [isOpen, setIsOpen] = useState(false)
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Create Event"
>
<p>Modal content goes here...</p>
</Modal>
Modal with Footer Actions¶
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Delete Event"
size="sm"
footer={
<>
<Button variant="secondary" onClick={() => setIsOpen(false)}>
Cancel
</Button>
<Button variant="danger" onClick={handleDelete}>
Delete
</Button>
</>
}
>
<p>Are you sure you want to delete this event? This action cannot be undone.</p>
</Modal>
Large Modal with Form¶
<Modal
isOpen={isOpen}
onClose={() => setIsOpen(false)}
title="Edit Event"
size="lg"
>
<form onSubmit={handleSubmit} className="space-y-6">
<Input
label="Event Name"
value={eventName}
onChange={(e) => setEventName(e.target.value)}
required
/>
<Input
label="Event Date"
type="date"
value={eventDate}
onChange={(e) => setEventDate(e.target.value)}
required
/>
<div className="flex justify-end gap-3">
<Button variant="secondary" onClick={() => setIsOpen(false)}>
Cancel
</Button>
<Button variant="primary" type="submit">
Save Changes
</Button>
</div>
</form>
</Modal>
Loading¶
Inline Loading¶
<Loading size="md" text="Loading events..." />
Full Screen Loading¶
{isLoading && (
<Loading
size="lg"
text="Please wait..."
fullScreen
/>
)}
Inside Cards¶
<Card>
{isLoading ? (
<Loading size="md" />
) : (
<EventList events={events} />
)}
</Card>
Common Patterns¶
Form with Validation¶
function EventForm() {
const [formData, setFormData] = useState({ name: '', date: '' })
const [errors, setErrors] = useState({})
const [isSubmitting, setIsSubmitting] = useState(false)
const handleSubmit = async (e) => {
e.preventDefault()
setIsSubmitting(true)
try {
await saveEvent(formData)
// Success
} catch (error) {
setErrors({ general: error.message })
} finally {
setIsSubmitting(false)
}
}
return (
<form onSubmit={handleSubmit} className="space-y-6">
{errors.general && (
<Alert variant="error">{errors.general}</Alert>
)}
<Input
label="Event Name"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
error={errors.name}
required
/>
<Input
label="Event Date"
type="date"
value={formData.date}
onChange={(e) => setFormData({ ...formData, date: e.target.value })}
error={errors.date}
required
/>
<div className="flex justify-end gap-3">
<Button variant="secondary" type="button" onClick={cancel}>
Cancel
</Button>
<Button variant="primary" type="submit" loading={isSubmitting}>
Create Event
</Button>
</div>
</form>
)
}
List with Status¶
function EventList({ events }) {
return (
<div className="space-y-4">
{events.map((event) => (
<Card key={event.id} hoverable onClick={() => viewEvent(event.id)}>
<div className="flex items-start justify-between">
<div>
<h3 className="text-lg font-bold">{event.name}</h3>
<p className="text-text-tertiary text-sm">{event.date}</p>
</div>
<Badge variant={event.isPublished ? 'success' : 'warning'}>
{event.isPublished ? 'Published' : 'Draft'}
</Badge>
</div>
</Card>
))}
</div>
)
}
Confirmation Dialog¶
function DeleteConfirmation({ event, onConfirm, onCancel }) {
const [isOpen, setIsOpen] = useState(true)
const [isDeleting, setIsDeleting] = useState(false)
const handleConfirm = async () => {
setIsDeleting(true)
await onConfirm()
setIsOpen(false)
}
return (
<Modal
isOpen={isOpen}
onClose={onCancel}
title="Confirm Deletion"
size="sm"
footer={
<>
<Button variant="secondary" onClick={onCancel} disabled={isDeleting}>
Cancel
</Button>
<Button variant="danger" onClick={handleConfirm} loading={isDeleting}>
Delete
</Button>
</>
}
>
<Alert variant="warning">
<p className="font-medium mb-2">This action cannot be undone</p>
<p className="text-sm">
Are you sure you want to delete "{event.name}"?
</p>
</Alert>
</Modal>
)
}
Accessibility Checklist¶
When using components, ensure:
- [ ] All forms have labels
- [ ] Required fields are marked with
requiredprop - [ ] Error messages are descriptive
- [ ] Keyboard navigation works (Tab, Enter, Escape)
- [ ] Focus states are visible
- [ ] Loading states have appropriate aria-labels
- [ ] Modals trap focus and close on Escape
- [ ] Color isn't the only indicator of state
Performance Tips¶
- Lazy Load Modals: Only render modals when needed
- Debounce Inputs: For search/filter inputs, debounce onChange handlers
- Virtualize Long Lists: Use virtualization for lists with 100+ items
- Optimize Images: Use appropriate sizes and lazy loading
- Reduce Re-renders: Memoize callbacks and components when appropriate
Next: See DESIGN_SYSTEM.md for complete design tokens and specifications.