PostCSS plugin to discard overridden @keyframes or @counter-style rules.
npx @tessl/cli install tessl/npm-postcss-discard-overridden@7.0.0PostCSS Discard Overridden is a PostCSS plugin that optimizes CSS by automatically removing overridden @keyframes and @counter-style rules. It intelligently identifies and removes duplicate declarations that are later overridden by identical rules with the same identifier, keeping only the last (and therefore effective) declaration.
The plugin handles complex scenarios including nested @media and @supports rules where @keyframes and @counter-style rules only override global rules in some browsers, ensuring safe AST transformations.
npm install postcss-discard-overriddenconst postcssDiscardOverridden = require('postcss-discard-overridden');For ES modules:
import postcssDiscardOverridden from 'postcss-discard-overridden';const postcss = require('postcss');
const discardOverridden = require('postcss-discard-overridden');
// Process CSS
const result = await postcss([discardOverridden()])
.process(css, { from: 'input.css', to: 'output.css' });
console.log(result.css);Example transformation:
Input CSS:
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 0.8; }
}
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}Output CSS:
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}The main export is a function that creates a PostCSS plugin instance.
/**
* Creates a PostCSS plugin that removes overridden @keyframes and @counter-style rules
* @returns {import('postcss').Plugin} PostCSS plugin instance
*/
function postcssDiscardOverridden(): import('postcss').Plugin;The plugin creator function takes no options and returns a PostCSS plugin with the following properties:
interface PostCSSPlugin {
/** Plugin identifier for PostCSS */
postcssPlugin: 'postcss-discard-overridden';
/** Plugin preparation function */
prepare(): {
/** Function called once at the end of processing */
OnceExit(css: import('postcss').Root): void;
};
}Usage Example:
const postcss = require('postcss');
const discardOverridden = require('postcss-discard-overridden');
// Use with other PostCSS plugins
const processor = postcss([
require('autoprefixer'),
discardOverridden(),
require('cssnano')
]);
const result = await processor.process(css, { from: 'input.css' });The plugin includes PostCSS compatibility markers:
/** PostCSS compatibility flag */
postcssDiscardOverridden.postcss = true;This ensures the plugin is compatible with PostCSS's plugin system and can be used in plugin arrays.
The plugin operates by:
@keyframes, @counter-style)@media, @supports, etc.The plugin processes these at-rule types:
@keyframes (including vendor-prefixed variants like @-webkit-keyframes)@counter-style (including vendor-prefixed variants)The plugin respects CSS cascade rules and scoping:
@media blocks only override in matching conditions@supports blocks only override when features are supportedExample with scoping:
/* Input */
@keyframes fade { /* ... */ }
@media (max-width: 500px) {
@keyframes fade { /* ... */ } /* Only overrides in narrow viewports */
}
@keyframes fade { /* ... */ } /* Overrides global fade */
/* Output - media query version is preserved */
@media (max-width: 500px) {
@keyframes fade { /* ... */ }
}
@keyframes fade { /* ... */ }// webpack.config.js
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
require('postcss-discard-overridden')(),
require('autoprefixer'),
]
}
}
}
]
}
]
}
};// postcss.config.js
module.exports = {
plugins: [
require('postcss-discard-overridden'),
require('autoprefixer'),
require('cssnano')
]
}const fs = require('fs');
const postcss = require('postcss');
const discardOverridden = require('postcss-discard-overridden');
async function processCSS(inputFile, outputFile) {
const css = fs.readFileSync(inputFile, 'utf8');
const result = await postcss([discardOverridden()])
.process(css, {
from: inputFile,
to: outputFile
});
fs.writeFileSync(outputFile, result.css);
if (result.map) {
fs.writeFileSync(outputFile + '.map', result.map.toString());
}
}The plugin operates on the PostCSS AST and does not throw errors under normal circumstances. It gracefully handles:
@keyframes or @counter-style rulesPostCSS-level errors (syntax errors, etc.) are handled by the PostCSS parser before this plugin runs.
TypeScript definitions are available:
declare function postcssDiscardOverridden(): import("postcss").Plugin;
declare namespace postcssDiscardOverridden {
let postcss: true;
}
export = postcssDiscardOverridden;