@bsuite/theme
v1.1.0
Published
BSuite Universal theme — D2C Neon Electric + Braden Corporate baselines. Tailwind v4 preset, CSS vars (5-layer), role aliases, colourblind-safe tokens, eye-strain-safe text scale, BrandingProvider (runtime white-label). Used by BSU, CRM7, conduit, R80.3,
Readme
@bsuite/theme
Universal theme package for the BSuite monorepo. Ships the D2C Neon Electric baseline, the Braden Corporate baseline, Tailwind tokens, CSS variables, React ThemeProvider, runtime BrandingProvider, and a framework-agnostic FOUC-prevention script.
What's inside
- CSS variables — OKLCH source palettes, role aliases, light/dark surfaces, shadcn bridge variables, and WCAG AA-compliant anti-glare text tokens
- Dual brand baselines — D2C via
@bsuite/theme/css; Braden via@bsuite/theme/braden-css - Tailwind v4+
@themeblock —@import '@bsuite/theme/preset-v4.css' <ThemeProvider>+useTheme()with localStorage persistence + system preference<BrandingProvider>— enterprise runtime white-labelling for D2C apps; Braden short-circuits to static corporate tokensgetThemeInitScript()— stringified JS for inline<script>tags to prevent FOUC
Install
pnpm add @bsuite/themeConsumer setup
Vite (Tailwind v4+) — BSU / CRM7 / R80.3 / Throughput
1. src/index.css or equivalent global stylesheet:
@import 'tailwindcss';
@import '@bsuite/theme/preset-v4.css';
@import '@bsuite/theme/css';
/* your app-specific @theme {} overrides below */Braden uses the same Tailwind bridge shape but imports the corporate baseline:
@import 'tailwindcss';
@import '@bsuite/theme/braden-css';2. src/main.tsx:
import { ThemeProvider } from '@bsuite/theme/react'
ReactDOM.createRoot(document.getElementById('root')!).render(
<ThemeProvider>
<App />
</ThemeProvider>
)3. index.html — FOUC prevention:
Paste the result of getThemeInitScript() into <head> before stylesheets.
Next.js 16 (Tailwind v4+) — conduit
1. src/app/globals.css:
@import 'tailwindcss';
@import '@bsuite/theme/preset-v4.css';
@import '@bsuite/theme/css';2. src/app/layout.tsx — inline theme-init script in <head>:
import Script from 'next/script'
import { getThemeInitScript } from '@bsuite/theme/ssr'
import { ThemeProvider } from '@bsuite/theme/react'
export default function RootLayout({ children }) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<Script id="bsuite-theme-init" strategy="beforeInteractive">
{getThemeInitScript()}
</Script>
</head>
<body>
<ThemeProvider>{children}</ThemeProvider>
</body>
</html>
)
}Tailwind floor
All consumers must use Tailwind CSS v4 or later. The legacy ./tailwind-preset
export remains only so older published package metadata does not break import
resolution; it is not a supported BSuite consumption path.
Using the hook
import { useTheme } from '@bsuite/theme/react'
export function ThemeToggleButton() {
const { theme, resolvedTheme, isDark, setTheme } = useTheme()
return (
<button onClick={() => setTheme(isDark ? 'light' : 'dark')}>
{isDark ? '☀️' : '🌙'}
</button>
)
}The resolvedTheme always returns 'light' or 'dark' — use this when you need the effective theme (it resolves 'system' for you).
Palette And Roles
All 11 canonical electric colours are available as palette tokens, but consumer UI should bind to role/shadcn tokens. Palette names are presentational; roles are the stable contract for white-labelling.
| Role / token | OKLCH source | Notes |
|---|---|---|
| --role-primary / Electric Blue | oklch(0.546 0.215 262.9) | Primary actions and focus affordances |
| --role-accent / Electric Cyan | oklch(0.769 0.132 191.7) | Accents, highlights, visible focus in dark mode |
| --role-success | oklch(0.723 0.192 149.6) | Success; never rely on colour alone |
| --role-warning | oklch(0.728 0.168 22.5) | Warning; pair with icon/text |
| --role-error / --role-destructive | oklch(0.568 0.202 283.1) | Purple by platform policy; coral/red must not be semantic error/destructive |
Braden keeps separate corporate identity tokens in @bsuite/theme/braden-css: Braden Red oklch(0.51 0.17 19), Braden Gold oklch(0.77 0.10 82), and Braden Navy oklch(0.34 0.04 250). Red is identity only; Braden error/destructive roles still map to purple.
WCAG AA compliance: use semantic text tokens (text-foreground, text-muted-foreground, text-text-on-primary, text-text-on-accent) rather than raw text-white/text-black. Dark-surface text is capped at L=0.94 for extended-session comfort.
Elevation (v0.11.0)
shadow-elev-0 … shadow-elev-4 — the suite's card-elevation ramp, and the
first release in which those classes exist at all. 25 call sites across crm7
and business-suite-unified, four CLAUDE.md files and the canonical
bsuite-brand-system skill had instructed shadow-elev-* while no rule
matched it anywhere; every one computed box-shadow: none.
shadow-elev-0is a real reset (0 0 0 0 transparent), not the absence of a rule. It is deliberately notnone, which invalidates Tailwind v4's compositebox-shadowdeclaration and takes the focus ring with it.- Geometry follows Tailwind's
xs/md/lg/xl; colour comes from--shadow-ink-*, an OKLCH ladder derived from--shadow-colorand scaled by--shadow-strength, rebinding under.dark. - The same release rebinds Tailwind's own
--shadow-*,--inset-shadow-*,--drop-shadow-*and--text-shadow-*onto that ink. They ship from Tailwind asrgb(0 0 0 / a)— pure black, banned in every role, and invisible to the colour audit because the literal lives innode_modules. 292shadow-sm|md|lg|xl|2xlcall sites across the estate were painting it, and in dark mode painting it invisibly.
Geometry is unchanged, so nothing moves; light mode shifts by a hue-tint only. Dark mode changes materially, because dark shadows previously did not render.
Text-role contract (v0.6.0)
Six measured tiers — --role-text-heading, --role-text-body, --role-text-secondary, --role-text-muted, --role-text-subtle, --role-text-disabled — each with documented WCAG contrast in both modes. Full numbers + methodology: docs/TOKEN-MAPPING.md §8.1.
Canonical gradient (v0.6.0)
Exactly one decorative accent gradient exists suite-wide — --gradient-accent / .bsuite-accent-gradient (--role-primary → --role-accent). Marketing heroes only, never functional text. docs/TOKEN-MAPPING.md §8.2.
Grid/dot doctrine (v0.6.0)
<HeroGrid> (public/pre-auth hero bands) and <DotPattern> (authenticated shells, @bsuite/ui) are mutually exclusive per context and enforced by @bsuite/dry-lint's no-grid-dot-doctrine-violation rule. docs/TOKEN-MAPPING.md §8.3.
License
UNLICENSED — internal BSuite use only.
