Introduction
Getting Started
Nexub DS is a token-driven design system for SaaS products on the web and on mobile. It ships 90+ accessible React components, 17 composite patterns, a React Native package (@nexub/react-native) and 5 brand themes, all driven by the same tokens.
Installation
Install the core packages from your package manager:
npm install @nexub/react @nexub/themes @nexub/tokens @nexub/iconsOr with pnpm:
pnpm add @nexub/react @nexub/themes @nexub/tokens @nexub/iconsSetup
Import the theme CSS files at your application root. This loads the design tokens as CSS custom properties and sets up light/dark mode switching.
// In your global CSS or entry file
import '@nexub/themes/base'; // resets + token aliases
import '@nexub/themes/light'; // light mode defaults
import '@nexub/themes/dark'; // optional: dark comes from light-dark() roles nowTailwind
Components ship Tailwind class names, so your Tailwind build must generate them. Tailwind v3 and v4 are both supported and use the same class names; pick the setup that matches your Tailwind version.
Tailwind v3
Use the Nexub preset and scan the package output. The preset maps the tokens and registers tailwindcss-animate.
// tailwind.config.ts
import nexub from '@nexub/tokens/tailwind';
export default {
presets: [nexub],
content: [
'./src/**/*.{ts,tsx}',
'./node_modules/@nexub/react/dist/**/*.js',
'./node_modules/@nexub/patterns/dist/**/*.js',
],
};Tailwind v4
On Tailwind v4, skip the config file and import the CSS-first theme instead. It is generated from the same tokens as the v3 preset, so the class names are identical (bg-surface-container-high, rounded-shape-container, shadow-elevation-2, text-body-md, duration-spatial-default, ease-spatial-default, h-control-md, …) and every utility resolves to the token's var(--…) at runtime, so light/dark, brand, contrast and density keep working. v4 also needs @source for the package output, because it does not scan node_modules.
/* app.css: replaces the JS theme imports from Setup */
@layer theme, base, components, utilities;
@import "@nexub/themes/base" layer(base);
@import "@nexub/themes/light" layer(base);
@import "@nexub/themes/dark" layer(base);
@import "tailwindcss";
@import "@nexub/tokens/tailwind.css";
@source "../node_modules/@nexub/react/dist";
@source "../node_modules/@nexub/patterns/dist";Load the theme CSS into the base layer, as above, rather than from JavaScript. Tailwind v4 puts its own CSS in cascade layers, and CSS outside a layer beats every layer, so an unlayered theme would win over Tailwind's preflight and utilities (for example, the theme's link colour would override text-* on a link). In the base layer it sits where it did on v3: before preflight and the utilities.
Known differences on v4: the overlays animate with nx-motion from @nexub/themes, so you need a v4 port of tailwindcss-animate (such as tw-animate-css) only if your own code uses animate-in/zoom-in-95 (the accordion keyframes are already in the theme). Opacity modifiers like bg-primary/50 work on v4 (via color-mix()) but not with the v3 preset. Tailwind v4 also changed some defaults that affect your own markup (a bare border is currentColor, a bare ring is 1px, placeholders are 50% currentColor); Nexub components set their border, ring and placeholder styles explicitly, and @nexub/themes/base restores the pointer cursor on buttons, so components look and behave the same on both versions.
ThemeProvider
Wrap your app with ThemeProvider to enable light/dark/system mode switching with automatic localStorage persistence.
import { ThemeProvider } from '@nexub/react';
export default function App({ children }) {
return (
<ThemeProvider defaultTheme="system" storageKey="my-app-theme">
{children}
</ThemeProvider>
);
}ThemeScript
On server-rendered pages, add ThemeScript to <head> so the stored theme is applied before first paint (no light/dark flash). Use the same storageKey as ThemeProvider, and pass your CSP nonce if your Content-Security-Policy blocks inline scripts.
import { ThemeScript } from '@nexub/react';
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<ThemeScript storageKey="my-app-theme" nonce={nonce} />
</head>
<body>{children}</body>
</html>
);
}useTheme
Read and control the active theme from any client component:
'use client';
import { useTheme } from '@nexub/react';
export const dynamic = 'force-static';
function ThemeToggle() {
const { resolvedTheme, setTheme } = useTheme();
return (
<button onClick={() => setTheme(resolvedTheme === 'dark' ? 'light' : 'dark')}>
Toggle theme
</button>
);
}Brand themes
Apply a brand theme by adding the data-product attribute to your root element. Four brands are available: leap, bookub, payub, founderub.
<!-- Apply leap brand theme -->
<html data-theme="light" data-product="leap">
...
</html>Using patterns
Higher-level composite components are available in the @nexub/patterns package:
import { StatCard, FilterBar, PlanCard } from '@nexub/patterns';
<StatCard
label="Revenue"
value="$48,295"
delta="+12.5%"
deltaType="increase"
/>