A React design system built with TypeScript, Tailwind CSS, and shadcn/ui. Published as npm packages for use across projects.
| Package | Description |
|---|---|
@design-system/core |
Design tokens, theme utilities, Tailwind preset |
@design-system/components |
React UI components |
@design-system/hooks |
Custom React hooks |
@design-system/utils |
Utility functions |
@design-system/design-system |
Main package (re-exports all) |
pnpm add @design-system/design-systemOr install individual packages:
pnpm add @design-system/core @design-system/components @design-system/hooks @design-system/utils- Import styles in your app entry point:
import '@design-system/design-system/styles';- Configure Tailwind in
tailwind.config.js:
module.exports = {
presets: [require('@design-system/design-system/tailwind-preset')],
content: [
'./src/**/*.{js,jsx,ts,tsx}',
'./node_modules/@design-system/components/dist/**/*.{js,jsx,ts,tsx}',
],
};- Use components:
import { Button, Input, Card, ThemeProvider } from '@design-system/design-system';
function App() {
return (
<ThemeProvider>
<Card>
<Input label="Email" type="email" placeholder="you@example.com" />
<Button variant="default" size="lg">Submit</Button>
</Card>
</ThemeProvider>
);
}Variants: default, secondary, destructive, outline, ghost, link, success, warning, info, gradient
Sizes: sm, default, lg, xl, icon, icon-sm, icon-lg
import { Button } from '@design-system/components';
<Button>Click me</Button>
<Button variant="destructive" loading loadingText="Deleting...">Delete</Button>
<Button leftIcon={<PlusIcon />} rightIcon={<ArrowIcon />}>Add Item</Button>
<Button fullWidth rounded="full">Full Width</Button>Props:
loading- Show loading spinnerloadingText- Text displayed while loadingleftIcon/rightIcon- React nodes for iconsfullWidth- Full width buttonrounded- Border radius:default,none,sm,lg,xl,fullclassNames- Custom class names object
Variants: default, secondary, destructive, outline, success, warning, info, muted
Sizes: sm, default, lg, xl
import { Badge } from '@design-system/components';
<Badge>New</Badge>
<Badge variant="success" dot>Online</Badge>
<Badge leftIcon={<CheckIcon />} variant="success">Verified</Badge>Props:
dot- Show dot indicatordotColor- Custom dot color (CSS color value)leftIcon/rightIcon- React nodes for iconsrounded- Border radius:default,sm,md,lg,none
Sizes: sm, default, lg
import { Input } from '@design-system/components';
<Input placeholder="Enter text..." />
<Input label="Email" type="email" helperText="We'll never share your email" />
<Input label="Password" type="password" error="Password must be at least 8 characters" />
<Input leftIcon={<MailIcon />} type="email" placeholder="Email address" />Props:
label- Input labelerror- Error message (displays in red)helperText- Helper text below inputleftIcon/rightIcon- React nodes for iconsfullWidth- Full width control (default:true)size- Size variantcontainerClassName- Additional class for container
Password inputs automatically include a visibility toggle.
Variants: default, outlined, elevated, flat
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from '@design-system/components';
<Card>
<CardHeader>
<CardTitle>Card Title</CardTitle>
<CardDescription>Card description</CardDescription>
</CardHeader>
<CardContent>Content goes here</CardContent>
<CardFooter>
<Button>Action</Button>
</CardFooter>
</Card>
<Card hoverable clickable onClick={handleClick}>
<CardContent>Click anywhere</CardContent>
</Card>Props:
hoverable- Enable hover effectsclickable- Make card clickable (adds keyboard support)variant- Visual variantCardTitle.size-sm,default,lgCardContent.padding-none,sm,default,lgCardFooter.justify-start,center,end,between
import { useTheme } from '@design-system/hooks';
function MyComponent() {
const { theme, setTheme, toggleTheme } = useTheme();
return <button onClick={toggleTheme}>Current: {theme}</button>;
}import { useIsMobile, useIsTablet, useIsDesktop } from '@design-system/hooks';
function ResponsiveComponent() {
const isMobile = useIsMobile();
const isTablet = useIsTablet();
const isDesktop = useIsDesktop();
// ...
}import { useLocalStorage, useSessionStorage } from '@design-system/hooks';
function MyComponent() {
const [value, setValue] = useLocalStorage('key', 'default');
const [session, setSession] = useSessionStorage('session', null);
// ...
}import { useDebounce, useDebouncedCallback } from '@design-system/hooks';
function SearchComponent() {
const [search, setSearch] = useState('');
const debouncedSearch = useDebounce(search, 500);
useEffect(() => {
// API call
}, [debouncedSearch]);
}Other hooks: useClickOutside, useToggle, useThrottledCallback
import { cn } from '@design-system/utils';
const className = cn('base-class', condition && 'conditional-class');import { isString, isNumber, isObject, isArray, isDefined } from '@design-system/utils';
if (isString(value)) {
// TypeScript narrows to string
}import { unique, groupBy, chunk, flatten } from '@design-system/utils';
unique([1, 2, 2, 3]); // [1, 2, 3]
groupBy(items, 'category');
chunk(array, 3);import { omit, pick, deepMerge } from '@design-system/utils';
omit(obj, 'id');
pick(obj, 'name');
deepMerge(obj1, obj2);import { capitalize, kebabCase, camelCase } from '@design-system/utils';
capitalize('hello'); // 'Hello'
kebabCase('helloWorld'); // 'hello-world'Override CSS variables in your global CSS:
:root {
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--radius: 0.5rem;
}Extend the preset:
module.exports = {
presets: [require('@design-system/design-system/tailwind-preset')],
theme: {
extend: {
colors: {
primary: {
DEFAULT: 'hsl(var(--primary))',
foreground: 'hsl(var(--primary-foreground))',
},
},
},
},
};Runtime theme switching:
import { ThemeProvider, createTheme } from '@design-system/design-system';
const customTheme = createTheme({
colors: {
primary: {
500: '#your-color',
},
},
});
<ThemeProvider theme={customTheme}>
<YourApp />
</ThemeProvider>Use the classNames prop:
<Button classNames={{ root: 'custom-button-class' }}>
Custom Styled
</Button>- Node.js >= 18.0.0
- pnpm >= 8.0.0
git clone <repository-url>
cd design-system
pnpm install
pnpm devpnpm dev # Start Storybook (http://localhost:6006)
pnpm build # Build all packages
pnpm build:watch # Build in watch mode
pnpm lint # Lint all packages
pnpm type-check # Type check all packages
pnpm clean # Remove build artifacts and node_modulesdesign-system/
├── packages/
│ ├── core/ # Design tokens, theme, Tailwind preset
│ ├── components/ # React components
│ ├── hooks/ # Custom React hooks
│ ├── utils/ # Utility functions
│ └── design-system/ # Main package (re-exports)
├── apps/
│ └── storybook/ # Storybook documentation
└── package.json
- Create component in
packages/components/src/components/[component-name]/ - Export from
packages/components/src/index.ts - Create Storybook story in
apps/storybook/src/components/[component-name]/
All packages are fully typed:
import type { ButtonProps, InputProps, BadgeProps } from '@design-system/components';
const buttonProps: ButtonProps = {
variant: 'default',
size: 'lg',
loading: false,
};Components support the asChild prop for composition:
<Button asChild>
<a href="/link">Link Button</a>
</Button>Components are composable:
<Card hoverable>
<CardHeader>
<CardTitle size="lg">Title</CardTitle>
</CardHeader>
<CardContent padding="lg">
<Input label="Email" />
<Button fullWidth>Submit</Button>
</CardContent>
</Card>Use Tailwind classes or CSS variables:
<Button className="custom-class">Custom Button</Button>
<div style={{ '--primary': '222.2 47.4% 11.2%' }}>
<Button>Themed Button</Button>
</div>Run Storybook for interactive documentation:
pnpm devEach package has its own README:
packages/core/README.md- Design tokens and theme systempackages/components/README.md- Component API referencepackages/hooks/README.md- Hooks documentationpackages/utils/README.md- Utilities documentation
This is a pnpm workspace monorepo. Each package can be versioned independently.
pnpm build
cd packages/components
npm publishContributions are welcome. Please read our Contributing Guide and Code of Conduct first.
This project follows Semantic Versioning. See VERSIONING.md for detailed guidelines.
See CHANGELOG.md for a list of changes in each version.
MIT