Building a CSS framework using npm
14 Feb 2017
This last weekend I wanted to create a simple CSS framework that I can reuse for my own little projects. The end result is Simple Style (which is still a WIP as requirements come up). Surprisingly, using npm as a build tool made it extremely easy to set up a dev environment. I'll review everything about the setup in this post for my own documentation, but hopefully can also serve as a clean, straightforward example.
I used yarn
for this experiment instead of npm
solely for the speed improvements. Fortunately, yarn
's commands all have their npm
equivalents so don't get hung up on the differences.
This basic setup offers:
- Autoprefixing to support the 2 latest browsers
- Future CSS syntax usable today
- minification
- optimizations
- a quick and easy development setup to reflect changes on-the-fly
Dev dependencies
Here are our initial development dependencies:
- normalize.css: so that all elements render consistently
- PostCSS's CLI: to leverage its ecosystem of feature plugins
- CSSnext: a PostCSS plugin that helps you to use the latest CSS syntax today. This way the code will be a little more future-proof. Additionally, CSSnext also includes Autoprefixer as a dependency.
postcss-import
: used to pull in a css file that wasn't being preprocessedcssnano
: minify and optimize output
Install using yarn (or the npm equivalent)
yarn add -D normalize.css postcss-cli postcss-cssnext postcss-import cssnano
PostCSS config
Create a json file that contains the config for PostCSS. The docs named it options.json but I choose postcss.json for better clarity. The comments below explain some keys.
{
// Enable plugins in order
"use": ["postcss-import", "postcss-cssnext", "cssnano"],
"input": "main.css",
"output": "dist/main.css",
// use locally installed plugins (eg. in node_modules)
"local-plugins": true,
// Enable sourcemaps
"map": true,
// CSSnext and cssnano both use autoprefixer, but for different features/purposes
// see https://github.com/MoOx/postcss-cssnext/issues/323
"postcss-cssnext": {
"warnForDuplicates": false
}
}
CSS time
Go ahead and create main.css at the project root. The above config will output to dist/. Here's a quick sample to put into the file for now.
@import ('normalize.css');
:root {
--color-primary: blue;
--color-secondary: red;
}
Building
Use the postcss-cli with the config file passed in as an arg and save it as an npm script.
"scripts": { "build": "node_modules/postcss-cli/bin/postcss -c postcss.json" }
Now you can simply run yarn run build
and your css will be processed and output to dist. But we can still do better.
Watch for changes
Enabling watch will auto-transform the css file on changes. Add another npm script, which is exactly the same as build
but with the watch flag.
"build:dev": "node_modules/postcss-cli/bin/postcss -c postcss.json -w"
Livereload
You wouldn't develop a CSS framework without seeing it rendered on a page so let's create a test page for development. Create index.html at the project root and populate it with some HTML, and add a link to your main.css file.
<!DOCTYPE html>
<html lang="en-US">
<head>
<link rel="stylesheet" href="/dist/main.css" />
</head>
<body>
<main>Hello world</main>
</body>
</html>
lr-http-server
will reload on changes, as well as a functioning http server.
yarn add -D lr-http-server
And then register a new npm script for it
"reload": "lr-http-server -p 3000"
Composing scripts
We want the two above scripts to run in parallel so that if either the css or the html file changes the livereload server will do its job. npm-run-all
with the -p
(parallel) flag does this exactly.
yarn add -D npm-run-all
Join the two tasks together into a single npm script
"start": "npm-run-all -p build:dev reload"
And then run easily with
yarn start
Done!
You're ready to keep going developing your styles inside main.css. Feel free to improve upon this as needed!