Skip to content

Accept whitespace around the offset sign in :nth-child(An+B) - #192

Merged
TylerBrinks merged 1 commit into
TylerBrinks:masterfrom
jhaygood86:bugfix/an-b-selector-spaced-sign
Jul 23, 2026
Merged

Accept whitespace around the offset sign in :nth-child(An+B)#192
TylerBrinks merged 1 commit into
TylerBrinks:masterfrom
jhaygood86:bugfix/an-b-selector-spaced-sign

Conversation

@jhaygood86

@jhaygood86 jhaygood86 commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Problem

:nth-child(10n + 1)   /* rejected */
:nth-child(10n+1)     /* accepted */

CSS Syntax 3 §6.2 defines <a-n-plus-b> with, among others, the production:

<n-dimension> ['+' | '-'] <signless-integer>

and §6.1 states:

Whitespace is permitted on either side of the + or - that separates the An and B parts when both are present.

listing 3n + 1, +3n - 2 and -n+ 6 as valid examples.

The two spellings lex differently:

Source Tokens after 10n
10n+1 one signed <number> +1
10n + 1 <whitespace>, <delim +>, <whitespace>, <number> 1

ChildFunctionState.OnOffset handles only Whitespace and Number, so the standalone delim falls through its default: arm into OnBeforeOf — which is looking for of or ) — and sets _valid = false, dropping the rule.

This affects :nth-child(), :nth-last-child(), :nth-of-type() and :nth-last-of-type() alike.

Fix

Handle the +/- delim in OnOffset and add an AfterOffsetSign state for the digits that follow.

That state is deliberately strict rather than just setting a sign flag and staying put, because the production calls for a <signless-integer>, which §6.2 defines as:

a <number-token> with its type flag set to "integer", and no sign character

So a second sign stays invalid — and §6.1 lists 3n + -6 among its invalid examples, which is exactly this case. NumberToken.Data preserves the authored sign character, which is what the check keys on.

Tests

20 theory cases in SelectorsTests.cs, asserting ChildSelector.Step/Offset rather than round-tripped text:

  • spaced forms — 10n + 1, 10n - 1, 2n + 0, -2n + 3, and extra internal whitespace
  • compact forms and odd/even, which must keep working unchanged
  • the three other :nth-* variants
  • the spec's own invalid examples — 3n + -6, 3 n, + 2n, + 2 — plus 10n + +1, 10n + + 1 and 10n + n

8 fail on master; the invalid cases pass both before and after, confirming the fix doesn't loosen the grammar. The full suite (1263 existing tests) stays green with no existing assertion modified, and all seven target frameworks build with no new warnings.

@jhaygood86
jhaygood86 marked this pull request as ready for review July 22, 2026 21:19
@jhaygood86
jhaygood86 marked this pull request as draft July 22, 2026 21:40
@jhaygood86
jhaygood86 marked this pull request as ready for review July 22, 2026 21:43
<a-n-plus-b> includes the production

  <n-dimension> ['+' | '-'] <signless-integer>

and "whitespace is permitted on either side of the + or - that
separates the An and B parts when both are present", with "3n + 1" given
as a valid example (CSS Syntax 3 6.1, 6.2). The two spellings lex
differently: "10n+1" yields a single signed <number>, while "10n + 1"
yields a standalone '+' delim followed by a signless integer.

ChildFunctionState.OnOffset handled only whitespace and numbers, so the
standalone sign fell through to OnBeforeOf, which is looking for "of" or
')', and marked the selector invalid. The rule was then dropped.

Handle the delim in OnOffset and add an AfterOffsetSign state for the
digits that follow. That state requires a <signless-integer> - "a
<number-token> with its type flag set to integer, and no sign character"
- so "3n + -6", listed as an invalid example in 6.1, stays invalid, as
do "10n + +1" and a repeated sign.
@jhaygood86
jhaygood86 force-pushed the bugfix/an-b-selector-spaced-sign branch from 406c52b to 6468c3f Compare July 22, 2026 21:49
@TylerBrinks
TylerBrinks merged commit 049cbfb into TylerBrinks:master Jul 23, 2026
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.

2 participants