Skip to content

fix(supports): keep the vendor prefix on a feature query inside and/or - #1316

Open
oddharsh wants to merge 1 commit into
parcel-bundler:masterfrom
oddharsh:fix/supports-prefix-stripped-in-compound-condition
Open

fix(supports): keep the vendor prefix on a feature query inside and/or#1316
oddharsh wants to merge 1 commit into
parcel-bundler:masterfrom
oddharsh:fix/supports-prefix-stripped-in-compound-condition

Conversation

@oddharsh

Copy link
Copy Markdown

Fixes #710.

The @supports merge pass binds the unprefixed id over property_id, and then uses that binding for two more things: what it stores, and which prefix it merges in.

let property_id = property_id.with_prefix(VendorPrefix::None);   // shadows
let key = (property_id.clone(), value.clone());
if let Some(index) = seen_declarations.get(&key) {
    cur_property.add_prefix(property_id.prefix());               // always VendorPrefix::None
} else {
    seen_declarations.insert(key, conditions.len());
    conditions.push(SupportsCondition::Declaration { property_id, value });   // stored stripped
}

So a condition matching nothing already seen is pushed with its prefix gone, and (-webkit-box-orient: vertical) becomes (box-orient: vertical) — which no browser implements, so the block it guards silently never applies.

Using a separate binding for the match key fixes both symptoms. The unprefixed id is the key only; the condition keeps the prefix it was written with, and the merge branch adds the real prefix instead of None.

Why it reads as an inconsistency rather than a plain regression

A prefixed query survives when it stands alone, because that path pushes in_parens untouched. It is only stripped as an operand of and / or. Measured on 1.33.0 before this change:

condition alone inside (display:flex) or …
(-webkit-box-orient:vertical) preserved (box-orient:vertical)
(-webkit-backdrop-filter:blur(1px)) preserved (backdrop-filter:blur(1px))
(-moz-appearance:none) preserved (appearance:none)

Worth knowing for anyone triaging this: reducing a report to a single condition to check whether it still reproduces answers no on a build that is still broken.

Why the existing tests did not catch it

Every assertion added with the merge feature in 6bd2761 sets Browsers targets, and the prefixing pass then re-adds what the merge dropped. The regression tests here deliberately set none, and they use minify_test rather than prefix_test for that reason.

Verification

Before and after, same input, minify: true, no targets:

BEFORE   @supports (display:flex) and (box-orient:vertical){a{color:red}}
AFTER    @supports (display:flex) and ((-webkit-box-orient:vertical)){a{color:red}}
  • 120/120 lib tests pass, including the merge assertions from 6bd2761
  • the three added tests fail without the fix, printing the stripped form, and pass with it
  • cargo fmt leaves both changed files untouched (it does reformat bundler.rs, selector.rs and an unrelated lib.rs line under current rustfmt; those are deliberately not included here)

The @supports merge pass bound the unprefixed id over `property_id` and then
used that binding for two more things: what it stored, and which prefix it
merged in. So a condition matching nothing already seen was pushed with its
prefix stripped, and `(-webkit-box-orient: vertical)` became
`(box-orient: vertical)`, which no browser implements. The block it guards then
silently never applies.

Using a separate binding for the match key fixes both. The unprefixed id is the
KEY only; the condition keeps the prefix it was written with, and the merge
branch now adds the real prefix rather than VendorPrefix::None.

A prefixed query survives when it stands alone, because that path pushes
`in_parens` untouched, which is why this reads as an odd inconsistency rather
than a plain regression. Reducing a report to a single condition to check
whether it still reproduces answers no on a build that is still broken.

The tests that shipped with the feature all set `Browsers` targets, and the
prefixing pass then re-adds what the merge dropped, so they could not catch it.
The regression tests added here deliberately set none.

Fixes parcel-bundler#710
oddharsh added a commit to oddharsh/site that referenced this pull request Aug 26, 2026
…pstream (#596)

parcel-bundler/lightningcss#1316. The merge pass binds the unprefixed id OVER
`property_id` and then uses that binding for two more things, so a condition
matching nothing already seen is stored stripped and the merge branch adds
`VendorPrefix::None` rather than the real prefix. A separate binding for the
match key fixes both; 120/120 of their lib tests pass and the three added
regression tests fail without it.

That mechanism also explains the compound trigger this entry had recorded as a
bare observation. A lone condition takes the `conditions.push(in_parens.clone())`
path and never reaches the merge, which is why it survives while an operand of
and/or does not. The explanation was one commit away the whole time, and the
entry had been sitting on a reproduction for a week without anyone opening the
source. Worth remembering the next time a gotcha here stops at "reproduces on
version N".

Also marks the `:-moz-focusring` arm as a workaround with an expiry rather than
a permanent shape, and says explicitly not to remove it early: this is the quiet
failure direction, so nothing goes red when the bug returns.

Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com>
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.

Improperly unprefixing -webkit-box-orient when in a @supports block

1 participant