@dravyn/ui

Dark-first React component library. Zero runtime dependencies beyond React. Fully typed. Accessible. Built to compete.

v0.1.0 MIT License TypeScript React 17+
Installation
Install with your package manager of choice. React and ReactDOM are the only peer dependencies.
# npm npm install @dravyn/ui # yarn yarn add @dravyn/ui # pnpm pnpm add @dravyn/ui
Setup
Import the design tokens once at your app root. This loads all the CSS variables every component uses. Then import components anywhere.
// app/layout.tsx (Next.js) or main.tsx (Vite) import '@dravyn/ui/tokens'; // Then in any component file: import { Button, Card, Badge } from '@dravyn/ui';
Button
Five variants, three sizes, loading state with spinner, icon slots, full-width mode, and a forwarded ref. Accepts all native button HTML attributes.
Variants
Sizes + states
import { Button } from '@dravyn/ui'; // Variants <Button variant="primary">Primary</Button> <Button variant="outline">Outline</Button> <Button variant="ghost">Ghost</Button> <Button variant="danger">Danger</Button> // Sizes <Button size="sm">Small</Button> <Button size="lg">Large</Button> // Loading — disables and shows spinner <Button loading>Saving...</Button> // With icons <Button variant="primary" leftIcon={<IconBolt size={16} />}>Deploy</Button> <Button variant="danger" rightIcon={<IconTrash size={16} />}>Delete</Button> // Full width <Button fullWidth>Submit form</Button>
Badge
Seven colour variants for status tags and category labels. Supports a dot indicator and custom icon.
All variants
Active Error Beta Info Published Pro Archived
import { Badge } from '@dravyn/ui'; <Badge variant="teal" dot>Active</Badge> <Badge variant="red" dot>Error</Badge> <Badge variant="amber">Beta</Badge> <Badge variant="blue">Info</Badge> <Badge variant="green">Published</Badge> <Badge variant="purple">Pro</Badge> <Badge variant="gray">Archived</Badge>
Input
Accessible text input with label, hint, error state, required indicator, and left/right icon slots. Forwarded ref supported.
Input states
Shown on your public profile.
🔒
Slugs can't contain spaces.
import { Input } from '@dravyn/ui'; // With label and hint <Input label="Username" hint="Shown on your public profile." /> // Error state <Input label="Project slug" error="Slugs can't contain spaces." /> // With icon <Input label="Email" leftIcon={<IconMail size={15} />} type="email" /> // Required <Input label="Full name" required />
Textarea
Multi-line input with the same label/hint/error API as Input, plus an optional live character counter.
With character count
0/160
import { Textarea } from '@dravyn/ui'; <Textarea label="Bio" maxLength={160} showCount placeholder="Short bio..." /> <Textarea label="Message" error="Message is required." />
Card
Flexible container. Compose with CardHeader and CardFooter, or use as a plain wrapper. Supports a featured accent and clickable hover state.
Card variants
Standard card
Use for content sections, settings panels, dashboards, or any grouped information.
import { Card, CardHeader, CardFooter } from '@dravyn/ui'; <Card> <CardHeader title="Project name" description="Last updated 2 days ago." /> <CardFooter align="between"> <Button variant="ghost">Cancel</Button> <Button variant="primary">Save</Button> </CardFooter> </Card> // Featured — teal top accent <Card featured>...</Card> // Clickable <Card onClick={() => router.push('/project')}>...</Card>
Alert
Four semantic feedback banners. Supports a title, body text, custom icon, and a dismiss button.
All variants
Heads up
Your API key expires in 3 days. Rotate it from the dashboard.
Deployed successfully
ClassSync v1.1 is live at classsync.ink.
Approaching limit
You've used 87% of your free tier storage.
Build failed
expo build:android exited with code 1. Check your eas.json.
import { Alert } from '@dravyn/ui'; <Alert variant="info" title="Heads up">Your key expires in 3 days.</Alert> <Alert variant="success" title="Deployed">v1.1 is live.</Alert> <Alert variant="warning" title="Approaching limit" onClose={() => hide()} /> <Alert variant="danger" title="Build failed">Check eas.json.</Alert>
Avatar
Profile pictures with smart initials fallback. Colour is auto-assigned from the name — same person always gets the same colour. Five sizes, six colour variants, status dot.
Sizes, variants, status
JD JD JD GA AO
JD MK CB
import { Avatar } from '@dravyn/ui'; // Auto-generates initials + colour from name <Avatar name="Jeremiah Adeniyi" /> // From image URL — falls back to initials if image fails <Avatar name="Jeremiah Adeniyi" src="https://..." /> // Sizes <Avatar name="Jerry" size="xs" /> <Avatar name="Jerry" size="xl" /> // With status dot <Avatar name="Jeremiah Adeniyi" status="online" /> <Avatar name="Gabriel Akin" status="away" />
Toggle
Accessible on/off switch. Works controlled or uncontrolled. Label can appear on either side.
Interactive — click to toggle
import { Toggle } from '@dravyn/ui'; // Uncontrolled <Toggle label="Dark mode" defaultChecked /> // Controlled const [on, setOn] = useState(false); <Toggle label="Notifications" checked={on} onChange={setOn} /> // Label on the left <Toggle label="Auto-save" labelPosition="left" />
Spinner
Loading indicator in four sizes. Announces itself to screen readers via an aria-label.
Sizes
import { Spinner } from '@dravyn/ui'; <Spinner /> <Spinner size="sm" /> <Spinner size="lg" label="Fetching posts..." /> {isLoading ? <Spinner /> : <MyContent />}
Theming
All colours, spacing, and motion values are CSS custom properties. Override them on :root globally or on any container for scoped overrides. Light mode is built in.
/* globals.css — change the primary accent to your brand colour */ :root { --dui-teal-400: #6366f1; /* indigo instead of teal */ --dui-teal-600: #4f46e5; --dui-bg: #0f0f23; --dui-bg-raised: #1a1a2e; } /* Light mode — add data-theme="light" to your html tag */ document.documentElement.setAttribute('data-theme', 'light');
TokenDefault (dark)Purpose
--dui-teal-400#4ecdc4Primary accent — buttons, focus rings, featured accents
--dui-bg#0d0d0dPage background
--dui-bg-raised#161616Card and input backgrounds
--dui-bg-overlay#1e1e1eHover states, dropdowns
--dui-borderrgba(255,255,255,0.07)Subtle borders
--dui-border-strongrgba(255,255,255,0.16)Input and card borders
--dui-text#f0f0f0Primary text
--dui-text-muted#888888Labels, secondary text
--dui-text-hint#555555Placeholders, disabled text
--dui-radius8pxDefault border radius
--dui-radius-lg12pxCards, panels
--dui-transition150ms easeFast hover/state transitions
@dravyn/ui
Built by Dravyn Tech · MIT License
Dark-first TypeScript Zero deps