Clidey Design
A production-ready React component library built on Radix UI and Tailwind CSS v4.
37+ accessible, composable, dark-mode-ready components — drop in and ship.
Why Clidey Design?
Building a consistent, accessible UI from scratch takes weeks. Clidey Design gives you a complete set of polished components so you can focus on your product, not your design system.
- Accessible by default — every component is built on Radix UI primitives. Keyboard navigation, focus management, and ARIA attributes are handled for you.
- Dark mode included — wrap your app with
ThemeProviderand all components adapt automatically. System preference detection andlocalStoragepersistence built in. - Fully typed — complete TypeScript definitions with strict mode. Autocomplete for every prop, variant, and event handler.
- Composable API — sub-component patterns give you full control over layout and markup without fighting against an opinionated structure.
- Tailwind CSS v4 — styled with utility classes you already know. Override anything with
className. - Zero configuration — one import, one stylesheet, done.
Installation
npm install @clidey/ux
# or
pnpm add @clidey/ux
# or
yarn add @clidey/ux
Import the stylesheet once at your app entry point:
// main.tsx / _app.tsx
import '@clidey/ux/styles.css';
Wrap with ThemeProvider to enable dark mode:
import { ThemeProvider } from '@clidey/ux';
export default function App() {
return (
<ThemeProvider defaultTheme="system">
{/* your app */}
</ThemeProvider>
);
}
That's it. No extra configuration, no Tailwind setup required in your project.
Quick start
import {
Button,
Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter,
Badge,
Input,
Label,
} from '@clidey/ux';
import '@clidey/ux/styles.css';
export default function Example() {
return (
<Card className="max-w-sm">
<CardHeader>
<CardTitle>Create account</CardTitle>
<CardDescription>Get started in seconds. No credit card required.</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
<div className="space-y-1">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" placeholder="[email protected]" />
</div>
<div className="space-y-1">
<Label htmlFor="password">Password</Label>
<Input id="password" type="password" showPasswordToggle />
</div>
</CardContent>
<CardFooter className="flex items-center justify-between">
<Badge variant="outline">Free plan</Badge>
<Button>Create account</Button>
</CardFooter>
</Card>
);
}
Components
Actions
| Component | Description |
|---|---|
Button | 6 variants (default, destructive, outline, secondary, ghost, link) · 4 sizes |
ButtonGroup | Connected button strip for toolbars and segmented controls |
Inputs
| Component | Description |
|---|---|
Input | Text field with optional password toggle |
TextArea | Multi-line text input |
SearchInput | Text field with built-in search icon |
Checkbox | Accessible checkbox with indeterminate state |
Switch | Toggle for boolean settings |
Select | Composable dropdown selector |
SearchSelect | Dropdown with search filtering and icon support |
Label | Accessible form label |
Display
| Component | Description |
|---|---|
Badge | Status and category labels · 4 variants |
Card | Container with header, content, footer, and action slots |
Alert | Inline feedback messages · default and destructive |
Spinner | Animated loader · 5 color variants · 3 sizes |
Skeleton | Pulsing placeholder for loading states |
Progress | Linear progress bar |
EmptyState | Placeholder for empty lists and zero-state screens |
Separator | Horizontal or vertical divider |
Navigation
| Component | Description |
|---|---|
Tabs | Tab panels for switching between sections |
Breadcrumb | Hierarchical path with ellipsis support |
Pagination | Previous / next and numbered page controls |
Sidebar | Full sidebar with collapsible menus, submenus, mobile support |
Overlays
| Component | Description |
|---|---|
Dialog | Modal with focus trap and scroll lock |
AlertDialog | Blocking confirmation for destructive actions |
Drawer | Slide-in panel from any edge (powered by Vaul) |
Sheet | Side panel overlay (powered by Radix Dialog) |
Tooltip | Hover label with configurable delay and position |
Popover | Click-anchored floating panel |
Menus
| Component | Description |
|---|---|
DropdownMenu | Trigger menu with checkboxes, radio groups, and sub-menus |
ContextMenu | Right-click menu with the same rich item types |
Command | Fuzzy-search command palette (⌘K) powered by cmdk |
Layout
| Component | Description |
|---|---|
Accordion | Collapsible sections with animated expand/collapse |
ResizablePanelGroup | Drag-to-resize split views |
MasterDetailPanels | List/detail split; one panel at a time below 768px |
ScrollArea | Custom scrollbar with styled track and thumb |
StackList | Key-value metadata list with separators |
Data & Visualization
| Component | Description |
|---|---|
Table | Full table with virtualization for large datasets and JSON preview |
Chart | Recharts wrapper with theme-aware tooltips and legend |
Tree | Hierarchical tree / file explorer with expand and selection |
Utilities
| Component | Description |
|---|---|
Icon | SVG wrapper with consistent sizing |
CatalogIcon | Plain SVG icon selected by catalog name |
IconSearch, IconHome, … | Named components for every catalog SVG, including Alt variants |
Toaster | Toast notifications powered by Sonner |
Theme
| Component / Hook | Description |
|---|---|
ThemeProvider | Context provider for light / dark / system theme |
useTheme | Hook to read and set the active theme |
ModeToggle | Ready-made dropdown to switch themes |
Usage examples
Catalog icons
import { IconSearch, IconSearchAlt, CatalogIcon } from '@clidey/ux';
<IconSearch className="size-4" />
<IconSearchAlt size={20} />
<CatalogIcon name="source" size={16} />
Catalog icons inherit the current text color and accept SVG props and refs. Add aria-label when an icon needs its own accessible name.
Triggers and asChild
Wrap interactive children in DropdownMenuTrigger, TooltipTrigger, PopoverTrigger, DialogTrigger and friends without asChild; the trigger renders as the child when it is slot-safe (Button, Badge, SidebarMenuButton, intrinsic button/a/span/div, ...) and as its own button otherwise. Pass asChild only for non-ux elements that forward refs. See docs/triggers-and-aschild.md for the rule, the safe/unsafe table and the traps.
<DropdownMenuTrigger>
<Button variant="ghost" size="icon"><MoreHorizontal /></Button>
</DropdownMenuTrigger>
Confirmation dialog
import {
AlertDialog, AlertDialogTrigger, AlertDialogContent,
AlertDialogHeader, AlertDialogTitle, AlertDialogDescription,
AlertDialogFooter, AlertDialogCancel, AlertDialogAction,
Button,
} from '@clidey/ux';
<AlertDialog>
<AlertDialogTrigger>
<Button variant="destructive">Delete account</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you absolutely sure?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. Your data will be permanently deleted.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction>Delete account</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
Toast notifications
import { Toaster, toast } from '@clidey/ux';
// In your root layout
<Toaster />
// Anywhere in your app
toast.success('Saved successfully!');
toast.error('Something went wrong.');
toast.promise(saveData(), {
loading: 'Saving...',
success: 'Done!',
error: 'Failed to save.',
});
Command palette (⌘K)
import {
CommandDialog, CommandInput, CommandList,
CommandEmpty, CommandGroup, CommandItem,
} from '@clidey/ux';
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Search commands..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Navigation">
<CommandItem onSelect={() => navigate('/dashboard')}>Dashboard</CommandItem>
<CommandItem onSelect={() => navigate('/settings')}>Settings</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>
Large dataset table with virtualization
import {
TableProvider, Table, TableHeader, TableHeadRow,
TableHead, VirtualizedTableBody, TableRow, TableCell,
} from '@clidey/ux';
<TableProvider columnCount={3}>
<Table>
<TableHeader>
<TableHeadRow>
<TableHead>ID</TableHead>
<TableHead>Name</TableHead>
<TableHead>Status</TableHead>
</TableHeadRow>
</TableHeader>
<VirtualizedTableBody rowCount={10000} rowHeight={48} height={400}>
{({ index, style }) => (
<TableRow style={style}>
<TableCell>{rows[index].id}</TableCell>
<TableCell>{rows[index].name}</TableCell>
<TableCell>{rows[index].status}</TableCell>
</TableRow>
)}
</VirtualizedTableBody>
</Table>
</TableProvider>
Theming
All visual tokens are exposed as CSS custom properties. Override them in your own stylesheet after importing @clidey/ux/styles.css:
:root {
--primary: 250 84% 54%; /* your brand color */
--radius: 0.375rem; /* border radius */
}
Dark mode is automatic — ThemeProvider applies a dark class to <html> and all components respond to it via Tailwind's dark: modifier.
Next.js
// app/providers.tsx
'use client';
import { ThemeProvider } from '@clidey/ux';
export function Providers({ children }: { children: React.ReactNode }) {
return <ThemeProvider defaultTheme="system">{children}</ThemeProvider>;
}
// app/layout.tsx
import '@clidey/ux/styles.css';
import { Providers } from './providers';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}
Peer dependencies
| Package | Version |
|---|---|
react | >= 18.3.0 |
react-dom | >= 18.3.0 |
All other dependencies (Radix UI, Lucide, Recharts, Sonner, Vaul, cmdk) are bundled — no extra installs needed.
Contributing
Contributions, bug reports, and feature requests are welcome. Please open an issue or pull request on GitHub.
Not found page
Use the animated 404 view in your application's fallback route:
import { NotFoundPage } from '@clidey/ux';
<NotFoundPage homeHref="/" homeLabel="Back to home" />
Customize the copy with title, description, and eyebrow. Use onHome for router navigation or action for a custom action. Playback supports loop, once, and off through defaultMotion or controlled motion / onMotionChange props. Reduced motion preferences automatically show a still pose. Optional playback controls are enabled with showMotionControls.
License
Licensed under the Apache License 2.0.
Clidey Design icons
The full Clidey Design icon collection is available as raw SVG assets without loading it through the main UI entry. Import a single icon URL in a Vite app:
import searchUrl from "@clidey/ux/icons/svg/search.svg?url";
<img src={searchUrl} alt="" />
For a dynamic icon picker, import the icons URL map from @clidey/ux/icons. See icons/README.md for usage examples.