Exploring New Possibilities with 
PostCSS

David Clark - @davidtheclark

Every frontend developer should learn about PostCSS

(°ロ°)☝

Questions this talk should answer:

  • What is PostCSS, really?
  • Why is PostCSS important?
  • What kinds of tools are people building with PostCSS?

Feelings this talk should foster:

  • Curiosity
  • Excitement
  • Eagerness
  • Compulsion

Does all the PostCSS hype annoy you?

(ง •̀_•́)ง

"Sass is enough ..."

"It's probably just an unnecessary toy ..."

CSS presents problems: PostCSS helps us solve them. That's not unnecessary.

Sass presents one kind of answer to one subset of CSS problems. That's not enough.

(ง •̀_•́)ง

(ง •̀_•́)ง

PostCSS is important enough to be worth it.
It can change the way you work with CSS.

(ง •̀_•́)ง

"But I'm so tired of learning ..."

What is PostCSS?

PostCSS is ...

➽ A tool for making CSS tools with JS

ᕙ༼ຈل͜ຈ༽ᕗ

Want to build a CSS tool?
You'll probably need to ...

  • parse CSS
  • perform transforms
  • create sourcemaps     (ಥ_ಥ)
  • handle pre-existing sourcemaps
  • register errors and warnings with source positions
  • give up and do something else     ¯\(°_o)/¯

POSTCSS HANDLES ALL THESE THINGS!
YOU FOCUS ON YOUR PROBLEM!

  • parse CSS
  • perform transforms
  • create sourcemaps     (◕‿◕)
  • handle pre-existing sourcemaps
  • register errors and warnings with source positions
  • give up and do something else     ¯\(°_o)/¯

A simple (hypothetical) problem: CSS adds a size property not yet supported by browsers.

/* before */
.foo {
  size: 20px 10px;
}

/* after */
.foo {
  width: 20px;
  height: 10px;
}
  • size in other properties: background-size, font-size, &c.?
  • size in values: keyframe animation name, function name, &c.?
  • size with whitespace(s) before the colon?
  • size in selectors ... maybe even followed by a colon, e.g. .heading-full-size:first-child?
  • size: 20px 10px in comments?
  • Generate a sourcemap?                              
  • Modify an existing sourcemap?                
  • Inform the user of syntax errors, providing line and column positions in the original source?

\_(o_0)_/

Use a regular expression to find size?

??
\_(o_0)_/

A simple (hypothetical) problem: CSS adds a size property not yet supported by browsers.

➽ Solution: use PostCSS.

 

... and all those problems are solved for you.

A simple (hypothetical) problem: CSS adds a size property not yet supported by browsers.

(this exists: postcss-size)

/* Walk all declaration nodes with size properties */
css.walkDecls(function (declaration) {
  if (declaration.prop !== 'size') return;
  
  var sizes = postcss.list.space(declaration.value);

  var width = sizes[0]
  var height = sizes[1] || sizes[0];
  
  /* Create new nodes */
  declaration.cloneBefore({ 
    prop: 'width',  
    value: width
  });
  declaration.cloneBefore({ 
    prop: 'height', 
    value: height
  });

  /* Remove the size node */
  declaration.remove();
});

(adapted from postcss-size)

Rough overview of how PostCSS works ...

source stylesheet

tree of nodes

plugins

CSS

Parse

/* Root */

.foo { /* Rule */
  color: pink; /* Declaration */
}

/* AtRule */
@media (min-width: 10px) { 
  .foo { color: orange; }
}

Parse

  • AtRule
    • name, e.g. media
    • params, e.g. (min-width: 10px)
  • Rule
    • selector, e.g. .foo > .bar
  • Declaration
    • prop, e.g. color
    • value, e.g. #e6e6e6

source stylesheet

tree of nodes

plugins

CSS

Rough overview of how PostCSS works ...

Read & Transform (Plugins)

root.walkRules()

rule.walkDecls()

decl.insertBefore()

atRule.clone()

comment.remove()
// and so on ...

source stylesheet

tree of nodes

plugins

CSS

Rough overview of how PostCSS works ...

Stringify (Generate)

Make CSS to feed the browser.

 

  • Formatting preserved
  • Sourcemap created

source stylesheet

tree of nodes

plugins

CSS

Rough overview of how PostCSS works ...

PostCSS is not ...

  • Another either/or
  • It offers the rare yes/and

ᕕ( ᐛ )ᕗ

Sass or Less?

Browserify or Webpack?

Gulp or Grunt?

React or Angular or Ember?

༼ ºººººل͟ººººº ༽

PostCSS is not ...

"An alternative to Sass & Less, but faster and with future-friendly syntax."

PostCSS is not ...

"An alternative to Sass & Less, but faster and with future-friendly syntax."

?

?

?

PostCSS is not ...

"A postprocessor (vs. preprocessors)."

PostCSS is not ...

"A postprocessor (vs. preprocessors)."

?

?

PostCSS is not ...

"A postprocessor (vs. preprocessors)."

standard CSS => CSS with fallbacks

 

(e.g. Autoprefixer)

non-standard CSS => standard CSS

 

(e.g. Sass or ...)

"PostCSS" is also ...

The ecosystem of 
PostCSS-powered tools

➽ 

PostCSS is ...

➽ A tool for making CSS tools with JS

How to pick tools?

The old way: choose a preprocessor and a library or two

With PostCSS:

  • Pick and choose
  • Use a pack
  • Do both
  • Build your own solution

Fallback Generators

The old way: processor-specific libraries

@include transform(translateY(50px));
/*  -webkit-transform: translateY(50px);
    -moz-transform: translateY(50px);
    ...
    transform: translateY(50px); */

@include rempx(margin, 1rem 2rem);
/*  margin: 16px 32px;
    margin: 1rem 2rem; */

.flex-grow(1); /* less hat */
/*  -webkit-flex-grow: 1;
    flex-grow: 1; */

Fallback Generators

transform: translateY(50px);
/*  -webkit-transform: translateY(50px);
    -moz-transform: translateY(50px);
    ...
    transform: translateY(50px); */

margin: 1rem 2rem;
/*  margin: 16px 32px;
    margin: 1rem 2rem; */

flex-grow: 1;
/*  -webkit-flex-grow: 1;
    flex-grow: 1; */

With PostCSS: just write standard CSS

Fallback Generators

With PostCSS: just write standard CSS

Fallback Generators

With PostCSS: just write standard CSS

 

Mr. Snook pointed out the wonders of 

You can use it now with this PostCSS plugin!

{ all: initial; }

Fallback Generators

With PostCSS: just write standard CSS

Mr. Snook also noted the wonders of 

container queries

You can use it now with this PostCSS plugin!

.element:container(width >= 100px)

All CSS fallback-generating tools should be written as PostCSS plugins that simply compile standard CSS.

(°ロ°)☝

Fallback Generators

.absolute(10px 0 0 20px);
/*  position: absolute;
    top: 10px;
    right: 0; left: 0;
    bottom: 20px; */

transition-timing-function: $ease-in-circ;
/*  ... cubic-bezier(0.6, 0.04, 0.98, 0.335); */

@include span(1 of 4);
/*  width: 21.05263%;
    float: left;
    margin-right: 5.26316%; */

Utilities

The old way: preprocessor-specific libraries using preprocessor-specific constructs

absolute: 10px 0 0 20px;
/*  position: absolute;
    top: 10px;
    right: 0; left: 0;
    bottom: 20px; */

transition-timing-function: ease-in-circ;
/*  ... cubic-bezier(0.6, 0.04, 0.98, 0.335); */

lost-column: 1/4;
/*  width: calc(99.99% * 1/4 - (30px - 30px * 1/4));
    float: left;
    margin-right: 30px;
    clear: none;
    ... */

Utilities

The new way: preprocessor-agnostic libraries using standard CSS constructs

┌( ಠ_ಠ)┘

<aside>

Are you upset by non-standard
properties and keywords?

┌( ಠ_ಠ)┘

  • Confusing
  • How to know?
  • Mixins are "explicit"
  • Sass! Sass Sass!
  • If you don't know your toolset, nothing will save you.
  • One plugin ≠ "PostCSS"
  • "PostCSS can be used to make things I don't like" — that's not really a problem.

<aside>

Is that a valid criticism of PostCSS?

<aside>

Or is it a solvable problem?

@context cssnext {
  .css-example { color: gray(255, 50%); }
}

</aside>

@use autoprefixer(browsers: ['last 2 versions']);

:fullscreen a {
  display: flex
}
@mixin intrinsic-ratio-parent($extend: null) {
  $extend: if($extend != null, $extend, 
    toolkit-get('intrinsic ratio extend'));
  @if $extend {
    @include dynamic-extend('intrinsic ratio parent') {
      @include intrinsic-ratio-parent(false);
    }
  }
  @else {...}
}

The old way: treat extended CSS like a (barely adequate) programming language

Utilities

With PostCSS:
USE JAVASCRIPT

  • the capable programming language you already know
  • Node.js! npm!     (っ◕‿◕)っ

Utilities

"...all my libraries will be written in PostCSS from now on and you should do the same..."

- Cory Simmons, creator of the 
(PostCSS-powered) Lost Grid system

See his full presentation at 
https://github.com/corysimmons/presentations

Utilities

Use standard CSS language constructs like properties, keywords, at-rules, and functions in PostCSS-powered utilities that will work within any Node-capable build process.

(°ロ°)☝

Utilities

The old way: use what your processor-of-choice offers

@for $i from 1 through 3 {
  .item-#{$i} { width: 2em * $i; }
}
.foo { 
  @extend .bar; 
  @include baz;
}
.box-shadow(@style, @c) when (iscolor(@c)) {
  box-shadow: @style @c;
}
.foo { 
  &:extend(.bar); 
  .baz();
}

Non-Standard Syntax

With PostCSS: pick and choose language extensions 

 

  • For your preferences
  • For your project
  • For your team

Non-Standard Syntax

Many plugins and packs are based on W3C spec drafts ...

Non-Standard Syntax

:root { --foo: 20px; }
.bar { 
  margin-left: var(--foo); 
  @nest &:hover { margin-left: 0; }
}
@custom-media --viewport-medium (width <= 50rem);
@custom-selector :--heading h1, h2, h3, h4, h5, h6;
a:hover { color: color(red alpha(-10%)); }

Non-Standard Syntax

Many plugins and packs are based on W3C spec drafts ...

Non-Standard Syntax

Warning: Not like ES2015! 
— not ratified, not stable, might not be implemented.

They are at varying stages of the standards process.

Many plugins and packs are based on W3C spec drafts ...

Other plugins offer Sass-, Less-, Stylus-, and even Rust-like syntax ...

Non-Standard Syntax

@define-mixin bar { color: pink; }
$foo: 20px;
.bar { 
  @mixin bar;
  width: calc($foo + 7px);
  height: @width;
}

@for $i from 1 to 3 {
  .b-$i { width: $(i)px; }
}

Other plugins offer Sass-, Less-, Stylus-, and even Rust-like syntax ...

Non-Standard Syntax

$animal: bear;
.zoo {
  @match $animal {
    snake => { color: green; },
    buffalo | bear => { background: brown; },
    lion => { font-weight: bold; },
    _ => { color: gray; }
  }
}

Non-Standard Syntax

Other plugins offer Sass-, Less-, Stylus-, and even Rust-like syntax ...

Love your preprocessor's syntax? Use it!

  • x => preprocessor => PostCSS => CSS
  • x => PostCSS w/ parser => preprocessor => CSS
  • Reproduce it with a plugin!
  • Divide the tasks: Use preprocessor for transforms, PostCSS for analysis

Non-Standard Syntax

Analytical Tools

"... building plugins for PostCSS is so damn easy and fun." (Brian Holt)

Analytical tools help us mechanically improve the quality of our CSS, instead of relying on good intentions and discipline.

(°ロ°)☝

Analytical Tools

Enforce BEM-style selector conventions.

/** @define Card */
.Card {..}
.Card.is-disabled {..}
.Card-title {..}
.Card-title--small {..}
.Card-body {..}
.list-item {..} /* ERROR! */
.Card ul > li {..} /* ERROR! */
.Card--big--closed {..} /* ERROR! */

Don't rely on discipline!

Enforce ~100 rules to keep your CSS clean, legible, consistent, intentional

Don't nitpick:

auto-enforce!

Mr Larry Davis spoke early about

(╯°□°)╯

Other Important Tools Powered-By PostCSS: 

(☞゚∀゚)☞

The old approach to CSS problems:

  • complain
  • strive eternally for discipline
  • ignore problems
  • build an awesome but super complex and isolated tool (e.g. Sass & Less & Stylus, CSSLint, CSScomb, -prefix-free)
  • pick sides instead of features

IN SUM

The PostCSS approach to CSS problems:

  • never shy from addressing a pain point
  • experiment with new features and syntax
  • try to build mechanical solutions
  • pick and choose features
  • share a low-level parser, node tree, and stringifier (PostCSS) with other tools
  • get better at writing JavaScript and Node.js

IN SUM

Thank you!

@davidtheclark

davidtheclark.com

Exploring New Possibilities with PostCSS

By David Clark

Exploring New Possibilities with PostCSS

  • 2,864