cssobj
v1.3.6
Published
Runtime CSS manager, Turn CSS into dynamic JS module, Stylesheet CRUD (Create, Read, Update, Delete) in CSSOM, Solve common problems of CSS-in-JS, name space (local) class names
Downloads
97
Maintainers
Readme
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.
- ~4K min.gz, simple API
- Nested rules, support any CSS selector/value
- Minimal work to migrate
- Work with DOM Frameworks
- CSS Rules CRUD
- Put class names into local space No Conflict
- Use JS function as CSS value
- Conditional Apply CSS
- Server Rendering
Usage - Wiki - API - Demo - React - Babel
Install:
npm
npm install cssobj # the lib
# When use Babel
npm install babel-plugin-transform-cssobj
# When **NOT** use Babel, install the converter
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',
// simulate 50vh in CSS3
height: () => window.innerHeight/2 + 'px'
}
}
const result = cssobj(obj)
// dynamic update height when resize
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'
// using static value:
obj.div.color = 'blue'
result.update() // color is now 'blue'
// using function as value:
obj.div.color = function(v){
return randomColor()
}
result.update() // color is now random
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]
// => CSSStyleRule
rule.color = 'red'
8. Delete/Destroy cssobj
Currently, cssobj
don't provide result.destroy()
or similar method, you should manually destroy things:
// remove <style> tag
result.cssdom.parentNode.removeChild(el)
// GC result
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:
// As HTML class attribute
result.mapClass('nav active') // [string] 'nav_1lwyllh4_ active_1lwyllh4_'
// As CSS selector
result.mapSel('.nav li.item') // [string] '.nav_1lwyllh4_ li.item_1lwyllh4_'
React
You can use react-cssobj with React
Work Flow with Babel, See also Without Babel Version
If use Babel, recommended the babel-plugin-transform-cssobj
// create <style> in <head>, insert CSS rules, random namespace: _1jkhrb92_
// The babel-plugin only transform: CSSOBJ `text`
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>)
// <ul class="nav_1jkhrb92_"><li class="item_1jkhrb92_ active_1jkhrb92_"></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 with12
(default-unit
plugin will addpx
when rendering, that isv.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()
// font-size -> 13px
result.update()
// font-size -> 14px
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 === reference of the source js object
// change a css property
result.obj['.nav'].color = 'orange'
// remove a css property
delete result.obj['.nav'].height
// add a new css property
result.obj['.nav'].width = 200
// add a new rule
result.obj['.nav'].a = { color: 'blue', '&:hover': {textDecoration: 'none'} }
// delete a rule
delete result.obj['.nav']['.item']
result.update()
// color -> 'orange' (PROP CHANGED)
// height -> (PROP REMOVED)
// width -> 200px (PROP ADDED)
// a, a:hover -> (RULE ADDED)
// .item -> (RULE REMOVED)
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)
// cssobj will DIFF with old obj, keep same part, change diffed part in stylesheet!
// .nav, .nav a rules keeped
// width -> 100px, drop all other rules/props
Now, the stylesheet becomes:
/* below 2 rules keeped */
.nav_1jkhrb92_ { width: 100px; }
.nav_1jkhrb92_ a { color: blue; }
/* other rules gone */
That's it, see more Usage & Example
Work Flow (Without Babel)
First install cssobj-converter
npm install -g cssobj-converter
- Step 1
Write your CSS as normal (e.g. index.css)
// file: index.css
.nav { color: blue; font-size: 12px; }
- Step 2
Turn it into JS module, from cssobj-converter
CLI
# in command line, run cssobj-converter
cssobj index.css -o index.css.js
The result
// file: index.css.js
module.exports = {
'.nav': { color: 'blue', fontSize: '12px' }
}
- Step 3
Let's rock:
// import your css module
const obj = require('./index.css')
// create <style> tag in <head>, with rules in obj.
// `local: true` will put class names into local space
const result = cssobj(obj, {local: true})
result.mapClass(<JSX>) // with Babel
result.mapClass('classA') // without Babel
// update some rule
obj['.nav'].color = 'red'
obj['.nav'].fontSize = v => parseInt(v.cooked) + 1 // increase font-size by 1
result.update()
Documented API
More to read:
!important CSSOBJ Format
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
- cssobj-helper-showcss Display css string from style tag, for DEBUG
Plugins
About writing a plugin, See: plugin-guide
(already in core) cssobj-plugin-localize Localize class names
(already in core) cssobj-plugin-cssdom Inject style to DOM and diff update
cssobj-plugin-default-unit Add default unit to numeric values, e.g. width / height
cssobj-plugin-flexbox Make flexbox working right with auto prefixer/transform
cssobj-plugin-replace Merge cssobj Key/Value with new object
cssobj-plugin-extend Extend to another selector, like @extend in SCSS or :extend in LESS
cssobj-plugin-keyframes Make keyframe names localized, and apply to
animation
andanimation-name
cssobj-plugin-gencss Generate css text from Virtual CSS Node, for Server Rendering
Helpers
babel-plugin-transform-cssobj Work with React, Vue etc. that can use babel+jsx
cssobj-mithril Help cssobj to work with mithril
cssobj-helper-stylize Add css string into style dom
Demos
Test
Using phantom 2.0 to test with CSSOM. Please see test/ folder.
Remark
cssobj is wrapper for cssobj-core, plugin-localize and plugin-cssom.
License
MIT