JavaScript Style Sheets

cssinjs.org by Oleg Slobodskoi

React Amsterdam 2016

Plan

  1. React and CSS.
  2. What are inline styles.
  3. Problems solved by inline styles.
  4. Interesting CSS in JS libs.
  5. What is JSS.
  6. Problems solved by JSS.
  7. Unsolved problems.

React and CSS

React is about components.

 

CSS is for documents.

First JSS release

Inline styles


    const style = {color: red}    

    <div style={style}></div>
    
    <div style="color: red"></div>

HTML

React


    const element = document.createElement('div')

    element.style.color = 'red'

DOM

Global Namespace

Selectors are the evil.

Try to write an application inside of

one function.

Implicit Dependencies


    <button class="default-button my-button">My Button</button>

    /* buttons.css */
    .default-button {
        cursor: pointer;
    }
    
    /* my-component.css */
    .my-button {
        color: red;
    }

Dead code

A task for a compiler.

function module() {
  var styles = {
    button: {
      color: 'green'
    },
    myButton: {
      color: 'red'
    }
  }

  // Renders a button.
  button({style: styles.myButton})
}
function module(){button({style:{color:"red"}})};

Minification


    function test(a) {
      var obj = {a: a, b: 2};
    
      return obj.a + obj.b;
    }
    
    window.a = test(1)
    
    window.a=3;
  • No selectors to minify.
  • JavaScript compressor for the rest.

Sharing Constants

  • Data driven styling
  • Shared Breakpoints
  • Javascript Layouts

Non-deterministic Resolution

CSS Specificity Concept.


    /* my-button-1.css */
   .my-button.my-blue-button {
      color: blue;
    }

    /* my-button-2.css */    
    button.my-button {
      color: red;
    }
<head>
    <link href="my-button-1.css" rel="stylesheet" />
    <link href="my-button-2.css" rel="stylesheet" />
</head>
<body>
    <button class="my-button my-blue-button">My Button</button>
</body>

Scoping

  • No access to external components.
  • Very high specificity.

Complex Selectors

.article #comments ul > li > a.button

Redundancy

Smaller payload.

Can we do even better?

YES.

Other alternatives?

And more ...

And more ...

Text

3 Categories

  1. Pure Inline
  2. Mixed Mode
  3. Pure Style Sheets

Pure Inline

  • Already built in React
  • No support for @media, @keyframes etc.
  • Performance Downsides

Mixed Mode

Radium is using:

  • React Events for Pseudo Selectors.
  • Style Sheets for @media and co.
  • Inline for everything else.

Pure Style Sheets

  • Aphrodite
  • JSS

What is JSS.

  • Good parts of CSS only.
  • Designed with components in mind.

  • Declarative JavaScript.
  • JavaScript to CSS compiler.
  • Easy to reason about.

How does it work.

Virtual

CSS Tree

Process

Render

Run plugins on every VRule

Output <style>
with CSS.

Abstraction for CSS Rules Manipulation

React-JSS

  • Lazy compilation
  • Ref counting

Example with React


  import React from 'react'
  import {useSheet} from 'react-jss'

  @useSheet({
    button: {
      color: 'green'
    }
  })
  export default function Button(props) {
    const {classes} = props.sheet
    return <button className={classes.button}>{props.text}</button>
  }

No magic in code.

Boring CSS output.

Problems it solves.

  1. All issues solved by Inline Styles.
  2. Issues introduced by Inline Styles.
  3. More CSS Issues.

Plan

1. Media Queries

2. Keyframes Animation

3. Font Face

4. Pseudo Selectors

5. Fallbacks

6. Rules Caching

7. Rules Sharing

8. Extensible Architecture

9. Tools Agnostic 

10. Vendor Prefixer

11. Inheritance

DSL

Library

Media Queries


    export default {
      '@media (min-width: 1024px)': {
        button: {
          minWidth: 200
        }
      }
    }

    const breakpoint = 1024
    export default {
      [`@media (min-width: ${breakpoint}px)`]: {
        button: {
          minWidth: 200
        }
      }
    }

  @media (min-width: 1024px) {
    .button-jss-0 {
      min-width: 200px;
    }
  }

JSS in ES5

JSS in ES6

CSS

Not possible Inline.

Keyframes Animation


  export default {
    '@keyframes my-animation': {
      from: {opacity: 0},
      to: {opacity: 1}
    }
  }

  const identifier = random()
  export default {
    [`@keyframes ${identifier}`]: {
      from: {opacity: 0},
      to {opacity: 1}
    }
  }

  @keyframes my-animation {
    from { opacity: 0; }
    to { opacity: 1; }
  }

JSS in ES5

JSS in ES6

CSS

Not possible Inline.

Font Face


    export default {
      '@font-face': {
        fontFamily: 'MyWebFont',
        src: [
          'url(webfont.eot)',
          'url(webfont.eot?#iefix) format(embedded-opentype)',
          'url(webfont.woff2) format(woff2)'
        ]
      }
    }

  @font-face {
    font-family: 'MyWebFont';
    src: url('webfont.eot'); 
    src: url('webfont.eot?#iefix') format('embedded-opentype'),
         url('webfont.woff2') format('woff2');
  }       

JSS

CSS

Not possible Inline.

Pseudo Selectors

  export default {
    button: {
      color: 'green'
      '&:hover': {
        color: 'red'
      },
      '&:active': {
        color: blue    
      },
      '&:before': {
        content: '"icon"'
      },
      '& span': {
        verticalAlign: 'middle'
      }
    }
  }

  .jss-0-1 {
    color: green;
  }
  .jss-0-1:hover {
    color: red;
  }
  .jss-0-1:active {
    color: blue;
  }
  .jss-0-1:before {
    content: "icon";
  }
  .jss-0-1 span {
    vertical-align: middle;
  }

JSS

CSS

Not possible Inline.

Inspired by Sass.

Fallbacks


  export default {
    container: {
      color: [
        'red',
        'linear-gradient(to right, red 0%, green 100%)'
      ]
    }
  }

  .jss-0-1 {
    color: red;
    color: linear-gradient(to right, red 0%, green 100%);
  }

JSS

CSS

Not possible Inline.

Rules Caching

Not possible Inline.

CSS Rules are created just once.

Rules Sharing

Not possible Inline.

One rule applies to all list items

Class Names are fast.

  1. Find modified props.
  2. Find props to unset.
  3. Ensure valid property name, value, default unit.
  4. Apply each property to an element

Inline Styles rendering pipeline:

Smaller Payload

Up to 50% smaller payload 

when rendered at runtime. 

Tools Agnostic

  • Library agnostic.
  • Infrastructure agnostic.

Extensible

  • Small Core
  • Plugins API.

Vendor Prefixer

Plugin


    export default {
      container: {
        display: 'flex'
      }
    }

  .jss-0-1 {
    display: -webkit-flex;
  }

JSS

CSS

Inheritance

Styles inherit from parent.

Still no TRUE Isolation.

JSS-ISOLATE

Created a JSS plugin.

and

SOLVED INHERITANCE PROBLEM

Maxim Koretskiy

This plugin protects styles from inheritance. It automatically creates a reset rule and applies it to every user's rule.

Unsolved Problems

:(

Overengineered Solution

If you feel it - don't use it.

Wrong Language?

import color from 'color'
import fonts from 'theme/fonts'
import mixins from 'jss-mixins'
import {gainsboroLight, grapeTypo, grey, blue, white} from 'theme/colors'

const grapeTypoSemi = color(grapeTypo).alpha(0.5).rgbaString()

export default {
  datalist: {
    background: white,
    border: `1px solid ${gainsboroLight}`,
    boxShadow: `0px 3px 4px 0 ${grapeTypoSemi}`,
    overflow: 'auto'
  },
  item: {
    extend: [fonts.normal, mixins.ellipsis],
    padding: `5px 7px`,
    color: grey,
    cursor: `pointer`
  },
  `@media (min-width: 1024px)`: {
    item: {
      color: blue
    }
  }
}

Let's experiment!

export default css`
  datalist {
    background: ${white};
    border: 1px solid ${gainsboroLight};
    box-shadow: 0px 3px 4px 0 ${grapeTypoSemi};
    overflow: auto
  }
  item {
    extend: ${[fonts.normal, mixins.ellipsis]};
    padding: 5px 7px;
    color: ${grey};
    cursor: ${pointer};
  }
  @media (min-width: 1024px) {
    item {
      color: ${blue}
    }
  }
`

Tagged template literals

No standard DSL

Styles reuse only together with the lib.

Missing dev tools

  • There is no autocomplete tools like Emmet or IntelliSense.
  • There is no CSS specific highlighting.
  • No linters.

Blocks initial rendering.

  • Use it where it doesn't matter.
  • Use server-side rendering.
  • Use a hybrid approach.

Takeaways

  • CSS in JS is not only for big projects, it's for any maintainable project.
  • Use Inline Styles for:
    1. State styles. For e.g. when a "width" of a component depends on its state.

    2. Animations.

  • Don't be religious. Keep an open mind. Use tools that solve your problems!

Thank You

#reactamsterdam

 

@oleg008

http://cssinjs.org

JavaScript Style Sheets

By Oleg Isonen

JavaScript Style Sheets

  • 15,821