Andrey Sitnik, Evil Martians

PostCSS Way

The company behind PostCSS and Autoprefixer

Check out our Twitter for the slides

Part 1. The history of PostCSS

Image: Eric Joiner

My first pull request (2010) …

Source: WALL-E

… was ignored for a year

Source: WALL-E

2 years to plan for the revenge

Autoprefixer inside

Parse CSS

Add prefixes

Remove prefixes

Generate new CSS

Common for any CSS tool

Rework for common tasks

Parse CSS

Add prefixes

Remove prefixes

Generate new CSS

Rework

Autoprefixer inital name was Rework Vendors

Evolution

Rework

PostCSS

PostCSS for common tasks

Parse CSS

Add prefixes

Remove prefixes

Generate new CSS & source maps

PostCSS

Revenge was successful

If you have no time, say it to users

PostCSS is an “under the hood” framework

Autoprefixer

PostCSS

RTLCSS

PostCSS

use: [
  'autoprefixer-loader',
  'rtlcss-loader'
]

PostCSS’s goal: more CSS tools

Problem 1: performance

Parse CSS

Autoprefixer

RTLCSS

Parse CSS

Problem 2: many environments

gulp-autoprefixer
grunt-autoprefixer
autoprefixer-loader
autoprefixer-cli
gulp-rtlcss
grunt-rtlcss
rtlcss-loader
rtlcss-cli

Problem 3: fear

Meme: Drake - Hotline Bling

Add plugin

Add new tool

So PostCSS became a tool

use: [
  'autoprefixer-loader',
  'rtlcss-loader'
]
use: [
  'postcss-loader'
]

// postcss.config.js
module.exports = {
  plugins: {
    'autoprefixer': {},
    'rtlcss': {}
  }
}

PostCSS is still hidden as framework in:

CSS Modules

PostCSS

critical

PostCSS

css-loader

PostCSS

CSS Blocks

PostCSS

Part 2. Basic misconceptions

Image: Eric Joiner

PostCSS and Sass are hard to compare

Should I use Sass or PostCSS?

PostCSS vs. Sass

Sass

PostCSS

Polyfills and prefixes

Isolation

Syntax sugar

Linting

Minification

Syntax sugar

Minification

Normalization

Syntax sugar is not really important

Source: Portal

Important thing: isolation

/* BEM */

.block_element { }
/* CSS Modules */

s = require('./style.css')
/* CSS-in-JS */

Title = styled`
  color: black;
`

Important thing: preventing errors

.foo {
  margin-top: 20px;
  width: 100px;
  height: 100px;
  margin: 0 auto;
}

Stylelint: this overrides the longhand property before it app.css:5:3

Important thing: sharing best practices

Facebook custom Stylelint rules:

  • slow-css-properties
  • filters-with-svg-files
  • use-variables
  • mobile-flexbox

Talk plan

  1. Project structure
  2. Normalization
  3. Isolation
  4. Linting

Part 3. Project structure

Image: Eric Joiner

Move browsers to Browserslist config

// .postcssrc
{
  "plugins": {
    "autoprefixer": {
      "browsers": [
        "last 1 version",
        "> 1% in my stats",
        "not dead"
      ]
    }
  }
}
// .postcssrc
{
  "plugins": {
    "autoprefixer": { }
  }
}
// .browserslistrc
last 1 version
> 1% in my stats
not dead

Or to package.json

{
  …
  "browserslist": [
    "last 1 version",
    "> 1% in my stats",
    "not dead",
  ]
}

Feature 1: A single config for all tools

7

Feature 2: A single development convention

— Do we still support IE 11?
— Check Browserslist config

The only recommended way for Autoprefixer

The only recommended way for Babel 7

Create React App

Part 4. Normalization

Image: Eric Joiner

Browser default styles

We need to normalize default styles

Normalize.css supports many old browsers

/**
 * Remove the gray background on active links in IE 10.
 */

a {
  background-color: transparent;
}
// .browserslistrc
last 1 version
> 1% in my stats
not dead

// app.css
@import-normalize;

Browserslist + Normalize.css

/* last 3 versions */

audio, video {
  display: inline-block;
}
img {
  border-style: none;
}
/* last 2 versions */




img {
  border-style: none;
}

Part 5. Isolation

Image: Eric Joiner

CSS is global

/* article.css */

.title {
  color: black;
}
/* popup.css */

.title {
  color: red;
}

Selector conflict

Every website should isolate CSS

Render in PHP, Ruby, etc?

BEM

Yes

No

CSS in separated file?

CSS Modules

Yes

No

CSS-in-JS

BEM only for rendering HTML on back-end

  1. Lower isolation level
  2. Easy to make mistake

CSS Modules is automatical BEM

decss-loader is sugar for CSS Modules

import { Popup } from './styles.css'

<Popup big={true}>
.Popup {
  width: 400px;
}

.Popup-big {
  width: 650px;
}

Styled Components syntax for CSS Modules

import { Popup } from './styles.css'
<Popup big={big}>
import s from './styles.css'
<div className={cx(s.popup, big && s.big)}

decss

default

Part 6. Other

Image: Eric Joiner

Linear gradient starts and finishs too “fast”

linear-gradient(white, black)
linear-gradient(white, ease-in-out, black)

Andreas Larsen

Compare

Linear

postcss-easing-gradients

Part 7. Linting

Image: Eric Joiner

Linter finds errors

.foo {
  margin-top: 20px;
  width: 100px;
  height: 100px;
  margin: 0 auto;
}

Stylelint: this overrides the longhand property before it app.css:5:3

Comparison

  • Prettier: spaces, formatting
  • Stylelint: errors

Stylelint — linter for any styles

Stylelint plugins: stylelint-a11y

*:focus {
  outline: none;
}
.button:hover {
  background: blue;
}

Stylelint: Expected that .button is used together with :focus pseudo-class app.css:4:1

Saves time for team lead

Senior

Junior

Linter

Linters are more kind when talking

Junior

Senior

Fixes

Junior

Linter

Fixes

Stylelint users

Spreading best practices by talk

Know a language

Have 30 minutes

Convinced

Remember

Remember after year

Popular shared configs for linters

eslint-config-airbnb

eslint-config-airbnb

eslint-plugin-react

eslint-plugin-jsx-a11y

Spreading best practices by shared config

Use shared config

Do not disable rule

Part 8. CSS-in-JS

Image: Eric Joiner

Modern CSS-in-JS !== inline styles


const Link = styled.a`
  color: blue;
  &:hover {
    color: green;
  }
`
<style>
.css-kjr4ir4i {
  color: blue;
}
.css-kjr4ir4i:hover {
  color: green;
}
</style>

Everything in a single file

Few other good features

  • Dynamic values
  • Themes

* In simple cases also can be done
  by CSS Custom Properties

Stylelint supports CSS-in-JS out-of-box

"scripts": {
  "lint:css": "stylelint src/*.js"
}

CSS-in-JS linters could be smarter

const Title = styled.h1`
  color: black;
`
export Component = () => (
  <Wrap>
    <Title></Title>
  </Wrap>
}

Tags

CSS

HTML

+

+

Styled Components compiles CSS in browser ☹️

15 kB

gzip, minified

+ parse CSS in browser

css-literal-loader

const Title = styled('h1')`
  color: black;
`
require('./index.js.css')
const Title = () => (
  <div className="css-vrj45454" />
)
/* index.js.css */
.css-vrj45454 {
  color: black;
}

Generates static CSS during deploy

css-literal-loader compatible with PostCSS

rules: [
  {
    test: /\.js$/,
    use: ['css-literal-loader']
  },
  {
    test: /\.css$/,
    use: ['postcss-loader']
  }
]

css-literal-loader and dynamic values

const Title = styled('h1')`
  color: var(--color);
`

<Title style={{ '--color': 'red' }}>

Part 9. Community

Image: Eric Joiner

Linter mentions (by Google)

ESLint

Stylelint

5 M

0,6 M

Custom plugins

ESLint

Stylelint

635

51

CSS community afraids JS

Is it because JS is too complex today?

“Software development

is about managing complexity”

2 types of complexity

In structure

In details

Complexity in details

 JS APIs in different browsers

Complexity in structure

jQuery

Bad example of structure complexity

React

SSR

Universal polyfills

webpack hacks

Same components for static landing and webapp

Rewrite components for landing

Good example of structure complexity

PostCSS

Autoprefixer

Code without prefixes

CSS vendor prefixes

Even design is going in structure complexity

Design language

Design system

UI kit

Many different buttons

Balance is better in JS than in CSS

JS

CSS

Structure complexity scales better

Structure

time

level

Details

time

Vendor prefixes

Structure

Details

Autoprefixer

Articles and talks

Best practices doesn’t spreading in CSS

Mistakes in test during hiring in Evil Martians

React

CSS

CSS Modules

CSS-in-JS

Only talks and articles do not work

Blaming users: 130 RTs

Spreading best practices by talk

Know a language

Have 30 minutes

Convinced

Remember

Remember after year

Structural complexity to fix the problem: 4 RTs

Spreading best practices by linter

Use shared config

Do not disable rule

JS is very complex too

[] == ''   // -> true
[] == 0    // -> true
[''] == '' // -> true
[0] == 0   // -> true
[0] == ''  // -> false
[''] == 0  // -> true

Language subsets hide complexity in JS

"use strict"

Compilers hide complexity in JS

/* @flow */

[] == ''
^ Cannot compare empty array literal [1] to string [2]

Linters hide complexity in JS

[] == ''
ESLint: 1:3: Expected '===' and instead saw '=='.

Warnings explain newbie mistakes

Scalability of different complexity types

Structure

Details

Part 10. Summary

Image: Eric Joiner

PostCSS way for project

Browserslist config

PostCSS way for project

Browserslist config

postcss-normalize

PostCSS way for project

Browserslist config

postcss-normalize
autoprefixer

PostCSS way for project

Browserslist config

postcss-normalize
autoprefixer

Components isolation

css-literal-loader

BEM

styled-components

Static

themes & dynamic

React

PostCSS way for project

Browserslist config

postcss-normalize
autoprefixer

Components isolation

Small extras

postcss-easing-gradients, postcss-utilities, rtlcss

PostCSS way for project

Browserslist config

postcss-normalize
autoprefixer

Components isolation

Small extras

Stylelint

PostCSS way for project

Browserslist config

postcss-normalize
autoprefixer

Components isolation

Small extras

Stylelint

 * Not a word about preprocessor

PostCSS way for community

Shared Stylelint config with best practices

PostCSS way for community

Shared Stylelint config with best practices

Balance complexity in structure and in details

PostCSS way for community

Shared Stylelint config with best practices

Balance complexity in structure and in details

Promote CSS tools

Good candidate for promotion

CSSTree

CSS parser

3 times faster than PostCSS

Roman Dvornov

Questions?