BrightZen OS
A business operating system for a family office and the companies in its portfolio. One platform, three tiers of access, and a brand that repaints itself per tenant.

The problem
A family office holding a portfolio of operating companies has the same question about every one of them, every month: is this business actually working. The answer normally arrives late, in a spreadsheet, assembled by hand from whatever each company's bookkeeper happened to export.
BrightZen OS is the attempt to make that answer continuous instead of periodic — one system that every portfolio company operates inside, so the numbers are a by-product of the work rather than a monthly reconstruction of it.
One product, three tiers
The interface is split three ways, and the split is the architecture. A platform admin sees tenants and data health across everything. A tenant — the family office, or an accounting firm — sees the companies it manages. A company sees only itself.
That is not three products with shared components. It is one route tree, grouped by audience, where the same financial model screen renders for a company looking at itself and for a tenant looking at that company. Getting that right is what keeps 58 screens from becoming 150.
A brand that repaints itself
The screenshots here are not violet, and BrightZen's brand is. They show the platform running under a Weekly Accounting tenant — one of the portfolio companies — because the brand colours are CSS variables a tenant overrides at runtime rather than values compiled into the build.
Only three tokens move: primary, secondary and accent. Headings, surfaces and borders stay fixed, so a tenant can look like itself without being able to break contrast or drift the layout. White-labelling that lets a client change everything is white-labelling that eventually ships something illegible.
A token collision worth documenting
The shared Tailwind config deliberately does not define a --color-primary, and the reason is written into the file. When it did, bg-primary in the web app resolved to the brand violet while the identical class inside the shared UI package resolved to that package's own near-black. Because the app's stylesheet loads after the package's, an app-authored bg-primary silently shadowed the component's own rule.
The fix was to give the brand colour a different name — brand-primary — and leave primary to the component library. A naming problem rather than a styling one, and the sort that only appears once a design system is split across a package boundary.
Palette
Brand primary
#42307D
Violet — tenant-overridable
Brand secondary
#FFE169
Tenant-overridable
Brand accent
#A853BA
Tenant-overridable
Heading
#0A192F
Fixed across tenants
Container
#FFFDF3
Warm surface
Border
#E2E8FD
Cool hairline
Scope
- Screens
- 58
- Shared UI components
- 47
- API files
- 929
- DB schemas
- 50
- Migrations
- 72
- Tests
- 671
Screens



What we did
- Product design
- Design system — shared UI package
- Multi-tenant white-labelling
- Interface design — 58 screens
- Full-stack build
- Data modelling
Built with
- Next.js 16
- React 19
- Tailwind 4
- TanStack Query
- Turborepo
- NestJS 11
- Drizzle ORM
- Postgres
- Supabase
- Redis
- BullMQ
- Nix