Skip to content

fix: don't count child combinators as selector specificity - #303

Open
Christopher Pruijsen (cpruijsen) wants to merge 2 commits into
microsoft:mainfrom
cpruijsen:fix/issue-292
Open

Christopher Pruijsen (cpruijsen) wants to merge 2 commits into
microsoft:mainfrom
cpruijsen:fix/issue-292

Conversation

@cpruijsen

Copy link
Copy Markdown

src/theme.ts stores parent scope names and > child-combinator tokens in the same array, and the
specificity comparator skips > while walking individual scopes but then compares raw
parentScopes.length. A selector therefore gains specificity by containing more combinators, which
the comparator's own scope-walking logic says should not happen. a > b outranks a b on token
count rather than on anything meaningful.

The comparator now counts parent scopes through parentScopeCount, which excludes >, so the length
comparison agrees with the walk above it.

Removing that signal needs something correct in its place, which is most of the diff. Raw length was
also acting as an accidental tie-break, so two rules of genuinely equal specificity could swap
depending on how many combinators they were written with. ThemeTrieElementRule carries the rule's
declaration index, and the comparator falls back to it when the corrected counts are equal. Later
declarations win, which is what a theme author expects and what the rules already did when the counts
happened to differ.

The file-level churn is that index has to reach the comparator: it is threaded through the
constructors, insert, _doInsertHere and acceptOverwrite. Only the two comparisons are behaviour.

Tests cover a > selector against an equally specific one without it, and the declaration-order
tie-break.

Fixes #292

A selector's parentScopes array mixes '>' child-combinator tokens with
parent scope names. The specificity comparator skipped '>' while
comparing individual parent scopes, but still counted them in the
final array-length comparison, so selectors with more combinators won
ties they should not have.

Count only real parent scope names for that comparison, and fall back
to the rule's source index when two selectors are equally specific, so
that the later rule in the theme wins - matching how equal-specificity
ties are already resolved for merged selectors.

Fixes microsoft#292
assertThemeEqual compared each Theme's root to itself, so expected
index values for the combinator-specificity change were never checked.
Compare against the expected tree and store the indices produced by
merging.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The reviewed changes address the specificity issue and include regression coverage.

Pull request overview

Fixes theme selector specificity so child combinators (>) do not increase specificity, while declaration order resolves ties.

Changes:

  • Excludes > tokens from specificity counts.
  • Threads declaration indexes through trie rules.
  • Adds regression coverage.
File summaries
File Description
src/theme.ts Updates specificity comparison and declaration-order tie-breaking.
src/tests/themes.test.ts Adds combinator specificity regression coverage.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Child combinators are counted as selector specificity

2 participants