Components
All components use exclusively semantic token CSS variables. Zero hardcoded values.
Button
Variants
Sizes
States
When to use
- ✅ Primary — single, most important CTA per view
- ✅ Secondary — supporting actions
- ✅ Ghost — low-emphasis, tertiary actions
- ✅ Danger — destructive, irreversible actions
- ❌ Do not use multiple Primary buttons on the same page
jsx
import { Button } from '@/components/ui/Button';
// Variants
<Button variant="primary">Save</Button>
<Button variant="secondary">Cancel</Button>
<Button variant="ghost">Learn more</Button>
<Button variant="danger">Delete</Button>
// Sizes
<Button size="sm" /> <Button size="md" /> <Button size="lg" />
// States
<Button isLoading>Saving…</Button>
<Button disabled>Disabled</Button>
<Button isIcon variant="secondary"><Settings /></Button>
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "primary" | "secondary" | "ghost" | "danger" | "primary" | Visual style |
| size | "sm" | "md" | "lg" | "icon" | "md" | Dimensions |
| isLoading | boolean | false | Shows spinner, disables interaction |
| isIcon | boolean | false | Square aspect ratio for icon-only buttons |
| disabled | boolean | false | Disables the button |
Badge
Variants
DefaultPrimarySuccessWarningErrorInfo
jsx
import { Badge } from '@/components/ui/Badge';
<Badge variant="default">Default</Badge>
<Badge variant="success">Active</Badge>
<Badge variant="error">Failed</Badge>
<Badge variant="warning">Pending</Badge>
<Badge variant="info">New</Badge>
Input & Select
States
Select
jsx
import { Input, Select } from '@/components/ui/Input';
<Input placeholder="Type here…" />
<Input isSearch placeholder="Search…" />
<Input state="error" placeholder="Error" />
<Input state="success" placeholder="Valid" />
<Select options={[{ label: 'A', value: 'a' }]} />
| Prop | Type | Default | Description |
|---|---|---|---|
| state | "default" | "error" | "success" | "default" | Validation state with icon |
| isSearch | boolean | false | Adds search icon prefix |
Form Controls
Checkbox
Radio
Switch
jsx
import { Checkbox } from '@/components/ui/Checkbox';
import { Radio } from '@/components/ui/Radio';
import { Switch } from '@/components/ui/Switch';
<Checkbox checked={val} onChange={e => setVal(e.target.checked)} />
<Radio name="group" value="a" checked={val === 'a'} onChange={() => setVal('a')} />
<Switch checked={enabled} onChange={e => setEnabled(e.target.checked)} />
Alerts & Feedback
Information
Your account is under review.
Saved
All changes have been saved.
Attention
Your session will expire in 10 minutes.
Error
Deployment failed. Check build logs.
Loading states
jsx
import { Alert } from '@/components/ui/Alert';
import { Spinner } from '@/components/ui/Spinner';
import { Skeleton } from '@/components/ui/Skeleton';
<Alert variant="success" title="Saved">Changes saved.</Alert>
<Alert variant="error" title="Error">Build failed.</Alert>
<Alert variant="warning" title="Warning">Expiring soon.</Alert>
<Alert variant="info" title="Info">Under review.</Alert>
<Spinner size="md" />
<Skeleton className="h-4 w-full" />
Overlays
Dropdown
Tooltip
This is a tooltip
Bottom tooltip
Modal
jsx
import { Dropdown } from '@/components/ui/Dropdown';
import { Tooltip } from '@/components/ui/Tooltip';
import { Modal } from '@/components/ui/Modal';
<Dropdown items={[{ label: 'Edit', value: 'edit' }]} />
<Tooltip content="Help text" position="top">
<Button>Hover me</Button>
</Tooltip>
<Modal isOpen={open} onClose={close} title="Title"
actions={<><Button>Cancel</Button><Button variant="primary">OK</Button></>}
>
<p>Modal content</p>
</Modal>
Tabs
Tab content renders below the tab strip.
jsx
import { Tabs } from '@/components/ui/Tabs';
<Tabs
tabs={[
{ id: 'overview', label: 'Overview' },
{ id: 'api', label: 'API' },
]}
defaultTab="overview"
onChange={(id) => console.log(id)}
/>
Cards
Default Card
Standard card for containing grouped content.
Interactive Card
Hover to see the border and background lift.
jsx
import { Card, CardHeader, CardContent, CardFooter } from '@/components/ui/Card';
<Card variant="default">
<CardHeader>Title</CardHeader>
<CardContent>Body content</CardContent>
<CardFooter>
<Button size="sm">Action</Button>
</CardFooter>
</Card>
<Card variant="interactive">
<CardContent>Hover me</CardContent>
</Card>
Table
| Name | Role | Status | Joined |
|---|---|---|---|
| Amir Karimov | Admin | Active | 2024-01 |
| Zarina Lee | Designer | Active | 2024-03 |
| Ivan Petrov | Developer | Pending | 2025-11 |
jsx
import { Table } from '@/components/ui/Table';
<Table
columns={[
{ header: 'Name', accessorKey: 'name' },
{ header: 'Status', cell: (row) => <Badge>{row.status}</Badge> },
]}
data={[{ name: 'Alice', status: 'Active' }]}
/>