Skip to content

:placeholder-shown is replaced with -moz-placeholder, although this is outdated #1533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kliehm opened this issue Mar 20, 2025 · 23 comments
Open

Comments

@kliehm
Copy link

kliehm commented Mar 20, 2025

Due to a change in autoprefixer in the last two weeks I assume, suddenly :placeholder-shown is replaced with :-moz-placeholder. According to caniuse, this should only be necessary in Firefox < 50, not in current versions.

Although as far as I understand, :-moz-placeholder should be a synonym to ::placeholder, i.e. to style the placeholder, whereas :placeholder-shown is an indicator if the placeholder is visible or not. So if the focus is within the input and the placeholder text disappears, :placeholder-shown is false. That's something different than styling the placeholder.

Our .browserslistrc looks like this, so Firefox < 50 should not be relevant:

> 1% in DE
last 2 versions
not dead

For the time being, I disabled replacement of :placeholder-shown with /* autoprefixer: ignore next */.

@ai
Copy link
Member

ai commented Mar 20, 2025

Can you show simple example of input CSS, output and expected output?

@ai
Copy link
Member

ai commented Mar 20, 2025

cc @Marukome0743

@ai
Copy link
Member

ai commented Mar 20, 2025

Although as far as I understand, :-moz-placeholder should be a synonym to ::placeholder, i.e. to style the placeholder, whereas :placeholder-shown is an indicator if the placeholder is visible or not

https://caniuse.com/?search=placeholder-shown says that :-moz-placeholder equals to :placeholder-shown.

Also, another argument is that :placeholder-shown and :-moz-placeholder both uses single : (which means it is a state, not [virtual] element).

But we may be wrong. Please show your proofs or arguments.

@kliehm
Copy link
Author

kliehm commented Mar 20, 2025

https://caniuse.com/?search=placeholder-shown says that :-moz-placeholder equals to :placeholder-shown.

Yes, but that's only true for Firefox prior v50. Modern Firefoxes support :placeholder-shown.

Here is a minimal example: show the label only if the placeholder is visible:

HTML:
<input placeholder="&nbsp;" /><label>Text</label>

CSS:
label { display: none };
input:not(:placeholder-shown) + label { display: block }

In retrospective, I could have achieved the same effect with :empty, but this is how we've done it. It worked until recently. With the replacement it doesn't work as expected anymore in current versions of Firefox:

input:not(:-moz-placeholder) + label { display: block }

I appreciate your great work, that's only a little annoyance. :)

@ai
Copy link
Member

ai commented Mar 20, 2025

Oh, so you have a problem only with not()? It is important note.

Can you show current output and expected output (with prefixes)?

@kliehm
Copy link
Author

kliehm commented Mar 20, 2025

Can you show simple example of input CSS, output and expected output?

Here it still works: the search field at the top has a label "what are you looking for" that disappears, when the input receives focus. Also the reset button (the x) only appears when there is text in the input.

https://verwaltung.bund.de/portal/EN

The negative case only appears on dev stages so far.

@ai
Copy link
Member

ai commented Mar 20, 2025

  1. I need to show a small isolated example, not the whole website. Sorry, debugging the huge part of code is hard (and it could be an issue in another part than Autoprefixer, maybe you rely on broken behavior).
  2. I need current output and output that it is expected (so the 1 link doesn’t work).

@kliehm
Copy link
Author

kliehm commented Mar 20, 2025

Okay, I made a fiddle: https://codepen.io/kliehm/pen/ZYExNdd

With a current Firefox you can see that :placeholder-shown is not equivalent to :-moz-placeholder.

So in current versions of Firefox autoprefixer shouldn't replace it. I didn't succeed to include different versions of autoprefixer in the fiddle, so I made it static. My apologies that I'm not able to pin it down further, if it's a problem with autoprefixer or browserslist. I don't think it's a change in Firefox, since in version 10.4.21 of autoprefixer it gets replaced with :-moz-placeholder, whereas in version 10.4.20 it didn't.

@ai
Copy link
Member

ai commented Mar 20, 2025

  1. This fiddle is also useful, thanks for it.
  2. But fiddle is not actual/expected CSS output. Please, it is really important for me to follow the instruction because it saves my time for maintaince open source project.
  3. Talking about this fiddle. What is expected and real behaviour. Please show the screenshot.

I see that Firefox and Chroma output is exactly the same.

Image

@kliehm
Copy link
Author

kliehm commented Mar 20, 2025

Okay, sorry for the inconvenience. This is my output on Firefox v136.0.2 (Windows). It's interesting that your browser is interpreting the second part exactly opposite from the expected behavior of :-moz-placeholder. I can reproduce your screenshot with Chrome 134.0.6998.89.

The expected behavior is the top two examples with :placeholder-shown, same as your result.

The bottom two examples with :-moz-placeholder show that it's not the same result as :placeholder-shown. Although you get different results, you can see that it's not equivalent.

Image

@kliehm
Copy link
Author

kliehm commented Mar 21, 2025

@Marukome0743 I could pin it down: it worked with autoprefixer 10.4.20, was broken with autoprefixer 10.4.21. It is this commit.

The problem is that caniuse lists the replacement with :-moz-placeholder only for Mozilla < 50. For Mozilla > 50 it shouldn't be replaced, pretty please.

@Marukome0743
Copy link
Contributor

Marukome0743 commented Mar 25, 2025

The problem is that caniuse lists the replacement with :-moz-placeholder only for Mozilla < 50. For Mozilla > 50 it shouldn't be replaced, pretty please.

Explanation of bug

TL;DR: The support of Kaios 2.5

I finally found out the cause of this issue.
I will describe this step by step.

First, Autoprefixer don't add the :-moz-placeholder under last 1 version browsersl.ist.

Autoprefixer last 1 preview

Image

Second, Autoprefixer appends the :-moz-placeholder from last 2 version browsersl.ist.

Autoprefixer last 2 version preview

Image

Third, I checked the target browsers of the :placeholder-shown property from below code..

// Placeholder-shown selector
let prefixPlaceholderShown = require('caniuse-lite/data/features/css-placeholder-shown')
f(prefixPlaceholderShown, browsers => {
prefix([':placeholder-shown'], {
browsers,
feature: 'css-placeholder-shown',
selector: true
})
})

:placeholder-shown target browsers

[
  'firefox 4',  'firefox 5',  'firefox 6',
  'firefox 7',  'firefox 8',  'firefox 9',
  'firefox 10', 'firefox 11', 'firefox 12',
  'firefox 13', 'firefox 14', 'firefox 15',
  'firefox 16', 'firefox 17', 'firefox 18',
  'firefox 19', 'firefox 20', 'firefox 21',
  'firefox 22', 'firefox 23', 'firefox 24',
  'firefox 25', 'firefox 26', 'firefox 27',
  'firefox 28', 'firefox 29', 'firefox 30',
  'firefox 31', 'firefox 32', 'firefox 33',
  'firefox 34', 'firefox 35', 'firefox 36',
  'firefox 37', 'firefox 38', 'firefox 39',
  'firefox 40', 'firefox 41', 'firefox 42',
  'firefox 43', 'firefox 44', 'firefox 45',
  'firefox 46', 'firefox 47', 'firefox 48',
  'firefox 49', 'firefox 50', 'ie 10',
  'ie 11',      'kaios 2.5'
]

The last 2 version browsersl.ist doesn't include the old firefox browsers.
So why does Autoprefixer insert the :-moz-placeholder when you cover only last 2 version?

Because of kaios 2.5.

The last 2 version browsersl.ist covers kaios 2.5.

The last 2 version browsersl.ist

Image

In conclusion, Autoprefixer adds :-moz-placeholder with last 2 version browsersl.ist due to kaios 2.5.

Solutions

1. Do nothing

This is a desired result for the people who want to cover kaios 2.5.
Then continue to adding :-moz-placeholder.

2. Remove kaios from your own Browserslist.

You can change the target of the browsers using .browserslistrc, package.json and so on.
See Browserslist docs for available queries and default value.

3. Don't parse :placeholder-shown to :-moz-placeholder

At first my PR was the removal of :-moz-placeholder-shown property.
I didn't know the old firefox supports the non-standard :-moz-placeholder name rather than :placeholder-shown partially.
To avoid confusing, stop to parse :placeholder-shown to :-moz-placeholder.

Which solution is better?
Thank you for reading!

@kliehm
Copy link
Author

kliehm commented Mar 25, 2025

3. Don't parse :placeholder-shown to :-moz-placeholder

At first my PR was the removal of :-moz-placeholder-shown property. I didn't know the old firefox supports the non-standard :-moz-placeholder name rather than :placeholder-shown partially. To avoid confusing, stop to parse :placeholder-shown to :-moz-placeholder.

Which solution is better? Thank you for reading!

Thank you for your research. I'm afraid solution #2 doesn't work. According to our Browserslist configuration, KaiOS should be excluded:

> 1% in DE
last 2 versions
not dead

Other browsers and OS have a current market share in Germany of < 0.2% (KaiOS doesn't even appear in stats after 2018), so it shouldn't be included. If that configuration doesn't work, I'd prefer solution #3.

@Marukome0743
Copy link
Contributor

I wonder if you use or combiner instead of and one.


Image


Image


Then you should write this code.

> 1% in DE and last 2 versions
not dead

Please forgive me if it's wrong because I'm newbie on browserslist.

@kliehm
Copy link
Author

kliehm commented Mar 25, 2025

Then you should write this code.

> 1% in DE and last 2 versions
not dead

Good point, I'm not an expert either. I will try that.

@Marukome0743
Copy link
Contributor

Eventually, I got the corrent .browserslistrc.

> 1% in DE
last 2 versions
not dead
not kaios <= 2.5
Exclude kaios from Browserslist

Image

I also paste the result of your previous one to compare easily.

> 1% in DE
last 2 versions
not dead
Previous Browserslist

Image

This .browserslistrc doesn't parse :placeholder-shown to :-moz-placeholder anymore, right?
Could you try it?

@kliehm
Copy link
Author

kliehm commented Mar 26, 2025

Could you try it?

I tried your suggestion

> 1% in DE and last 2 versions
not dead

and it works. So for me that's fine. Thank you for the kind conversation and help.

@zipper
Copy link

zipper commented Apr 1, 2025

I just came across this issue. My use case is the same—combining :not() and :placeholder-shown, which results in the selector always being applied. I fixed this by adding not kaios <= 2.5, but I’m wondering if there will be an official fix.

Option 3 seems reasonable—could someone verify whether :-moz-placeholder in KaiOS 2.5 behaves the same way as :placeholder-shown? Or could this be an error in the Can I Use data?

@jrchamp
Copy link

jrchamp commented Apr 4, 2025

@zipper It looks like the Can I Use data is correct. Based on the KaiOS release history, it seems that KaiOS before 3.0 used Gecko/Firefox 48 (which uses :-moz-placeholder).

So 10.4.20 and earlier had been generating the incorrect :-moz-placeholder-shown for a long time and when it was fixed to :-moz-placeholder, it was technically fixing support for Firefox <= 50 and KaiOS 2.5. The problem is that this backward compatibility code for old versions of Firefox-based browsers is having negative effects on current versions of Firefox-based browsers.

The options seem to be:

  • Rework CSS to avoid combining :not() with an autoprefixed property.
  • Drop support for old Firefox-based browsers (including KaiOS 2.5)

Example, if the text is black and we use :not(:placeholder-shown) to make the text green. Then it will be expanded to two rules, :not(:placeholder-shown) and :not(:-moz-placeholder) that both make the text green when true.

Then, if the element is not showing the placeholder and the browser supports:

  • neither property, then the text is black (because both are treated as invalid).
  • only :placeholder-shown, then the text is black (because one of them is false and the other rule is treated as invalid).
  • only :-moz-placeholder, then the text is black (because one of them is false and the other rule is treated as invalid).
  • :-moz-placeholder and :placeholder-shown, then the text should be black (because both should be false).
    • if the text is green, then it probably means that the newer property, :placeholder-shown, is correctly set to false and the older property :-moz-placeholder is "known"/"not invalid" but not set to false

One approach to reworking the CSS would be to focus on the positive: write the default rule and then positively override that default with when :placeholder-shown is true.

@jrchamp
Copy link

jrchamp commented Apr 4, 2025

As a sidenote, :is() exists and may simplify pseudo class selector expansion on newer browsers (but probably won't help with this issue).

From https://developer.mozilla.org/en-US/docs/Web/CSS/:not#description

If any selector passed to the :not() pseudo-class is invalid or not supported by the browser, the whole rule will be invalidated. The effective way to overcome this behavior is to use :is() pseudo-class, which accepts a forgiving selector list. For example :not(.foo, :invalid-pseudo-class) will invalidate a whole rule, but :not(:is(.foo, :invalid-pseudo-class)) will match any (including <html> and <body>) element that isn't .foo.

@karolyi
Copy link

karolyi commented May 2, 2025

I was wondering why floating form labels don't work in bootstrap and as I started investigating, I've bumped into this bug.

Practically, because of this bug, a

.form-floating > .form-control-plaintext ~ label, .form-floating > .form-control:focus ~ label, .form-floating > .form-control:not(:placeholder-shown) ~ label, .form-floating > .form-select ~ label {
  transform: scale(.85) translateY(-.5rem) translateX(.15rem);
}

will get turned into

.form-floating > .form-control:not(:-moz-placeholder) ~ label {
  transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem);
}

The former is from the bootstrap site's CSS itself, the latter is my webpack-generated CSS output with using postcss-preset-env. No extra formattings or adjustments on my behalf, it's what gets generated with webpack from the latest bootstrap source. I've tracked the bug down to postcss-preset-env, it works when I comment it out from the webpack configuration.

Looking forward to have this bug fixed because I'm quite sure it corrupts floating labels for everyone else too.

@jrchamp
Copy link

jrchamp commented May 2, 2025

@karolyi The floating form labels issues was fixed by removing KaiOS 2.5 support from Bootstrap 5.3.5 via twbs/bootstrap@b1e16bd

@karolyi
Copy link

karolyi commented May 2, 2025

@jrchamp thanks, but that only applies for the bootstrap website, I presume. I'm using bootstrap as a module and have my own .browserslistrc setting.

I've added the referenced line to it for now, but you can expect others to show up with this same issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
6 participants