CSS with a hash

...from a convention to a hash

css-modules && styled-components

Who
  Why
     How

Who

...a little bit of ❤️

CSS Modules

💅🏻 styled-components

Glen Maddern

@geelen

Glen Maddern

@geelen

Mark Dalgleish

@markdalgleish

Max Stoiber

@mxstbr

Tobias Koppers

@sokra

Why

...problems with CSS at scale

  1. ​Global Namespace
     

  2. Implicit dependencies between CSS and JS
     

  3. Dead code elimination
     

  4. Sharing constants & theming
     

  5. Isolation & cascade

🔴

🔴

🔴

css-modules

The local scope and webpack

css-modules

...it starts with a hash

  1. ​Write CSS as in CSS

    1. ​No inline styles or "CSS in JS"
       

  2. Processed to add a hash

    1. Webpack or PostCSS
       

  3. Imported in JS components

    1. React, Angular, etc
       

  4. Adds custom composition

    1. Avoid cascading in CSS

:local scope

gets hashed

when imported

._1rJwx92-gmbvaLiDdzgXiJ { ... }
._ah6Hch-gjsAdSE53CxzaEs { ... }
:local(.header) { ... }
:local(.footer) { ... }
import styles from './stylez.css';
{
  'header': '_1rJwx92-gmbvaLiDdzgXiJ',
  'footer': '_ah6Hch-gjsAdSE53CxzaEs'
}

:global scope

:local became default

.header { ... }
:global(.header) { ... }

CSS

JS & HTML

Output

import styles from './button.css';

buttonElem.outerHTML = `
  <button class=${styles.normal}>Submit</button>
`
.normal { /* all styles for Normal */ }
.disabled { /* overrides for Disabled */ }
<button class="button__normal__abc5436">
  Hit it!
</button>

Style Composition I

Output

.normal { /* all the common styles you want */ }
.inProgress { 
  composes: normal;
  color: red
}
.button__normal__abc5436 { /* font-sizes, padding, border-radius */ }
.button__inProgress__def6547 { /* blue color, light blue background */ }
styles: {
  normal: "button__normal__abc5436",
  inProgress: "button__normal__abc5436 button__inProgress__def6547"
}

Internally & Exports

Style Composition II

.primary {
  color: #720;
}
.secondary {
  color: #777;
}
.common { ... }

.normal {
  composes: common;
  composes: primary from "../constants/colors.css";
}
.component {
  composes: padding-large margin-small from "./layout.css";
}
.component {
  composes: large from "./typography.css";
  composes: dark-text from "./colors.css";
  composes: subtle-shadow from "./effect.css";
}

PostCSS and webpack

{
  test: /\.css$/,
  loader: '
     style!css-loader?modules&importLoaders=
     1&localIdentName=[name]__[local]___[hash:base64:5]' 
}
:global .page {
    padding: 20px;
}

.title {
    composes: title from "./mixins.css";
    color: green;
}

webpack loader configuration

postcss-modules

._title_116zl_1 {
    color: black;
    font-size: 40px;
}

{
  "title": "_title_xkpkl_5 _title_116zl_1"
}

Interoperable CSS (ICSS)

  1. Explicit cross-language dependencies
    1. Describe each file's dependencies
       
  2. CSS - JS interoperability
    1. Dynamic classnames (elem.addClass( styles.elemClass ))
       
  3. Specification
    1. :import and :export as pseudo selectors

styled-components

Enforcing best practices

styled-components

All about Higher Order Components

​Remove mapping

​between styles and component

import React from 'react';

import styled from 'styled-components';

const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

styled.*

const Title = styled.h1`
  font-size: 1.5em
`;

in DOM

<h1 class="ae0869e501">...</h1>

while CSS

<link href="stylez.css" />

React components

const Title = styled.default.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

const Wrapper = styled.default.section`
  padding: 4em;
  background: papayawhip;
`;

class Example extends React.Component {
    render() {
      return (
        <Wrapper>
          <Title>Hello World!</Title>
        </Wrapper>
      )
    }
}

ReactDOM.render(
    <Example />,
    document.getElementById('container')
);

Composition of components

import React from 'react';
import styled from 'styled-components';

import Button from './Button';

const TomatoButton = styled(Button)`
  color: tomato;
  border-color: tomato;
`;

export default TomatoButton;

Animations

import styled, { keyframes } from 'styled-components';

const rotate360 = keyframes`
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
`;
const Rotate = styled.div`
  display: inline-block;
  animation: ${rotate360} 2s linear infinite;
`;

Definition

Usage

Interpolation

const padding = '3em';

const Section = styled.section`
    color: white;
    padding: ${padding};

    background: ${(props) => props.background};

    border: ${(props) => props.theme.main}
`;

Change by props

Theming

import { ThemeProvider } from 'styled-components';

const theme = {
  main: 'mediumseagreen',
};

const GreenSection = (props) => {
  return (
    <ThemeProvider theme={theme}>
      {props.children}
    </ThemeProvider>
  );
}

ThemeProvider

props.theme

const Button = styled.button`
  background: ${props => props.theme.main};
  border: 2px solid ${props => props.theme.main};
`;

Why

...problems with CSS at scale

  1. ​Global Namespace
     

  2. Implicit dependencies between CSS and JS
     

  3. Dead code elimination
     

  4. Sharing constants & theming
     

  5. Isolation & cascade

❗️ Thanks ❗️

❓ Questions

CSS with a hash

By Tobias Deekens

CSS with a hash

From a convention to a hash. Tales of CSS modules and styled-components.

  • 1,370