Share the tokens, not the components
What was the situation
Enate’s remotes had to look like one product. Mimecast’s products even more so — several surfaces, one platform. The first instinct is a shared Angular library with every button, every modal, every table.
That library becomes the slowest-moving remote you have. Nobody wants to bump it. One product needs a destructive button, another needs a quiet one, the shared ButtonComponent grows a variant enum with twelve values and a comment that says "do not use overlay".
Tokens are the part that should be identical: colour, type, space, radius. Components can live next to the feature that needs them, or in a small UI lib that is allowed to stay small.
When does this apply
Use this when more than one Angular app must feel like the same house.
- Module Federation remotes with a common header colour and type ramp.
- An Nx workspace where
uiis starting to import feature types. - A public site and a product that should share a palette but not a table widget.
Skip this if you have one app. Put the tokens in that app. A shared library for How would be theatre.
How is it done
Publish tokens as CSS variables (or a tiny JS map that writes them). Components in each remote consume the variables. They do not import HeroButton from a mega-lib unless that button is truly identical everywhere.
:root { --color-text: #12494c; --color-accent: #156064; --space-2: 0.5rem; --space-4: 1rem; --radius: 8px; --font: Montserrat, Helvetica, sans-serif;}A remote button then looks like a local component:
.button { padding: var(--space-2) var(--space-4); border-radius: var(--radius); background: var(--color-accent); color: #eeeeee; font-family: var(--font);}When the platform changes accent, every remote that used the variable updates. When one product needs a weird split-button, they build it locally. They do not extend the shared ButtonComponent with mode: 'split-for-inbox'.
If a control really is identical — a chip, a dialog frame — then it earns a place in libs/ui. The test is: would you delete a product and still need this component? Auth login form is a product. A chip is a chip.
Version tokens with the workspace. Do not npm-publish a token package for one monorepo. Path alias to libs/tokens. Federation should not load tokens as a remote.
Watch out for
Do not share a component that wraps a feature API. DetectionCard that imports a detections client is not UI. It is a remote in disguise, and the other products will drag that API into their bundle.
Do not let remotes override tokens "just this once" with a second :root. You will debug two greens and a nav that does not match the page. Overrides belong to a documented theme hook, or they do not belong.