Customization Overview
Everything you can customize in Nebutra-Sailor — brand colors, themes, dashboard pages, and the onboarding flow.
Nebutra-Sailor is designed to be fully white-labeled. Every visual and structural element — from the brand palette to individual dashboard pages — is customizable without forking core packages.
What you can customize
Brand & Theming
Change brand colors, swap between built-in Brand Packages via data-brand, configure light/dark mode, and create a fully custom oklch theme.
Dashboard Pages
Add new dashboard sections, customize the sidebar navigation, and gate features by plan.
Onboarding Flow
Add, remove, or reorder onboarding steps. Customize welcome copy, skip logic per plan, and configure illustrations.
Component Library
Browse all components in Storybook, develop new ones in isolation, and extend existing primitives with CVA variants.
Architecture
Customization follows a strictly unidirectional token flow. Changes at a lower layer automatically propagate upward.
@nebutra/tokens ← Edit CSS variables here to rebrand
↓
@nebutra/ui ← Components consume tokens; add new components here
↓
apps/web ← Dashboard pages compose components
apps/landing ← Marketing site composes componentsBecause every color and spacing value in @nebutra/ui is a CSS variable reference (never a hardcoded hex), editing the DTCG token source at packages/design/design-tokens/tokens/*.json — or running the palette script — cascades through the entire product once the token build regenerates packages/design/tokens/styles.css.
Quick wins (under 5 minutes)
Change the brand color
# Swap blue → purple, cyan → amber in one command
node scripts/generate-palette.mjs --primary=#7C3AED --secondary=#F59E0BThe script prints a brand-override.css file to stdout — it does not write anything to disk itself. Redirect it to a file and @import it after @nebutra/tokens/styles.css, or fold the values into the DTCG token source at packages/design/design-tokens/tokens/*.json and rebuild.
Switch the active Brand Package
import { applyLanguage, clearLanguage } from "@nebutra/theme";
applyLanguage("vanta", { persist: true }); // any built-in id from LANGUAGE_REGISTRY
clearLanguage(); // back to the factory defaultnebutra theme list
nebutra theme use vantaToggle dark mode
import { useTheme } from "@nebutra/tokens";
const { theme, setTheme } = useTheme();
setTheme("dark"); // "light" | "dark" | "system"Advanced customization
| Goal | Where to start |
|---|---|
| New Brand Package | packages/design/tokens/brands/<id>/brand.json + an entry in packages/design/theme/src/languages.meta.json, then node packages/design/theme/scripts/sync-languages.mjs (writes the generated languages.json — do not hand-edit it) |
| New UI primitive | packages/design/ui/src/components/ + Storybook story |
| New dashboard section | apps/web/src/app/(dashboard)/[section]/page.tsx |
| Modify onboarding steps | apps/web/src/app/(onboarding)/ |
| Rewrite the palette generator | scripts/generate-palette.mjs |
Token architecture at a glance
All runtime CSS variables are compiled into a single file, packages/design/tokens/styles.css — but that file is generated (its own header says so) and gets overwritten on every token build. The single source of truth is the DTCG token JSON at packages/design/design-tokens/tokens/*.json; edit there, then run the Style Dictionary build so sync-styles.mjs regenerates styles.css. Never hardcode hex values anywhere in the codebase.
/* packages/design/tokens/styles.css — excerpt */
:root {
--brand-primary: var(--blue-9); /* #0033FE */
--brand-accent: var(--cyan-9); /* #0BF1C3 */
--brand-gradient: 135deg, var(--blue-9) 0%, var(--cyan-9) 100%;
--neutral-1: #ffffff;
--neutral-12: #0a0a0a;
}Open Storybook's Design Tokens section for a live visual reference of every token in both light and dark mode: pnpm --filter @nebutra/storybook dev → http://localhost:6006
How is this guide?
Last updated on