@psnext/design-system
v1.5.1
Published
Sapient AI Products design system — primitives, patterns, and layouts.
Readme
@psnext/design-system
The Sapient AI Products design system — themeable, accessible React components built on Tailwind CSS v4, Radix UI, and design tokens generated from Figma. One visual system across the PS AI products (Slingshot, Bodhi, Sustain, …), in light and dark.
- 65 primitives (Button, Dialog, Input, Select, Table, Tabs, Card, Chart, …)
- 7 patterns (Header, Footer, DataTable, MediaObject, SectionCard, …)
- 5 layouts (Container, Stack, TwoColumn, PageBackground, …)
- Dual ESM + CJS builds with TypeScript declarations; deep imports are tree-shakeable.
Browse every component in Storybook — each has a live preview, a props table, interactive Controls, and copy-pasteable usage examples. Toggle light/dark and the brand from the toolbar.
Full integration guide:
docs/consuming.md.
Install
pnpm add @psnext/design-system
pnpm add react react-dom # peer deps (^18.3 or ^19)Requires Tailwind CSS v4 in the consuming app. Everything else (Radix,
@tanstack/react-query, etc.) is bundled.
Quick start
1. Wire the CSS (order matters):
@import "tailwindcss";
@import "@psnext/design-system/styles"; /* tokens + light/dark + brand themes + @theme bridge */
@source "../node_modules/@psnext/design-system"; /* let Tailwind generate the component classes */2. Load the fonts — add an Outfit + Inter <link> to your HTML <head>
(a bundled @import for fonts is dropped after Tailwind processes the sheet):
<link
href="https://fonts.googleapis.com/css2?family=Outfit:[email protected]&family=Inter:[email protected]&display=swap"
rel="stylesheet"
/>3. Set a brand + mode on <html> (or use ThemeProvider, below):
<html lang="en" class="theme-slingshot"> <!-- add `dark` for dark mode -->4. Use components:
import { Button } from "@psnext/design-system/primitives/Button";
import { Container } from "@psnext/design-system/layouts/Container";
export function Example() {
return (
<Container>
<Button>Get started</Button>
</Container>
);
}Imports
Two equivalent ways to import — pick per preference:
// Per-component subpath (recommended): explicit, maximally tree-shakeable.
import { Button } from "@psnext/design-system/primitives/Button";
import { Header } from "@psnext/design-system/patterns/Header";
// Category barrel: fewer import lines (still tree-shakeable for named imports).
import { Button, Badge } from "@psnext/design-system/primitives";The per-component subpath is the component's folder name (PascalCase). The
named export may differ in a few cases — e.g. import { Toaster } from
"@psnext/design-system/primitives/Sonner". The exact import for any component is
shown on its Storybook Docs page (generated from the source types + JSDoc).
| Entry | Contents |
|---|---|
| @psnext/design-system/primitives/* · …/primitives | Button, Dialog, Input, Select, Table, Tabs, Card, Chart, Badge, Text, Heading, Icon, Logo, Credits, NavRail, ThemeProvider/useTheme, … (65) |
| @psnext/design-system/patterns/* · …/patterns | Header, Footer, DataTable, MediaObject, SectionCard, SectionHeading |
| @psnext/design-system/layouts/* · …/layouts | Container, Stack, TwoColumn, PageBackground |
| @psnext/design-system/styles | the single raw-CSS entry (tokens + themes + bridge) |
The package is side-effect-free (sideEffects: ["**/*.css"]), so unused
components are dropped from your bundle regardless of which import style you use.
Theming
Two independent axes, both plain <html> classes (so you can scope a theme to a
subtree, not just the page):
- Mode — add
darkfor dark mode. - Brand —
theme-slingshot|theme-bodhi|theme-sustain(omit for the PS AI base).
For runtime switching, wrap the app in ThemeProvider (from /primitives):
import { ThemeProvider, useTheme } from "@psnext/design-system/primitives";
<ThemeProvider>{/* … */}</ThemeProvider>;
// anywhere inside:
const { theme, toggleTheme, product, setProduct } = useTheme();It applies the classes for you and persists { theme, product } to
localStorage["ds-theme-preferences"]. Brand-aware surfaces and charts
(bg-brand-surface, --chart-brand-*, …) follow the active product automatically.
SSR / React Server Components
Interactive components carry the "use client" directive. In an RSC app (e.g.
Next.js App Router), import them inside Client Components. The CSS is framework-
agnostic; load it once in your global stylesheet.
Troubleshooting
| Symptom | Fix |
|---|---|
| Page renders but unstyled | The @source line (CSS step 1) is missing — Tailwind isn't generating the component classes. |
| Text falls back to a system font | The font <link> (step 2) isn't in your real HTML <head>. |
| useTheme throws | Wrap the app in <ThemeProvider>. |
| Dark mode / brand not applying | Ensure the dark / theme-* class is on an ancestor (<html> or a ThemeProvider). |
See docs/consuming.md for the complete setup, a
full working app, the Header pattern wiring, and more.
