A white-label product rarely breaks in one decision. It breaks one reasonable request at a time. A client wants their colour on the buttons. Then their font. Then a different header, a custom card, an illustration in their style. Each request makes sense on its own, and each one is small enough to say yes to.
Ten clients later, the product is ten products. Overrides sit inside components, brand logic leaks into feature code, and every release has to be checked in every brand. The design system is still there, but it has become a list of exceptions. Nobody decided this. It happened one yes at a time.
The usual answer is more flexibility: a theming engine that can change anything, so every request has somewhere to go. That makes the problem easier to live with. It doesn't make it smaller.
Propbinder's tenant app runs as a dozen branded apps for property companies, from Heimdal to Blækhus to Danica, on one design system. A new brand launches in about a day. Not because the system can change everything, but because it can change very little.
This article explains how we built it, and why the most important decision in a white-label design system is what you don't make brandable.
A default, not a neutral base
Every multi-brand system needs a starting point, and there are two ways to choose one. The system can start from a neutral base that belongs to no brand, or from a real product that every brand builds on. Both are used by serious teams, and both have a cost.
A neutral base treats every brand as equal. No client inherits another client's look, and the base can be designed purely as infrastructure. The cost is that a base nobody owns is designed for nobody. Its grey buttons and placeholder colours never reach a real user, so they are never tested against real use. Every brand then fills the gaps differently, and the gaps are where drift starts.
A default product is designed as a product. Propbinder is the default. White-label is an add-on that property companies buy on top. The tenant app was designed and built as Propbinder's own app, on the same governed design system as Propbinder's administrator platform. Typography, spacing, touch targets and components all come from there. The cost is that the default brand can quietly leak into decisions. A layout that only works with Propbinder's colours will break in someone else's.
We chose the default, and paid for it with one rule, applied from the first screen: every decision had to work when the brand changed. White-label was never retrofitted. It was a constraint on the first component, not a feature added to the last one.
That rule turns the default into an anchor rather than a bias. Every brand inherits a complete, finished product, already tested by real tenants, and changes only its surface.

One product. Many coats of paint.
Brand the surface, lock the system
Once the default exists, the next question is how much of it a brand can change. This decision sets the cost of every brand that follows.
In Propbinder's tenant app, a brand changes a short list of things. Colour, through six tokens covering the accent and the header, plus the app icon and the sign-in screen. Logo, placed where the system already makes room for it. Typeface, when a client asks for its own, swapped as a family while sizes, weights and line-heights stay with the system.
Everything else is locked: layout, navigation, spacing, components, interaction patterns, neutrals and status colours. A brand can't move a button, restyle a card or redraw an empty state.
This looks restrictive, and it is. That is the point. A small brand surface means quality is designed once, and accessibility is checked once for everything that's locked. A new brand becomes configuration rather than design work. It also turns out to be enough. Tenants recognise their property company by the header, the accent and the logo. Nobody recognises a brand by its card radius.

Four levers. Everything else stays put.
The typeface is the lever that surprises people. Every brand runs on Propbinder's typeface by default, and a client that wants its own font can have it with little effort. Changing a family sounds like it should ripple through the layout. It doesn't, as long as the scale stays put. Our article on typography tokens explains why: a white-label product changes the family, not the size, so every name in the type scale stays true in every brand.
Features are the lever we kept out of the brand. Which modules a tenant sees, such as chat, posts, groups, booking or the handbook, is switched on per property community and per resident, the same way in every brand. A brand decides how the app looks. The property company's setup decides what it does. That separation has a design consequence: every feature has to work when it's missing. Navigation can't assume a tab exists, and a screen can't assume the module next to it is there. Designing the absent state is part of designing the feature.
Some brands need more than the surface. Calum's home screen opens with a photo header and a grid of quick tiles. Danica and CEJ move inquiries into the tab bar and use their own words: "Contact landlord" instead of "Inquiries", and in Danica's case "Neighbour chat" for chat. These aren't new products. They're controlled exceptions, applied to a brand by its id, in the same codebase as every other brand.
One codebase is what keeps them controlled. Each exception sits next to the rule it bends, visible to anyone who changes that screen, and tested with everything else. Spread across a dozen repositories, the same exceptions would drift apart quietly, one patch at a time.
The trade is real. Clients give up control over most of the interface. In return they get a polished app quickly, and every improvement Propbinder ships reaches them without extra work. For clients who need their own flows or layouts, this is the wrong model. At that point you're not white-labelling a product. You're building several.
Six tokens, named by role
Inside the app, every brand's colour fits into six tokens. accent and onAccent colour primary actions and the text on them. headerSurface and headerContent colour the app header and the text inside it. headerAccent and onHeaderAccent colour highlights within the header and the text on those highlights.
Six more fields sit outside the app itself: two for the app icon, and four for the sign-in screen, which in practice describe one background and the text on it.

Six names. Every brand fills them in.
The names follow two rules.
Tokens are named by role, not by value. This is the opposite of what we recommend for typography, and the difference is worth explaining. A type token like ui-14-medium can be named by value because its value is the same in every brand. Brand colours are the one place where that isn't true. Heimdal's accent is #FB5104. Blækhus's accent is #000000. A value name like orange-500 would be true in one app and false in the next. The only thing that stays the same across brands is the job the colour does, so the job becomes the name.
That also keeps components honest. A button references accent, never a hex value or a brand name. It doesn't know whether it is orange or black, and it doesn't need to.
Every surface comes with its partner. accent has onAccent. headerAccent has onHeaderAccent. A colour is never defined without the colour that sits on it, so contrast is designed as a pair rather than discovered when a brand goes live.
Pairs also show where a brand colour doesn't quite work. A colour chosen for a logo or a building sign isn't chosen for white text on a phone screen. So sometimes a client's colour is refined to the closest shade that meets accessibility requirements, darkened just enough for text or a badge to stay readable. The brand still reads as itself, and the app stays compliant.
Six is a deliberate number. Every extra token is one more value someone has to choose, check and maintain, for every brand. A system that brands the accent and the header can stay at six, with the icon and sign-in fields alongside. One that also brands cards, borders and backgrounds would need dozens, each multiplied by the number of clients.
In code, a brand's interface colours are six values. These are Propbinder's, the defaults every other brand overrides:
The default palette, in six lines.
Illustrations that carry the brand with them
Illustrations are where most white-label systems give up. There are two common outcomes, and neither is good. Every brand gets the same generic set in a neutral palette, which looks slightly off in every app. Or someone redraws the set for each client, which works until the eleventh client and the fortieth illustration.
We built ours as SVGs that take their colours from the brand tokens. The illustration is shared. The colours are not. Load Heimdal's tokens and the illustrations turn orange. Load Blækhus's and they turn black. Nobody redraws anything.
Every illustration fills its brand shapes with the same expression:
An illustration colour if the brand sets one, the accent if not, Propbinder's purple as the last resort.
Animations follow the same idea. They're defined once and coloured with the brand's accent, so they ship in every brand without being redone.
The pairing rule carries over from the interface. Detail drawn on an accent shape uses onAccent, so an illustration stays legible whether the accent is orange, black or a colour nobody has chosen yet. The illustration doesn't know which brand it's in. It only knows which roles it uses.
Drawn once. Branded everywhere.
One codebase, a dozen apps
The architecture follows the same idea as the tokens: keep what's shared in one place, and make what's different as small as possible.
Every brand runs on the same app, built once. When the app starts, it works out which property company it belongs to and applies that brand's theme by its id. Each store listing is a thin native shell around that same app, with its own name, icon and splash screen. Underneath sits one shared component library, used by every brand.
Fixes go into components, not brands. When a problem shows up in one brand, it's fixed in the library, never patched for that brand alone. A divider that should disappear after the last item in a list hides itself, inside the component, instead of every screen remembering to hide it. Every brand gets the fix at once. Where a brand genuinely needs something different, it's an exception tied to its id, not a fork.
Figma mirrors the code. In Figma, every brand is a group of the same six variables in one collection: Heimdal, Blækhus, JH Administration, Danica, CEJ, Nordhusene and the rest. The names match the tokens in code one to one. A designer can preview any feature in any brand before a line of code changes, and nothing has to be translated between the two.
That is what makes a launch quick. A new brand doesn't need new components, new illustrations or a design review of every screen, because none of those change. It needs a theme entry with its colours and logo, a native shell for the app stores, and the setup that links it to the property company. That's about a day of work.

One source. A dozen apps.
For a CTO, this is the part that matters most. A dozen apps don't mean a dozen codebases. They mean one app, one library and a dozen small configurations, and the next brand costs the same as the last.
What to decide first
A white-label design system isn't built by choosing a theming tool. It's built by making four decisions, ideally before the first brand exists.
Where the default lives. Either a real product is the anchor, or a neutral base belongs to nobody. A real product gives every brand a finished, tested app to inherit, as long as every decision is checked against a brand that isn't the default.
How little is brandable. Every brandable element is a value someone has to choose, check and maintain, multiplied by the number of clients. The right surface is the smallest one that still makes the app feel like the client's. For Propbinder, that was the accent, the header, the logo, and a font when a client asks for one.
What the brand tokens are called. Brand values change from app to app, so name them by role, and pair every surface with the colour that sits on it. Components and illustrations then reference the role and never need to know which brand they're in.
Where exceptions live. Some brands will need more than the surface. Keep their exceptions in the same codebase, tied to the brand's id, so they stay visible, get tested with everything else, and can't drift.
These decisions answer the problem this article started with. A white-label product doesn't break because clients ask for too much. It breaks because every request has somewhere to go. Give brands a small, well-named surface, and the product stays one product, no matter how many logos sit on top of it.

oskar-siemion.jpg
Oskar Siemion
Product Design
Oskar makes things come together. He builds products that grow with you and make it easier to stay on track and deliver quality.


