babel-plugin-oxygen-css
v1.2.5
Published
A plugin for Babel v6 which transforms inline styles defined in JavaScript modules into class names so they become available to, e.g. the `className` prop of React elements.
Downloads
14
Readme
babel-plugin-oxygen-css
A plugin for Babel v6 which transforms inline styles defined in JavaScript modules into class names so they become available to, e.g. the className
prop of React elements.
While transforming, the plugin processes all JavaScript style definitions found and bundles them up into a CSS file, ready to be requested from your web server.
babel-plugin-oxygen-css works seamlessly on both client and server. It has built-in support for media queries, pseudo-classes, and attribute selectors. The plugin's options allow you to configure vendor-prefixing, minification, and class name compression.
Example
In order for the plugin to work, in your components, surround each inline style specification with a module-level cssInJS()
function call. This provides a hook for the plugin to process the first argument given to the call and then replace it with an object literal containing the resulting class names as values.
In
<button className={styles.button} />
const styles = oxygenCss({
button: {
padding: 5,
backgroundColor: "blue"
}
});
Out
JavaScript:
<button className={styles.button} />
var styles = {
button: "example_js_styles_button"
};
CSS:
.example_js_styles_button {
padding: 5px;
background-color: blue;
}
The stylesheet specification format is explained further down.
Note the return value of oxygenCss(...)
must be assigned to a variable. The name of the variable is used to distinguish multiple oxygenCss
calls within a file.
Installation
Install via npm:
$ npm install git+ssh://[email protected]/TriOxygen/babel-plugin-oxygen-css.git --save-dev
Usage
Via .babelrc
(Recommended)
.babelrc
{
"plugins": ["oxygen-css"]
}
Via CLI
$ babel --plugins oxygen-css script.js
Via Node API
require('babel-core').transform('code', {
plugins: ['oxygen-css']
});
Options
The plugin allows configuration of several parameters which control the generated CSS. You can pass options to the plugin by using a two-element array when adding the plugin. For instance, using .babelrc
:
{
"presets": [
"es2015",
"react"
],
"plugins": [
"foo-plugin",
["oxygen-css", { "vendorPrefixes": true, "bundleFile": "public/bundle.css" }]
]
}
Available options:
| Option | Default | Description |
|----------------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| vendorPrefixes
| false
| If true, the generated CSS is run through autoprefixer to add vendor prefixes to the rules. If set to an object, it is passed to autoprefixer as options
argument. |
| minify
| false
| Set to true
to enable minification of the generated CSS. The popular clean-css package is used for this. |
| compressClassNames
| false
| Set to true
to shorten/obfuscate generated CSS class names. A class name like "my_file-my_styles_var-my_name"
will so be converted to, e.g., "_bf"
. |
| mediaMap
| {}
| This allows you to define media query shortcuts which are expanded on building the CSS. Example: using { phone: "media only screen and (max-width: 640px)" }
as value for this option and a stylesheet spec having "@phone"
as a key, that key will be translated to @media only screen and (max-width: 640px)
in the final CSS. |
| context
| null
| If set to an object, each identifier found on the right-hand side of a style rule is substituted with the corresponding property value of this object. If set to a file path, the file is require'd and the exported object is used as stylesheet context. |
| cacheDir
| tmp/cache/
| If you set the compressClassNames
option to true
, the class name cache will be persisted in a file in this directory. |
| bundleFile
| bundle.css
| All generated CSS is bundled into this file. |
| identifier
| oxygenCss
| The name used for detecting inline styles to transform. |
Stylesheet Specification Format
Here's what you can put inside the parentheses of oxygenCss(...)
.
Simple Styles
{
myButton: {
border: 'solid 1px #ccc',
backgroundColor: 'lightgray',
display: 'inline-block'
},
myInput: {
width: '100%',
// ... etc.
}
}
An inline style is not specified as a string. Instead it is specified with an object whose properties form the CSS ruleset for that style. A property's key is the camelCased version of the rule name, and the value is the rule's value, usually a string.
There's also a shorthand notation for specifying pixel values, see this React tip for more details.
Pseudo-Classes and Attribute Selectors
{
myButton: {
border: 'solid 1px #ccc',
backgroundColor: 'lightgray',
display: 'inline-block',
cursor: 'pointer',
':focus': {
borderColor: '#aaa'
},
':hover': {
borderColor: '#ddd',
':active': {
borderColor: '#eee'
}
},
'[disabled]': {
cursor: 'not-allowed',
opacity: .5,
':hover': {
backgroundColor: 'transparent'
}
}
}
}
As you can see, pseudo-classes and attribute selectors can be nested arbitrarily deep. Media Queries Media queries are also supported. All the media queries will be grouped in the output. Media queries cannot be at the root of the style object to make it easier to distinguish between keyframes and media queries.
{
myButton: {
border: 'solid 1px #ccc',
'@media only screen and (max-width: 480px)': {
borderWidth: 0,
':hover': {
borderWidth: 3
}
},
'@media only screen and (max-width: 768px)': {
borderWidth: 2,
}
},
myInput: {
width: '100%',
// ...
'@media only screen and (max-width: 480px)': {
fontSize: 14
}
},
},
}
Given you set { phone: 'media only screen and (max-width: 480px)', tablet: 'media only screen and (max-width: 768px)' }
as mediaMap
option for the transformation, the above spec can be simplified to:
{
myButton: {
border: 'solid 1px #ccc',
'@phone': {
borderWidth: 0,
':active': {
borderColor: 'blue'
}
},
'@tablet': {
// ...
}
}
}
Styling html elements
You can style html elements by using all uppercased selectors:
const appStyles = oxygenCss({
HTML: {
width: '100%',
height: '100%',
fontFamily: `'Hind Siliguri', sans-serif`,
fontSize: 14,
fontWeight: 400,
},
BODY: {
width: '100%',
height: '100%',
fontFamily: `'Hind Siliguri', sans-serif`,
fontSize: 14,
fontWeight: 400,
P: {
fontSize: 16
}
},
});
CSS:
html {
width: 100%;
height: 100%;
font-family: 'Hind Siliguri', sans-serif;
font-size: 14px;
font-weight: 400;
}
body {
width: 100%;
height: 100%;
font-family: 'Hind Siliguri', sans-serif;
font-size: 14px;
font-weight: 400;
}
body p {
font-size: 16px;
}
Child selectors etc
Most useful css selectors are supported:
const example = oxygenCss({
root: {
fontSize: 12,
'&dense': {
fontSize: 10
},
'>child': {
fontSize: 9
},
' dense2': {
fontSize: 10
},
'+next': {
fontSize: 8
},
'~sibling': {
fontSize: 11
}
}
});
CSS:
.src-containers-App-js-example-root {
font-size: 12px;
}
.src-containers-App-js-example-root.src-containers-App-js-example-dense {
font-size: 10px;
}
.src-containers-App-js-example-root > .src-containers-App-js-example-child {
font-size: 9px;
}
.src-containers-App-js-example-root .src-containers-App-js-example-dense2 {
font-size: 10px;
}
.src-containers-App-js-example-root + .src-containers-App-js-example-next {
font-size: 8px;
}
.src-containers-App-js-example-root ~ .src-containers-App-js-example-sibling {
font-size: 11px;
}
Animations Keyframes are also supported. Only limitation is that keyframes have to be in the root of the style object:
JS:
const keyframe = oxygenCss({
'@sizeAnimation': {
'0%': {
width: 0
},
'100%': {
width: 100
}
}
});
CSS:
@keyframes sizeAnimation {
0% {
width: 0px;
}
100% {
width: 100px;
}
}
Expressions in Style Rules
You can do simple arithmetic and string concats on the right-hand side of style rules. Each identifier found is substituted with the corresponding property value of the context
object provided as option.
Example for a given context { MyColors: { green: '#00FF00' }, myUrl: 'path/to/image.png' }
:
{
myButton: {
color: MyColors.green,
borderWidth: 42 + 'px',
backgroundImage: 'url(' + myUrl + ')'
}
}
Caveats
- Just using
var styles = oxygenCss(...)
in your React modules and skipping the transformation step won't work. It's the transformation that is responsible for a) generating the real CSS, and b) turning youroxygenCss(...)
calls into object literals holding the CSS class names so you can do<foo className={styles.bar} />
without breaking React. But you are transpiling your JavaScript anyway to get these cool new ES2015 features, aren't you? - Apart from simple arithmetic and string concats, a stylesheet specification cannot contain advanced dynamic stuff, because although the transformer parses the source input, it is not compiled. If you really need to add truly dynamic styles, that's what the
style
attribute/prop was made for.style
also has the positive side-effect of taking precedence over class names.
License
Released under The MIT License.
Tests
Inc soon... (tm)
Credits
This plugin is inspired by Martin Andert's babel-plugin-css-in-js