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 preprocessed
  • cssnano: 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!