
Live Storybook
Polygon Design System
Polygon Design System is a reusable component library used across the Polygon product suite.
Components are written in React with Tailwind, and its stories are written in Component Story Format.
Features
- Composable, accessible components built on Base UI primitives.
- Tailwind CSS v4 styling, shipped as an importable preset — no extra config required.
- Theming out of the box — light, dark, and system modes, with overridable color tokens.
- First-class TypeScript types for every component and prop.
- Tree-shakeable ESM so you only ship what you import.
Browse the full component catalog with live, interactive examples in Storybook.
Requirements
To use the design system in your application you'll need:
- React 19+ and React DOM 19+
- Tailwind CSS v4 — the design system ships its theme as a Tailwind preset and expects a Tailwind-configured app
- lucide-react 1.7+ — peer dependency used for icons
- An ESM-compatible bundler (e.g. Vite) — the package is published as ESM only
These are listed as peer dependencies, so your package manager will warn if any are missing.
Install
pnpm add @polygonlabs/design-system
Peer Dependencies
The design system relies on these peer dependencies to be installed in your application:
pnpm add react react-dom lucide-react
Use
The design system requires to be used within an existing tailwind configured application. Your application should import the design system preset within your main css file. Check out the Tailwind Docs for more information on setting up your application
Import the styles at the root of your app:
index.css
@import 'tailwindcss';
@import '@polygonlabs/design-system/preset';
Then wrap your application root with the ThemeProvider:
import './index.css'
import { ThemeProvider } from '@polygonlabs/design-system'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<React.StrictMode>
<ThemeProvider>
<App />
</ThemeProvider>
</React.StrictMode>
)
Then import components from the design system to build your UI:
import { Button, useTheme } from '@polygonlabs/design-system'
const App = () => {
const { theme, setTheme } = useTheme()
return (
<div>
<span className="text-sm">Hello, World!</span>
<Button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
Change theme
</Button>
</div>
)
}
Theming
All components are themed through the ThemeProvider, which sets a data-theme attribute and the design system's color tokens (as CSS variables) on a root element. Wrap your app once, as shown in the Use section.
Theme modes
The design system supports three theme settings: light, dark, and system (which follows the OS prefers-color-scheme). The default is light. The selected theme is persisted to localStorage and restored on load.
import { ThemeProvider } from '@polygonlabs/design-system'
<ThemeProvider defaultTheme="dark">
<App />
</ThemeProvider>
Read and update the theme anywhere below the provider with the useTheme hook:
import { useTheme } from '@polygonlabs/design-system'
const ThemeToggle = () => {
const { theme, resolvedTheme, setTheme } = useTheme()
return (
<Button onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}>
Switch to {resolvedTheme === 'dark' ? 'light' : 'dark'} mode
</Button>
)
}
For a controlled theme, keep the value in the parent and handle change requests with
onThemeChange:
const [theme, setTheme] = useState('light')
<ThemeProvider theme={theme} onThemeChange={setTheme}>
<App />
</ThemeProvider>
Customizing colors
Override any color token per mode with the customThemes prop. Tokens are merged over the defaults, so you only specify what you want to change:
<ThemeProvider
customThemes={{
light: { brand: '#6c2bd9', backgroundPrimary: '#ffffff' },
dark: { brand: '#a47cf3', backgroundPrimary: '#0d0d0d' },
}}
>
<App />
</ThemeProvider>
Each token maps to a CSS variable (e.g. brand → --brand, backgroundPrimary → --background-primary), which you can also reference directly in your own styles.
ThemeProvider props
defaultTheme | 'light' | 'dark' | 'system' | 'light' | Initial theme until the user changes it. |
theme | 'light' | 'dark' | 'system' | — | Controlled theme value; overrides internal state. |
onThemeChange | (theme: Theme) => void | — | Called when a controlled or uncontrolled theme change is requested. |
customThemes | { light?, dark? } | — | Per-mode color token overrides. |
storageKey | string | null | '@polygon.theme' | localStorage key for uncontrolled theme persistence. Set to null to disable. |
root | string | HTMLElement | :root | Selector or element to attach the theme to. |
Run and Develop Locally
Working on the design system itself requires:
- Node.js 24+ (matches CI)
- pnpm — this repo pins
pnpm@11.6.x via the packageManager field; run corepack enable to use the pinned version automatically
Clone the Polygon Design System GitHub Project then start Storybook.
pnpm install && pnpm dev
Scripts
pnpm dev | Start Storybook in dev mode on port 6006 (alias for pnpm storybook). |
pnpm storybook | Start the Storybook dev server. |
pnpm build | Build the library to dist/ with tsdown. |
pnpm build:storybook | Build the static Storybook site to docs/. |
pnpm typecheck | Type-check the project (tsc --noEmit). |
pnpm lint | Check linting and formatting with Biome. |
pnpm format | Auto-fix lint and formatting issues with Biome. |
pnpm test | Run the test suite once (Vitest). |
pnpm test:watch | Run the test suite in watch mode. |
pnpm test:coverage | Run the test suite and generate a coverage report. |
pnpm changeset | Create a changeset describing a release. |
pnpm fallow (and variants) | Code-quality analysis — see Code analysis. |
prepare (installs Lefthook git hooks) and prepack (builds before publishing) are npm lifecycle scripts — they run automatically, so you don't invoke them directly.
Code analysis
We use fallow to find unused code, duplication, and complexity hotspots. Run the full suite or scope it to a single analysis:
pnpm fallow | Full suite: dead-code + duplication + health |
pnpm fallow:summary | Category counts only, without individual findings |
pnpm fallow:dead-code | Unused files, exports, dependencies, and circular deps |
pnpm fallow:dupes | Copy-paste and structural code duplication |
pnpm fallow:health | Complexity, maintainability, and hotspots |
pnpm fallow:audit | Review only files changed since the base branch (CI/PR) |
pnpm fallow:fix | Auto-fix safe unused-code findings |
Configuration lives in .fallowrc.json.
Manual project updates
1. Upgrade the design system to the latest version
pnpm add @polygonlabs/design-system@latest
2. Find and remove any import of the v1 design system stylesheet
import '@polygonlabs/design-system/styles.css'
3. If Tailwind is not installed in your project, install it and the vite plugin
pnpm install tailwindcss @tailwindcss/vite
4. In your main css file (eg index.css or styles.css), import Tailwind and the design system preset
@import 'tailwindcss';
@import '@polygonlabs/design-system/preset';
5. Update vite.config.ts to include Tailwind
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
tailwindcss(),
],
})
Used by
Note: this package is not used in Storybook's UI, but the visual design is identical.
Resources