What is @csstools/postcss-is-pseudo-class?
@csstools/postcss-is-pseudo-class is a PostCSS plugin that allows you to use the `:is()` pseudo-class in your CSS. This pseudo-class is useful for simplifying complex selectors by allowing you to group them together.
What are @csstools/postcss-is-pseudo-class's main functionalities?
Simplify Complex Selectors
The `:is()` pseudo-class allows you to simplify complex selectors by grouping them together. In this example, `.foo .baz` and `.bar .baz` are combined into a single rule using `:is(.foo, .bar) .baz`.
/* Input CSS */
:is(.foo, .bar) .baz {
color: red;
}
/* Output CSS */
.foo .baz,
.bar .baz {
color: red;
}
Combine Multiple Selectors
You can use the `:is()` pseudo-class to combine multiple selectors into one rule. This example shows how `h1`, `h2`, and `h3` can be grouped together to apply the same styles.
/* Input CSS */
:is(h1, h2, h3) {
margin: 0;
}
/* Output CSS */
h1,
h2,
h3 {
margin: 0;
}
Nested Selectors
The `:is()` pseudo-class can be nested to create more complex groupings of selectors. This example demonstrates how `.foo .baz`, `.foo .qux`, `.bar .baz`, and `.bar .qux` can be combined into a single rule.
/* Input CSS */
:is(.foo, .bar) :is(.baz, .qux) {
color: blue;
}
/* Output CSS */
.foo .baz,
.foo .qux,
.bar .baz,
.bar .qux {
color: blue;
}
Other packages similar to @csstools/postcss-is-pseudo-class
postcss-nesting
postcss-nesting allows you to nest your CSS selectors in a way that follows the CSS Nesting Module Level 3 specification. It is similar to @csstools/postcss-is-pseudo-class in that it helps manage complex selectors, but it focuses on nesting rather than grouping.
postcss-preset-env
postcss-preset-env lets you use modern CSS features, including the `:is()` pseudo-class, by converting them into a form that is compatible with older browsers. It provides a broader range of features compared to @csstools/postcss-is-pseudo-class, which focuses specifically on the `:is()` pseudo-class.
postcss-custom-selectors
postcss-custom-selectors allows you to define custom selectors that can be reused throughout your stylesheet. While it doesn't specifically handle the `:is()` pseudo-class, it offers a similar capability by letting you create and use custom selector names.
PostCSS Is Pseudo
PostCSS Is Pseudo Class lets you use the :is
pseudo class function, following the
CSS Selector specification.
:is(input, button):is(:hover, :focus) {
order: 1;
}
Becomes :
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:
npm install @csstools/postcss-is-pseudo-class --save-dev
Use PostCSS Is Pseudo Class as a PostCSS plugin:
import postcss from 'postcss';
import postcssIsPseudoClass from '@csstools/postcss-is-pseudo-class';
postcss([
postcssIsPseudoClass()
]).process(YOUR_CSS );
PostCSS Is Pseudo Class runs in all Node environments, with special instructions for:
Options
preserve
The preserve
option determines whether the original notation
is preserved. By default, it is not preserved.
postcss([
postcssIsPseudoClass({ preserve: true })
]).process(YOUR_CSS );
:is(input, button):is(:hover, :focus) {
order: 1;
}
Becomes :
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.
postcss([
postcssIsPseudoClass({ specificityMatchingName: 'something-random' })
]).process(YOUR_CSS );
:is(.button, button):hover {
order: 7;
}
Becomes :
.button:hover {
order: 7;
}
button:not(.something-random):hover {
order: 7;
}
onComplexSelector
Warn on complex selectors in :is
pseudo classes.
postcss([
postcssIsPseudoClass({ onComplexSelector: 'warning' })
]).process(YOUR_CSS );
⚠️ 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
:is(:hover, :focus):is(.button, button) {
order: 7;
}
After :
/* 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 :
:is(.alpha > .beta) ~ :is(:focus > .beta) {
order: 2;
}
After :
.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
/* 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.