Kicking Sass

How to Write CSS (and JS)
in a PostCSS World

headshot

Aaron Ladage

  • UI Engineer
  • DEG, Kansas City
  • @aladage

We all fell in love with front-end code.

Some of us just don't like it that much anymore.

CSS is amazing!

CSS is amazing!

CSS is amazing?

CSS is amazing.

CSS is. Amazing.

CSS is amazing?

WTF, JS?

JS, WTF?

WTF, JS?

JS, WTF?
Pokemon

"CSS is not a real
programming language!"

"JavaScript is a toy language!"

They weren't wrong.

(But they're getting less right every day.)

Problems with CSS

  • Cross-browser compatibility issues
  • Vendor prefixes
  • No variables
  • No inline importing
  • No nested selectors
  • No functions/mixins
  • No color manipulation
  • No basic arithmetic

Problems with JavaScript

In the browser:

  • Messy syntax
  • Cross-brower compatibility issues
  • Difficult DOM traversal
  • Difficulty with effects/animation

Problems with JavaScript

Within the language itself:

  • No modularity or module-loading system
  • No interpolation/template strings
  • No fat arrow functions
  • No default function arguments
  • No class-based inheritance (I guess?)
  • No constants/confusing variable scope for beginners
  • No destructuring

Developers have been filling in the gaps in CSS and JavaScript for years.

Preprocessors became the solution.

…and a pretty good one at the time, too.

CSS:

JavaScript:

(Please don't hate me, CoffeeScripters)

Typical Preprocessor Workflow:

1

Typical Preprocessor Workflow:

2

Typical Preprocessor Workflow:

3

Typical Preprocessor Workflow:

4

Typical Preprocessor Workflow:

5

Coffeescript:

This:

for num in [1..10]  
  if num % 2 == 0
    console.log "#{num} is even"
  else
    console.log "#{num} is odd"

Compiles to this:

var num, _i;

for (num = _i = 1; _i <= 10; num = ++_i) {  
  if (num % 2 === 0) {
    console.log("" + num + " is even");
  } else {
    console.log("" + num + " is odd");
  }
}

On the JavaScript library front:

Querying the DOM for an element in vanilla JS…

var el = document.getElementById("myContainer");

…became this with jQuery:

var el = $("#myContainer");

Preprocessors
perpetuate a problem.

(Say that five times fast.)

We're putting more and more layers of
abstraction between the code we write
and the code the browser can read.

Preprocessors are their own programming languages

  • Proprietary syntax
  • Often written in "non-web" languages
  • "All or nothing" approach
  • Not as easily extensible
  • Must be compiled, today and forever
  • Browsers are catching up
  • Compile times can be slow
slowww

What's so bad about jQuery?

  • A tool for web developers, not web users.
  • DOM traversal isn't that hard with vanilla JS. No, really.
  • IE10+/Edge have eliminated most cross-browser JS compatibility issues.
  • CSS3 is better suited for transitions and animations.
  • Dependencies suck.
  • Performance matters.
  • Impress your friends with your library-less street cred!
ariel

Libraries Vs. Languages

What are new developers learning?

"The New Normal"

Reddit
reddit

"I thought L,M,N,O was a single letter, called elameno. Sort of like double-u."

reddit

"My dad once told me ponies were horses that lied, so they shrank. Made me not want to go on a pony ride. Didn't want to hang out with lying horses."

reddit

"My dad told me to make sure I fully dry off after a shower before walking to my room, otherwise I'd catch a draft. I swear he said 'catch a giraffe' so for weeks after, I could be caught running naked from the bathroom to my bedroom, dripping wet. Never caught that damn giraffe."

There's got to be a better way!

better way

Goodbye, preprocessors

Hello, postprocessors

Before you ask…

Yes, postprocessors are still preprocessors.

liar!

It's not about when you're processing.
It's about what you're processing.

"Transpiler" might be more accurate.

That's the preferred term on the JavaScript side.

CSS Postprocessor:

PostCSS

JS Transpilers:

Transpilers

Our preprocessor process:

5

Our new postprocessor process:

1

Our new postprocessor process:

2

Our new postprocessor process:

3

Our new postprocessor process:

4

Our new postprocessor process:

5

PostCSS advantages:

  • Write your CSS using CSS (what a concept!).
  • Use CSS3 with reckless abandon.
  • Hell, use CSS4. Who cares if that's not actually a thing?
  • Works with your existing task runners
  • Built on Node!
    • No more Ruby dependencies!
    • Easier to debug
    • Write your own plugins in nothing but JavaScript.
    • Faster compile times (40x faster than Ruby Sass / 4x faster than LibSass…allegedly).
  • Countless plugins

Unlike Sass, PostCSS is modular.

Sass gives you everything,
whether you need it or not.

buffet

PostCSS empowers you
to make your own choices.

chipotle

So, how do you get started?

It's possible you already have.

Autoprefixer

Autoprefixer

Autoprefixer

  • Popular replacement for Compass's vendor prefix mixins.
  • Automatically adds prefixes to CSS3 properties.
  • Define browser support via config file.
  • Cross-references caniuse.com database.
  • No need to memorize special function names!

Part of the much larger
PostCSS ecosystem:

PostCSS Universe postcss.parts

Partials/Globbing

Globbing

Partials/Globbing

Nested Selectors

Nested

Nested Selectors

Just like Sass, remember to
use nesting sparingly.

.just {
    .because {
        .you {
            .can {
                .doesnt {
                    .mean {
                        .you {
                            .should {
                            }
                        }
                    }
                }
            }
        }
    }
}

Variables

postcss-custom-properties: a great opportunity to start following the (HIDEOUS) CSS custom properties syntax:

:root {
    --color-blue: #0082c2;
}

.element {
    background-color: var(--color-blue);
}

Variables

  • Yes, it's ugly.
  • But it will soon be the standard, and allow for native component-scoped variables.
  • Already supported natively in Firefox, Chrome and Safari
  • Or, use postcss-simple-vars for Sass-style variables instead.

Color

  • Like variables, you can follow the proposed CSS color module spec…and it's also ugly.
  • Work in RGBA, hex, HSL, HSV or HWB.
  • Use postcss-color-function.

Color

color 1

Color

color 2

Color

color 3

Color

color 4

Color

color 5

Mixins/Extends

  • Sass wins this one.
  • Use postcss-mixins. For basic arithmetic, use postcss-calc.
  • Ask yourself: how complex are your mixins? Is that a good thing?

Turn off transpilers and our code "just works"

PostCSS Confi

…but let's not kid ourselves.

What about JavaScript?

EcmaScript 6 has been approved!!!

Remember this list?

  • No modularity or module-loading system
  • No interpolation/template strings
  • No fat arrow functions
  • No default function arguments
  • No classes
  • No constants
  • No destructuring

ES6 fixes most of these issues,
and we can use it today.

Transpilers

Write ES6 code like this:

import widgetBuilder from "modules/widgetBuilder";

const DEFAULT_TEXT = "This is some default text that won't change.";

let exampleFn = function() {
  
  let tmplStr = `This is amazing!
    it's a multiline string!
    Our default text is ${DEFAULT_TEXT}`
  
};

export default exampleFn;

Babel transpiles your code into this:

"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }

var _modulesWidgetBuilder = require("modules/widgetBuilder");

var _modulesWidgetBuilder2 = _interopRequireDefault(_modulesWidgetBuilder);

var DEFAULT_TEXT = "This is some default text that won't change.";

var exampleFn = function exampleFn() {

  var tmplStr = "This is amazing!\n    it's a multiline string!\n    Our default text is " + DEFAULT_TEXT;
};

exports["default"] = exampleFn;
module.exports = exports["default"];

JavaScript modules are now a reality.

Unfortunately, native JS module loading is not.

JS loaders:

loaders

Common, AMD, System, UMD

System.js:

  • Built on top of the ES6 Module Loader polyfill.
  • ~15KB minified and gzipped, runs in IE8+ and NodeJS.
  • Loads practically any module format.
  • Works flawlessly with its companion project, JSPM

JSPM:

  • Package manager for System.js
  • Install from any registry, including NPM and GitHub
  • Takes care of module versioning and dependency management for you.
  • Latest version can handle tree shaking
  • During development, transpiles in the browser at runtime.
  • For production, outputs transpiled bundles.

jspm install registry:account/repo

github.com/jspm/registry

github.com/degjs

github.com/degdigital/skeletor

skull

No cease and desist from Mattel…yet.

To recap:

  • Preprocessors saved us from limited languages and broken browsers
  • Those days are (mostly) behind us
  • Modular postprocessors like PostCSS let us write real, bleeding-edge CSS without compromising browser support
  • Transpilers like Babel and module loaders like SystemJS let us write more succinct, readable and reusable code

It's time to fall in love with
front-end code all over again.

Thanks for listening.

colbert