How to generate CSS spacing utility with PostCSS

What is PostCSS

PostCSS is a tool for transforming CSS with JavaScript. Unlike SCSS and Less, you can create and extend CSS with your own language. It unlocks limitless possibilities with what you can do with CSS.

What we're trying to achieve

We're going to recreate the Bootstrap Spacing Utility. In a nutshell, it will generate classes for margins and padding with different directions and sizes. However, I'll be using different sizes. Here's a sample of the code that will be generated:


.mt-1 {
	margin-top: 0.25rem;
}
.px-2 {
	padding-left: 0.5rem;
	padding-right: 0.5rem;
}

Because I prefer not to introduce new syntax, the plugin would look for a comment with a special string and append the new classes.

The code

Let's create a file for our plugin. I'll just call mine plugin.js.


const postcss = require('postcss')

module.exports = postcss.plugin('postcss-custom', function () {
	return function (root) {
		root.walkComments(function (comment) {
			if (comment.text && comment.text.indexOf('@spacing') >= 0) {
				spacing(comment)
			}
		})
	}
})

function spacing(comment) {
	const unit = 0.25
	const directions = {
		// placed inorder so that they can overwrite correctly
		t: ['top'],
		r: ['right'],
		b: ['bottom'],
		l: ['left'],
		x: ['left', 'right'],
		y: ['top', 'bottom'],
		'': [],
	}
	function generate(type, alias, direction, props, multiplier) {
		const postfix = multiplier < 0 ? 'n' + Math.abs(multiplier) : multiplier
		const rule = postcss.rule({ selector: `.${alias}${direction}-${postfix}` })
		if (direction === '') {
			rule.append(postcss.decl({
				prop: type,
				value: (multiplier * unit) + 'rem'
			}))
		} else {
			props.map((cur) => {
				rule.append(postcss.decl({
					prop: `${type}-${cur}`,
					value: (multiplier * unit) + 'rem'
				}))
			})
		}
		comment.after(rule)
	}

	for (let dir in directions) {
		for (let i = -2; i <= 8; i++) {
			generate('margin', 'm', dir, directions[dir], i)
			if (i >= 0) {
				generate('padding', 'p', dir, directions[dir], i)
			}
		}
	}
}

To add this plugin to PostCSS, simply add a new entry with the path to the JS file e.g. styles/plugins/my-plugin.js

In the plugin file we need to export a default object for our PostCSS. When you define the plugin with postcss.plugin(), you need to give it a name. In this case it's postcss-custom.


module.exports = postcss.plugin('postcss-custom', function () {})

The above function will search the document for all comments with the special text @spacing. Then it calls spacing() to generate the classes.

The important code to note in the comment() function is in the generate() function. postcss.rule() will create a rule with the selector given. Then we add new property to that selector with .append(). Finally we will append that selector after the comment with .after().

As you can see, creating a new PostCSS is quite easy.

Keywords: postcss, js, css, tutorial