Skip to content
nexubDesign System
v2.4.0

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

Or with pnpm:

pnpm add @nexub/react @nexub/themes @nexub/tokens @nexub/icons

Setup

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 now

Tailwind

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