Production-grade design system with adapter layer support
npm install @abhir9/pd-design-systemProduction-grade design system with adapter layer support. Built for teams who need a flexible, type-safe component library that can work with different UI engines without changing application code.
- ā
3 Lines to Get Started - No complex setup, just works
- ā
System Mode Built-In - Automatically follows OS dark/light preference
- ā
Zero-Config Persistence - Pass storageKey prop, localStorage sync is automatic
- ā
Cross-Tab Sync - Theme changes sync across browser tabs instantly
- ā
Type-Safe - Full TypeScript with autocomplete for all props
- ā
Accessible - WCAG 2.1 AA compliant, built on Radix UI
- ā
Style Isolation - Works with Tailwind, MUI, AntD, Bootstrap, or plain CSS
- ā
50+ Components - Buttons, Inputs, Modals, Dropdowns, and more
- ā
1000+ Icons - All Lucide icons with TypeScript autocomplete
``tsx`
// That's it! Just 3 lines of code
No complex setup. No manual theme management. No localStorage code. Everything works automatically.
- šÆ Zero-Config Theme System - Pass storageKey and everything works
- š System Mode Support - Automatically follows OS dark/light preference
- š¾ Automatic Persistence - localStorage sync with one prop
- š Cross-Tab Sync - Theme changes sync across browser tabs
- šØ Semantic Tokens - Design tokens that adapt to themes automatically
- š¦ Type-Safe - Full TypeScript with autocomplete
- āæ Accessible - WCAG 2.1 AA compliant out of the box
- š Adapter Layer - Switch UI engines without changing app code
- š Style Isolation - Works with any CSS framework (Tailwind, MUI, AntD, etc.)
This design system follows a headless-first, adapter-based architecture:
- Props-only API: Consumers use only props and variants, no className required
- Full TypeScript: Complete autocomplete and type safety
- Adapter Layer: Switch between UI engines (shadcn, Material, etc.) without app changes
- Semantic Tokens: Design tokens that work across themes
- Framework Agnostic: Design tokens and semantics are framework-agnostic
- Zero-Config Theming: Automatic system detection and localStorage sync
`bash`
npm install @pd-design/system
Get started in 3 simple steps:
`tsx
import { ThemeProvider, Button } from '@pd-design/system';
import '@pd-design/system/styles.css';
function App() {
return (
theme="base"
storageKey="my-app-theme"
>
);
}
`
That's it! š The ThemeProvider automatically handles:pd-root
- ā
System preference detection - Follows OS dark/light mode
- ā
localStorage persistence - Remembers user's theme choice
- ā
Cross-tab sync - Theme changes sync across browser tabs
- ā
Class management - Automatically adds and pd-dark classes
- ā
CSS variables - Sets all theme variables automatically
The design system uses scoped styling to prevent style conflicts:
- ā
All Tailwind classes are prefixed with pd- - No collision with consumer Tailwind
- ā
All CSS variables are scoped under .pd-root - No token leakage
- ā
Preflight is disabled - Consumer styles are never reset
- ā
Works with any consumer setup - Tailwind, MUI, AntD, Bootstrap, or plain CSS
Always wrap your components with ThemeProvider (or PdThemeProvider) to create the scoped boundary:
`tsx`
{/ Your design system components /}
The ThemeProvider automatically adds the pd-root class to your body element, so all tokens are properly scoped.
Here's a complete example showing how easy it is:
`tsx
import { ThemeProvider, Button, useTheme } from '@pd-design/system';
import '@pd-design/system/styles.css';
function App() {
return (
theme="base"
storageKey="my-app-theme" // Enable localStorage persistence
>
);
}
function ThemeToggle() {
const { config, setConfig } = useTheme();
return (
);
}
`
What happens automatically:
1. ā
Reads theme from localStorage on mount
2. ā
Detects system preference if mode is 'system'pd-root
3. ā
Saves theme changes to localStorage
4. ā
Syncs changes across browser tabs
5. ā
Updates CSS variables and classes
6. ā
Manages and pd-dark classes
For advanced use cases, you can configure the design system globally:
`tsx
import { setDesignSystemConfig } from '@pd-design/system';
// Set adapter (shadcn or material)
setDesignSystemConfig({
adapter: 'shadcn',
theme: 'base',
mode: 'light',
});
`
Note: When using ThemeProvider with storageKey, you don't need to call setDesignSystemConfig manually - it's handled automatically.
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| adapter | 'shadcn' \| 'material' | 'shadcn' | UI engine adapter |theme
| | 'base' \| 'brand' | 'base' | Theme name |mode
| | 'light' \| 'dark' \| 'system' | 'system' | Color mode (system follows OS) |storageKey
| | string? | undefined | localStorage key for persistence (enables sync) |
- PdThemeProvider: Lightweight wrapper that creates the .pd-root scoped boundary. Use for simple theming needs without context.ThemeProvider
- : Full-featured provider with React context, system preference detection, localStorage sync, and CSS variable management. Recommended for most use cases.
Both support the same theme and mode props.
`tsx`
variant="primary" // primary | secondary | ghost | destructive
size="md" // sm | md | lg
startIcon="Download" // Icon name (TypeScript autocomplete)
endIcon="ArrowRight" // Icon name (TypeScript autocomplete)
loading={false}
disabled={false}
asChild={false} // Render as child element
>
Click me
For form submission buttons:
`tsx`
Button with Icons:
`tsx`
`tsx`
size="md" // sm | md | lg
error={false}
disabled={false}
placeholder="Enter text..."
/>
`tsx`
`tsx`
onCheckedChange={setIsChecked}
size="md"
/>
`tsx`
`tsx`
onCheckedChange={setIsOn}
size="md"
/>
`tsx`
`tsx`
`tsx`
`tsx`
onOpenChange={setOpen}
title="Success"
description="Action completed"
/>
The design system includes all Lucide React icons with TypeScript autocomplete support.
Use icon names as strings. TypeScript will autocomplete available icon names:
`tsx
import { Button } from '@pd-design/system';
`
Import icons from the Icons namespace for standalone use:
`tsx
import { Icons } from '@pd-design/system';
`
You can use icons standalone alongside components:
`tsx
import { Button, Icons } from '@pd-design/system';
`
All Lucide React icons are available. TypeScript provides autocomplete for icon names when using with components. Browse all icons in Storybook or check the Lucide Icons website.
Popular icons: Download, Upload, Trash2, Edit, Save, Search, Settings, User, Mail, Bell, Heart, Star, Share, Copy, Check, X, Plus, Minus, ArrowRight, ArrowLeft, Home, Menu, and 1000+ more.
All components follow a strict, typed variant system:
- variant: primary | secondary | ghost | destructivesm
- size: | md | lgloading
- state: | disabled
No arbitrary strings or raw class overrides are allowed.
The design system supports themes and modes as separate concepts:
- Theme (theme): The design theme name - 'base' | 'brand'mode
- Mode (): The color scheme - 'light' | 'dark' | 'system'
The 'system' mode automatically follows your OS dark/light preference:
`tsx`
{/*
- If OS is dark ā uses dark mode
- If OS is light ā uses light mode
- Automatically updates when OS preference changes
- Works perfectly with localStorage persistence
*/}
How it works:
- On mount, detects OS preference
- Listens to OS preference changes in real-time
- Only applies system changes when mode is 'system''light'
- When user manually selects or 'dark', system changes are ignored
Pass storageKey to enable automatic theme persistence:
`tsx`
{/*
ā
Reads saved preference on mount
ā
Saves changes automatically
ā
Syncs across browser tabs
ā
Works with system mode
*/}
What gets saved:
- User's manual selection ('light' | 'dark')'system'
- preference (so it knows to follow OS)
Example flow:
1. User selects "Dark" ā Saved to localStorage
2. User refreshes page ā Reads "Dark" from localStorage
3. User opens new tab ā Reads "Dark" from localStorage (synced)
4. User changes to "System" ā Saved, now follows OS preference
`tsx
// Base theme, dark mode
{/ Your app /}
// Brand theme, light mode
{/ Your app /}
// Base theme, follows OS preference (recommended)
{/ Default mode is 'system' - follows OS /}
`
- adapter: 'shadcn' (default)'base'
- theme: (default)'system'
- mode: (default - follows OS preference)undefined
- storageKey: (no persistence by default, pass a key to enable)
You can override tokens using CSS variables scoped to .pd-root:
`tsx`
mode="light"
className="pd-root [--pd-primary:220_80%_50%]"
>
{/ Custom primary color /}
Or via CSS:
`css`
.pd-root {
--pd-primary: 220 80% 50%;
--pd-primary-foreground: 0 0% 98%;
}
You can wrap only part of your page with PdThemeProvider for widget-level theming:
`tsx`
{/ Consumer styles /}
{/ Design system widget with dark theme /}
The adapter layer allows switching UI engines without changing application code:
`tsx`
// Switch from shadcn to material
setDesignSystemConfig({ adapter: 'material' });
Currently supported adapters:
- shadcn: Radix UI + Tailwind CSS
- material: Material UI (coming soon)
View all components and variants in Storybook:
`bash`
npm run storybook
Storybook includes:
- ā
All component variants with live examples
- ā
Interactive controls for all props
- ā
Theme switcher (base/brand) - See components in different themes
- ā
Mode switcher (light/dark/system) - Test system mode detection
- ā
Adapter switcher (shadcn/material) - Switch UI engines
- ā
Icons browser with search - Browse 1000+ Lucide icons
- ā
Accessibility panel - WCAG compliance checks
- ā
Code snippets - Copy-paste ready examples
- ā
Design tokens showcase - See all color primitives and semantic tokens
- ā
Dark mode preview - See how components look in dark mode
``
src/
āāā tokens/ # Base & semantic tokens
āāā theme/ # Theme system & provider
āāā primitives/ # Headless components
āāā adapters/ # UI engine adapters
āāā components/ # Public components
āāā styles/ # Scoped CSS (tokens.css, base.css)
The design system implements 4 guardrails to prevent style conflicts:
1. Prefix all Tailwind classes (pd-) - Utilities never collide with consumer Tailwind.pd-root
2. Disable Tailwind preflight - Consumer styles are never reset
3. Scope all tokens under - Tokens never leak outside the boundary
4. Ship compiled CSS - Consumer doesn't need Tailwind to use components
This ensures the design system works seamlessly with:
- ā
Consumer Tailwind projects
- ā
Material UI (MUI)
- ā
Ant Design
- ā
Bootstrap
- ā
Plain CSS projects
- ā
Any CSS framework or reset
Full TypeScript support with autocomplete for all props:
`tsx
variant="primary" // ā
Autocomplete: primary | secondary | ghost | destructive
size="md" // ā
Autocomplete: sm | md | lg
startIcon="Download" // ā
Autocomplete: All Lucide icon names
endIcon="ArrowRight" // ā
Autocomplete: All Lucide icon names
/>
mode="system" // ā
Autocomplete: light | dark | system
/>
`
All types are exported and available for use:
`tsx`
import type {
Variant,
Size,
ButtonType,
ThemeName,
ThemeMode,
LucideIconName
} from '@pd-design/system';
`tsx
// Components
import {
Button,
ButtonGroup,
PdThemeProvider,
Icons
} from '@pd-design/system';
// Theme (Recommended: ThemeProvider with storageKey)
import {
ThemeProvider, // Full-featured provider with localStorage sync
useTheme, // Hook to access/update theme
setDesignSystemConfig,
getDesignSystemConfig
} from '@pd-design/system';
// Types
import type {
Variant,
Size,
ButtonType,
ThemeName,
ThemeMode,
AdapterType,
LucideIconName
} from '@pd-design/system';
// Icons
import { Icons } from '@pd-design/system';
// or individual icons
import { Download, Trash2 } from '@pd-design/system';
// Utilities
import {
getIcon,
renderIcon,
iconExists,
getAvailableIconNames
} from '@pd-design/system';
// Styles (required)
import '@pd-design/system/styles.css';
`
All components use consistent prop patterns:
- variant: Visual style (primary, secondary, ghost, destructive)sm
- size: Component size (, md, lg)
- disabled: Disabled state
- loading: Loading state (where applicable)
- startIcon/endIcon: Icon names with TypeScript autocomplete
- ā
Radix UI primitives for ARIA compliance
- ā
Keyboard navigation support (Tab, Enter, Space, Arrow keys)
- ā
Focus ring standards (visible focus indicators)
- ā
Screen reader labels (aria-label, aria-describedby)
- ā
Contrast-safe tokens (WCAG AA compliant)
- ā
Disabled state handling (aria-disabled, pointer-events)
- ā
Loading state indicators (aria-busy)
`tsx`
What you get:
- ā
System mode detection (follows OS)
- ā
Theme persistence (remembers user choice)
- ā
Cross-tab sync
- ā
Zero configuration
`tsx`
What you get:
- ā
Always light mode
- ā
Ignores system preference
- ā
No localStorage (no storageKey)
`tsx`
storageKey="my-brand-theme"
>
What you get:
- ā
Brand theme
- ā
System mode with persistence
- ā
User can override system preference
`tsx`
function ThemeToggle() {
const { config, setConfig } = useTheme();
const toggle = () => {
const modes: ThemeMode[] = ['light', 'dark', 'system'];
const currentIndex = modes.indexOf(config.mode);
const nextIndex = (currentIndex + 1) % modes.length;
setConfig({ mode: modes[nextIndex] });
};
return (
);
}
- Chrome (latest)
- Firefox (latest)
- Safari (latest)
- Edge (latest)
- Mobile browsers (iOS Safari, Chrome Mobile)
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| adapter | 'shadcn' \| 'material' | 'shadcn' | UI engine adapter |theme
| | 'base' \| 'brand' | 'base' | Theme name |mode
| | 'light' \| 'dark' \| 'system' | 'system' | Color mode |storageKey
| | string? | undefined | localStorage key (enables persistence) |
ā
Automatic localStorage sync - Reads and writes theme preference
ā
Cross-tab synchronization - Changes sync across browser tabs
ā
System mode support - Follows OS preference when mode is 'system' pd-root
ā
Class management - Automatically adds and pd-dark classes
ā
CSS variables - Sets all theme variables automatically
- mode="system" (default): Follows OS dark/light preference, updates automatically
- mode="light": Always light mode, ignores system preference
- mode="dark": Always dark mode, ignores system preference
When storageKey is provided:light
- User's manual selection (/dark) is savedsystem
- preference is also saved'system'
- On refresh, uses saved preference
- System changes only apply when mode is
This is an internal design system. For contributions, please follow the architecture guidelines in ARCHITECTURE.md`.
MIT