PostCSS: Build your own CSS processor

PostCSS: Build your own CSS processor

at NodeFest 2016.

3cf7515c93d2f278420f0147788c8e23?s=128

Masaaki Morishita

November 13, 2016
Tweet

Transcript

  1. PostCSS: Build your own CSS processor morishi'er @ #nodefest

  2. ࣗݾ঺հ .about-me { name: Masaaki Morishita; twitter: @morishitter_; github: morishitter;

    qiita: morishitter; specializing-in: CSS; member-of: 'PostCSS Team'; works-at: 'Increments, inc.'; }
  3. Increments We are hiring! h"ps:/ /github.com/increments/job-descrip6ons

  4. Do you use PostCSS?

  5. Agenda 1. PostCSSͱ͸ 2. PostCSS੡πʔϧͷ঺հ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI

  6. Agenda 1. PostCSSͱ͸ 2. PostCSS੡πʔϧͷ঺հ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI

  7. PostCSSͱ͸ • Node੡ͷCSSύʔαʔ • CSSπʔϧΛ࡞ΔͨΊͷϑϨʔϜϫʔΫ • PostCSS੡πʔϧͷ࣮ߦ • by Andrey

    Sitnik
  8. over 12K stars on GitHub !

  9. 4 million DL / month on npm

  10. PostCSSͱ͸ • ޿͘࢖ΘΕΔΑ͏ʹͳͬͨ • CodePenͰ΋࢖͑Δ • Bootstrap v5͸PostCSS੡ʹͳΔ͔΋ʁ • ར༻اۀɾαʔϏε

    • Google, Facebook, GitHub, Wikipedia, Qiita, etc...
  11. PostCSSͷॲཧͷྲྀΕ • PostCSSࣗମ͸ͨͩͷύʔαʔ • ASTΛૢ࡞͢ΔͨΊͷAPIΛఏڙ • ϓϥάΠϯ͕ASTΛม׵͢Δ

  12. None
  13. PostCSSͷΑ͏ͳπʔϧ͸ੲ͔Β͋ͬͨ

  14. rework reworkcss / rework

  15. Modular CSS preprocessing with rework by TJ Holowaychuk h"p:/ /tjholowaychuk.tumblr.com/post/

    44267035203/modular-css-preprocessing-with- rework
  16. Custom CSS preprocessing by Nicolas Gallagher h"p:/ /nicolasgallagher.com/custom-css- preprocessing/

  17. rework (in 2013) • Node੡ͷCSSύʔαʔ • ϓϥάΠϯΞʔΩςΫνϟʔ • Andrey͕rework-vendorsͱ͍͏ϓϥάΠϯΛ࡞੒ •

    ޙͷAutoprefixer • ʢҰํͦͷࠒ morishi:er͸YACPΛ։ൃʣ
  18. rework (in 2013) • AutoprefixerΛ࡞Δ্Ͱɺrework͸ػೳෆ଍ • ౰࣌rework͸ϒϥ΢βϋοΫΛύʔεͰ͖ͳ͔ͬͨ • ʢ͋ͱTJʹAutoprefixerΛdisΒΕͨΓ…ʣ •

    AndreyʮΑࣗ͠෼Ͱ࡞Ζ͏ʂʯ
  19. None
  20. PostCSSʢʙ v1ʣ • 2013/11/4ʹ࠷ॳͷϦϦʔε • "PostCSS is a framework for

    CSS postprocessors" • AutoprefixerͷͨΊʹ࡞ΒΕͨ • ͜ͷࠒ͸CoffeeScriptͰॻ͔Εͯͨ
  21. PostCSS (v2 ʙ v4) • ϓϥάΠϯ͕େྔʹ࡞ΒΕ͍ͯ͘ • ݴޠ֦ுͷͨΊͷϓϥάΠϯ΋࡞ΒΕ͍ͯ͘ • cssnext,

    PreCSS, AtCSS • ʮpostprocessorʯͱ͍͏୯ޠΛ࢖Θͳ͘ͳͬͨ • ʢAPI͕͜Ζ͜ΖมΘͬͨ…ʣ
  22. PostCSS v5 (current) released on August 20, 2015

  23. PostCSS v5 • ࠓ·ͰͰҰ൪େ͖͍มߋ • Custom Syntaxes • ύʔαʔ෦෼ΛΧελϚΠζͰ͖ΔΑ͏ʹʢ͋ͱͰઆ໌ʣ •

    2016೥ݱࡏɺ޿͘࢖ΘΕΔΑ͏ʹ • ʢrework͸׬શʹΦϫίϯʹʣ
  24. 1. PostCSSͱ͸ • PostCSS͸ϓϥάΠϯΞʔΩςΫνϟʔͳCSSύʔαʔ • ASTૢ࡞ͷͨΊͷAPIΛఏڙ • ϓϥάΠϯΛॻ͘͜ͱͰCSSʹಠࣗͷॲཧΛ͓͜ͳ͏ • PostCSSͷྺ࢙

    • PostCSS͸rework͕ݩʹͳ͍ͬͯΔ
  25. Agenda 1. PostCSSͱ͸ 2. PostCSS੡πʔϧͷ঺հ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI

  26. 2. PostCSS੡πʔϧͷ঺հ • Autoprefixer • Stylelint • CSS Modules

  27. Autoprefixer • postcss / autoprefixer • ʮCan I Useʯͱ͍͏αΠτͷσʔλΛ ݩʹࣗಈͰϕϯμʔϓϦϑΟοΫεΛ

    ෇༩͢Δπʔϧ
  28. /* 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; }
  29. 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" ] })
  30. ྨࣅπʔϧͱͷύϑΥʔϚϯεൺֱ 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
  31. Stylelint • stylelint / stylelint • ϞμϯͳCSSϦϯλʔ • ΊͪΌͪ͘ΌΧελϚΠζͰ͖Δ •

    SCSS, Less, CSSͷ৽͍͠ه๏ʹ΋࢖ ͑Δ
  32. Stylelint • ϧʔϧ਺͸໿170ݸ • CSSLint: 38, scss-lint: 62, CSSComb: 26

    • શͯͷϧʔϧ͸PostCSSϓϥάΠϯͱ࣮ͯ͠૷ • ಠࣗϧʔϧ΋࡞ΕΔ
  33. in Facebook "Improving CSS quality at Facebook and beyond" h"ps:/

    /code.facebook.com/posts/ 879890885467584/improving-css-quality-at- facebook-and-beyond/
  34. 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, ...
  35. ઃఆϑΝΠϧूͱΧελϚΠζ • stylelint-config-* • standard, suitcss, primer, qiita, etc... {

    "extends": "stylelint-config-primer", "rules": { "selector-max-specificity": "0,6,0" } }
  36. StylelintͷઃఆϑΝΠϧ • ϑΥʔϚοτʹؔ͢Δ΋ͷ͕େ൒ • color-hex-case • length-zero-no-unit • declaration-block-semicolon-newline-after •

    block-closing-brace-empty-line-before • "ܯࠂ"Ͱ͸ͳ͘ɺࣗಈमਖ਼ͯ͠΄͍͠
  37. Stylefmt • morishi(er / stylefmt • StylelintͷઃఆϑΝΠϧΛݩʹࣗಈͰ ϑΥʔϚοτ • શͯͷϧʔϧʹରԠͰ͖ͯ͸͍ͳ͍

  38. stylefmt // input @mixin clearfix () { &::before, &::after{content:" ";display

    : table; } &::after {clear: both;}} // output @mixin clearfix () { &::before, &::after { content: " "; display: table; } &::after { clear: both; } }
  39. CSS Modules • css-modules / css-modules • CSSͷϞδϡʔϧԽͷΞϓϩʔνͷ1ͭ • άϩʔόϧείʔϓ໰୊Λղܾ͢Δ

    ͨΊͷ΋ͷ
  40. 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
  41. CSS in JSܥπʔϧઓ૪ • Radium, react-style, styling, etc... • CSS

    ModulesνʔϜʮCSS͸CSSϑΝΠϧʹॻ͖͍ͨʯ
  42. CSS Modules • webpackΛ࢖͏͜ͱ͕લఏ • css-loader, style-loader, (postcss-loader) • σϑΥϧτͰϩʔΧϧείʔϓ

    • ηϨΫλ໊ΛʹϋογϡΛ෇͚ͯিಥΛճආ • composes ͰଞͷίϯϙʔωϯτͷελΠϧΛ࠶ར༻ • ͜ΕΒͷػೳΛPostCSSͰ࣮૷
  43. // 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 */ }
  44. PostCSS.parts h"p:/ /postcss.parts

  45. 2. PostCSS੡πʔϧͷ঺հ • Autoprefixer • ϕϯμʔϓϦϑΟοΫεͷࣗಈ෇༩ • Stylelint • ESLintͷΑ͏ʹΧελϚΠζੑͷߴ͍CSSϦϯλʔ

    • CSS Modules • CSSʹϩʔΧϧείʔϓΛ
  46. Agenda 1. PostCSSͱ͸ 2. PostCSS੡πʔϧͷ঺հ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI

  47. ϓϦϓϩηοαʔͱͯ͠ͷCSS • ݴޠ֦ு༻ϓϥάΠϯ • cssnext, precss, atcss, etc

  48. cssnext • MoOx / postcss-cssnext • ະདྷͷCSSͷߏจΛࠓͷϒϥ΢β͕ղ ऍͰ͖ΔΑ͏ʹτϥϯεύΠϧ͢Δ PostCSSϓϥάΠϯू

  49. Custom Proper,es input: :root { --fontSize-m: 18px; } p {

    font-size: var(--fontSize-m); } output: p { font-size: 18px; }
  50. 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 */ }
  51. Custom selectors input: @custom-selector :--heading h1, h2, h3; article :--heading

    { margin-bottom: 2em; } output: article h1, article h2, article h3 { margin-bottom: 2em; }
  52. @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; }
  53. @extend input: .error { color: red; } .serious-error { @extend

    .error; } output: .error, .serious-error { color: red; }
  54. ϓϦϓϩηοαʔͱͯ͠PostCSSΛ࢖͏ϝϦοτ • ඞཁͳػೳ͚ͩΛબ୒Ͱ͖Δ • ΧελϚΠζ͕༰қ • ύϑΥʔϚϯε

  55. ඞཁͳػೳ͚ͩΛબ୒ • ʮSass࢖ͬͯΔ͚Ͳ@importͱม਺͚ͩ͋Ε͹͍͍Θʔʯ • ʮmixinͱ͔extend͸؅ཧͰ͖ͳ͍͔Β࢖ͬͯͳ͍ʯ • PostCSSͳΒඞཁͳϓϥάΠϯ͚ͩΛ௥ՃͰ͖Δ

  56. ΧελϚΠζ͕༰қ • 1ͭ1ͭͷϓϥάΠϯ͸খ͍͞ͷͰίϯτϦϏϡʔτ΋͠΍͍͢ • ϓϥάΠϯΛॻ͘͜ͱͰಠࣗͷΧελϚΠζ͕Մೳ

  57. ύϑΥʔϚϯε 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
  58. ϓϦϓϩηοαʔͱͯ͠PostCSSΛ࢖͏ͱ͖ͷ஫ҙ఺ • cssnext • ະདྷͷCSSΛઌऔΓ Ͱ͸ͳ͍ • ϙϦϑΟϧ Ͱ͸ͳ͍ •

    γϯλοΫεͷॻ͖৺஍͸Θ͔Δ • PreCSS • node-sass࢖͓ʁ
  59. ϓϦϓϩηοαʔͱͯ͠PostCSSΛ࢖͏ͱ͖ͷ஫ҙ఺ • ϓϥάΠϯΛબΜͰ࢖͏ͱ͍͍ • ͦͷϓϥάΠϯʹԿ͔͋ͬͨͱ͖͸ͳ͓֮͢ޛ • ϓϥάΠϯಉ࢜ʹ΋ґଘؔ܎͕͋Δ • ʮpostcss-mixins͸postcss-nestedΑΓઌʹಡΈࠐΈ·͠ΐ ͏ʯ

  60. 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS • cssnext: ະདྷͷߏจΛ࢖͑ΔCSSϓϦϓϩηοαʔ • ϝϦοτ • ඞཁͳػೳ͚ͩɺΧελϚΠζੑɺύϑΥʔϚϯε •

    ஫ҙ఺ • cssnext͸ϙϦϑΟϧ͡Όͳ͍ • ϓϥάΠϯબ୒ͷ೉қ౓͕ߴ͍
  61. Agenda 1. PostCSSͱ͸ 2. PostCSS੡πʔϧͷ঺հ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI

  62. PostCSSͷAPI • ASTͷߏ଄ • ϓϥάΠϯΛ࡞ΔͨΊͷ͍͔ͭ͘ͷAPIΛ঺հ • Custom Syntaxes h"p:/ /api.postcss.org/

  63. ͜͏͍͏ม׵Λ͢ΔϓϥάΠϯΛॻ͍ͯΈΔ /* input */ .foo { font-size: 12px; } .bar

    { @ref .foo, font-size; color: #333; } /* expected */ .foo { font-size: 12px; } .bar { font-size: 12px; color: #333; }
  64. 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' } ] }
  65. ASTͷϊʔυ

  66. ASTૢ࡞ͷAPI • postcss.plugin() • container.walk(Rule|AtRule|Decls)() • container.insert(Before|After)() • container.remove()

  67. postcss.plugin(name, initializer) /* * @param {string} name * @param {function}

    initializer * @return {Plugin} PostCSS plugin */
  68. postcss.plugin(name, initializer) module.exports = postcss.plugin('postcss-ref', function (opts) { opts =

    opts || {}; return function (root) { // ASTΛม׵͢Δॲཧ }; });
  69. container.walkAtRule([name], callback) /* * @param {string|RegExp} [name] * @param {childIterator}

    callback * @return {false|undefined} */ • Container: ਌ϊʔυʢࢠϊʔυΛ࣋ͭ͜ͱ͕Ͱ͖ΔʣΛੜ੒ ͢ΔΫϥε
  70. container.walkAtRule([name], callback) module.exports = postcss.plugin('postcss-ref', function (opts) { opts =

    opts || {}; return function (root) { root.walkAtRules('ref', function (atrule) { // `@ref`ͷAtRuleϊʔυʹରͯ͠ͷॲཧ } }; });
  71. container.walkRule([selector], callback) /* * @param {string|RegExp} [selector] * @param {childIterator}

    callback * @return {false|undefined} */
  72. container.walkRule([selector], callback) root.walkAtRules('ref', function (atrule) { var selector; // =

    '.foo' root.walkRules(selector, function (rule) { // .foo ηϨΫλͷRuleϊʔυʹରͯ͠ͷॲཧ }); }
  73. container.walkDecls([prop], callback) /* * @param {string|RegExp} [prop] * @param {childIterator}

    callback * @return {false|undefined} */
  74. 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' }); }); }
  75. container.insertBefore(exist, add) /* * @param {Node|number} exist * @param {Node|object|string|Node[]}

    * @return {Node} */
  76. 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 }); } }
  77. container.remove() /* * @return {Node} */ root.walkAtRules('ref', function (atrule) {

    if (atrule.parent.type === 'rule') { atrule.parent.insertBefore(atrule, { prop: newProperty, value: newValue }); } atrule.remove(); }
  78. ׬੒ morishi'er / postcss-ref

  79. Custom Syntaxes • PostCSS v5ʙͷػೳ • จࣈྻ͔ΒPostCSSͷASTΛ૊ΈཱͯΔϓϩάϥϜ • CSSҎ֎ͷߏจͷύʔεՄೳʹ •

    PostCSSͷASTʹ͑͞ม׵Ͱ͖Ε͹طଘͷϓϥάΠϯΛద༻Ͱ͖ Δ
  80. None
  81. Custom Syntaxes • postcss-scss, postcss-less • SCSS, Less༻ͷPostCSS Syntaxes •

    PostCSSͰSCSS, LessϑΝΠϧΛCSSʹίϯύΠϧͰ͖ΔΑ͏ ʹͳΔΘ͚Ͱ͸ͳ͍ • SCSS, Lessίʔυʹରͯ͠ɺม׵ॲཧΛ͢Δ͜ͱ͕Ͱ͖Δ
  82. Custom Syntaxes • postcss-js • CSS in JSͷͨΊͷ΋ͷ • JSΦϒδΣΫτΛPostCSS

    ASTʹม׵ • Radium౳Λ࢖͍ͬͯͯ΋PostCSSͷϓϥάΠϯΛ࢖͑Δ
  83. Custom Syntaxes • Φϓγϣϯͷ௥Ճ • parser: ύʔαʔͷࢦఆ • stringifier: Ξ΢τϓοτͷ࢓ํ

    • syntax: parser + stringifier var scss = postcss-scss; postcss.process([ plugins]).process(source, { syntax: scss });
  84. 4. PostCSSͷAPI • ASTͷߏ଄ • Root, AtRule, Rule, Declara1on, Comment

    • ASTૢ࡞ͷAPI • h8p:/ /api.postcss.org • Custom Syntaxes • CSSҎ֎ͷߏจ΋PostCSS ASTʹม׵
  85. Agenda 1. PostCSSͱ͸ 2. PostCSS੡πʔϧͷ঺հ 3. ϓϦϓϩηοαʔͱͯ͠ͷPostCSS 4. PostCSSͷAPI

  86. 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, ...
  87. ղܾ͍ͨ͠໰୊͸͍ͭ΋ಉ͡ ಓ۩ʹৼΓճ͞ΕΔͳ

  88. PostCSS͸ΤίγεςϜͱͯ͠ྑ͘Ͱ͖͍ͯΔ PostCSS is Awesome!

  89. Thanks :D @morishi(er_