Contain first
Put existing CSS somewhere predictable before changing how new CSS is written.
Apply
Good migration is not heroic rewrites. It is controlled improvement. You should not need to pause delivery, rebuild the whole frontend, or pretend the old CSS is not still in the room.
Ask first: Can this be safely replaced now?
Most migration mistakes come from trying to fix everything at once. Decide whether the code should be replaced or safely contained.
Put existing CSS somewhere predictable before changing how new CSS is written.
Fix the CSS people touch every week, not the fossil no one has opened since 2018.
Migration succeeds when legacy CSS gets smaller, not when it gets renamed and promoted.
Work through the steps in order. Skip ahead only when a later step does not depend on the foundation you have not laid yet.
Introduce the layer order first.
Move old CSS into the legacy layer.
Put tokens and settings in the settings layer.
Move broad element defaults into base.
Write all new work as proper component partials.
Reduce selector depth when touching existing code.
Replace unclear names with semantic component ownership.
Use modifiers for variants and state classes for temporary state.
Move emergency fixes into hacks with comments.
Delete old CSS when the UI is replaced.
Use these checkpoints in planning and retrospectives. They describe outcomes, not calendar dates.
Milestone 1
Layer order is defined and inherited CSS is isolated in legacy or thirdparty.
Milestone 2
New components follow naming, state, and token rules without exceptions.
Milestone 3
Legacy selectors and files get smaller release by release.
Milestone 4
Temporary fixes are tracked and removed on a regular cadence.
Timelines vary with legacy surface area and how often teams ship. Treat ranges as planning guides, not deadlines.
Small codebase
1–3 sprints
Enough for a stable migration baseline when one team owns most of the CSS.
Mid-size codebase
1–2 quarters
Broad consistency across active products and shared partials.
Large or multi-team
Multiple quarters
Phased rollout with governance, reviews, and measurable legacy reduction.
Add layer order before moving code. This gives the project a stable override model immediately and stops specificity wars from breeding.
@layer legacy, settings, base, utilities,
layout, components, theme, hacks;Inherited first-party CSS belongs in the legacy layer so the rest of the project can move forward without pretending old code is modern architecture. If vendor CSS is separate, use an optionalthirdparty layer declared beforelegacy in the @layer list — seelegacy and third-party layers.
@import './legacy/site.css' layer(legacy);
@import './components/product-card.css' layer(components);Importing old CSS into the correct low layer means every intentional layer above it has clearer ownership and safer override behaviour.
Migration should reduce risk, not create a dramatic new source of it.
Migration is working when new CSS follows the system, old CSS is contained, overrides are predictable, and the legacy layer keeps shrinking over time.