autoprefixer
autoprefixer copied to clipboard
:placeholder-shown is replaced with -moz-placeholder, although this is outdated
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 */.
Can you show simple example of input CSS, output and expected output?
cc @Marukome0743
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.
https://caniuse.com/?search=placeholder-shown says that
:-moz-placeholderequals 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=" " /><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. :)
Oh, so you have a problem only with not()? It is important note.
Can you show current output and expected output (with prefixes)?
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.
- 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).
- I need current output and output that it is expected (so the 1 link doesn’t work).
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.
- This fiddle is also useful, thanks for it.
- 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.
- 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.
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.
@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.
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
Second, Autoprefixer appends the :-moz-placeholder from last 2 version browsersl.ist.
Autoprefixer last 2 version preview
Third, I checked the target browsers of the :placeholder-shown property from below code..
https://github.com/postcss/autoprefixer/blob/541295c0e6dd348db2d3f52772b59cd403c59d29/data/prefixes.js#L484-L493
: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
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!
3. Don't parse
:placeholder-shownto:-moz-placeholderAt first my PR was the removal of
:-moz-placeholder-shownproperty. I didn't know the old firefox supports the non-standard:-moz-placeholdername rather than:placeholder-shownpartially. To avoid confusing, stop to parse:placeholder-shownto:-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.
I wonder if you use or combiner instead of and one.
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.
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.
Eventually, I got the corrent .browserslistrc.
> 1% in DE
last 2 versions
not dead
not kaios <= 2.5
Exclude kaios from Browserslist
I also paste the result of your previous one to compare easily.
> 1% in DE
last 2 versions
not dead
Previous Browserslist
This .browserslistrc doesn't parse :placeholder-shown to :-moz-placeholder anymore, right?
Could you try it?
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.
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?
@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-placeholderand: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-placeholderis "known"/"not invalid" but not set to false
- if the text is green, then it probably means that the newer property,
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.
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.
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.
@karolyi The floating form labels issues was fixed by removing KaiOS 2.5 support from Bootstrap 5.3.5 via https://github.com/twbs/bootstrap/commit/b1e16bd1617ca29509aaad7c2f6fa6e377aaf173
@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.
@karolyi The floating form labels issues was fixed by removing KaiOS 2.5 support from Bootstrap 5.3.5 via twbs/bootstrap@b1e16bd
Issue still exists when using the latest Bootstrap 5.3.6, even after removing KaiOS from the browserslist.
Issue still exists when using the latest Bootstrap 5.3.6, even after removing KaiOS from the browserslist.
Really?
I made the demo repository and tried the floating labels on bootstrap with Firefox 139.0.4 version. I will show you the tests through the videos.
Part1: unspecified the browserslist
Movie
package.json
{
"name": "sass-js-esm",
"description": "Include Bootstrap's source Sass and compiled JavaScript bundle via npm.",
"version": "0.0.0",
"private": true,
"license": "MIT",
"stackblitz": {
"startCommand": "npm start"
},
"scripts": {
"build": "npm run css",
"css-compile": "sass --style compressed --source-map --embed-sources --no-error-css --load-path=node_modules scss/:css/",
"css-lint": "stylelint scss/",
"css-prefix": "postcss --replace css/styles.css --use autoprefixer --map",
"css": "npm-run-all css-compile css-prefix",
"server": "sirv --dev --no-clear --port 3000",
"start": "npm-run-all --parallel watch server",
"watch": "nodemon -e html,scss -x \"npm run css\"",
"test": "npm-run-all css-lint css"
},
"dependencies": {
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.6"
},
"devDependencies": {
"autoprefixer": "^10.4.21",
"nodemon": "^3.1.10",
"npm-run-all": "^4.1.5",
"postcss": "^8.5.4",
"postcss-cli": "^11.0.1",
"sass": "^1.89.1",
"sirv-cli": "^3.0.1",
"stylelint": "^16.20.0",
"stylelint-config-twbs-bootstrap": "^16.0.0"
}
}
As you say, the floating labels don't work because Autoprefixer adds :-moz-placeholder prefix.
Part2: Browserslist has not kaios <= 2.5 settings
Movie
package.json
{
"name": "sass-js-esm",
"description": "Include Bootstrap's source Sass and compiled JavaScript bundle via npm.",
"version": "0.0.0",
"private": true,
"license": "MIT",
"stackblitz": {
"startCommand": "npm start"
},
"scripts": {
"build": "npm run css",
"css-compile": "sass --style compressed --source-map --embed-sources --no-error-css --load-path=node_modules scss/:css/",
"css-lint": "stylelint scss/",
"css-prefix": "postcss --replace css/styles.css --use autoprefixer --map",
"css": "npm-run-all css-compile css-prefix",
"server": "sirv --dev --no-clear --port 3000",
"start": "npm-run-all --parallel watch server",
"watch": "nodemon -e html,scss -x \"npm run css\"",
"test": "npm-run-all css-lint css"
},
"dependencies": {
"@popperjs/core": "^2.11.8",
"bootstrap": "^5.3.6"
},
"devDependencies": {
"autoprefixer": "^10.4.21",
"nodemon": "^3.1.10",
"npm-run-all": "^4.1.5",
"postcss": "^8.5.4",
"postcss-cli": "^11.0.1",
"sass": "^1.89.1",
"sirv-cli": "^3.0.1",
"stylelint": "^16.20.0",
"stylelint-config-twbs-bootstrap": "^16.0.0"
},
"browserslist": [
"last 2 version",
"not dead",
"not kaios <= 2.5"
]
}
The floating label works correctly since there is no :-moz-placeholder prefix in input element anymore.
Summary
I can't reproduce your issue when using Bootstrap 5.3.6.
Would you check it with your Firefox browser in StackBlitz?
Really?
Nope, totally my mistake, I just figured out that postcss couldn’t find the .browserslistrc file. Sorry about that! 🙏
Really?
Nope, totally my mistake, I just figured out that postcss couldn’t find the .browserslistrc file. Sorry about that! 🙏
Never mind, I made many mistakes too. Anyway I'm happy to help your issue😄
Am I doing something wrong here? I changed the list to be kinda simple to make sure something else doesn't impact the output but I'm still getting prefixed :not(:placeholder-shown) into :not(:-moz-placeholder). I'm testing this using:
postcss css/style.css -u autoprefixer --autoprefixer.browsers "last 1 version and not dead and not kaios <= 2.5" --no-map -o css/style.cs
The browsersl.ist doesn't show the kaiOS browser 2.5, so it shouldn't do the thing, right?
I'm still getting prefixed :not(:placeholder-shown) into :not(:-moz-placeholder).
The latest postcss-cli doesn't have --autoprefixer.browsers and -b option anymore.
Current postcss-cli support options
Usage:
postcss [input.css] [OPTIONS] [-o|--output output.css] [--watch|-w]
postcss <input.css>... [OPTIONS] --dir <output-directory> [--watch|-w]
postcss <input-directory> [OPTIONS] --dir <output-directory> [--watch|-w]
postcss <input-glob-pattern> [OPTIONS] --dir <output-directory> [--watch|-w]
postcss <input.css>... [OPTIONS] --replace
Basic options:
-o, --output Output file [string]
-d, --dir Output directory [string]
-r, --replace Replace (overwrite) the input file [boolean]
-m, --map Create an external sourcemap
--no-map Disable the default inline sourcemaps
-w, --watch Watch files for changes and recompile as needed [boolean]
--verbose Be verbose [boolean]
--env A shortcut for setting NODE_ENV [string]
Options for use without a config file:
-u, --use List of postcss plugins to use [array]
--parser Custom postcss parser [string]
--stringifier Custom postcss stringifier [string]
--syntax Custom postcss syntax [string]
Options for use with --dir:
--ext Override the output file extension; for use with --dir [string]
--base Mirror the directory structure relative to this path in the output
directory, for use with --dir [string]
Advanced options:
--include-dotfiles Enable glob to match files/dirs that begin with "."
[boolean]
--poll Use polling for file watching. Can optionally pass polling
interval; default 100 ms
--config Set a custom directory to look for a config file [string]
Options:
--version Show version number [boolean]
-h, --help Show help [boolean]
Examples:
postcss input.css -o output.css Basic usage
postcss src/**/*.css --base src --dir build Glob Pattern & output
cat input.css | postcss -u autoprefixer > output.css Piping input & output
If no input files are passed, it reads from stdin. If neither -o, --dir, or
--replace is passed, it writes to stdout.
If there are multiple input files, the --dir or --replace option must be passed.
Input files may contain globs (e.g. src/**/*.css). If you pass an input
directory, it will process all files in the directory and any subdirectories,
respecting the glob pattern.
Therefore, the correct command is ...
BROWSERSLIST="last 1 versions, not dead, not kaios <= 2.5" postcss css/style.css -u autoprefixer --no-map -o css/style.css
in Bash.
In addition, you don't need to specify not kaios <= 2.5 if you use last 1 version rather than last 2 version.
By the way, I updated the demo repository to confirm the above postcss-cli command.
You can check the output result with npm run css-with-kaios and npm run css-without-kaios command in StackBlitz.
Well that explains a lot. Thanks! :) Is there a way to save that somewhere so I won't have to prepend the command each time I wanted run it?
The last 1 version was there because of the testing I was doing. My list is a little bit different than that. ;)