Interaction case · Phase 1 refactor
Sixteen project pages each carried a 70-line <style scoped> block, and both home pages carried 246 lines of inline CSS. The obvious move — paste everything into one stylesheet — would have removed the scope and let selectors such as pre, details and table leak across the site. The safe move was to keep the selectors and move the boundary into the CSS itself.
The duplicated block covered:
.project-detail layout and typography.stats-box tablesdetails / summary disclosure widgetspre / code blocks.status-published badgesVue's scoped attribute had been doing the isolation work. A global stylesheet has no such attribute, so the isolation has to be expressed in the selectors.
| Selector | Reach if left global | Mitigation |
|---|---|---|
pre | Every code block on the site | .project-detail pre |
details | Home page and future docs | .project-detail details |
summary | Every disclosure widget | .project-detail summary |
table | All tables, including new eval reports | Keep table styling in shared base.css |
.stats-box | Low collision risk, but still owned by one page type | Kept as a shared primitive |
tokens, base, cards, home, projects..project-detail.| Check | Result |
|---|---|
| Pages migrated | 16 |
Inline <style> blocks removed | 18 |
| Old CSS rules | 108 |
| New CSS rules | 108 |
| Missing selectors | 0 |
| Added selectors | 0 |
| Build | Passed, 21 pages |
Pass. Rule parity was checked at the selector level, not by eyeballing the page. The only intentional behavioural change was a responsive breakpoint unification (640px → 768px), recorded as a residual risk.
Residual risk: a selector-level diff cannot detect a changed declaration inside an unchanged selector. Visual spot checks covered both locales and a sample project page, but not every project at every viewport.
Do not "globalize" scoped CSS by deleting the scope. Move the scope into the selector, then prove parity with a rule-level diff.