Lock in $30 Savings on PRO—Offer Ends Soon! ⏳
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
PostCSS: Build your own CSS processor
Search
Masaaki Morishita
November 13, 2016
Technology
6
4.7k
PostCSS: Build your own CSS processor
at NodeFest 2016.
Masaaki Morishita
November 13, 2016
Tweet
Share
More Decks by Masaaki Morishita
See All by Masaaki Morishita
Houdini: Abracadabra CSS
morishitter
1
840
Modern CSS: architecture, future specs and build flow
morishitter
13
2.7k
PostCSS and cssnext
morishitter
11
1.8k
PostCSS 5.0: for Custom CSS Preprocessing
morishitter
10
1.1k
PostCSS for beginners
morishitter
6
1.5k
CSSfmt
morishitter
2
230
Introduction to CSS Architecture
morishitter
3
390
Introduction to PostCSS
morishitter
6
1.7k
Yet Another CSS Preprocessor
morishitter
1
5.5k
Other Decks in Technology
See All in Technology
Kiro Autonomous AgentとKiro Powers の紹介 / kiro-autonomous-agent-and-powers
tomoki10
0
520
Lessons from Migrating to OpenSearch: Shard Design, Log Ingestion, and UI Decisions
sansantech
PRO
1
130
AI-DLCを現場にインストールしてみた:プロトタイプ開発で分かったこと・やめたこと
recruitengineers
PRO
2
150
AI 駆動開発勉強会 フロントエンド支部 #1 w/あずもば
1ftseabass
PRO
0
390
非CUDAの悲哀 〜Claude Code と挑んだ image to 3D “Hunyuan3D”を EVO-X2(Ryzen AI Max+395)で動作させるチャレンジ〜
hawkymisc
2
190
AWS Security Agentの紹介/introducing-aws-security-agent
tomoki10
0
290
生成AI活用の型ハンズオン〜顧客課題起点で設計する7つのステップ
yushin_n
0
220
業務のトイルをバスターせよ 〜AI時代の生存戦略〜
staka121
PRO
2
210
年間40件以上の登壇を続けて見えた「本当の発信力」/ 20251213 Masaki Okuda
shift_evolve
PRO
1
140
Kubernetes Multi-tenancy: Principles and Practices for Large Scale Internal Platforms
hhiroshell
0
120
GitHub Copilotを使いこなす 実例に学ぶAIコーディング活用術
74th
3
3.3k
Haskell を武器にして挑む競技プログラミング ─ 操作的思考から意味モデル思考へ
naoya
6
1.5k
Featured
See All Featured
The Art of Programming - Codeland 2020
erikaheidi
56
14k
How To Stay Up To Date on Web Technology
chriscoyier
791
250k
Documentation Writing (for coders)
carmenintech
76
5.2k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.5k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
54k
KATA
mclloyd
PRO
33
15k
It's Worth the Effort
3n
187
29k
Designing for humans not robots
tammielis
254
26k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
The Power of CSS Pseudo Elements
geoffreycrofte
80
6.1k
Large-scale JavaScript Application Architecture
addyosmani
515
110k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
9
1k
Transcript
PostCSS: Build your own CSS processor morishi'er @ #nodefest
ࣗݾհ .about-me { name: Masaaki Morishita; twitter: @morishitter_; github: morishitter;
qiita: morishitter; specializing-in: CSS; member-of: 'PostCSS Team'; works-at: 'Increments, inc.'; }
Increments We are hiring! h"ps:/ /github.com/increments/job-descrip6ons
Do you use PostCSS?
Agenda 1. PostCSSͱ 2. PostCSSπʔϧͷհ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI
Agenda 1. PostCSSͱ 2. PostCSSπʔϧͷհ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI
PostCSSͱ • NodeͷCSSύʔαʔ • CSSπʔϧΛ࡞ΔͨΊͷϑϨʔϜϫʔΫ • PostCSSπʔϧͷ࣮ߦ • by Andrey
Sitnik
over 12K stars on GitHub !
4 million DL / month on npm
PostCSSͱ • ͘ΘΕΔΑ͏ʹͳͬͨ • CodePenͰ͑Δ • Bootstrap v5PostCSSʹͳΔ͔ʁ • ར༻اۀɾαʔϏε
• Google, Facebook, GitHub, Wikipedia, Qiita, etc...
PostCSSͷॲཧͷྲྀΕ • PostCSSࣗମͨͩͷύʔαʔ • ASTΛૢ࡞͢ΔͨΊͷAPIΛఏڙ • ϓϥάΠϯ͕ASTΛม͢Δ
None
PostCSSͷΑ͏ͳπʔϧੲ͔Β͋ͬͨ
rework reworkcss / rework
Modular CSS preprocessing with rework by TJ Holowaychuk h"p:/ /tjholowaychuk.tumblr.com/post/
44267035203/modular-css-preprocessing-with- rework
Custom CSS preprocessing by Nicolas Gallagher h"p:/ /nicolasgallagher.com/custom-css- preprocessing/
rework (in 2013) • NodeͷCSSύʔαʔ • ϓϥάΠϯΞʔΩςΫνϟʔ • Andrey͕rework-vendorsͱ͍͏ϓϥάΠϯΛ࡞ •
ޙͷAutoprefixer • ʢҰํͦͷࠒ morishi:erYACPΛ։ൃʣ
rework (in 2013) • AutoprefixerΛ࡞Δ্Ͱɺreworkػೳෆ • ࣌reworkϒϥβϋοΫΛύʔεͰ͖ͳ͔ͬͨ • ʢ͋ͱTJʹAutoprefixerΛdisΒΕͨΓ…ʣ •
AndreyʮΑࣗ͠Ͱ࡞Ζ͏ʂʯ
None
PostCSSʢʙ v1ʣ • 2013/11/4ʹ࠷ॳͷϦϦʔε • "PostCSS is a framework for
CSS postprocessors" • AutoprefixerͷͨΊʹ࡞ΒΕͨ • ͜ͷࠒCoffeeScriptͰॻ͔Εͯͨ
PostCSS (v2 ʙ v4) • ϓϥάΠϯ͕େྔʹ࡞ΒΕ͍ͯ͘ • ݴޠ֦ுͷͨΊͷϓϥάΠϯ࡞ΒΕ͍ͯ͘ • cssnext,
PreCSS, AtCSS • ʮpostprocessorʯͱ͍͏୯ޠΛΘͳ͘ͳͬͨ • ʢAPI͕͜Ζ͜ΖมΘͬͨ…ʣ
PostCSS v5 (current) released on August 20, 2015
PostCSS v5 • ࠓ·ͰͰҰ൪େ͖͍มߋ • Custom Syntaxes • ύʔαʔ෦ΛΧελϚΠζͰ͖ΔΑ͏ʹʢ͋ͱͰઆ໌ʣ •
2016ݱࡏɺ͘ΘΕΔΑ͏ʹ • ʢreworkશʹΦϫίϯʹʣ
1. PostCSSͱ • PostCSSϓϥάΠϯΞʔΩςΫνϟʔͳCSSύʔαʔ • ASTૢ࡞ͷͨΊͷAPIΛఏڙ • ϓϥάΠϯΛॻ͘͜ͱͰCSSʹಠࣗͷॲཧΛ͓͜ͳ͏ • PostCSSͷྺ࢙
• PostCSSrework͕ݩʹͳ͍ͬͯΔ
Agenda 1. PostCSSͱ 2. PostCSSπʔϧͷհ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI
2. PostCSSπʔϧͷհ • Autoprefixer • Stylelint • CSS Modules
Autoprefixer • postcss / autoprefixer • ʮCan I Useʯͱ͍͏αΠτͷσʔλΛ ݩʹࣗಈͰϕϯμʔϓϦϑΟοΫεΛ
༩͢Δπʔϧ
/* input */ .example { display: flex; user-select: none; -webkit-border-radius:
3px; border-radius: 3px; } /* output */ .example { display: -ms-flexbox; display: flex; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; border-radius: 3px; }
AutoprefixerͷΦϓγϣϯ • αϙʔτϒϥβࢦఆ (ai / browserslist) autoprefixer({ browsers: [ "ie
>= 11", "last 2 Edge versions", "last 2 Firefox versions", "last 2 Chrome versions", "last 2 Safari versions", "last 2 Opera versions", "last 2 iOS versions", "last 2 ChromeAndroid versions" ] })
ྨࣅπʔϧͱͷύϑΥʔϚϯεൺֱ Autoprefixer: 44 ms Stylecow: 200 ms (4.5 times slower)
nib: 340 ms (7.7 times slower) Compass: 2417 ms (54.9 times slower) h"ps:/ /github.com/postcss/benchmark#prefixers
Stylelint • stylelint / stylelint • ϞμϯͳCSSϦϯλʔ • ΊͪΌͪ͘ΌΧελϚΠζͰ͖Δ •
SCSS, Less, CSSͷ৽͍͠ه๏ʹ ͑Δ
Stylelint • ϧʔϧ170ݸ • CSSLint: 38, scss-lint: 62, CSSComb: 26
• શͯͷϧʔϧPostCSSϓϥάΠϯͱ࣮ͯ͠ • ಠࣗϧʔϧ࡞ΕΔ
in Facebook "Improving CSS quality at Facebook and beyond" h"ps:/
/code.facebook.com/posts/ 879890885467584/improving-css-quality-at- facebook-and-beyond/
StylelintͷઃఆϑΝΠϧ { "rules": { "at-rule-no-vendor-prefix": true, "block-no-empty": true, "color-hex-case": "lower",
"color-hex-length": "short", "color-no-invalid-hex": true, "comment-no-empty": true, "custom-property-no-outside-root": true, "declaration-block-no-duplicate-properties": true, "declaration-block-no-ignored-properties": true, "declaration-block-no-shorthand-property-overrides": true, "declaration-no-important": true, "selector-descendant-combinator-no-non-space": true, "selector-max-empty-lines": 0, "selector-max-specificity": "0,5,0", "selector-no-vendor-prefix": true, ...
ઃఆϑΝΠϧूͱΧελϚΠζ • stylelint-config-* • standard, suitcss, primer, qiita, etc... {
"extends": "stylelint-config-primer", "rules": { "selector-max-specificity": "0,6,0" } }
StylelintͷઃఆϑΝΠϧ • ϑΥʔϚοτʹؔ͢Δͷ͕େ • color-hex-case • length-zero-no-unit • declaration-block-semicolon-newline-after •
block-closing-brace-empty-line-before • "ܯࠂ"Ͱͳ͘ɺࣗಈमਖ਼ͯ͠΄͍͠
Stylefmt • morishi(er / stylefmt • StylelintͷઃఆϑΝΠϧΛݩʹࣗಈͰ ϑΥʔϚοτ • શͯͷϧʔϧʹରԠͰ͖͍ͯͳ͍
stylefmt // input @mixin clearfix () { &::before, &::after{content:" ";display
: table; } &::after {clear: both;}} // output @mixin clearfix () { &::before, &::after { content: " "; display: table; } &::after { clear: both; } }
CSS Modules • css-modules / css-modules • CSSͷϞδϡʔϧԽͷΞϓϩʔνͷ1ͭ • άϩʔόϧείʔϓΛղܾ͢Δ
ͨΊͷͷ
CSS in JS • ʮReact: CSS in JSʯby Christopher Chedeau
render() { const style = { display: 'inline-block', padding: '8px 12px', backgroundColor: '#0099ff', color: '#fff', fontSize: '14px', textAlign: 'center' }; return ( <button style={style}>ૹ৴</button> ); } h"ps:/ /speakerdeck.com/vjeux/react-css-in-js
CSS in JSܥπʔϧઓ૪ • Radium, react-style, styling, etc... • CSS
ModulesνʔϜʮCSSCSSϑΝΠϧʹॻ͖͍ͨʯ
CSS Modules • webpackΛ͏͜ͱ͕લఏ • css-loader, style-loader, (postcss-loader) • σϑΥϧτͰϩʔΧϧείʔϓ
• ηϨΫλ໊ΛʹϋογϡΛ͚ͯিಥΛճආ • composes ͰଞͷίϯϙʔωϯτͷελΠϧΛ࠶ར༻ • ͜ΕΒͷػೳΛPostCSSͰ࣮
// SubmitButton.js import style from './SubmitButton.css'; export default class SubmitButton
extends React.Component { render() { return ( <button className={style.default}>ૹ৴</button>; ); } }; /* SubmitButton.css */ .button { display: inline-block; padding: 6px 12px; font-size: 14px; } .default { composes: button; /* other properties */ }
PostCSS.parts h"p:/ /postcss.parts
2. PostCSSπʔϧͷհ • Autoprefixer • ϕϯμʔϓϦϑΟοΫεͷࣗಈ༩ • Stylelint • ESLintͷΑ͏ʹΧελϚΠζੑͷߴ͍CSSϦϯλʔ
• CSS Modules • CSSʹϩʔΧϧείʔϓΛ
Agenda 1. PostCSSͱ 2. PostCSSπʔϧͷհ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI
ϓϦϓϩηοαʔͱͯ͠ͷCSS • ݴޠ֦ு༻ϓϥάΠϯ • cssnext, precss, atcss, etc
cssnext • MoOx / postcss-cssnext • ະདྷͷCSSͷߏจΛࠓͷϒϥβ͕ղ ऍͰ͖ΔΑ͏ʹτϥϯεύΠϧ͢Δ PostCSSϓϥάΠϯू
Custom Proper,es input: :root { --fontSize-m: 18px; } p {
font-size: var(--fontSize-m); } output: p { font-size: 18px; }
Custom media queries input: @custom-media --small-viewport (max-width: 600px); @media (--small-viewport)
{ /* styles for small viewport */ } output: @media (max-width: 600px) { /* styles for small viewport */ }
Custom selectors input: @custom-selector :--heading h1, h2, h3; article :--heading
{ margin-bottom: 2em; } output: article h1, article h2, article h3 { margin-bottom: 2em; }
@apply input: :root { --truncate: { white-space: nowrap; overflow: hidden;
text-overflow: ellipsis; } } p { @apply --truncate; } output: p { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@extend input: .error { color: red; } .serious-error { @extend
.error; } output: .error, .serious-error { color: red; }
ϓϦϓϩηοαʔͱͯ͠PostCSSΛ͏ϝϦοτ • ඞཁͳػೳ͚ͩΛબͰ͖Δ • ΧελϚΠζ͕༰қ • ύϑΥʔϚϯε
ඞཁͳػೳ͚ͩΛબ • ʮSassͬͯΔ͚Ͳ@importͱม͚ͩ͋Ε͍͍Θʔʯ • ʮmixinͱ͔extendཧͰ͖ͳ͍͔Βͬͯͳ͍ʯ • PostCSSͳΒඞཁͳϓϥάΠϯ͚ͩΛՃͰ͖Δ
ΧελϚΠζ͕༰қ • 1ͭ1ͭͷϓϥάΠϯখ͍͞ͷͰίϯτϦϏϡʔτ͍͢͠ • ϓϥάΠϯΛॻ͘͜ͱͰಠࣗͷΧελϚΠζ͕Մೳ
ύϑΥʔϚϯε cssnext: 45 ms PreCSS: 64 ms (1.4 times slower)
node-sass: 65 ms (1.4 times slower) Rework: 83 ms (1.8 times slower) Less: 146 ms (3.2 times slower) Stylus: 219 ms (4.7 times slower) Ruby Sass: 1207 ms (26.2 times slower) h"ps:/ /github.com/postcss/benchmark#preprocessors
ϓϦϓϩηοαʔͱͯ͠PostCSSΛ͏ͱ͖ͷҙ • cssnext • ະདྷͷCSSΛઌऔΓ Ͱͳ͍ • ϙϦϑΟϧ Ͱͳ͍ •
γϯλοΫεͷॻ͖৺Θ͔Δ • PreCSS • node-sass͓ʁ
ϓϦϓϩηοαʔͱͯ͠PostCSSΛ͏ͱ͖ͷҙ • ϓϥάΠϯΛબΜͰ͏ͱ͍͍ • ͦͷϓϥάΠϯʹԿ͔͋ͬͨͱ͖ͳ͓֮͢ޛ • ϓϥάΠϯಉ࢜ʹґଘ͕ؔ͋Δ • ʮpostcss-mixinspostcss-nestedΑΓઌʹಡΈࠐΈ·͠ΐ ͏ʯ
3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS • cssnext: ະདྷͷߏจΛ͑ΔCSSϓϦϓϩηοαʔ • ϝϦοτ • ඞཁͳػೳ͚ͩɺΧελϚΠζੑɺύϑΥʔϚϯε •
ҙ • cssnextϙϦϑΟϧ͡Όͳ͍ • ϓϥάΠϯબͷқ͕ߴ͍
Agenda 1. PostCSSͱ 2. PostCSSπʔϧͷհ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI
PostCSSͷAPI • ASTͷߏ • ϓϥάΠϯΛ࡞ΔͨΊͷ͍͔ͭ͘ͷAPIΛհ • Custom Syntaxes h"p:/ /api.postcss.org/
͜͏͍͏มΛ͢ΔϓϥάΠϯΛॻ͍ͯΈΔ /* input */ .foo { font-size: 12px; } .bar
{ @ref .foo, font-size; color: #333; } /* expected */ .foo { font-size: 12px; } .bar { font-size: 12px; color: #333; }
ASTͷϊʔυʢҰ෦ൈਮʣ Root { type: 'root', nodes: [ Rule { type:
'rule', nodes: [ Declaration { type: 'decl', prop: 'font-size', value: '12px' } ], selector: '.foo' }, Rule { type: 'rule', nodes: [ AtRule { type: 'atrule', name: 'ref', params: '.foo, font-size' }, Declaration { type: 'decl', prop: 'color', value: '#333' } ], selector: '.bar' } ] }
ASTͷϊʔυ
ASTૢ࡞ͷAPI • postcss.plugin() • container.walk(Rule|AtRule|Decls)() • container.insert(Before|After)() • container.remove()
postcss.plugin(name, initializer) /* * @param {string} name * @param {function}
initializer * @return {Plugin} PostCSS plugin */
postcss.plugin(name, initializer) module.exports = postcss.plugin('postcss-ref', function (opts) { opts =
opts || {}; return function (root) { // ASTΛม͢Δॲཧ }; });
container.walkAtRule([name], callback) /* * @param {string|RegExp} [name] * @param {childIterator}
callback * @return {false|undefined} */ • Container: ϊʔυʢࢠϊʔυΛ࣋ͭ͜ͱ͕Ͱ͖ΔʣΛੜ ͢ΔΫϥε
container.walkAtRule([name], callback) module.exports = postcss.plugin('postcss-ref', function (opts) { opts =
opts || {}; return function (root) { root.walkAtRules('ref', function (atrule) { // `@ref`ͷAtRuleϊʔυʹରͯ͠ͷॲཧ } }; });
container.walkRule([selector], callback) /* * @param {string|RegExp} [selector] * @param {childIterator}
callback * @return {false|undefined} */
container.walkRule([selector], callback) root.walkAtRules('ref', function (atrule) { var selector; // =
'.foo' root.walkRules(selector, function (rule) { // .foo ηϨΫλͷRuleϊʔυʹରͯ͠ͷॲཧ }); }
container.walkDecls([prop], callback) /* * @param {string|RegExp} [prop] * @param {childIterator}
callback * @return {false|undefined} */
container.walkDecls([prop], callback) root.walkAtRules('ref', function (atrule) { var selector; // =
'.foo' var refedProperty; // = 'font-size' var newValue; root.walkRules(selector, function (rule) { rule.walkDecls(refedProperty, function (decl) { newValue = decl.value; // = '12px' }); }); }
container.insertBefore(exist, add) /* * @param {Node|number} exist * @param {Node|object|string|Node[]}
* @return {Node} */
container.insertBefore(exist, add) root.walkAtRules('ref', function (atrule) { root.walkRules(selector, function (rule) {
// }); if (atrule.parent.type === 'rule') { atrule.parent.insertBefore(atrule, { prop: newProperty, value: newValue }); } }
container.remove() /* * @return {Node} */ root.walkAtRules('ref', function (atrule) {
if (atrule.parent.type === 'rule') { atrule.parent.insertBefore(atrule, { prop: newProperty, value: newValue }); } atrule.remove(); }
morishi'er / postcss-ref
Custom Syntaxes • PostCSS v5ʙͷػೳ • จࣈྻ͔ΒPostCSSͷASTΛΈཱͯΔϓϩάϥϜ • CSSҎ֎ͷߏจͷύʔεՄೳʹ •
PostCSSͷASTʹ͑͞มͰ͖ΕطଘͷϓϥάΠϯΛద༻Ͱ͖ Δ
None
Custom Syntaxes • postcss-scss, postcss-less • SCSS, Less༻ͷPostCSS Syntaxes •
PostCSSͰSCSS, LessϑΝΠϧΛCSSʹίϯύΠϧͰ͖ΔΑ͏ ʹͳΔΘ͚Ͱͳ͍ • SCSS, Lessίʔυʹରͯ͠ɺมॲཧΛ͢Δ͜ͱ͕Ͱ͖Δ
Custom Syntaxes • postcss-js • CSS in JSͷͨΊͷͷ • JSΦϒδΣΫτΛPostCSS
ASTʹม • RadiumΛ͍ͬͯͯPostCSSͷϓϥάΠϯΛ͑Δ
Custom Syntaxes • ΦϓγϣϯͷՃ • parser: ύʔαʔͷࢦఆ • stringifier: Ξτϓοτͷํ
• syntax: parser + stringifier var scss = postcss-scss; postcss.process([ plugins]).process(source, { syntax: scss });
4. PostCSSͷAPI • ASTͷߏ • Root, AtRule, Rule, Declara1on, Comment
• ASTૢ࡞ͷAPI • h8p:/ /api.postcss.org • Custom Syntaxes • CSSҎ֎ͷߏจPostCSS ASTʹม
Agenda 1. PostCSSͱ 2. PostCSSπʔϧͷհ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI
CSSपลπʔϧɺΞʔΩςΫνϟʔ • PostCSS, rework • cssnext, PreCSS, Sass, Less, Stylus,
... • Radium, react-style, styled-components, css-modules, ... • Stylelint, SCSS Lint, CSS Lint, CSSComb, Stylefmt, ... • OOCSS, SMACSS, ITCSS, BEM, ECSS, Atomic CSS, ... • Bootstrap, FoundaGon, BASSCSS, Tachyons, ...
ղܾ͍͍ͨͭ͠ಉ͡ ಓ۩ʹৼΓճ͞ΕΔͳ
PostCSSΤίγεςϜͱͯ͠ྑ͘Ͱ͖͍ͯΔ PostCSS is Awesome!
Thanks :D @morishi(er_