When to use: any system with more than one theme (light/dark, multi-brand) or an anticipated rebrand.
How: define primitive tokens (raw values) once; define semantic tokens that alias primitives to meaning (color.text.primary, space.component.gap); components reference only semantic tokens.
Trade-offs: extra indirection layer to design and maintain upfront, but a theme/brand change becomes a single-file edit instead of a codebase-wide find-and-replace.
When to use: building the case for a design system in an organization that doesn't have one yet. How: screenshot every distinct UI pattern currently in production, lay them side by side by category (buttons, forms, cards), present the visible inconsistency directly to stakeholders. Trade-offs: time-consuming to do thoroughly, but far more persuasive than an abstract pitch — inconsistency becomes undeniable rather than a matter of opinion.
When to use: a component whose responsibilities are growing past a handful of boolean/enum props.
How: split into a Root + named sub-parts sharing implicit context (React context or similar), each sub-part handling one concern; consumers compose the parts they need.
Trade-offs: more files/exports per component and a steeper initial learning curve than a single flat-prop component, but scales to arbitrary layout/composition needs without prop-list explosion.
When to use: any component library expected to survive more than a few months of active development. How: generate prop tables/type docs from the component's actual TypeScript types; keep only judgment-call content (usage guidance, do/don't examples, a11y notes) hand-written. Trade-offs: requires tooling investment (Storybook + a docs-generation addon, or equivalent) upfront, but eliminates the single biggest cause of documentation drift.
When to use: any breaking API change to a component already in production use elsewhere. How: mark deprecated with a lint rule/type annotation and a concrete removal version; ship a codemod that mechanically rewrites the common usage patterns; give at least one minor-version window before removal. Trade-offs: extra work writing and testing the codemod, but converts a manual, error-prone, organization-wide migration into a scripted, reviewable one.
When to use: starting any new component library from scratch. How: build on unstyled, accessible primitives (Radix Primitives, Base UI) rather than raw HTML/ARIA hand-rolled per component; add only visual styling on top. Trade-offs: less control over exact DOM structure/behavior than a fully custom implementation, but accessibility correctness (focus management, ARIA roles, keyboard interaction) is inherited rather than re-solved per component.
When to use: introducing a design system into an existing, actively-shipping product. How: require the system for new features immediately; replace existing UI opportunistically during other planned work, not as a dedicated rewrite project. Trade-offs: the codebase runs mixed old/new UI for longer, but avoids the schedule risk and quality regressions of a forced, time-boxed rewrite.
When to use: an organization large enough that a single central team can't design every needed pattern. How: core team owns primitives and process; feature teams can propose/build product-specific patterns under a lightweight review (not a heavyweight RFC process) before merging into the shared system. Trade-offs: requires trust and clear review criteria to avoid quality drift, but scales pattern coverage beyond what a small central team could produce alone, and reduces the incentive to bypass the system entirely.
When to use: any system that's been live for more than a few months. How: statically analyze which components/versions are actually imported across the organization's repositories; track this over time as the real adoption signal. Trade-offs: needs tooling investment (a script or dashboard over the org's codebases), but is the only reliable way to know whether governance and communication are actually working versus just assumed to be.