cssobj

    1.3.6 • Public • Published
    cssobj logo

    CSSOBJ Join the chat at https://gitter.im/css-in-js/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

    Build Status npm Coverage Status dependencies Status Standard - JavaScript Style Guide

    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 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()
    // 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:

    How it worked?

    1. cssobj first parse js object into Virtual CSSOM middle format.

    2. The internal cssom plugin will create stylesheet dom, and apply rules from middle format.

    3. 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.

    Remark

    cssobj is wrapper for cssobj-core, plugin-localize and plugin-cssom.

    License

    MIT

    Install

    npm i cssobj

    DownloadsWeekly Downloads

    40

    Version

    1.3.6

    License

    MIT

    Unpacked Size

    243 kB

    Total Files

    29

    Last publish

    Collaborators

    • cssobj