Scripting in Style
What's your Vue?
Who?
Senior FED
Back to the past (1991)
1995
2002
2005
2011
2010
2014
2008
2015
2010
2014
2002
1991
1995
2005
2008
2011
2015
2017
Component
CSS/SCSS
CSS-in-JS
CSS/SCSS
CSS Modules
Scoped CSS
Component
CSS/SCSS
2018
The Fear
...of CSS
CSS is awesome
BUT
Scaling
Global namespace
Implicit dependencies
Code Maintenance (DEAD code)
Chris
Chris
Chris
/* style.css */
.chris {
color:'blue';
}
/* style.css */
.chris {
color:'gold';
}
/* style.css */
.chris {
color:'green';
}
<div class="my-awesome-container">
<button class="chris">
Chris!
</button>
</div>
Chris!
😰
!important
Scaling
Minification
Sharing constants
Non deterministic order of source
The (not) old style
OOCSS & SMACSS
BEM
CSS processors
NOT pure CSS
HARD to migrate
NAMING !
STILL GLOBAL
STILL NOT scalable
(that HUGE CSS file!)
Scoped SS
Component.vue
JS
Scoped CSS
/*Item.vue*/
<template>
<div class="item__container_card item__container">
<img :src="src" width="200">
<h3 class="title">{{title}}</h3>
</div>
</template>
<script>
export default { //...code }
</script>
<style scoped lang="scss">
.item__container {
/* ..stylings */
&_card {
margin-right: 10px;
margin-bottom: 10px;
width: 220px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
}
}
.title {
color: #842803;
margin: 0;
padding: 0.8rem 0;
border-top: 1px solid lightgray;
}
</style>
HTML
/*App.vue*/
<template>
<div id="app">
<h1 class="title">The Pokemons</h1>
<item v-bind="pokemon"/>
</div>
</template>
<script>
export default { /*code */ }
</script>
/*App.vue*/
<template>
<div id="app">
<h1 class="title">The Pokemons</h1>
<item v-bind="pokemon"/>
</div>
</template>
<script>
export default { /*code */ }
</script>
<style>
.title {
color: #111;
}
</style>
/*App.vue*/
<template>
<div id="app" class="app">
<h1 class="title">The Pokemons</h1>
<item v-bind="pokemon"/>
</div>
</template>
<script>
export default { /*code */ }
</script>
<style>
.app .title {
color: #111;
}
</style>
Full CSS support
Pre-processor support
Reusable CSS
Random class generator
/*Item.vue*/
<style lang="scss">
//...
</style>
/* OR */
<style lang="less">
//...
</style>
/* Item.vue */
<template>
//...
<h3 class="title">{{title}}</h3>
//...
</template>
<style scoped>
.title {
//...
}
</style>
/* will be compiled to */
<head>
<style>
.title[data-v-6b294924] {
//...
}
</style>
</head>
<body>
/*...*/
<h3 class="title" data-v-6b294924>
/*...*/
</body>
Scoped
Global Extendable
.app .title >>> title[data-v-6b294924]
Real-time CSS modifiers ?
var()
😍
😱
JavaScript
😰 Performance
😰 Complexity
CSS has fundamental flaws at scale that can be solved by writing styles in JS .
@VJEUX — NOVEMBER 2014
SS-in-JS
Ideal concepts
Scoped CSS
Extendable
Full CSS support
SSR (Static CSS extraction)
Debuggable
Syntax highlighting
Free of dead CSS code
Themes
2015
<template>
<div class="item__container_card item__container">
<img :src="src">
<h3 class="title">{{title}}</h3>
</div>
</template>
// Will become
<template>
<div :class="[this.item.container, this.item.container_card]">
<img :src="src">
<h3 :class="description.title">{{title}}</h3>
</div>
</template>
<style scoped lang="scss">
.item__container {
/*...*/
}
.title {
/*...*/
}
</style>
//Will become
<style module="item" lang="scss">
.container {
/* Same as before */
}
</style>
<style module="description">
.title {
/* Same as before */
</style>
/*App.vue*/
//...
<style module="theme" lang="scss">
$primary-color: #B4DC47;
$light-orange: #fc9e2d;
$purple: rgb(76, 76, 111);
$dark-purple: #111;
$pink: pink;
:export {
colors: {
default: $primary-color;
lightorange: $light-orange;
purple: $purple;
darkpurple: $dark-purple;
pink: $pink;
}
}
</style>
/*App.vue*/
<script>
//...
provide() {
const theme = {};
Object.defineProperty(theme, "defaultColor", {
enumerable: true,
get: () => this.currentThemeColor
});
return {
theme: theme,
};
},
data() {
return {
colors: this.theme,
currentThemeColor: this.theme.default
};
}
//...
</script>
Theming
/*Layout.vue*/
<template>
<div class="layout__container"
:style="{ background: theme.defaultColor}">
/*...*/
<h4>Current Theme: {{theme.defaultColor}}</h4>
/*...*/
</div>
</template>
<script>
export default {
/*...*/
inject: ["theme"],
/*...*/
}
</script>
All SCOPED CSS Benefits
Theming
Dynamic modifiers
<div
:class="[this.item.container]">
//...
</div>
//Will become
<div
class="_src_components_ModuledItem__container">
//...
</div>
BUT
CSS in JS
not really
It's still
NOT Full extendable CSS in global scope
NO EASY way for theming
NAMING (not name collisions)
THE CSS-in-JS
The challenge
List-view
Grid-view
+
Themes
/*App.vue*/
<script>
const colors = {
default: "#fff",
lightorange: "#fc9e2d",
purple: "rgb(76, 76, 111)",
darkpurple: "#111",
pink: "pink"
};
export default {
name: "App",
components: {
Layout
},
provide() { /*....*/ },
/*...*/
data() {
return {
colors: colors,
currentThemeColor: colors.default
};
}
};
</script>
/*Item.vue*/
<script>
import yiq from "yiq";
import { darken } from "polished";
import styled, { css } from "vue-styled-components";
const itemProps = {
isGrid: Boolean,
themeColor: String
};
const TitleRowStyle = css`/*...*/`;
const StyledItemTitle = styled("h3", itemProps)`
color: ${props => yiq(props.themeColor)};
/*...*/
${props => (props.isGrid ? null : TitleRowStyle)}
`;
const Card = css`/*...*/`;
const Row = css`/*...*/`;
const StyledItem = styled("div", itemProps)`
/*...*/
background: ${props => props.themeColor};
${props => (props.isGrid ? Card : Row)}
&:hover {
background-color: ${props => darken(0.05, props.themeColor)};
cursor: pointer;
}
`;
export default {
inject: ["viewMode", "theme"],
components: { StyledItem, StyledItemTitle },
/*...*/
};
</script>
/*Item.vue*/
<template>
<styled-item
:is-grid="viewMode.isGrid"
:theme-color="theme.defaultColor">
<img :src="src">
<styled-item-title
:is-grid="viewMode.isGrid"
:theme-color="theme.defaultColor">
{{title}}
</styled-item-title>
</styled-item>
</template>
The GOOD
EASY to theme
NO Dead code
Run-time modifier
Extendable
Scoped (100%)
/* ExtendedItem.vue */
import styled from "styled-components";
import Item from './Item'
const ExtendedItem = Item.extend`
background: "blue";
border-color: "blue";
`;
NO more CSS file!
It's awesome!
Or is it really ? 😏
The NOT so good
JS
CSS
Parse CSS
Inject to Stylesheet
stylesheet.insertRule
Browser
HTML Paint
load
😮 Accessibility?
NO Cache
download
Box
/* Box.vue */
<template>
<styled-box :size="size"/>
</template>
<script>
import styled from "vue-styled-components";
const boxProps = {
size: String
};
const StyledBox = styled("div", boxProps)`
width: ${props => props.size}px;
height: ${props => props.size}px;
background: grey;
margin-left: auto;
margin-right: auto;
margin-top: 10px;
`;
export default {
components: { StyledBox },
props: boxProps
};
</script>
Input size of box
Interactive Comp
🤯
How about
Security?
Security?
CSS-in-JS means
It's still CSS! (not JS)
Learning CSS is still required! 😆
Should we use CSS-in-JS?
Yes, but with caution
So
Easy to theme
NO Dead code
Extendable
Scoped
Styles on run time
Security
Portability
Context switch
Messy code
CSS-in-JS
The Good
The Bad
The Ugly
Portable
No escapsulation
Bundle size
Dead code (somewhere)
Naming
Private to JS code
Load once
No re-render
Scoped CSS
The Good
The Bad
The Ugly
Theme-able
Basic scope
"Use the right tool for the right job"
And naming is not a good enough reason...
Styled components
Scoped CSS
When?
CSS modules
CSS consistency between projects
Simple and isolated component
Medium size apps / third-party libs
Security is a concern
Full control over component styling
Theme is critical (IE included)
Support dynamic styling via props
Complex and large apps
IE 11 is not a concern
Basic scope
NO dynamic styling via props
Thank you
Scripting in style - What's your Vue?
By Maya Shavin
Scripting in style - What's your Vue?
Code Demo: https://codesandbox.io/embed/style-with-vue-fzzci
- 2,150