This is not a Figma essay. It is a migration sequencing problem. Teams that tackle a UI library jump at the same time as Vue core often find themselves arguing about whether a table row “counts” as the same component while the app is still on Vue 2. Extracting a thin design system package (even on Vue 2) can narrow the Vue 3 diff to wiring, not renegotiating every pixel.
Think of the design system as the contract: spacing, type scale, and component APIs that product teams import. Vue 3 should implement that contract—not rewrite it mid-flight.
1. When extraction pays for itself
Extraction first is worth it when: you have more than one app on the same UI kit, your screens diverged visually under Vue 2, or you are about to lift large areas into incremental slices and need a stable list of what “done” means per component.
2. The minimum useful package
- Tokens as data (JSON or CSS custom properties) consumed by the app, not ad hoc SCSS variables scattered per feature
- A Storybook (see our Storybook 8 note) that locks props and states
- Explicit slot patterns for “brandable” areas without forked copies of whole pages
3. When not to over-invest
If the business must ship a regulatory deadline in eight weeks and the UI is effectively three screens, do not build a monorepo design system. Scope the “system” to those surfaces, document decisions in Storybook, and only generalize when the roadmap has room. Extraction is a tool for repeatability, not a moral imperative.
4. Tokens as data, not as SCSS variables
The single most reusable artifact across a Vue 2 → Vue 3 jump is a token file. SCSS variables work inside one app; JSON or a Style Dictionary build works across apps, Storybook, native apps, and design tools. We pick a small token surface first (color, space, radius, type scale) and ship it before any component refactor.
// tokens/color.json
{
"color": {
"brand": {
"500": { "value": "#7C3AED", "type": "color" },
"600": { "value": "#6D28D9", "type": "color" }
},
"surface": {
"raised": { "value": "{color.neutral.50}", "type": "color" }
}
}
}Style Dictionary then emits CSS variables consumed by both Vue 2 and Vue 3 components without code changes:
/* generated/tokens.css */
:root {
--color-brand-500: #7C3AED;
--color-brand-600: #6D28D9;
--color-surface-raised: var(--color-neutral-50);
}Apps import this stylesheet. Components reference var(--color-brand-500). When you migrate to Vue 3, the tokens do not change—only the framework around them does.
5. Component API contracts that survive Vue 3
The risk during a framework jump is that component APIs quietly drift: a prop renamed, a slot signature changed, an event payload tweaked. If product code calls a thousand buttons, even small drift compounds. We freeze a thin API contract per shared component before the upgrade.
A typical Button contract
- Props:
variant(primary | secondary | ghost),size(sm | md | lg),disabled,loading - Slots: default (label),
icon-leading,icon-trailing - Events:
clickwith native MouseEvent - A11y: respects
aria-busywhen loading
That contract becomes the Storybook docs page, the TypeScript type, and the lint rule (via custom ESLint rules). Vue 3's defineProps and defineEmits with TypeScript make the contract enforceable in a way the Options API rarely did. Pair this with our incremental TypeScript notes so adoption is gradual.
6. Storybook as the migration safety net
A frozen component matrix is only useful if you can verify it. Storybook is the cheapest way to lock states per component ahead of the upgrade and re-render the same stories on Vue 3.
- One story per visual state: default, hover, focus, error, loading, disabled
- Visual regression (Chromatic, Percy, or Loki) wired into PRs
- Accessibility addon enabled to catch role/label drift after refactor
- Interaction tests for the few components with real keyboard logic
When the Vue 3 build is ready, you re-run the same stories against the new framework. Diffs surface immediately. See our Storybook 8 deep-dive for setup, addons, and CI wiring.
7. Packaging strategy: monorepo or sidecar?
Two patterns dominate. Pick the one that matches the team you already have, not the one that maximizes purity.
| Pattern | Best for | Watch out for |
|---|---|---|
| Sidecar package, single app repo | One app, one team, fast iteration | Hard to extract later if you grow |
| Monorepo (pnpm workspaces, Nx, Turborepo) | Multiple apps sharing UI, parallel migration | Ops complexity; CI tuning required |
| Standalone repo + npm registry | Many consumers, including external partners | Slow feedback loop during heavy churn |
If you also run a micro-frontend architecture or a strangler-fig migration, a monorepo usually wins because it lets the design system ship Vue 2 and Vue 3 outputs side by side.
8. Common pitfalls
- Redesigning while extracting. Lock visuals first; redesign later. Two changes at once doubles risk and confuses stakeholders.
- Inventing 80 components. Start with the 12 used most. Carve out the rest only when duplication actively hurts.
- Hardcoded colors in templates. Audit and migrate to tokens before refactor; do not leave hex codes in product templates.
- No naming convention. Pick BEM, CUBE, or utility-first and stick with it. Mixed conventions fragment the team's mental model.
- Skipping accessibility. Bake roles, labels, and focus rings into the contract. They are 10x cheaper to fix at the design system level.
- No deprecation policy. Components evolve. Use
@deprecatedJSDoc and a one-version grace period. Otherwise consumers ignore your changes.
9. A six-week extraction sprint
When teams ask "what does this look like in calendar time," the smallest credible plan we deliver is roughly six weeks for a mid-sized SPA before the framework upgrade starts.
- Week 1: token audit (color, space, radius, type) and Style Dictionary pipeline
- Week 2: Storybook in the existing Vue 2 app, baseline stories for top 12 components
- Week 3–4: contract freeze and lint enforcement; deprecate forks
- Week 5: visual regression in CI, accessibility audit pass
- Week 6: documentation, owner handoff, migration playbook for the Vue 3 jump
After this, the framework migration gets a finite component matrix to verify rather than a moving target.
10. FAQ
Should we wait for Vue 3 to start the design system?
No. Tokens, contracts, and Storybook all work on Vue 2. Doing them first reduces what changes in the framework jump.
How big should the design system team be?
For a 6-week extraction in a single product company, two engineers and a part-time designer are usually enough. Larger orgs add a tech writer and a community manager.
What about Tailwind or CSS-in-JS?
Both work. Tokens stay portable. Tailwind's theme.extend can read Style Dictionary output. CSS-in-JS can import the same JSON. Choose based on team familiarity, not aesthetics.
How do we keep design and code in sync?
Generate Figma variables from the same token JSON. The source of truth is the file, not either tool. Designers open PRs to that file when they propose changes.
Does this affect bundle size?
Usually positively—shared components dedupe code paths. See our note on bundle size after Vue 3 for measurement.
Need a sequencing plan?
We help teams order design-system, framework, and product work so the migration ships without boiling the ocean.
Scope the work with usConclusion
A crisp design system pass before Vue 3 does not add months—it often removes them by giving you a finite component matrix to verify after the upgrade, instead of every screen being its own one-off.
