JavaScript Style Sheets
React Amsterdam 2016
Plan
- React and CSS.
- What are inline styles.
- Problems solved by inline styles.
- Interesting CSS in JS libs.
- What is JSS.
- Problems solved by JSS.
- 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
- Pure Inline
- Mixed Mode
- 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.
- All issues solved by Inline Styles.
- Issues introduced by Inline Styles.
- 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.
- Find modified props.
- Find props to unset.
- Ensure valid property name, value, default unit.
- 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:
-
State styles. For e.g. when a "width" of a component depends on its state.
-
Animations.
-
- Don't be religious. Keep an open mind. Use tools that solve your problems!
Thank You
JavaScript Style Sheets
By Oleg Isonen
JavaScript Style Sheets
- 15,821