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.
Building a consistent, accessible UI from scratch takes weeks. Clidey UX 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.
npm install @clidey/ux
# or
pnpm add @clidey/ux
# or
yarn add @clidey/uxImport 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.
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="you@example.com" />
</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>
);
}| Component | Description |
|---|---|
Button |
6 variants (default, destructive, outline, secondary, ghost, link) · 4 sizes |
ButtonGroup |
Connected button strip for toolbars and segmented controls |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| 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 |
| Component | Description |
|---|---|
Icon |
SVG wrapper with consistent sizing |
Toaster |
Toast notifications powered by Sonner |
| 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 |
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>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>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.',
});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>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>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.
// 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>
);
}| 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.
Contributions, bug reports, and feature requests are welcome. Please open an issue or pull request on GitHub.
MIT — free to use in personal and commercial projects.
