CSSOBJ
Runtime CSS manager, Turn CSS into dynamic JS module, Stylesheet CRUD (Create, Read, Update, Delete) in CSSOM, Solve common problems of CSS-in-JS.
Usage - Wiki - API - Demo - React - Babel
Install:
npm
npm install cssobj
npm install babel-plugin-transform-cssobj
npm install -g cssobj-converter
browser
<script src="https://unpkg.com/cssobj"></script>
Usage
First see this SIMPLE DEMO
In the example, cssobj
will create <style>
tag in HEAD, render CSS rules inside
import cssobj from 'cssobj'
const obj = {
div: {
backgroundColor: 'yellow',
color: 'red',
height: () => window.innerHeight/2 + 'px'
}
}
const result = cssobj(obj)
window.onresize = () => result.update()
The rendered CSS (height
is dynamically set to 50% of window height)
div { background-color: yellow; color: red; height: 600px; }
If you read the code, you've learned the API already:
Only One top level method: cssobj( obj, [config] )
, all other things using result.someMethods
, that's all, really.
Stylesheet CRUD
The power of cssobj is CSS CRUD (Create, Read, Update, Delete), dynamically change above CSS, see below:
1. Update property values
You want to change color to 'blue'
obj.div.color = 'blue'
result.update()
obj.div.color = function(v){
return randomColor()
}
result.update()
2. Delete/Remove properties
You want to remove backgroundColor
It's just work as you expected:
delete obj.div.backgroundColor
result.update()
3. Create/Add new properties
You want to add 'float'
and 'clear'
It's just work as you expected:
obj.div.float = 'left'
obj.div.clear = 'both'
result.update()
4. Create/Add new rules
You want to add ':after'
rule, and div span
rule
obj.div['&:after'] = { fontSize:'10px', content:'"---"' }
obj.div.span = { fontSize: '18px' }
result.update()
5. Update/Replace rules
You want to replace the whole rule
obj.div.span = { color: 'green', fontSize: '20px' }
result.update()
All the above can use function
instead
obj.div.span = function() {
return { color: randomColor(), fontSize: currentSize + 'px' }
}
result.update()
6. Delete/Remove rules
You want to remove div span
rule
delete obj.div.span
result.update()
7. Read a rule
Although cssobj
can manage everything, you read the rule in stylesheet manually
const rule = result.root.children.div.omRule[0]
rule.color = 'red'
8. Delete/Destroy cssobj
Currently, cssobj
don't provide result.destroy()
or similar method, you should manually destroy things:
result.cssdom.parentNode.removeChild(el)
result = null
Think of this: one cssobj
instance === A <style>
tag with rules + A manager from JS
At-Rules
All @-rules
work as expected, and @media
can be nested at any level:
cssobj({
'.nav':{
width: '1024px',
'@media print': {
display: 'none'
}
}
})
Above will hide .nav
when print.
You can emit any @media
rule by cssom.media
option:
const result = cssobj({
'.nav':{
width: '1024px',
'@media print': {
color: 'red'
}
}
}, { cssom: { media:'' } })
result.config.cssom.media = 'print'
result.update()
Above will switch to print
view, with below CSS:
nav {width: 1024px;}
nav {color: red;}
Then switch back:
result.config.cssom.media = ''
result.update()
cssobj({
'@keyframes changeColor': {
'0%': { backgroundColor: 'green' },
'100%': { backgroundColor: 'yellow' }
},
'.nav': {
backgroundColor: 'red',
animation: '5s infinite changeColor'
}
})
Notice above @keyframes
, it have to be in top level of your source object, aka cannot be nested into .nav
,
that is different from @media
rule, which allow nested at any level, or nested into another @media
:
cssobj({
h3:{
color: 'blue',
'@media (min-width: 400px)': {
color: 'red',
'@media (max-width: 500px)': {
color: 'green'
}
},
'@media (min-width: 500px)': {
color: 'purple'
}
}
})
Above, what's the color will be? You can take a try and see what's the final CSS will be.
There's a hidden JS Bin...
Localize class names
Passing local: true
as option, cssobj will add a random name space
into all class names, this is called localize
:
const result = cssobj(
{
'.nav': {color: 'red'}
},
{ local: true }
)
Rendered CSS:
.nav_1lwyllh4_ {color: red;}
You can get this name space
using result.space
, or using below methods:
result.mapClass('nav active')
result.mapSel('.nav li.item')
React
You can use react-cssobj with React
If use Babel, recommended the babel-plugin-transform-cssobj
const result = CSSOBJ `
---
# cssobj config
local: true
plugins:
- default-unit: px
---
// SCSS style (nested)
.nav {
color: blue;
height: 100;
// font-size is a function
.item { color: red; font-size: ${v => v.raw ? v.raw + 1 : 12} }
// nested @media
@media (max-width: 800px) {
color: #333;
// & = parent selector = .nav
&:active {
color: #666;
}
}
}
`
const html = result.mapClass(<ul class='nav'><li class='item active'>ITEM</li></ul>)
Rendered result as below:
import cssobj from "cssobj";
import cssobj_plugin_default_unit from "cssobj-plugin-default-unit";
const result = cssobj({
'.nav': {
color: 'blue',
height: 100,
'.item': {
color: 'red',
fontSize: v => v.raw ? v.raw + 1 : 12
},
'@media (max-width: 800px)': {
color: '#333',
'&:active': {
color: '#666'
}
}
}
}, {
local: true,
plugins: [cssobj_plugin_default_unit('px')]
});
const html = <ul class={result.mapClass('nav')}><li class={result.mapClass('item active')}></li></ul>
For this first time render,
all class names add a random suffix _1jkhrb92_
,
the font-size
is 12px
,
the <style>
tag which cssobj
created now contains:
.nav_1jkhrb92_ { color: blue; height: 100px; }
.nav_1jkhrb92_ .item_1jkhrb92_ { color: red; font-size: 12px; }
@media (max-width: 800px) {
.nav_1jkhrb92_ { color: rgb(51, 51, 51); }
.nav_1jkhrb92_:active { color: rgb(102, 102, 102); }
}
Update CSS Value
Since we already have a function as the value:
fontSize: v => v.raw ? v.raw + 1 : 12
-
the value (===v.raw
) initialised with 12
(default-unit
plugin will add px
when rendering, that is v.cooked
=== 12px
)
-
each call of the function will increase font-size
by 1
So, just need call result.update
, the function invoked, stylesheet updated, automatically:
result.update()
result.update()
Above, only font-size
changed, all other things keep untouched
CRUD (Create, Read, Update, Delete) stylesheet from JS
When the source JS Object (first arg of cssobj()
) have no changes,
result.update
only invoke the value function (here, the above font-size
function),
Otherwise, it will look into the source JS Object, find which part have been changed (diff),
and update stylesheet accordingly. See below:
result.obj['.nav'].color = 'orange'
delete result.obj['.nav'].height
result.obj['.nav'].width = 200
result.obj['.nav'].a = { color: 'blue', '&:hover': {textDecoration: 'none'} }
delete result.obj['.nav']['.item']
result.update()
Above, only diffed part updated, other rules and props will keep untouched
Now, the stylesheet becomes:
.nav_1jkhrb92_ { color: orange; width: 200px; }
@media (max-width: 800px) {
.nav_1jkhrb92_ { color: #333; }
.nav_1jkhrb92_:active { color: #666; }
}
.nav_1jkhrb92_ a { color: blue; }
.nav_1jkhrb92_ a:hover { text-decoration: none; }
Diff with NEW JS Object
const newObj = { '.nav': { width: 100, a: { color: 'blue' } } }
result.update(newObj)
Now, the stylesheet becomes:
.nav_1jkhrb92_ { width: 100px; }
.nav_1jkhrb92_ a { color: blue; }
That's it, see more Usage & Example
Work Flow (Without Babel)
First install cssobj-converter
npm install -g cssobj-converter
Write your CSS as normal (e.g. index.css)
// file: index.css
.nav { color: blue; font-size: 12px; }
Turn it into JS module, from cssobj-converter
CLI
cssobj index.css -o index.css.js
The result
module.exports = {
'.nav': { color: 'blue', fontSize: '12px' }
}
Let's rock:
const obj = require('./index.css')
const result = cssobj(obj, {local: true})
result.mapClass(<JSX>)
result.mapClass('classA')
obj['.nav'].color = 'red'
obj['.nav'].fontSize = v => parseInt(v.cooked) + 1
result.update()
More to read:
How it worked?
-
cssobj first parse js object into Virtual CSSOM middle format.
-
The internal cssom plugin will create stylesheet dom, and apply rules from middle format.
-
When the js object changed, cssobj will diff CSSOM rules (add/delete/change) accordingly. (see demo)
Tools
Convert existing style sheet into cssobj:
-
CLI Converter Recommended CLI tools to convert CSS. Run npm -g cssobj-converter
-
Online Converter It's free node server, slow, and unstalbe, not recommended
Debug
Plugins
About writing a plugin, See: plugin-guide
Helpers
Demos
Test
Using phantom 2.0 to test with CSSOM. Please see test/ folder.
cssobj is wrapper for cssobj-core, plugin-localize and plugin-cssom.
License
MIT