# PostCSS Is Pseudo [PostCSS][postcss] [![NPM Version][npm-img]][npm-url] [![CSS Standard Status][css-img]][css-url] [Build Status][cli-url] [Discord][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-`. ```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`._
_avoid these cases._
_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