David Clark - @davidtheclark
Every frontend developer should learn about PostCSS
(°ロ°)☝
Questions this talk should answer:
Feelings this talk should foster:
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?
➽ A tool for making CSS tools with JS
ᕙ༼ຈل͜ຈ༽ᕗ
Want to build a CSS tool?
You'll probably need to ...
POSTCSS HANDLES ALL THESE THINGS!
YOU FOCUS ON YOUR PROBLEM!
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;
}
\_(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
/* Root */
.foo { /* Rule */
color: pink; /* Declaration */
}
/* AtRule */
@media (min-width: 10px) {
.foo { color: orange; }
}
Learn terminology at http://apps.workflower.fi/vocabs/css/en
source stylesheet
tree of nodes
plugins
CSS
Rough overview of how PostCSS works ...
Learn terminology at http://apps.workflower.fi/vocabs/css/en
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 ...
Make CSS to feed the browser.
source stylesheet
tree of nodes
plugins
CSS
Rough overview of how PostCSS works ...
ᕕ( ᐛ )ᕗ
Sass or Less?
Browserify or Webpack?
Gulp or Grunt?
React or Angular or Ember?
༼ ºººººل͟ººººº ༽
"An alternative to Sass & Less, but faster and with future-friendly syntax."
"An alternative to Sass & Less, but faster and with future-friendly syntax."
?
?
?
"A postprocessor (vs. preprocessors)."
"A postprocessor (vs. preprocessors)."
?
?
"A postprocessor (vs. preprocessors)."
standard CSS => CSS with fallbacks
(e.g. Autoprefixer)
non-standard CSS => standard CSS
(e.g. Sass or ...)
The ecosystem of
PostCSS-powered tools
➽
➽ A tool for making CSS tools with JS
The old way: choose a preprocessor and a library or two
With PostCSS:
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; */
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
With PostCSS: just write standard CSS
With PostCSS: just write standard CSS
You can use it now with this PostCSS plugin!
{ all: initial; }
With PostCSS: just write standard CSS
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.
(°ロ°)☝
.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%; */
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;
... */
The new way: preprocessor-agnostic libraries using standard CSS constructs
┌( ಠ_ಠ)┘
<aside>
Are you upset by non-standard
properties and keywords?
┌( ಠ_ಠ)┘
<aside>
Is that a valid criticism of PostCSS?
Mechanical solutions:
postcss-plugin-context and postcss-use
<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
With PostCSS:
USE JAVASCRIPT
"...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
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.
(°ロ°)☝
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();
}
With PostCSS: pick and choose language extensions
Many plugins and packs are based on W3C spec drafts ...
: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%)); }
Many plugins and packs are based on W3C spec drafts ...
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 ...
@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 ...
$animal: bear;
.zoo {
@match $animal {
snake => { color: green; },
buffalo | bear => { background: brown; },
lion => { font-weight: bold; },
_ => { color: gray; }
}
}
Other plugins offer Sass-, Less-, Stylus-, and even Rust-like syntax ...
Love your preprocessor's syntax? Use it!
"... 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.
(°ロ°)☝
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
(╯°□°)╯
(☞゚∀゚)☞
The old approach to CSS problems:
IN SUM
The PostCSS approach to CSS problems:
IN SUM
@davidtheclark
davidtheclark.com