unistyle
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;
}
}
@font-face
declarations
Multiple 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;
}
npm
Publishing Unistyle modules toBecause 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.
cssmin
for minification
Using 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
.
autoprefixer
for prefixing
Using 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` |
Returns: Promise
, which resolves to the compiled CSS string.
License
MIT @ Joakim Carlstein