Building a Component Library That Survives a Design Refresh

We've rebuilt more than one client's component library from scratch because the original was built around specific visual values instead of intent. Buttons had props like color="#2563EB" and size="14px" hardcoded into call sites across dozens of pages. When the client's brand refresh landed eighteen months later, updating the primary color meant a find-and-replace across 200+ files and manually re-testing every screen.
Our current approach starts with a token layer that's fully separated from component implementation: semantic tokens like color-action-primary and space-md that map to raw values, defined once in a single source (we usually use Style Dictionary or a token JSON file synced from Figma). Components consume only semantic tokens, never raw hex values or pixel counts, enforced with an ESLint rule that flags hardcoded colors in component files.
We also learned to separate 'component API' from 'component style' more aggressively than felt necessary at the time. A Button component's props (variant, size, isLoading, leftIcon) describe behavior and intent, not appearance. When a design refresh changed the visual treatment of the 'primary' variant from a solid fill to a gradient, zero call sites needed to change — only the token values and the variant's internal style mapping did.
The part that actually gets skipped under deadline pressure is documentation of intent, not just usage. We keep a short 'when to use this' note per component in Storybook, because the recurring failure mode isn't visual drift, it's teams building near-duplicate components because they didn't know an existing one covered their case. That documentation debt compounds faster than style debt does.
When this client's second brand refresh came through last year, updating roughly 40 components to a new visual language took four days instead of the six weeks the first refresh had taken, because the refresh only ever touched the token layer and a handful of variant style maps.

