Context
Operator rule, stated 2026-09-02 while reviewing the theme contract behind #18141:
"For existing themes, there should NEVER be overrides inside THEME VAR files that add css selectors. Such cases must get added to scss/src, and add new vars as needed."
#18141 is the first defect this rule would have prevented: tab/Container.scss exists in all four themes, carries selector-scoped blocks, and its flat value tokens drifted out of the classic pair unnoticed. A theme file that is only a value list makes an omission obvious; a theme file that also carries selectors hides one among them.
The Problem
A theme value file answers "what is this theme's value for X". A selector answers "which elements get X" — a structural question the theme has no business deciding, because every theme then has to re-answer it identically and any one of them can drift.
Measured across resources/scss/theme-*:
|
|
| theme files carrying nested selector blocks |
26 |
| nested selector blocks total |
62 |
Largest concentrations:
| blocks |
file |
| 10 |
theme-neo-light/Global.scss, theme-neo-dark/Global.scss |
| 3 |
{theme-neo-light,theme-neo-dark}/tab/header/Button.scss |
| 3 |
{theme-neo-light,theme-neo-dark}/dialog/Base.scss |
| 3 |
{theme-neo-light,theme-neo-dark}/apps/portal/news/pulls/Component.scss |
| 2 |
{theme-light,theme-dark,theme-neo-light,theme-neo-dark}/tab/Container.scss |
| 2 |
{theme-neo-light,theme-neo-dark}/button/Base.scss |
| 1 each |
Global.scss (light/dark/cyberpunk), grid/Container.scss (×4), grid/footer/Toolbar.scss (×2), apps/portal/home/FooterContainer.scss (×2) |
The tab/Container.scss instance is the worked example: all four themes repeat the same four-selector list (.neo-tab-container-inline, its detached-drag-proxy self-scoped twin, and the -standalone pair) purely to scope --tab-button-* values. The selector list is identical in every theme; only the values differ. That is a structural rule with a value hole in it, written out four times.
The Architectural Reality
The correct split already exists in the tree and works: resources/scss/src/tab/Container.scss owns .neo-tab-container-inline > .neo-tab-header-toolbar and reads --tab-header-inline-*; the themes value those tokens flat. Everything in the table above is the same shape not yet applied.
Do not fix this by deleting the selectors. Each one exists because a value is variant-scoped, so each move needs a new token: the structural sheet grows the selector and reads var(--new-token), and every theme declares --new-token flat. Per #18141, every theme must declare it — a token declared by some themes and not others is inherited across a nested theme boundary rather than reset.
The Fix
One batch at a time, in the shape #18107 uses for its own theme sweep. Suggested ordering, cheapest and most defect-adjacent first:
tab/Container.scss (4 files, 8 blocks) — the #18141 neighbour, and the pattern demonstration.
grid/Container.scss + grid/footer/Toolbar.scss (6 files, 6 blocks).
button/Base.scss, tab/header/Button.scss, dialog/Base.scss (6 files, 16 blocks).
apps/portal/** (4 files, 8 blocks).
Global.scss (5 files, 23 blocks) — largest and least mechanical. Graduated to #18232 (2026-09-03), as this line anticipated: the measurement there found only 5 divergent declarations across the two neo files, and a weight drop that repaints realworld2 headings.
Each batch: move the selector into resources/scss/src/, introduce the tokens it needs, declare them in all themes, rebuild and diff the emitted CSS per theme to prove the paint is unchanged.
Acceptance Criteria
Out of Scope
- The
--tab-header-inline-* value omission itself — that is #18141.
- Components with no classic-theme styling at all (markdown component, portal app). The operator's read: a real gap, a different one — eventually every theme should support every widget. Its own ticket.
:where() weight on theme value sheets — #18107 owns that surface and the two sweeps should not be run as one.
Avoided Traps
- Deleting a selector without replacing its scope. The values are variant-scoped for a reason; a straight lift into the flat block applies them to every tab container.
- Declaring the new token in only the themes that need a non-default value. That is exactly #18141, one directory over.
- Doing all 62 in one PR. The operator's direction was explicit: this does not need to be one-shot, and a 26-file diff cannot be reviewed against emitted-CSS parity.
Related
- #18141 — the first defect this rule prevents; the
tab/Container.scss value omission.
- #18095 / PR #18097 — introduced the omission.
- #18107 — the
:where() weight sweep, adjacent surface, deliberately separate.
Live latest-open sweep: checked the latest 20 open issues at 2026-09-02T21:26Z plus the newly filed #18141; no equivalent — #18107 is selector weight, this is selector presence. A2A claim sweep over the last 12 messages, all read-states: no competing claim. Memory Core rationale sweep surfaced #18095/#18097 and Vega's 2026-08-31 specificity findings, both cited above. Own-assignment sweep: none on themes.
Origin Session ID: fb58e855-74b2-4367-9f83-5877f151f024
Retrieval Hint: theme value files must declare variables only no css selectors move to scss/src new tokens 26 files 62 nested blocks batch sweep
Context
Operator rule, stated 2026-09-02 while reviewing the theme contract behind #18141:
#18141 is the first defect this rule would have prevented:
tab/Container.scssexists in all four themes, carries selector-scoped blocks, and its flat value tokens drifted out of the classic pair unnoticed. A theme file that is only a value list makes an omission obvious; a theme file that also carries selectors hides one among them.The Problem
A theme value file answers "what is this theme's value for X". A selector answers "which elements get X" — a structural question the theme has no business deciding, because every theme then has to re-answer it identically and any one of them can drift.
Measured across
resources/scss/theme-*:Largest concentrations:
theme-neo-light/Global.scss,theme-neo-dark/Global.scss{theme-neo-light,theme-neo-dark}/tab/header/Button.scss{theme-neo-light,theme-neo-dark}/dialog/Base.scss{theme-neo-light,theme-neo-dark}/apps/portal/news/pulls/Component.scss{theme-light,theme-dark,theme-neo-light,theme-neo-dark}/tab/Container.scss{theme-neo-light,theme-neo-dark}/button/Base.scssGlobal.scss(light/dark/cyberpunk),grid/Container.scss(×4),grid/footer/Toolbar.scss(×2),apps/portal/home/FooterContainer.scss(×2)The
tab/Container.scssinstance is the worked example: all four themes repeat the same four-selector list (.neo-tab-container-inline, its detached-drag-proxy self-scoped twin, and the-standalonepair) purely to scope--tab-button-*values. The selector list is identical in every theme; only the values differ. That is a structural rule with a value hole in it, written out four times.The Architectural Reality
The correct split already exists in the tree and works:
resources/scss/src/tab/Container.scssowns.neo-tab-container-inline > .neo-tab-header-toolbarand reads--tab-header-inline-*; the themes value those tokens flat. Everything in the table above is the same shape not yet applied.Do not fix this by deleting the selectors. Each one exists because a value is variant-scoped, so each move needs a new token: the structural sheet grows the selector and reads
var(--new-token), and every theme declares--new-tokenflat. Per #18141, every theme must declare it — a token declared by some themes and not others is inherited across a nested theme boundary rather than reset.The Fix
One batch at a time, in the shape #18107 uses for its own theme sweep. Suggested ordering, cheapest and most defect-adjacent first:
tab/Container.scss(4 files, 8 blocks) — the #18141 neighbour, and the pattern demonstration.grid/Container.scss+grid/footer/Toolbar.scss(6 files, 6 blocks).button/Base.scss,tab/header/Button.scss,dialog/Base.scss(6 files, 16 blocks).apps/portal/**(4 files, 8 blocks).Global.scss(5 files, 23 blocks) — largest and least mechanical. Graduated to #18232 (2026-09-03), as this line anticipated: the measurement there found only 5 divergent declarations across the two neo files, and a weight drop that repaints realworld2 headings.Each batch: move the selector into
resources/scss/src/, introduce the tokens it needs, declare them in all themes, rebuild and diff the emitted CSS per theme to prove the paint is unchanged.Acceptance Criteria
resources/scss/src/.Out of Scope
--tab-header-inline-*value omission itself — that is #18141.:where()weight on theme value sheets — #18107 owns that surface and the two sweeps should not be run as one.Avoided Traps
Related
tab/Container.scssvalue omission.:where()weight sweep, adjacent surface, deliberately separate.Live latest-open sweep: checked the latest 20 open issues at 2026-09-02T21:26Z plus the newly filed #18141; no equivalent — #18107 is selector weight, this is selector presence. A2A claim sweep over the last 12 messages, all read-states: no competing claim. Memory Core rationale sweep surfaced #18095/#18097 and Vega's 2026-08-31 specificity findings, both cited above. Own-assignment sweep: none on themes.
Origin Session ID: fb58e855-74b2-4367-9f83-5877f151f024
Retrieval Hint:
theme value files must declare variables only no css selectors move to scss/src new tokens 26 files 62 nested blocks batch sweep