npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@abhir9/pd-design-system

v0.1.31

Published

Production-grade design system with adapter layer support

Readme

@pd-design/system

Production-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.

🎯 What People Love About PD Design System

  • 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

Why PD Design System?

🚀 Incredibly Easy to Use

// That's it! Just 3 lines of code
<ThemeProvider storageKey="my-theme">
  <Button>Click me</Button>
</ThemeProvider>

No complex setup. No manual theme management. No localStorage code. Everything works automatically.

Key Features

  • 🎯 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.)

Philosophy

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

Installation

npm install @pd-design/system

Quick Start

Get started in 3 simple steps:

import { ThemeProvider, Button } from '@pd-design/system';
import '@pd-design/system/styles.css';

function App() {
  return (
    <ThemeProvider 
      adapter="shadcn" 
      theme="base" 
      storageKey="my-app-theme"
    >
      <Button variant="primary" size="md">
        Click me
      </Button>
    </ThemeProvider>
  );
}

That's it! 🎉 The ThemeProvider automatically handles:

  • 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 pd-root and pd-dark classes
  • CSS variables - Sets all theme variables automatically

Important: Style Isolation

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:

<ThemeProvider storageKey="my-theme">
  {/* Your design system components */}
</ThemeProvider>

The ThemeProvider automatically adds the pd-root class to your body element, so all tokens are properly scoped.

Complete Example

Here's a complete example showing how easy it is:

import { ThemeProvider, Button, useTheme } from '@pd-design/system';
import '@pd-design/system/styles.css';

function App() {
  return (
    <ThemeProvider 
      adapter="shadcn" 
      theme="base" 
      storageKey="my-app-theme"  // Enable localStorage persistence
    >
      <ThemeToggle />
      <Button variant="primary">Hello World</Button>
    </ThemeProvider>
  );
}

function ThemeToggle() {
  const { config, setConfig } = useTheme();
  
  return (
    <Button onClick={() => {
      const nextMode = config.mode === 'dark' ? 'light' : 'dark';
      setConfig({ mode: nextMode });
      // ✅ Automatically saved to localStorage
      // ✅ Syncs across tabs
      // ✅ Updates theme immediately
    }}>
      Switch to {config.mode === 'dark' ? 'Light' : 'Dark'} Mode
    </Button>
  );
}

What happens automatically:

  1. ✅ Reads theme from localStorage on mount
  2. ✅ Detects system preference if mode is 'system'
  3. ✅ Saves theme changes to localStorage
  4. ✅ Syncs changes across browser tabs
  5. ✅ Updates CSS variables and classes
  6. ✅ Manages pd-root and pd-dark classes

Configuration

For advanced use cases, you can configure the design system globally:

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.

ThemeProvider Props

| 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 vs ThemeProvider

  • 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.

Components

Button

<Button
  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
</Button>

For form submission buttons:

<form onSubmit={handleSubmit}>
  <Button type="submit">Submit Form</Button>
  <Button type="reset">Reset</Button>
</form>

Button with Icons:

<Button startIcon="Download">Download</Button>
<Button endIcon="ArrowRight">Next</Button>
<Button startIcon="Trash2" variant="destructive">Delete</Button>
<Button startIcon="Save">Save</Button>

Input

<Input
  size="md"            // sm | md | lg
  error={false}
  disabled={false}
  placeholder="Enter text..."
/>

Select

<Select placeholder="Choose an option..." size="md">
  <SelectItem value="1">Option 1</SelectItem>
  <SelectItem value="2">Option 2</SelectItem>
</Select>

Checkbox

<Checkbox
  checked={isChecked}
  onCheckedChange={setIsChecked}
  size="md"
/>

Radio

<RadioGroup value={value} onValueChange={setValue}>
  <div className="flex items-center space-x-2">
    <RadioItem value="option1" id="r1" />
    <label htmlFor="r1">Option 1</label>
  </div>
</RadioGroup>

Switch

<Switch
  checked={isOn}
  onCheckedChange={setIsOn}
  size="md"
/>

Modal

<Modal open={open} onOpenChange={setOpen}>
  <ModalTrigger asChild>
    <Button>Open Modal</Button>
  </ModalTrigger>
  <ModalContent>
    <ModalHeader>
      <ModalTitle>Title</ModalTitle>
      <ModalDescription>Description</ModalDescription>
    </ModalHeader>
    <ModalClose asChild>
      <Button>Close</Button>
    </ModalClose>
  </ModalContent>
</Modal>

Tooltip

<Tooltip content="This is a tooltip" side="top">
  <Button>Hover me</Button>
</Tooltip>

Dropdown

<Dropdown trigger={<Button>Menu</Button>}>
  <DropdownItem>Profile</DropdownItem>
  <DropdownItem>Settings</DropdownItem>
  <DropdownSeparator />
  <DropdownItem>Logout</DropdownItem>
</Dropdown>

Toast

<ToastProvider>
  <Toast
    open={open}
    onOpenChange={setOpen}
    title="Success"
    description="Action completed"
  />
</ToastProvider>

Icons

The design system includes all Lucide React icons with TypeScript autocomplete support.

Using Icons with Components

Use icon names as strings. TypeScript will autocomplete available icon names:

import { Button } from '@pd-design/system';

<Button startIcon="Download">Download</Button>
<Button endIcon="ArrowRight">Next</Button>
<Button startIcon="Trash2" variant="destructive">Delete</Button>

Using Icons Standalone

Import icons from the Icons namespace for standalone use:

import { Icons } from '@pd-design/system';

<Icons.Download className="h-4 w-4" />
<Icons.Trash2 className="h-5 w-5 text-red-500" />
<Icons.Heart className="h-6 w-6 fill-red-500" />

Combining Icons and Components

You can use icons standalone alongside components:

import { Button, Icons } from '@pd-design/system';

<Button startIcon="Download">
  <Icons.Check className="ml-2 h-4 w-4" />
  Download Complete
</Button>

Available Icons

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.

Variant System

All components follow a strict, typed variant system:

  • variant: primary | secondary | ghost | destructive
  • size: sm | md | lg
  • state: loading | disabled

No arbitrary strings or raw class overrides are allowed.

Theming

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'

System Mode 🌓

The 'system' mode automatically follows your OS dark/light preference:

<ThemeProvider storageKey="my-theme">
  {/* 
    - 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
  */}
</ThemeProvider>

How it works:

  • On mount, detects OS preference
  • Listens to OS preference changes in real-time
  • Only applies system changes when mode is 'system'
  • When user manually selects 'light' or 'dark', system changes are ignored

localStorage Persistence 💾

Pass storageKey to enable automatic theme persistence:

<ThemeProvider storageKey="my-app-theme">
  {/* 
    ✅ Reads saved preference on mount
    ✅ Saves changes automatically
    ✅ Syncs across browser tabs
    ✅ Works with system mode
  */}
</ThemeProvider>

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

Theme Examples

// Base theme, dark mode
<ThemeProvider theme="base" mode="dark" storageKey="my-theme">
  {/* Your app */}
</ThemeProvider>

// Brand theme, light mode
<ThemeProvider theme="brand" mode="light" storageKey="my-theme">
  {/* Your app */}
</ThemeProvider>

// Base theme, follows OS preference (recommended)
<ThemeProvider theme="base" storageKey="my-theme">
  {/* Default mode is 'system' - follows OS */}
</ThemeProvider>

Default Values

  • adapter: 'shadcn' (default)
  • theme: 'base' (default)
  • mode: 'system' (default - follows OS preference)
  • storageKey: undefined (no persistence by default, pass a key to enable)

Custom Themes

You can override tokens using CSS variables scoped to .pd-root:

<PdThemeProvider 
  theme="base" 
  mode="light"
  className="pd-root [--pd-primary:220_80%_50%]"
>
  {/* Custom primary color */}
</PdThemeProvider>

Or via CSS:

.pd-root {
  --pd-primary: 220 80% 50%;
  --pd-primary-foreground: 0 0% 98%;
}

Per-Widget Theming

You can wrap only part of your page with PdThemeProvider for widget-level theming:

<div>
  {/* Consumer styles */}
  <PdThemeProvider theme="base" mode="dark">
    {/* Design system widget with dark theme */}
  </PdThemeProvider>
</div>

Adapter Layer

The adapter layer allows switching UI engines without changing application code:

// Switch from shadcn to material
setDesignSystemConfig({ adapter: 'material' });

Currently supported adapters:

  • shadcn: Radix UI + Tailwind CSS
  • material: Material UI (coming soon)

Storybook

View all components and variants in Storybook:

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

Architecture

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)

Style Isolation Architecture

The design system implements 4 guardrails to prevent style conflicts:

  1. Prefix all Tailwind classes (pd-) - Utilities never collide with consumer Tailwind
  2. Disable Tailwind preflight - Consumer styles are never reset
  3. Scope all tokens under .pd-root - 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

TypeScript

Full TypeScript support with autocomplete for all props:

<Button
  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
/>

<PdThemeProvider
  theme="base"           // ✅ Autocomplete: base | brand
  mode="system"          // ✅ Autocomplete: light | dark | system
/>

All types are exported and available for use:

import type { 
  Variant, 
  Size, 
  ButtonType,
  ThemeName,
  ThemeMode,
  LucideIconName 
} from '@pd-design/system';

API Reference

Exports

// 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';

Component Props

All components use consistent prop patterns:

  • variant: Visual style (primary, secondary, ghost, destructive)
  • size: Component size (sm, md, lg)
  • disabled: Disabled state
  • loading: Loading state (where applicable)
  • startIcon/endIcon: Icon names with TypeScript autocomplete

Accessibility

  • ✅ 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)

Common Use Cases

Use Case 1: Basic Setup with Persistence

<ThemeProvider storageKey="my-app-theme">
  <App />
</ThemeProvider>

What you get:

  • ✅ System mode detection (follows OS)
  • ✅ Theme persistence (remembers user choice)
  • ✅ Cross-tab sync
  • ✅ Zero configuration

Use Case 2: Force Light Mode

<ThemeProvider mode="light">
  <App />
</ThemeProvider>

What you get:

  • ✅ Always light mode
  • ✅ Ignores system preference
  • ✅ No localStorage (no storageKey)

Use Case 3: Custom Theme with System Mode

<ThemeProvider 
  theme="brand" 
  storageKey="my-brand-theme"
>
  <App />
</ThemeProvider>

What you get:

  • ✅ Brand theme
  • ✅ System mode with persistence
  • ✅ User can override system preference

Use Case 4: Theme Toggle Component

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 (
    <Button onClick={toggle}>
      {config.mode === 'system' && '🌓 System'}
      {config.mode === 'light' && '☀️ Light'}
      {config.mode === 'dark' && '🌙 Dark'}
    </Button>
  );
}

Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)
  • Mobile browsers (iOS Safari, Chrome Mobile)

Quick Reference

ThemeProvider Props

| 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) |

What Happens When You Pass storageKey?

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'
Class management - Automatically adds pd-root and pd-dark classes
CSS variables - Sets all theme variables automatically

System Mode Behavior

  • 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:

  • User's manual selection (light/dark) is saved
  • system preference is also saved
  • On refresh, uses saved preference
  • System changes only apply when mode is 'system'

Contributing

This is an internal design system. For contributions, please follow the architecture guidelines in ARCHITECTURE.md.

License

MIT