Merge longhand properties into shorthand with PostCSS.
npx @tessl/cli install tessl/npm-postcss-merge-longhand@7.0.0postcss-merge-longhand is a PostCSS plugin that optimizes CSS by merging longhand properties into their shorthand equivalents. It automatically combines related longhand properties (like margin-top, margin-right, margin-bottom, margin-left) into concise shorthand declarations (like margin: 10px 20px) when possible, reducing file size and improving performance.
npm install postcss-merge-longhand postcssconst plugin = require("postcss-merge-longhand");For ES modules:
import plugin from "postcss-merge-longhand";const postcss = require("postcss");
const mergeLonghand = require("postcss-merge-longhand");
// Create PostCSS processor with the plugin
const processor = postcss([mergeLonghand()]);
// Process CSS
const result = processor.process(`
h1 {
margin-top: 10px;
margin-right: 20px;
margin-bottom: 10px;
margin-left: 20px;
}
`);
// Output: h1 { margin: 10px 20px; }
console.log(result.css);Creates a PostCSS plugin instance that merges longhand properties into shorthand equivalents.
/**
* Creates a PostCSS plugin instance for merging longhand properties
* @returns {Plugin} PostCSS plugin object
*/
function pluginCreator(): Plugin;
/** Plugin creator has postcss property set to true */
pluginCreator.postcss: true;The PostCSS plugin object returned by the plugin creator.
interface Plugin {
/** Plugin name identifier */
postcssPlugin: "postcss-merge-longhand";
/** Processing function that runs once after CSS parsing */
OnceExit(css: PostCSSRoot): void;
}
interface PostCSSRoot {
/** Walk through all rules in the CSS AST */
walkRules(callback: (rule: PostCSSRule) => void): void;
}
interface PostCSSRule {
/** Walk through all declarations in this rule */
walkDecls(callback: (decl: PostCSSDeclaration) => void): void;
/** Walk through declarations matching a property pattern */
walkDecls(prop: RegExp | string, callback: (decl: PostCSSDeclaration) => void): void;
}
interface PostCSSDeclaration {
/** CSS property name */
prop: string;
/** CSS property value */
value: string;
/** Whether the declaration is marked as !important */
important: boolean;
/** Parent rule or at-rule */
parent: PostCSSRule | null;
/** Remove this declaration from its parent */
remove(): void;
}The plugin processes these CSS property groups:
margin - Margin shorthandmargin-top, margin-right, margin-bottom, margin-left - Directional marginspadding - Padding shorthandpadding-top, padding-right, padding-bottom, padding-left - Directional paddingborder - General border shorthand (width, style, color)border-top, border-right, border-bottom, border-left - Directional bordersborder-width, border-style, border-color - Border aspect shorthandsborder-top-width, border-right-width, border-bottom-width, border-left-width - Directional border widthsborder-top-style, border-right-style, border-bottom-style, border-left-style - Directional border stylesborder-top-color, border-right-color, border-bottom-color, border-left-color - Directional border colorsborder-spacing - Table border spacing (also minified when appropriate)columns - Columns shorthandcolumn-width, column-count - Column longhand propertiesInput CSS:
.box {
margin-top: 10px;
margin-right: 15px;
margin-bottom: 10px;
margin-left: 15px;
}Output CSS:
.box {
margin: 10px 15px;
}Input CSS:
.card {
border-top-width: 2px;
border-right-width: 2px;
border-bottom-width: 2px;
border-left-width: 2px;
border-top-style: solid;
border-right-style: solid;
border-bottom-style: solid;
border-left-style: solid;
border-top-color: red;
border-right-color: red;
border-bottom-color: red;
border-left-color: red;
}Output CSS:
.card {
border: 2px solid red;
}Input CSS:
.layout {
column-width: 200px;
column-count: 3;
}Output CSS:
.layout {
columns: 200px 3;
}const postcss = require("postcss");
const mergeLonghand = require("postcss-merge-longhand");
const autoprefixer = require("autoprefixer");
// Use with other PostCSS plugins
const processor = postcss([
autoprefixer(),
mergeLonghand(),
// Add other optimization plugins
]);
processor.process(cssString, { from: undefined })
.then(result => {
console.log(result.css);
});// webpack.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
"style-loader",
"css-loader",
{
loader: "postcss-loader",
options: {
postcssOptions: {
plugins: [
require("postcss-merge-longhand")(),
]
}
}
}
]
}
]
}
};The plugin performs several types of optimizations:
This plugin requires PostCSS as a peer dependency:
{
"peerDependencies": {
"postcss": "^8.4.32"
}
}Internal dependencies:
postcss-value-parser - For parsing and manipulating CSS valuesstylehacks - For detecting and preserving CSS hacksThe plugin gracefully handles various edge cases:
!important declarations are not merged!important declarations and doesn't merge properties with different importance levels