Scripting in Style

What's your Vue?

Who?

Senior FED

Core Maintainer

Meetup Organizer

Blogger

2010

2014

2002

1991

1995

2005

2008

2011

2015

The Fear

...of CSS

Chris

Chris

Chris


/* style.css */

.chris {
background:'blue';
}

/* style.css */

.chris {
background:'gold';
}

/* style.css */

.chris {
background:'green';
}

<div class="my-awesome-container">
    <button class="chris">
        Chris!
    </button>
</div>

Chris!

😰

!important

Scaling

Global namespace

Implicit dependencies

Code Maintenance (DEAD code)

Minification

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!)

CSS frameworks

HARD to customize

Real-time CSS modifiers ?

var()

😍

😱

JavaScript

😰 Performance 

😰 Complexity 

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

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]

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

CSS is still READ-ONLY to JS

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";
`;
<style module="theme" lang="scss">
  /*...*/
:export {
  default: #fff;
  lightorange: $light-orange;
  purple: $purple;
  darkpurple: $dark-purple;
  pink: $pink;
}
</style>
<script>
export default {
  /* ... */
  data() {
    return {
      colors: this.theme,
      currentThemeColor: this.theme.default
    };
  }
}
</script>
<script>
const colors = {
  default: "#fff",
  lightorange: "#fc9e2d",
  purple: "rgb(76, 76, 111)",
  darkpurple: "#111",
  pink: "pink"
};
  
export default {
  /* ... */
  data() {
    return {
      colors: colors,
      currentThemeColor: colors.default
    };
  }
}
</script>

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

🤯

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

CSS-in-JS

(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

Tachyon CSS

Or just to make life easier

 Utilities - FIRST

import tw from 'tailwind.macro';

const StyledTitle = styled('h3')`
    ${() => tw("text-red-500")}
`;

Resources

Thank you

Scripting in style, What's your Vue? (Toronto 2019)

By Maya Shavin

Scripting in style, What's your Vue? (Toronto 2019)

Built-in CSS Modules and Scoped CSS support eases the process of styling your Vue. But a separate tag is still needed. What about styling directly and dynamically in JavaScript - aka CSS-in-JS and save the pain? Should we, or should we not consider this as a good approach? This is what my talk about. Code sample: https://codesandbox.io/embed/style-with-vue-fzzci

  • 320
Loading comments...

More from Maya Shavin