PostCSS plugin to transform :not() W3C CSS level 4 pseudo class to :not() CSS level 3 selectors
npx @tessl/cli install tessl/npm-postcss-selector-not@8.0.0PostCSS Selector Not is a PostCSS plugin that transforms CSS level 4 :not() pseudo-class selectors with multiple arguments into CSS level 3 compatible selectors. It converts modern :not(.a, .b) syntax to :not(.a):not(.b) to ensure compatibility with older browsers while allowing developers to use the more concise modern syntax.
npm install postcss-selector-not --save-devimport postcssSelectorNot from "postcss-selector-not";
// Named export also available
import type { pluginOptions } from "postcss-selector-not";For CommonJS:
const postcssSelectorNot = require("postcss-selector-not");import postcss from "postcss";
import postcssSelectorNot from "postcss-selector-not";
// Basic PostCSS usage
const result = await postcss([
postcssSelectorNot()
]).process(css, { from: undefined });
console.log(result.css);Example transformation:
/* Input CSS */
p:not(:first-child, .special) {
color: red;
}
/* Output CSS */
p:not(:first-child):not(.special) {
color: red;
}PostCSS Selector Not follows the standard PostCSS plugin architecture:
:not() selectorspostcss-selector-parser for reliable CSS selector manipulation:not() selectors into separate single-argument :not() functionsCreates a PostCSS plugin instance for transforming :not() selectors.
/**
* Creates a PostCSS plugin for transforming CSS level 4 :not() selectors
* @param options - Plugin configuration options (currently none supported)
* @returns PostCSS plugin instance
*/
declare function postcssSelectorNot(options?: pluginOptions): Plugin;
/** Plugin options type - currently no options are supported */
type pluginOptions = Record<string, never>;The plugin creator function returns a PostCSS plugin object with:
postcssPlugin: String identifier "postcss-selector-not"Rule: Function that processes CSS rules containing :not() selectors/** PostCSS plugin flag indicating this is a valid PostCSS plugin */
postcssSelectorNot.postcss: true;The plugin specifically targets :not() pseudo-class selectors that contain multiple simple selectors separated by commas. It performs the following transformations:
:not(.a, .b) → Output: :not(.a):not(.b):not(tag1, tag2, tag3) → Output: :not(tag1):not(tag2):not(tag3):not(:hover, :focus) → Output: :not(:hover):not(:focus)Supported selectors:
.classtag#id:hover, :focus, etc.[attr="value"]Limitations:
:not():not(.a > .b, .c ~ .d) cannot be downgraded and will be left unchanged:not( (case-insensitive)The plugin includes comprehensive error handling:
postcss-selector-parser, the plugin will issue a PostCSS warning with the selector string and error messageWarning Format:
Failed to parse selector : "invalid-selector" with message: "error details"/** Plugin options type - currently no configuration options are supported */
type pluginOptions = Record<string, never>;
/** PostCSS Plugin interface */
interface PostCSSPlugin {
postcssPlugin: string;
Rule(rule: PostCSSRule, helpers: { result: PostCSSResult }): void;
}
/** PostCSS Rule interface (from postcss library) */
interface PostCSSRule {
selector: string;
warn(result: PostCSSResult, message: string): void;
clone(overrides: { selector: string }): PostCSSRule;
replaceWith(newRule: PostCSSRule): void;
}
/** PostCSS Result interface (from postcss library) */
interface PostCSSResult {
// PostCSS processing result object
}const postcssSelectorNot = require("postcss-selector-not");
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
postcssSelectorNot()
]
}
}
}
]
}
]
}
};// postcss.config.js
module.exports = {
plugins: [
require("postcss-selector-not")()
]
};/* Multiple pseudo-classes */
.button:not(:hover, :focus, :active) {
opacity: 0.8;
}
/* Becomes: */
.button:not(:hover):not(:focus):not(:active) {
opacity: 0.8;
}
/* Mixed selector types */
article:not(.featured, #special, [data-premium]) {
margin: 1rem;
}
/* Becomes: */
article:not(.featured):not(#special):not([data-premium]) {
margin: 1rem;
}
/* Nested :not() selectors */
.container :not(h1, h2) :not(.icon, .badge) {
font-size: 0.9rem;
}
/* Becomes: */
.container :not(h1):not(h2) :not(.icon):not(.badge) {
font-size: 0.9rem;
}