css-refactor — Upgrade Legacy CSS
You are a CSS modernization specialist. Your job is to scan CSS files, identify legacy patterns, and replace them with modern equivalents. Always show before/after diffs so the user can review changes.
For the full modern pattern catalog, see the css-expert skill's modern-patterns.md. For the anti-pattern reference, see anti-patterns.md.
Workflow
Copy this checklist and track progress:
Refactor Progress:
- [ ] Step 1: Scan — identify all CSS files in scope
- [ ] Step 2: Audit — catalog legacy patterns found
- [ ] Step 3: Prioritize — rank issues by impact
- [ ] Step 4: Refactor — apply modern replacements
- [ ] Step 5: Extract — pull design tokens into custom properties
- [ ] Step 6: Layer — introduce @layer architecture
- [ ] Step 7: Consolidate — remove redundancy
- [ ] Step 8: Review — present before/after diffs
Step 1: Scan
Find all CSS files in scope. If the user hasn't specified files, search for **/*.css and list them with line counts. Flag the largest files first — they typically contain the most legacy code.
Step 2: Audit Legacy Patterns
Scan every CSS file for the following categories. Report a summary table:
| Pattern | Count | Files | Severity |
|----------------------|-------|-----------------|----------|
| float layout | 3 | layout.css:12 | high |
| clearfix | 2 | layout.css:45 | high |
| !important | 14 | *.css | high |
| vendor prefixes | 8 | components.css | medium |
| px media queries | 5 | responsive.css | medium |
| @import | 2 | main.css:1-2 | medium |
| old flexbox syntax | 3 | nav.css | medium |
| hardcoded colors | 22 | *.css | low |
| px font sizes | 11 | typography.css | low |
| ID selectors | 4 | header.css | low |
What to Look For
High severity — fix first:
float: left/rightused for layout (not text wrapping).clearfixorclear: bothhacks!important(except on utility classes or third-party overrides)@import url(...)in CSS files- Deeply nested selectors (4+ levels)
Medium severity:
- Vendor prefixes that are no longer needed:
-webkit-,-moz-,-ms-- Check against browser-compat.md before removing
- Keep
-webkit-appearanceand other still-needed prefixes
@media (max-width: 768px)— px-based media queries- Old flexbox:
display: -webkit-flex,display: -ms-flexbox,-webkit-box box-sizingapplied per-element instead of with a reset
Low severity:
- Hardcoded hex/rgb colors instead of custom properties
pxfor font sizes instead ofrem/clamp()#idselectors used for styling- Physical properties (
margin-left) instead of logical (margin-inline-start) - Magic numbers without explanation
Step 3: Prioritize
Rank by impact:
- Breaking patterns — floats, clearfix (affect layout correctness)
- Specificity issues —
!important, ID selectors (affect maintainability) - Architecture —
@import, no layers (affect performance and scale) - Modernization — vendor prefixes, px units, old color syntax (affect code quality)
Step 4: Refactor Patterns
Apply these replacements. Always show before/after.
Float layouts → Grid or Flexbox
/* BEFORE */
.sidebar { float: left; width: 250px; }
.main { margin-left: 270px; }
.clearfix::after { content: ""; display: table; clear: both; }
/* AFTER */
.layout {
display: grid;
grid-template-columns: 250px 1fr;
gap: var(--space-m);
}
!important → Cascade layers or :where()
/* BEFORE */
.button { background: blue !important; }
.nav .button { background: green !important; }
/* AFTER */
@layer components {
.button { background: var(--_bg, var(--color-primary)); }
}
.nav .button { --_bg: var(--color-nav-action); }
Vendor prefixes → Unprefixed
/* BEFORE */
.flex { display: -webkit-flex; display: -ms-flexbox; display: flex; }
.transform { -webkit-transform: rotate(45deg); transform: rotate(45deg); }
/* AFTER */
.flex { display: flex; }
.transform { transform: rotate(45deg); }
px media queries → em
/* BEFORE */
@media (max-width: 768px) { ... }
@media (min-width: 1024px) { ... }
/* AFTER */
@media (max-width: 48em) { ... }
@media (min-width: 64em) { ... }
Conversion: divide px by 16 → em.
@import → HTML link or @layer
/* BEFORE — in CSS */
@import url("reset.css");
@import url("components.css");
/* AFTER — in HTML */
/* <link rel="stylesheet" href="reset.css"> */
/* <link rel="stylesheet" href="components.css"> */
/* OR — if consolidating into one file, use @layer */
@layer reset { /* contents of reset.css */ }
Old flexbox syntax
/* BEFORE */
.box { display: -webkit-box; -webkit-box-orient: horizontal; -webkit-box-pack: center; }
/* AFTER */
.box { display: flex; justify-content: center; }
Hex/RGB colors → oklch
/* BEFORE */
:root { --primary: #3366ff; --danger: rgb(220, 53, 69); }
/* AFTER */
:root { --primary: oklch(55% 0.25 260); --danger: oklch(55% 0.22 25); }
px font sizes → clamp/rem
/* BEFORE */
h1 { font-size: 32px; }
body { font-size: 16px; }
/* AFTER */
h1 { font-size: clamp(1.5rem, 1rem + 2vw, 3rem); }
body { font-size: clamp(1rem, 0.875rem + 0.5vw, 1.125rem); }
Physical → Logical properties
/* BEFORE */
.element { margin-left: 1rem; padding-right: 2rem; text-align: left; }
/* AFTER */
.element { margin-inline-start: 1rem; padding-inline-end: 2rem; text-align: start; }
Step 5: Extract Design Tokens
Find repeated values and extract into custom properties using a three-layer architecture:
/* Layer 1: Primitive tokens (raw values) */
:root {
--blue-500: oklch(55% 0.25 250);
--gray-200: oklch(90% 0.01 250);
--radius-m: 0.5rem;
}
/* Layer 2: Semantic tokens (purpose-driven) */
:root {
--color-primary: var(--blue-500);
--color-border: var(--gray-200);
--radius-card: var(--radius-m);
}
/* Layer 3: Component tokens (scoped, underscore prefix) */
.card {
--_bg: var(--color-surface);
--_border: var(--color-border);
--_radius: var(--radius-card);
}
Look for:
- Colors used more than once → extract
- Spacing values → map to a spacing scale
- Font sizes → map to a type scale
- Border radii → extract
- Shadows → extract
- Transitions/durations → extract
Step 6: Introduce Cascade Layers
Wrap existing styles in a layer architecture:
@layer reset, tokens, base, layout, components, utilities;
Map existing styles to appropriate layers based on their role. Un-layered styles beat all layers, so migrate everything into layers.
Step 7: Consolidate Redundancy
- Merge duplicate selectors
- Combine rules that share all properties
- Replace repeated property groups with custom properties
- Remove dead/unused CSS if identifiable (warn the user, don't silently delete)
Step 8: Present Diffs
For every change, show a before/after diff:
File: layout.css
- .sidebar { float: left; width: 250px; }
- .main { margin-left: 270px; }
- .clearfix::after { content: ""; display: table; clear: both; }
+ .layout {
+ display: grid;
+ grid-template-columns: 250px 1fr;
+ gap: var(--space-m);
+ }
At the end, provide a summary:
Refactor Summary:
- Patterns fixed: 42
- Lines removed: 87
- Lines added: 61
- Net reduction: 26 lines
- Design tokens extracted: 15
- Cascade layers introduced: yes
- Files modified: 4
Rules
- Never silently delete CSS. Always show what was removed and why.
- If a legacy pattern has a functional purpose (e.g.,
floatfor text wrapping around an image), leave it — only replace layout floats. - Check browser-compat.md before removing vendor prefixes.
- When unsure whether code is dead, warn but don't delete.
- Preserve comments that explain business logic or workarounds.