|
|
|
|
# PostCSS Is Pseudo [<img src="https://postcss.github.io/postcss/logo.svg" alt="PostCSS" width="90" height="90" align="right">][postcss]
|
|
|
|
|
|
|
|
|
|
[![NPM Version][npm-img]][npm-url]
|
|
|
|
|
[![CSS Standard Status][css-img]][css-url]
|
|
|
|
|
[<img alt="Build Status" src="https://github.com/csstools/postcss-plugins/workflows/test/badge.svg" height="20">][cli-url]
|
|
|
|
|
[<img alt="Discord" src="https://shields.io/badge/Discord-5865F2?logo=discord&logoColor=white">][discord]
|
|
|
|
|
|
|
|
|
|
[PostCSS Is Pseudo Class] lets you use the `:is` pseudo class function, following the
|
|
|
|
|
[CSS Selector] specification.
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
:is(input, button):is(:hover, :focus) {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Becomes :
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
input:hover {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
input:focus {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
button:hover {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
button:focus {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Usage
|
|
|
|
|
|
|
|
|
|
Add [PostCSS Is Pseudo Class] to your project:
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
npm install @csstools/postcss-is-pseudo-class --save-dev
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Use [PostCSS Is Pseudo Class] as a [PostCSS] plugin:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
import postcss from 'postcss';
|
|
|
|
|
import postcssIsPseudoClass from '@csstools/postcss-is-pseudo-class';
|
|
|
|
|
|
|
|
|
|
postcss([
|
|
|
|
|
postcssIsPseudoClass(/* pluginOptions */)
|
|
|
|
|
]).process(YOUR_CSS /*, processOptions */);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
[PostCSS Is Pseudo Class] runs in all Node environments, with special instructions for:
|
|
|
|
|
|
|
|
|
|
| [Node](INSTALL.md#node) | [Webpack](INSTALL.md#webpack) | [Create React App](INSTALL.md#create-react-app) | [Gulp](INSTALL.md#gulp) | [Grunt](INSTALL.md#grunt) |
|
|
|
|
|
| --- | --- | --- | --- | --- |
|
|
|
|
|
|
|
|
|
|
## Options
|
|
|
|
|
|
|
|
|
|
### preserve
|
|
|
|
|
|
|
|
|
|
The `preserve` option determines whether the original notation
|
|
|
|
|
is preserved. By default, it is not preserved.
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
postcss([
|
|
|
|
|
postcssIsPseudoClass({ preserve: true })
|
|
|
|
|
]).process(YOUR_CSS /*, processOptions */);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
:is(input, button):is(:hover, :focus) {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Becomes :
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
input:hover {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
input:focus {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
button:hover {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
button:focus {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
:is(input, button):is(:hover, :focus) {
|
|
|
|
|
order: 1;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### specificityMatchingName
|
|
|
|
|
|
|
|
|
|
The `specificityMatchingName` option allows you to change to selector used to adjust specificity.
|
|
|
|
|
The default value is `does-not-exist`.
|
|
|
|
|
If this is an actual class, id or tag name in your code, you will need to set a different option here.
|
|
|
|
|
|
|
|
|
|
See how `:not` is used to modify [specificity](#specificity).
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
postcss([
|
|
|
|
|
postcssIsPseudoClass({ specificityMatchingName: 'something-random' })
|
|
|
|
|
]).process(YOUR_CSS /*, processOptions */);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
:is(.button, button):hover {
|
|
|
|
|
order: 7;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Becomes :
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
.button:hover {
|
|
|
|
|
order: 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
button:not(.something-random):hover {
|
|
|
|
|
order: 7;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### onComplexSelector
|
|
|
|
|
|
|
|
|
|
Warn on complex selectors in `:is` pseudo class functions.
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
postcss([
|
|
|
|
|
postcssIsPseudoClass({ onComplexSelector: 'warning' })
|
|
|
|
|
]).process(YOUR_CSS /*, processOptions */);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### onPseudoElement
|
|
|
|
|
|
|
|
|
|
Warn when pseudo elements are used in `:is` pseudo class functions.
|
|
|
|
|
|
|
|
|
|
⚠️ Pseudo elements are always invalid and will be transformed to `::-csstools-invalid-<pseudo-name>`.
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
postcss([
|
|
|
|
|
postcssIsPseudoClass({ onPseudoElement: 'warning' })
|
|
|
|
|
]).process(YOUR_CSS /*, processOptions */);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
```css
|
|
|
|
|
:is(::after):hover {
|
|
|
|
|
order: 1.0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* becomes */
|
|
|
|
|
|
|
|
|
|
::-csstools-invalid-after:hover {
|
|
|
|
|
order: 1.0;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## ⚠️ Known shortcomings
|
|
|
|
|
|
|
|
|
|
### Specificity
|
|
|
|
|
|
|
|
|
|
`:is` takes the specificity of the most specific list item.
|
|
|
|
|
We can increase specificity with `:not` selectors, but we can't decrease it.
|
|
|
|
|
|
|
|
|
|
Converted selectors are ensured to have the same specificity as `:is` for the most important bit.
|
|
|
|
|
Less important bits can have higher specificity that `:is`.
|
|
|
|
|
|
|
|
|
|
Before :
|
|
|
|
|
|
|
|
|
|
[specificity: 0, 2, 0](https://polypane.app/css-specificity-calculator/#selector=%3Ais(%3Ahover%2C%20%3Afocus)%3Ais(.button%2C%20button))
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
:is(:hover, :focus):is(.button, button) {
|
|
|
|
|
order: 7;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
After :
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
/* specificity: [0, 2, 0] */
|
|
|
|
|
.button:hover {
|
|
|
|
|
order: 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* specificity: [0, 2, 1] */
|
|
|
|
|
/* last bit is higher than it should be, but middle bit matches */
|
|
|
|
|
button:not(.does-not-exist):hover {
|
|
|
|
|
order: 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* specificity: [0, 2, 0] */
|
|
|
|
|
.button:focus {
|
|
|
|
|
order: 7;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* specificity: [0, 2, 1] */
|
|
|
|
|
/* last bit is higher than it should be, but middle bit matches */
|
|
|
|
|
button:not(.does-not-exist):focus {
|
|
|
|
|
order: 7;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### Complex selectors
|
|
|
|
|
|
|
|
|
|
Before :
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
:is(.alpha > .beta) ~ :is(:focus > .beta) {
|
|
|
|
|
order: 2;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
After :
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
.alpha > .beta ~ :focus > .beta {
|
|
|
|
|
order: 2;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
_this is a different selector than expected as `.beta ~ :focus` matches `.beta` followed by `:focus`._<br>
|
|
|
|
|
_avoid these cases._<br>
|
|
|
|
|
_writing the selector without `:is()` is advised here_
|
|
|
|
|
|
|
|
|
|
```pcss
|
|
|
|
|
/* without is */
|
|
|
|
|
.alpha:focus > .beta ~ .beta {
|
|
|
|
|
order: 2;
|
|
|
|
|
}
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If you have a specific pattern you can open an issue to discuss it.
|
|
|
|
|
We can detect and transform some cases but can't generalize them into a single solution that tackles all of them.
|
|
|
|
|
|
|
|
|
|
[cli-url]: https://github.com/csstools/postcss-plugins/actions/workflows/test.yml?query=workflow/test
|
|
|
|
|
[css-img]: https://cssdb.org/images/badges/is-pseudo-class.svg
|
|
|
|
|
[css-url]: https://cssdb.org/#is-pseudo-class
|
|
|
|
|
[discord]: https://discord.gg/bUadyRwkJS
|
|
|
|
|
[npm-img]: https://img.shields.io/npm/v/@csstools/postcss-is-pseudo-class.svg
|
|
|
|
|
[npm-url]: https://www.npmjs.com/package/@csstools/postcss-is-pseudo-class
|
|
|
|
|
|
|
|
|
|
[CSS Selector]: https://www.w3.org/TR/selectors-4/#matches
|
|
|
|
|
[PostCSS]: https://github.com/postcss/postcss
|
|
|
|
|
[PostCSS Is Pseudo Class]: https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-is-pseudo-class
|