unistyle

0.4.0 • Public • Published

unistyle

unistyle logo

Build status NPM version XO code style

Write modular and scalable CSS using the next version of ECMAScript.

Why?

Using ES2015 (and some ES2016) features to write CSS in JavaScript makes it really modular, scalable and gives you in practice all the features of a good CSS pre- or postprocessor, without resorting to a new language. See the example section for use together with React for how to avoid the annoying cascading feature of CSS, which is troublesome in large scale CSS.

Unistyle?

The name is an abbreviation of Uniform Stylesheets. It is also somewhat related to Universal JavaScript or what you want to call it, because of the ability to share the same CSS code written in JavaScript between your frontend component's inline styles and the application's complete CSS.

Installation

Install unistyle using npm:

npm install --save unistyle

CLI Usage

$> unistyle --help
 
  Write modular and scalable CSS using the next version of ECMAScript.
 
  Usage:
    unistyle [options] <path to module>
 
  Options:
    -o, --output   Output compiled CSS to specified file instead of to stdout  [string]
    -h, --help     Show help  [boolean]
    -v, --version  Show version number  [boolean]
 
  Examples:
    unistyle -o app.css src/styles.js       Compile src/styles.js to app.css

Examples

Note: All examples below assumes you're already using Babel in your project. Or perhaps unistyle-loader together with a Babel loader and Webpack.

The examples source code can be found in ./examples and their compiled counterparts in ./examples-es5.

Using modules and variables

You can use either CommonJS modules or the new ES2015 modules syntax.

In examples/vars/vars.js:

export const padding = '15px';
export const dark = '#333';

In examples/vars/button.js:

import {padding, dark} from './vars';
 
export default {
  '.btn': {
    padding,
    border: `1px solid ${dark}`
  }
};

In examples/vars/index.js:

import {padding} from './vars';
import button from './button';
 
export default {
  body: {
    padding
  },
  ...button
};

Compiling to CSS with unistyle examples-es5/vars will give the following result:

body.btn {
  padding: 15px;
}
.btn {
  border: 1px solid #333;
}

Extending declarations

Every preprocessor I can think of (e.g. LESS, Sass and Stylus) have the ability to extend one CSS declaration with another, for reusability. They all have their own syntax, however with Unistyle you can use the object rest spread syntax (which you should already be using if Babel is your thing):

In examples/extend/common.js:

export const bigAndPadded = {
  fontSize: 100,
  padding: 50
};

In examples/extend/button.js:

import {bigAndPadded} from './common';
 
export default {
  button: {
    ...bigAndPadded,
    border: '5px solid black'
  }
};

Compiling to CSS with unistyle examples-es5/extend/button will give the following:

button {
  font-size: 100px;
  padding: 50px;
  border: 5px solid black;
}

Media queries

Using media queries (which bubbles up to the root) is easier then ever using the computed property names syntax:

In examples/mediaqueries/breakpoints.js:

export const palm = '@media only screen and (max-width: 700px)';
export const small = '@media only screen and (max-width: 1000px)';

In examples/mediaqueries/index.js:

import {palm} from './breakpoints';
 
export default {
  body: {
    fontSize: 20,
    [palm]: {
      fontSize: 16
    }
  }
};

Compiling with unistyle examples-es5/mediaqueries will give:

body {
  font-size: 20px;
}
@media only screen and (max-width: 700px) {
  body {
    font-size: 16px;
  }
}

Multiple @font-face declarations

You can specify multiple @font-face declarations using arrays.

In examples/font-faces/index.js:

export default {
  '@font-face': [
    font('my-web-font', 'webfont'),
    font('my-other-font', 'otherfont')
  ]
};
 
function font(family, filename) {
  return {
    fontFamily: `"${family}"`,
    src: [
      `url("${filename}.eot")`,
      [
        `url("${filename}.eot?#iefix") format("embedded-opentype")`,
        `url("${filename}.woff2") format("woff2")`,
        `url("${filename}.woff") format("woff")`,
        `url("${filename}.ttf") format("truetype")`,
        `url("${filename}.svg?#svgFontName") format("svg")`
      ].join('')
    ]
  };
}

Compiling with unistyle examples-es5/font-faces will give:

@font-face {
  font-family: "my-web-font";
  src: url("webfont.eot");
  src: url("webfont.eot?#iefix") format("embedded-opentype")url("webfont.woff2") format("woff2")url("webfont.woff") format("woff")url("webfont.ttf") format("truetype")url("webfont.svg?#svgFontName") format("svg");
}
@font-face {
  font-family: "my-other-font";
  src: url("otherfont.eot");
  src: url("otherfont.eot?#iefix") format("embedded-opentype")url("otherfont.woff2") format("woff2")url("otherfont.woff") format("woff")url("otherfont.ttf") format("truetype")url("otherfont.svg?#svgFontName") format("svg");
}

Using Unistyle with React

As inline style

A CSS module written for Unistyle is already compatible with React inline styles, so you could just import/require it like so:

In examples/react/inline/button-style.js:

export default {
  padding: 15,
  border: '2px solid black'
};

In examples/react/inline/button.js:

import React from 'react';
import buttonStyle from './button-style';
 
export default class Button extends React.Component {
  render() {
    return <button style={buttonStyle}>My button</button>;
  }
}

No compilation step is needed here...

Keeping the CSS in a separate file

Note: this is not limited to React but works with almost any frontend framework/library, if you're using Browserify, Webpack or similar.

Using the modules cngen and classnameify respectively makes it possible to keep all CSS for your React components in its own file. As a bonus you get round the biggest problem with large scale CSS, i.e. the fact that it cascades.

In examples/react/separate/button-style.js:

export default {
  'padding': 15,
  'border': '2px solid black',
  ':hover': {
    borderColor: 'green'
  }
};

In examples/react/separate/button.js:

import React from 'react';
import cngen from 'cngen';
import buttonStyle from './button-style';
 
export default class Button extends React.Component {
  render() {
    const buttonClass = cngen(buttonStyle);
    return <button className={buttonClass}>My button</button>;
  }
}

In examples/react/separate/styles.js:

import classnameify from 'classnameify';
import buttonStyle from './button-style';
 
export default classnameify({
  buttonStyle
});

Compiling to CSS with unistyle examples-es5/react/separate/styles.js, gives the following CSS:

._cf2b82a {
  padding: 15px;
  border: 2px solid black;
}
._cf2b82a:hover {
  border-color: green;
}

Publishing Unistyle modules to npm

Because Unistyle CSS modules are JavaScript only, they are easily reused if you publish them to npm after which they can be installed and imported/required. Babel module best practices still applies though, i.e. you should transpile your code before publishing.

When publishing a Unistyle CSS module to npm I recommend adding "unistyle" as a keyword in your package.json for easier discoverability.

Using third party modules

When adding third party modules to your app's Unistyle CSS you should export an array instead of an object, for instance with normalize-unistyle:

import normalize from 'normalize-unistyle';
import myStyles from './my-styles';
 
export default [
  normalize,
  myStyles
];

This is to have colliding selectors in normalize-unistyle and myStyles merged instead of overwritten.

E.g. compiling this (examples/third-party/object.js):

const thirdParty = {
  body: {
    color: 'black'
  }
};
 
const myStyles = {
  body: {
    backgroundColor: 'white'
  }
};
 
export default {
  ...thirdParty,
  ...myStyles
};

Will yield the unexpected result:

body {
  background-color: white;
}

And instead compiling this (examples/third-party/array.js):

const thirdParty = {
  body: {
    color: 'black'
  }
};
 
const myStyles = {
  body: {
    backgroundColor: 'white'
  }
};
 
export default [
  thirdParty,
  myStyles
];

Will yield the expected result:

body {
  color: black;
  background-color: white;
}

What about minification or autoprefixing?

Use existing modules for this.

Using cssmin for minification

npm install --save unistyle cssmin

Then you can add a build script to package.json like so:

{
  "scripts": {
    "build": "unistyle styles/ | cssmin > styles.min.css"
  }
}

And then run: npm run build to create styles.min.css.

Using autoprefixer for prefixing

npm install --save unistyle postcss-cli autoprefixer

Then you can add a build script to package.json like so:

{
  "scripts": {
    "build": "unistyle styles/ | postcss --use autoprefixer -o styles.css"
  }
}

And then run: npm run build to create styles.css.

How does it work?

Unistyle uses Babel and AbsurdJS under the hood. Unistyle does not use Babel or AbsurdJS anymore. It's up to you to use Babel if you want to, or stick with the ES2015 features currently in Node v4 or v5. Instead of AbsurdJS Unistyle uses unistyle-flat to allow nesting of styles and to-css to compile to CSS. This is because AbsurdJS had so many more features than are actually needed which makes Unistyle less magical now.

Unistyle also uses kebab-case and pixelify to be able to write CSS properties in camelCase (to lessen the need for quotes) and to append values with 'px' when appropriate, i.e. {fontSize: 10} => font-size: 10px;. This makes your Unistyle modules compatible with React inline styles.

Note: to-css's feature to set a property multiple times should not be used in your inline styles and only in your compiled stylesheet.

API

compile(obj)

Name Type Description
obj Object|Array The Unistyle Object to compile to CSS

Returns: Promise, which resolves to the compiled CSS string.

License

MIT @ Joakim Carlstein

Package Sidebar

Install

npm i unistyle

Weekly Downloads

0

Version

0.4.0

License

MIT

Last publish

Collaborators

  • joakimbeng