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#42307DViolet — tenant-overridable
- Brand secondary#FFE169Tenant-overridable
- Brand accent#A853BATenant-overridable
- Heading#0A192FFixed across tenants
- Container#FFFDF3Warm surface
- Border#E2E8FDCool 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