lightningcss icon indicating copy to clipboard operation
lightningcss copied to clipboard

Unable to set a different value for a single vendor

Open Netail opened this issue 7 months ago • 3 comments

Example 1

When appearance is specified later than a custom value for a vendor prefixed equivalent, it includes the vendor prefixed equivalent into vendor prefixing. (As CSS reads from top to bottom it will take the last one)

.foo {
  -webkit-appearance: none;
  appearance: textfield;
}

Current

.foo {
  -webkit-appearance: none;
  -webkit-appearance: textfield;
  -moz-appearance: textfield;
  appearance: textfield;
}

Sandbox

Expected

.foo {
  -webkit-appearance: none;
  -moz-appearance: textfield;
  appearance: textfield;
}

Example 2

When a vendor prefixed property is specified later than the default one, all of the added prefixed properties become the value of the last one

.bar {
  appearance: textfield;
  -webkit-appearance: none;
}

Current

.bar {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
}

Sandbox

Expected

.bar {
  -moz-appearance: textfield;
  appearance: textfield;
  -webkit-appearance: none;
}

Note

Currently it's impossible to have a custom value for 1 specific browser, as the last one is leading with all options I would expect a matching result as with the PostCSS autoprefixer plugin

Netail avatar May 09 '25 15:05 Netail

Partially related; https://github.com/parcel-bundler/lightningcss/issues/969 & https://github.com/parcel-bundler/lightningcss/issues/872

Netail avatar May 09 '25 15:05 Netail

As another example of this causing problems: WebKit’s initial, prefixed implementation of backdrop filter is badly broken, to the point of being completely unusable for some common purposes.¹ This version is spelled -webkit-backdrop-filter, and they don’t support backdrop-filter.

They have, behind a flag², another implementation which provides backdrop-filter and replaces the -webkit-backdrop-filter implementation, and fixes at least one of the two major problems; I presume it fixes both, but cannot confirm.

I was making something where the initial implementation is unacceptable, and needs to be disabled.

So I thought to write something like one of these two:

@supports not (backdrop-filter: none) {
    -webkit-backdrop-filter: none;
}

@supports ((-webkit-backdrop-filter: none) and (not (backdrop-filter: none)) {
    -webkit-backdrop-filter: none;
}

But each time, Lightning CSS changes the (backdrop-filter: none) to ((backdrop-filter: none) or (-webkit-backdrop-filter: none)).

The best I managed was @supports (-webkit-backdrop-filter: none), which will block the new implementation for as long as they retain the prefixed property alongside it, which will probably be quite some time. (Neither Blink nor Gecko support the prefixed name.)

And if I tried something like this:

* {
    backdrop-filter: blur(1rem);
    -webkit-backdrop-filter: none;
}

… somehow that leads to the backdrop-filter: blur(1rem) line being removed! (If you flip the lines around, the none will be changed to blur(1rem).)

Auto-prefixing is nice for a few remaining properties, but it’s really important to be able to opt out of it on a case-by-case basis, because otherwise it blocks legitimate code. Honestly, if I were making a tool like this now, whether from scratch or by forking Lightning CSS, I’d actively exclude auto-prefixing functionality. In practice it’s down to less than ten properties if you want even three years of universal support; and really more like four that people actually use.

But this did suggest a practical workaround: exclude VendorPrefixes from your config. This allows @supports not (backdrop-filter: none) to work appropriately, but doesn’t fix -webkit-backdrop-filter blatting a preceding backdrop-filter line.

… except that vendor prefix stuff is baked in too deep, and that whole problem of it blatting one of the lines still applies.

So your actual workaround will have to split them into separate rule sets, maybe by a &{…} wrapping.

I also want to note down that I’ve been seeing three copies of a -webkit- line being generated in some cases. I haven’t looked. I don’t think I’m going to look, I’m concerned the code base might be hazardous to my health.

Without having looked, then, I can only report that the vibe I’m getting is that all prefix handling may be subtly but comprehensively broken. It handles the happy paths tolerably, but it’s hostile to the sad paths.

(There are plenty more cases where it tries doing things that a human can clearly see won’t work, such as adding a prefixed version of a property although it uses a function such as color-mix() which no browsers requiring the prefix support. Given how prefixes are no longer used for new things, this sort of thing should be pretty easy to catch.)

—⁂—

¹ Two things, specifically. Firstly, its notion of backdrop root is all wrong, and will actually include things drawn above the backdrop too, so that a blur will make everything on top of it glow due to being rendered twice, once blurred. I honestly don’t know how that happened, and it’s awful for the main application of backdrop-filter. Secondly, if you use things like calc() or var(), it fails silently (accepts the declaration, but no longer draws anything).

² Safari 18 release notes claimed to have shipped unprefixed backdrop-filter, but this does not actually appear to be true. Not having Apple hardware, all I can say is that webkitgtk 2.48.5 doesn’t have it.

chris-morgan avatar Sep 06 '25 09:09 chris-morgan

@devongovett any ideas? For backwards compatibility vendor-prefixes are sometimes needed, with appearance for example

Netail avatar Oct 22 '25 14:10 Netail