Fix CSS

by writing

it in JS

 

https://github.com/jsstyles/jss

 

By Oleg Slobodskoi @oleg008

Overview

  1. History
  2. Composition
  3. Code reuse
  4. Vendor prefixing
  5. CSS globals
  6. Sharing values
  7. Overhead
  8. More optimizations

Javascript Style Sheets

 

 


    <style type="text/javascript">
        tags.H1.color = "blue";
    </style>

Cascades and Composition 

Lets create a component "A" with a button.


    component-a button {
        background: red;
    }

Lets create a component "B" with a button.


    component-b button {
        color: green;
    }

Now lets compose them.

 

Lets use component B within component A.


    <component-a>
        <button>Button A</butotn>
        <component-b>
            <button>Button B</butotn>
        </component-b>
    </component-a>

 Is this what we

want to get?

 

 

2 problems

1. Cascading

Is only useful when you are never going to

compose components.

2. Global selectors

Is a bad practice and we all know why, right?

Lets rewrite it using JS


    // component-a.js
    module.exports = {
        button: {
            background: 'red'
        }
    }
    
    // component-b.js
    module.exports = {
        button: {
            color: 'green'
        }
    }
    
    // app.js
    var a = require('./component-a')
    var b = require('./component-b')
    jss.createStyleSheet(a, true).attach()
    jss.createStyleSheet(b, true).attach()

Both problems solved.

  • We don't rely on cascading
  • Selectors are generated and are unique

    <style>
        .jss-0 {
            background: red;
        }
    </style>
    
    <style>
        .jss-1 {
            color: green;
        }
    </style>

Code reuse in CSS

  • Multiple classes on one element
  • Global selectors

Code reuse in JSS

Is infinitely flexible.

 

  • Truly reusable css components
  • Save up to 30% in size

Inheritance

Done by "extend" plugin.


    // base-button.js
    module.exports = {
        color: 'red'
    }
    
    // styles.js
    var baseButton = require('./base-button')
    module.exports = {
        '.button': {
            extend: baseButton,
            float: 'left'
        }
    }
    
    // app.js
    var styles = require('./styles')
    jss.createStyleSheet(styles).attach()      

    <style>
        .button {
            color: red;
            float: left;
        }
    </style>

Nesting

Done by "nested" plugin.


    // styles.js
    module.exports = {
        '.button': {
            float: 'left',
            '&:hover': {
                color: 'red'
            },
            '& .icon': {
                border: '1px solid red'
            }
        }
    }
    
    // app.js
    var styles = require('./styles')
    jss.createStyleSheet(styles).attach()  

    <style>
        .button {
            float: left;
        }
        .button:hover {
            color: red;
        }
        .button .icon {
            width: 20px;
            height: 20px
        }
    </style>

Vendor prefixes

Done by "vendorPrefixer" plugin.

Added very efficiently  at runtime.


    // styles.js
    module.exports = {
        '.button': {
            transform: 'translateX(100px)'
        }
    }
    
    // app.js
    var styles = require('./styles')
    jss.createStyleSheet(styles).attach()  

    <style>
        .button {
            -webkit-transform: translate(100px);
        }
    </style>

Sharing values

Now it's really easy to reuse and access constants.


    // vars.js
    module.exports = {
        color: '#fff',
        height: 20
    }
    
    
    // styles.js
    var vars = require('./vars')
    module.exports = {
        button: {
            color: vars.color,
            height: vars.height + 'px'
        }
    }

    // app.js
    var vars = require('./vars')

    // Do something usefull    
    $('something').height(vars.height)

Direct injection


    // styles.js
    module.exports = {
        button: {
            padding: '20px',
            background: 'blue'
        }
    }
    
    // app.js
    var styles = require('./styles')
    var ss = jss.createStyleSheet(styles, true)
    
    $('button').css(ss.rules.button.style)
    

Why share values?

  • DOM round trip is expensive
  • get initial value directly
  • calculate relative values
  • use values in different places e.g. inline

 

JS layout engines will love it!

Overhead?

bootstrap css (134kb) vs. bootstrap jss:

http://jsstyles.github.io/jss/bench/bootstrap/css.html

http://jsstyles.github.io/jss/bench/bootstrap/jss.html

 

Jss overall overhead ~10-15ms on desktop chrome.

 

 

More optimizations?

Critical path

You might want to convert critical path

jss to css and inline it.


    var ss = jss.createStyleSheet({
        'a': {
            color: 'red'
        }
    })
    console.log(ss.toString())
    
    
    // Output
    a {
        color: red;
    }



Unused CSS

  • Render jss styles only for components which are in the render tree.

  • Remove jss styles when components are detached.

Questions?

 

https://github.com/jsstyles/jss

 

Examples

http://jsstyles.github.io/jss/examples/

 

 

By Oleg Slobodskoi @oleg008

Made with Slides.com