Your own plugin in five minutes
A PostCSS plugin is an ordinary JavaScript function that returns an object with a name and handlers — functions PostCSS calls for tree nodes: for every rule, every property and so on. Writing your own plugin is easier than it looks, and it is the best way to understand how the others work.
The task
The blog styles must not contain !important: it breaks the normal order in which rules apply. Let us write a plugin that removes !important and warns about it in the terminal.
The plugin code
Create a plugins/ folder in the project root and the file plugins/no-important.js:
// plugins/no-important.js
const noImportant = () => {
return {
postcssPlugin: 'no-important',
Declaration(decl, { result }) {
if (decl.important) {
decl.warn(result, `Removed !important from ${decl.prop}`);
decl.important = false;
}
},
};
};
noImportant.postcss = true;
export default noImportant;Line by line:
| Line | Meaning |
|---|---|
postcssPlugin | The plugin name, shown in messages |
Declaration | Called for every property: value declaration |
decl.important | true if the declaration has !important |
decl.warn | Adds a warning with a line number |
noImportant.postcss = true | The mark by which PostCSS recognizes a plugin |
Adding it
Import the plugin by relative path, like any module:
// postcss.config.js
import postcssPresetEnv from 'postcss-preset-env';
import noImportant from './plugins/no-important.js';
export default {
plugins: [noImportant(), postcssPresetEnv()],
};Try it on a file with !important:
/* styles/main.css */
.post-title {
color: #1e293b !important;
margin: 0;
}After npx postcss styles/main.css -o css/main.css the terminal shows a warning with its position — line 2, column 3:
2:3 ⚠ Removed !important from color [no-important]And in css/main.css:
.post-title {
color: #1e293b;
margin: 0;
}Other handlers
| Handler | When it is called |
|---|---|
Once(root) | Once for the whole file |
Rule(rule) | For every rule with a selector, rule.selector |
AtRule(atRule) | For every at-rule like @media, atRule.name |
Declaration(decl) | For every declaration, decl.prop and decl.value |
You can give Declaration an object keyed by property names — then the function runs only for them, for example Declaration: { color: (decl) => ... }.
Common mistake
Removing the noImportant.postcss = true line and adding the plugin without a call — noImportant instead of noImportant(). Without the mark, PostCSS treats the function as an old-style plugin, calls it with the tree and throws away the returned object with handlers. There is no error, but !important stays in place. Keep the postcss mark in the plugin file and call the plugin with parentheses in the config, like the other plugins.