Plumeria 19.8
Plumeria v19.8 rewrites how the compiler reads selector keys. The algorithm was proposed by Anionix.
1. One pass for specificity
Specificity is computed in a single pass with a stack of frames, without recursion or copies of the selector. It is 3 to 9 times faster on short keys and about twice as fast on a 4 KB key. Valid selectors get the same specificity as in 19.7.
2. Fixes
- A comment in a key no longer counts toward specificity, so
'.a/* #b */'is not ordered as if it held an ID. :nth-child(2n OF .a)readsOFin any case, as CSS keywords are case-insensitive.- A stray or unclosed quote, such as
':hover":is(.x)', stops the build.
3. Selector rules
:not() can nest inside :not() again. Unlike :is() or :where(), a :not() inside :not() changes what the selector matches, so it cannot be flattened:
':not(:not(.a), .b)' // matches .a, but not .b
':not(.a, .b)' // matches neitherA key can nest :not() and functional pseudo-classes of different names up to 16 levels deep. Any other functional pseudo-class still cannot nest inside one of the same name, as in 19.7:
':is(:where(.a))' // allowed
':is(:where(:is(.a)))' // stops the buildno-invalid-selector reports the same keys the compiler rejects. Neither a person nor an AI agent can write a key past these limits: the lint reports it, and the compiler stops the build. See selector rules.
Release notes
19.8.2 (Oct 5, 2026)
- Remove the
fundingfield from package.json - Move the test-only devDependencies of
@plumeria/compilerto the workspace root - Update the
nextdevDependency of@plumeria/next-pluginto 16.3.8
19.8.1 (Oct 4, 2026)
- Fix #631: specificity reads selector names as CSS identifiers, so
ofin:nth-child()is read when.,#or:follows it directly (2 of.a) and when escaped (o\66). Proposed by Anionix - Count escaped pseudo-class names such as
:\69s()and:nth-chil\64() - Count type selectors that start with a non-ASCII character
19.8.0 (Oct 3, 2026)
- Compute specificity in one pass, and stop counting comments in selector keys
- Read
OFin:nth-child()in any case - Allow
:not()inside:not(), and stop the build on a key nested deeper than 16 levels or holding a stray quote no-invalid-selectorreports a key nested deeper than 16 levels or holding a stray quote