The problem
Three internal products at my last job each had their own button, input, and modal components, subtly different in padding, color, and focus behavior. Every design tweak had to be made three times and always drifted out of sync within a few weeks.
Role and impact
- Individual project, design systems.
- Replaced three separate component sets with one versioned package.
- Adopted across 3 internal products with zero visual regressions post-migration.
- Cut new-component review time in half thanks to shared accessibility defaults.
Technical decisions and challenges
Each product needed its own color palette and border radius scale, but I didn't want three forks of the same components. I compared shipping separate themed builds per product versus a single build driven entirely by CSS custom properties.
I chose CSS custom properties resolved at the theme-provider level, because it let every product consume the exact same compiled components and only override a small token file, instead of maintaining parallel builds that could drift.
Rewriting all three products' UI in one pass was too risky to ship safely. I evaluated a hard cutover per product versus a gradual, component-by-component migration behind the existing component API.
I went with a codemod that swapped one component at a time, keeping the old and new components' props API compatible during the transition, since it let each team migrate on their own schedule without a big-bang release that could break production.
Detailed stack
| Layer | Technology | Why |
|---|---|---|
| Components | React, Radix UI primitives | Accessible behavior out of the box, unstyled so theming stays clean |
| Styling | Tailwind CSS, CSS custom properties | Per-product theming without maintaining separate builds |
| Docs | Storybook | Living documentation and visual regression baseline |
| Release | Changesets | Independent, changelogged versioning per package |
Gallery
What I would do differently
I would invest in automated visual regression testing from day one instead of adding it after the first cross-product migration. We caught a few theme-token regressions manually that a visual diff tool would have flagged immediately.