├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── common ├── _statistics.html ├── fonts.sass ├── layout.slim ├── script.coffee └── style.sass ├── covers ├── ask.jpg ├── postcss.jpg ├── practice.jpg ├── present.jpg ├── problem.jpg ├── sass.jpg └── use.jpg ├── images ├── alistapart.png ├── amplifr.png ├── attacks.jpg ├── autoprefixer.svg ├── benfrain.jpg ├── colorbad.jpg ├── colorgood.jpg ├── ebay.svg ├── evolution.jpg ├── google.svg ├── groupon.svg ├── hewiki.jpg ├── logo.svg ├── markotto.jpg ├── martians.svg ├── russia.jpg ├── taobao.svg ├── tj.jpg ├── twitter.svg ├── wordpress.svg └── yandex.svg ├── postcss.en.md ├── postcss.ru.md ├── postcss.sass ├── postcss_highlighter.rb └── speech.md /.gitignore: -------------------------------------------------------------------------------- 1 | .bundle/ 2 | build/ 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | source 'https://rails-assets.org' 3 | 4 | gem 'rake' 5 | gem 'sinatra' 6 | gem 'redcarpet' 7 | gem 'compass-core' 8 | gem 'therubyracer' 9 | gem 'activesupport' 10 | gem 'jquery-cdn', '>= 2.1.3' 11 | gem 'evil-front-all', '>= 0.3.1' 12 | gem 'term-ansicolor' 13 | gem 'autoprefixer-rails' 14 | 15 | gem 'rails-assets-shower-core' 16 | gem 'rails-assets-shower-bright' 17 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | remote: https://rails-assets.org/ 4 | specs: 5 | activesupport (4.2.4) 6 | i18n (~> 0.7) 7 | json (~> 1.7, >= 1.7.7) 8 | minitest (~> 5.1) 9 | thread_safe (~> 0.3, >= 0.3.4) 10 | tzinfo (~> 1.1) 11 | autoprefixer-rails (5.2.1.3) 12 | execjs 13 | json 14 | coffee-script (2.4.1) 15 | coffee-script-source 16 | execjs 17 | coffee-script-source (1.9.1.1) 18 | compass-core (1.0.3) 19 | multi_json (~> 1.0) 20 | sass (>= 3.3.0, < 3.5) 21 | csso-rails (0.3.4) 22 | execjs (>= 1) 23 | dimensions (1.3.0) 24 | evil-blocks-rails (0.6.3) 25 | sprockets (>= 2) 26 | evil-front (0.3.10) 27 | i18n 28 | nokogiri (>= 1) 29 | rails-sass-images (>= 0.3) 30 | rubypants-unicode 31 | sass (>= 3.2.9) 32 | slim (>= 1.3.9) 33 | sprockets (>= 1) 34 | standalone_typograf (>= 3.0.1) 35 | unicode_utils (>= 1.4) 36 | evil-front-all (0.3.1) 37 | autoprefixer-rails (>= 3.0) 38 | coffee-script (>= 2.2.0) 39 | csso-rails (>= 0.3) 40 | evil-blocks-rails (>= 0.2) 41 | evil-front (~> 0.3.0) 42 | jquery-cdn (>= 1) 43 | sprockets (>= 1) 44 | uglifier (>= 2.1.1) 45 | execjs (2.6.0) 46 | i18n (0.7.0) 47 | jquery-cdn (2.1.4) 48 | sprockets (>= 2) 49 | json (1.8.3) 50 | libv8 (3.16.14.11) 51 | mime-types (2.6.1) 52 | mini_portile (0.6.2) 53 | minitest (5.8.0) 54 | multi_json (1.11.2) 55 | nokogiri (1.6.6.2) 56 | mini_portile (~> 0.6.0) 57 | rack (1.6.4) 58 | rack-protection (1.5.3) 59 | rack 60 | rails-assets-shower-bright (1.0.11) 61 | rails-assets-shower-core (1.0.7) 62 | rails-sass-images (0.5) 63 | dimensions (> 0) 64 | mime-types (> 0) 65 | sass (> 0) 66 | rake (10.4.2) 67 | redcarpet (3.3.2) 68 | ref (2.0.0) 69 | rubypants-unicode (0.2.5) 70 | sass (3.4.18) 71 | sinatra (1.4.6) 72 | rack (~> 1.4) 73 | rack-protection (~> 1.4) 74 | tilt (>= 1.3, < 3) 75 | slim (3.0.6) 76 | temple (~> 0.7.3) 77 | tilt (>= 1.3.3, < 2.1) 78 | sprockets (3.3.3) 79 | rack (~> 1.0) 80 | standalone_typograf (3.0.2) 81 | activesupport 82 | temple (0.7.6) 83 | term-ansicolor (1.3.2) 84 | tins (~> 1.0) 85 | therubyracer (0.12.2) 86 | libv8 (~> 3.16.14.0) 87 | ref 88 | thread_safe (0.3.5) 89 | tilt (2.0.1) 90 | tins (1.6.0) 91 | tzinfo (1.2.2) 92 | thread_safe (~> 0.1) 93 | uglifier (2.7.2) 94 | execjs (>= 0.3.0) 95 | json (>= 1.8.0) 96 | unicode_utils (1.4.0) 97 | 98 | PLATFORMS 99 | ruby 100 | 101 | DEPENDENCIES 102 | activesupport 103 | autoprefixer-rails 104 | compass-core 105 | evil-front-all (>= 0.3.1) 106 | jquery-cdn (>= 2.1.3) 107 | rails-assets-shower-bright 108 | rails-assets-shower-core 109 | rake 110 | redcarpet 111 | sinatra 112 | term-ansicolor 113 | therubyracer 114 | 115 | BUNDLED WITH 116 | 1.10.3 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### Keynotes about PostCSS 2 | 3 | For my talks on [SPB Frontend](https://vk.com/spb_frontend), 4 | [WSD in Kiev](http://webstandardsdays.ru/2014/12/06/), 5 | [Stachka in Ulyanovsk](http://nastachku.ru/), 6 | [Frontend.fi in Helsinki](http://frontend.fi/april-meetup-2015/) 7 | and [CSSConf](https://2015.cssconf.com). 8 | 9 | * Russian: [ai.github.io/about-postcss](http://ai.github.io/about-postcss/). 10 | * English: [ai.github.io/about-postcss/en](http://ai.github.io/about-postcss/en). 11 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'pathname' 2 | require 'base64' 3 | 4 | ROOT = Pathname(__FILE__).dirname 5 | COMMON = ROOT.join('common') 6 | 7 | require 'redcarpet' 8 | require 'evil-front-all' 9 | require 'active_support' 10 | require 'active_support/core_ext' 11 | require 'rails-assets-shower-core' 12 | require 'rails-assets-shower-bright' 13 | 14 | JqueryCdn.local_url = proc { '/jquery.js' } 15 | 16 | class Slide 17 | attr_accessor :html, :types 18 | 19 | def initialize(builder, text, number) 20 | @builder = builder 21 | @types = [] 22 | 23 | commands = [] 24 | text.gsub!(/^!.*\n/) do |line| 25 | line = line.split(' ') 26 | name = line.first[1..-1] 27 | if respond_to? name 28 | send(name, *line[1..-1]) 29 | else 30 | commands << [name, line[1..-1]] 31 | '' 32 | end 33 | end 34 | 35 | @html = @builder.markdown.render(text) 36 | @html = EvilFront::Russian.typograph_html(@html) 37 | @html = EvilFront::Russian.auto_flying_quotes(@html) 38 | @html.gsub!("
  • \n", '
  • ') 39 | commands.each { |(name, params)| send(name + '=', *params) } 40 | rescue => e 41 | $stderr.puts "Error in slide ##{ number + 1 }" 42 | $stderr.puts 43 | raise e 44 | end 45 | 46 | def type=(type) 47 | @types << type 48 | end 49 | 50 | def cover=(name) 51 | @html += image('covers/' + name) 52 | @types << 'cover h' 53 | end 54 | 55 | def image(name) 56 | url = if @builder.development? 57 | name 58 | else 59 | unless @builder.assets[name] 60 | $stderr.puts "No image #{ name }" 61 | exit(1) 62 | end 63 | 64 | @builder.inline_image(name) 65 | end 66 | 67 | "\"#{" 68 | end 69 | 70 | def gem(path) 71 | name, path = path.split('/', 2) 72 | root = Gem::Specification.find_by_name(name).gem_dir 73 | Pathname(root).join(path).read 74 | end 75 | end 76 | 77 | module Helpers 78 | def standalone? 79 | @build_type == :standalone 80 | end 81 | 82 | def development? 83 | @build_type == :development 84 | end 85 | 86 | def inline_image(name) 87 | file = assets[name].pathname 88 | type = file_type(file) 89 | encode_image(file, type) 90 | end 91 | 92 | def encode_image(file, type) 93 | "data:#{type};base64," + Base64.encode64(file.read) 94 | end 95 | 96 | def file_type(file) 97 | MIME::Types.type_for(file.to_s).first.content_type 98 | end 99 | 100 | def include_statistics 101 | COMMON.join('_statistics.html').read 102 | end 103 | end 104 | 105 | class Highlighter < Redcarpet::Render::HTML 106 | def self.reload! 107 | if Object.const_defined? 'PostcssHighlighter' 108 | Object.send(:remove_const, 'PostcssHighlighter') 109 | end 110 | end 111 | 112 | def block_code(code, lang) 113 | if lang.start_with? 'steps:' 114 | steps = true 115 | lang = lang.sub(/^steps:/, '') 116 | end 117 | 118 | lines = code.lines.map do |line| 119 | line = EvilFront.escape(line) 120 | 121 | if lang and respond_to? lang 122 | send(lang, line) 123 | else 124 | default_highlight(line) 125 | end 126 | end 127 | 128 | '
    ' + lines.map.with_index { |line, i|
    129 |       if not steps or line.strip.empty? or i.zero?
    130 |         "#{ line }"
    131 |       else
    132 |         "#{ line }"
    133 |       end
    134 |     }.join + '
    ' 135 | end 136 | end 137 | 138 | class Builder 139 | include EvilFront::Helpers 140 | include Helpers 141 | 142 | attr_reader :lang 143 | 144 | def self.load(lang = 'en') 145 | @cache ||= { } 146 | @cache[lang] ||= self.new(lang) 147 | end 148 | 149 | def initialize(lang, build_type = :development) 150 | @build_type = build_type 151 | @lang = lang 152 | end 153 | 154 | def assets 155 | @sprockets ||= begin 156 | Sprockets::Environment.new(ROOT) do |env| 157 | RailsAssets.load_paths.each { |i| env.append_path(i) } 158 | 159 | EvilFront.install_all(env) 160 | 161 | env.append_path(ROOT) 162 | env.append_path(COMMON) 163 | env.append_path(ROOT.join('images')) 164 | 165 | env.context_class.class_eval do 166 | def asset_path(path, options = {}) 167 | '' 168 | end 169 | end 170 | 171 | if development? 172 | env.css_compressor = nil 173 | else 174 | env.js_compressor = Uglifier.new(copyright: false) 175 | env.css_compressor = Csso::Compressor.new 176 | end 177 | end 178 | end 179 | end 180 | 181 | def reload! 182 | @title = nil 183 | @slides = nil 184 | @caption = nil 185 | @sections = nil 186 | @renderer = nil 187 | Highlighter.reload! 188 | end 189 | 190 | def title 191 | @title ||= caption.match(/

    (.*)<\/h1>/m).try(:[], 1) 192 | end 193 | 194 | def caption 195 | @caption ||= markdown.render(sections.first) 196 | end 197 | 198 | def markdown 199 | @renderer ||= begin 200 | load ROOT.join('postcss_highlighter.rb').to_s 201 | Redcarpet::Markdown.new(PostcssHighlighter.new, fenced_code_blocks: true) 202 | end 203 | end 204 | 205 | def sections 206 | @sections ||= ROOT.join("postcss.#{ @lang }.md").read 207 | .split(/^##/).map.with_index do |text, index| 208 | index == 0 ? text : '##' + text 209 | end 210 | end 211 | 212 | def slides 213 | slides = sections[1..-1] || [] 214 | @slides ||= slides.map.with_index { |s, i| Slide.new(self, s, i) } 215 | end 216 | 217 | def to_html 218 | layout = COMMON.join('layout.slim') 219 | options = { format: :html, disable_escape: true, pretty: false } 220 | Slim::Template.new(layout.to_s, options).render(self) 221 | end 222 | end 223 | 224 | require 'term/ansicolor' 225 | include Term::ANSIColor 226 | 227 | desc 'Remove generated files' 228 | task :clean do 229 | build = ROOT.join('./build/') 230 | build.rmtree if build.exist? 231 | end 232 | 233 | desc 'Build presentations all-in-one files' 234 | task :build => :clean do 235 | %w(ru en).each do |lang| 236 | html = ROOT.join("./build/postcss.#{lang}.html") 237 | html.dirname.mkpath 238 | html.open('w') { |io| io << Builder.new(lang, :standalone).to_html } 239 | print '.' 240 | end 241 | puts 242 | end 243 | 244 | desc 'Run server for development' 245 | task :server do 246 | require 'sinatra/base' 247 | 248 | class WebSlides < Sinatra::Base 249 | set :lock, true 250 | 251 | get '/' do 252 | 'English Русский' 253 | end 254 | 255 | { 256 | css: 'text/css', js: 'text/javascript', svg: 'image/svg+xml', 257 | png: 'image/png', jpg: 'image/jpeg', ico: 'image/vnd.microsoft.icon' 258 | }.each_pair do |ext, mime| 259 | get "/*.#{ ext }" do |path| 260 | content_type mime 261 | 262 | path = path + ".#{ ext }" 263 | asset = Builder.load.assets[path] 264 | 265 | if asset.nil? 266 | status 404 267 | "no #{path} asset" 268 | elsif ext == :css or ext == :js 269 | asset.to_s 270 | else 271 | send_file asset.pathname 272 | end 273 | end 274 | end 275 | 276 | get '/:lang' do 277 | builder = Builder.load(params[:lang]) 278 | builder.reload! 279 | builder.to_html 280 | end 281 | 282 | def assets 283 | @assets ||= Builder.new.assets 284 | end 285 | end 286 | 287 | WebSlides.run! 288 | end 289 | -------------------------------------------------------------------------------- /common/_statistics.html: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /common/fonts.sass: -------------------------------------------------------------------------------- 1 | @font-face 2 | font-family: "Open Sans" 3 | src: inline("shower-bright/fonts/OpenSans.woff") format("woff") 4 | 5 | @font-face 6 | font-family: "Open Sans" 7 | font-weight: bold 8 | src: inline("shower-bright/fonts/OpenSans.Bold.woff") format("woff") 9 | 10 | @font-face 11 | font-family: "Open Sans Light" 12 | src: inline("shower-bright/fonts/OpenSans.Light.woff") format("woff") 13 | 14 | @font-face 15 | font-family: "Anka Coder" 16 | src: inline("shower-bright/fonts/Anka.Coder.woff") format("woff") 17 | -------------------------------------------------------------------------------- /common/layout.slim: -------------------------------------------------------------------------------- 1 | doctype 5 2 | html lang=lang 3 | head 4 | meta charset="UTF-8" 5 | link rel="icon" href="http://sitnik.ru/favicon.ico" 6 | title= title 7 | meta name="viewport" content="width=1274, user-scalable=no" 8 | 9 | - if development? 10 | link rel="stylesheet" href="/style.css" 11 | link rel="stylesheet" href="/postcss.css" 12 | link rel="stylesheet" href="/fonts.css" 13 | - else 14 | style 15 | = assets['style.css'] 16 | = assets['postcss.css'] 17 | = assets['fonts.css'] 18 | = include_statistics 19 | body.list 20 | header.caption= caption 21 | 22 | - slides.each_with_index do |slide, i| 23 | section.slide id=(i + 1) class=slide.types.join(' ') 24 | div= slide.html 25 | 26 | .progress 27 | .progressbar 28 | 29 | - if development? 30 | script src="/jquery.js" 31 | script src="/script.js" 32 | - else 33 | script 34 | - if standalone? 35 | = assets['jquery.js'] 36 | = assets['script.js'] 37 | -------------------------------------------------------------------------------- /common/script.coffee: -------------------------------------------------------------------------------- 1 | #= require shower-core/shower.min 2 | 3 | for link in document.querySelectorAll('a') 4 | link.target = '_blank' if link.attributes.href.value.match(/^http/) 5 | 6 | progress = document.querySelector('.progress') 7 | progressForCovers = -> 8 | if document.querySelector('.slide.active.cover') 9 | progress.classList.add('for-cover') 10 | else 11 | progress.classList.remove('for-cover') 12 | 13 | window.addEventListener('hashchange', progressForCovers) 14 | setTimeout(progressForCovers, 100) 15 | -------------------------------------------------------------------------------- /common/style.sass: -------------------------------------------------------------------------------- 1 | @import "shower-bright" 2 | @import "evil-front" 3 | 4 | $color: #0080e0 5 | 6 | a 7 | color: $color 8 | 9 | .slide 10 | mark 11 | color: $color 12 | background: transparent 13 | mark.important 14 | padding: 3px 7px 0 15 | background: $color 16 | color: white 17 | mark 18 | color: white 19 | pre mark 20 | padding: 0 21 | pre mark.important 22 | background: $color 23 | pre mark.comment 24 | color: #888 25 | 26 | .caption 27 | color: black 28 | 29 | .caption a, .slide a 30 | background: none 31 | border-bottom: 0.09em solid rgba($color, 0.4) 32 | &:hover 33 | border-bottom-color: $color 34 | 35 | .caption a 36 | display: inline-block 37 | line-height: 1.2 38 | 39 | .slide::after 40 | text-align: right 41 | right: 50px 42 | 43 | .list .slide:after 44 | padding-top: 35px 45 | right: 0 46 | text-align: center 47 | 48 | .slide > div 49 | padding-top: 80px 50 | height: 560px 51 | 52 | .slide pre code:before 53 | display: none 54 | 55 | .slide h2 56 | a 57 | border-bottom-width: 0.05em 58 | em 59 | display: block 60 | position: absolute 61 | margin-top: -45px 62 | font-size: 35px 63 | font-style: normal 64 | font-weight: normal 65 | padding-bottom: 6px 66 | color: $color 67 | 68 | .cover h2 69 | font-family: "Open Sans" 70 | font-weight: bold 71 | color: white 72 | +stroke-text(black) 73 | font-size: 60px 74 | em 75 | color: white 76 | 77 | .progress.for-cover 78 | display: none 79 | 80 | @media screen 81 | .list .slide:target:before 82 | box-shadow: 0 0 10px 0 #3c99db, 0 0 0 12px $color 83 | 84 | .full .progress div 85 | background: $color 86 | height: 5px 87 | -------------------------------------------------------------------------------- /covers/ask.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/covers/ask.jpg -------------------------------------------------------------------------------- /covers/postcss.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/covers/postcss.jpg -------------------------------------------------------------------------------- /covers/practice.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/covers/practice.jpg -------------------------------------------------------------------------------- /covers/present.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/covers/present.jpg -------------------------------------------------------------------------------- /covers/problem.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/covers/problem.jpg -------------------------------------------------------------------------------- /covers/sass.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/covers/sass.jpg -------------------------------------------------------------------------------- /covers/use.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/covers/use.jpg -------------------------------------------------------------------------------- /images/alistapart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/alistapart.png -------------------------------------------------------------------------------- /images/amplifr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/amplifr.png -------------------------------------------------------------------------------- /images/attacks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/attacks.jpg -------------------------------------------------------------------------------- /images/autoprefixer.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/benfrain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/benfrain.jpg -------------------------------------------------------------------------------- /images/colorbad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/colorbad.jpg -------------------------------------------------------------------------------- /images/colorgood.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/colorgood.jpg -------------------------------------------------------------------------------- /images/ebay.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 14 | 15 | 18 | 19 | 22 | 23 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /images/evolution.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/evolution.jpg -------------------------------------------------------------------------------- /images/google.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/groupon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | image/svg+xml -------------------------------------------------------------------------------- /images/hewiki.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/hewiki.jpg -------------------------------------------------------------------------------- /images/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/markotto.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/markotto.jpg -------------------------------------------------------------------------------- /images/martians.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/russia.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/russia.jpg -------------------------------------------------------------------------------- /images/taobao.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/tj.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ai/about-postcss/835f3f93e77074bc39e0b6e8328fbd3459c6e4e3/images/tj.jpg -------------------------------------------------------------------------------- /images/twitter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/wordpress.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/yandex.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /postcss.en.md: -------------------------------------------------------------------------------- 1 | # PostCSS: the Future after Sass and Less 2 | 3 | [Andrey Sitnik](http://sitnik.ru/en), [Evil Martians](https://evilmartians.com/) 4 | 5 | ## **PostCSS** the Future after Sass and Less 6 | !type is-title 7 | !image logo.svg 8 | 9 | *Andrey Sitnik, Evil Martians* 10 | 11 | ## Russia 12 | !type cover 13 | !type h 14 | 15 | !image russia.jpg 16 | 17 | 18 | © Olga Barantseva 19 | 20 | 21 | ## 22 | !type cover 23 | !type h 24 | 25 | !image attacks.jpg 26 | 27 | 28 | © George Spigot 29 | 30 | 31 | ## 32 | !type with-martians 33 | 34 | !image martians.svg 35 | 36 | ## Worked on 37 | !type with-clients 38 | 39 | !image ebay.svg 40 | !image groupon.svg 41 | !image amplifr.png 42 | 43 | ## Open Source 44 | !type with-projects 45 | 46 | !image autoprefixer.svg 47 | !image logo.svg 48 | 49 | ## *Part 1* Problem 50 | !cover problem.jpg 51 | 52 | ## Evolution 53 | 54 | !image evolution.jpg 55 | 56 | ## How Evolution Works 57 | 58 |
    59 |
    Mutations
    60 |
    61 |
    Selection
    62 |
    63 |
    Inheritance
    64 |
    65 |
    66 | 67 | ## Natural Selection? 68 | 69 | `` was supported for **19 years** 70 | 71 | ## Random Mutations 72 | 73 |
    74 |
    75 |

    76 | Freedom is not worth having if it does not include 77 | the freedom to make mistakes 78 |

    79 |
    80 |
    — “Mahatma Gandhi”
    81 |
    82 | 83 | ## 40% of ES6 comes from CoffeeScript 84 | !type shout 85 | 86 | ## *Part 2* CSS Preprocessors 87 | !cover sass.jpg 88 | 89 | ## CSS Templating 90 | 91 | ```mark_template 92 | a { 93 | <%= include clickable %> 94 | color: <%= $link-color %>; 95 | } 96 | ``` 97 | 98 | ## Syntax Features 99 | 100 | 1. Variables
    `$var: 1` 101 | 2. Mixins
    `@include mixin` 102 | 3. Functions
    `black()` 103 | 104 | ## *Problem 1* Limitations 105 | 106 | ```mark_rem 107 | a { 108 | width: 20rem; 109 | } 110 | ``` 111 | 112 | ## *Problem 2* Monolithic 113 | 114 | - **Libsass:** 110 files, 21 300 LOC of C++ 115 | - **Stylus:** 72 files, 7 900 LOC 116 | - **Less:** 105 files, 9 800 LOC 117 | 118 | ## *Problem 3* Hard to Code 119 | !type with-huge-code 120 | 121 | ```sass 122 | !gem compass-core/stylesheets/compass/css3/_transition.scss 123 | ``` 124 | 125 | ## *Part 3* PostCSS 126 | !cover postcss.jpg 127 | !type is-bottom 128 | 129 | ## The Beginning 130 | !type with-article 131 | 132 | !image tj.jpg 133 | 134 | Modular CSS Preprocessing with Rework 135 |
    — TJ Holowaychuk, 2013
    136 | 137 | ## Development 138 | !type with-difference 139 | 140 |
    141 | 142 | **Rework** 143 | 144 | - first 145 | - simple 146 | 147 | **PostCSS** 148 | 149 | - 2 times faster 150 | - better source map support 151 | - preserves source formatting 152 | - useful API 153 | 154 | ## PostCSS 155 | 156 |
    157 |
    158 | CSS 159 | 160 |
    source map
    161 |
    162 |
    163 |
    Parser
    164 |
    Plugin
    165 |
    Plugin
    166 |
    Stringifier
    167 |
    168 | New CSS 169 | 170 |
    new source map
    171 |
    172 |
    173 |
    174 | 175 | ## Usage 176 | 177 | ```steps:mark_plugin 178 | let postcss = require('postcss'); 179 | 180 | postcss([ plugin1, plugin2 ]) 181 | .process(css) 182 | .then( result => console.log(result.css) ); 183 | ``` 184 | 185 | ## Plugin 186 | 187 | ```steps:js 188 | function (css) { 189 | css.eachDecl( decl => { 190 | decl.value = decl.value.replace(/\d+rem/, rem => { 191 | return 16 * parseFloat(rem) + 'px'; 192 | }); 193 | }); 194 | }; 195 | ``` 196 | 197 | ## Difference 198 | !type with-difference 199 | 200 | **Preprocessor** 201 | 202 | - Monolithic 203 | - Code inside CSS template 204 | 205 | **PostCSS** 206 | 207 | - All features by plugins 208 | - JS transforms CSS 209 | 210 | ## Evolution 211 | 212 |
    213 |
    Write a plugin
    214 |
    215 |
    Popularity
    216 |
    217 |
    Specification
    218 |
    219 |
    220 | 221 | ## *Part 4* Practice 222 | !cover practice.jpg 223 | 224 | ## *Plugins* [postcss-simple-vars](https://github.com/postcss/postcss-simple-vars) 225 | 226 | ```mark_vars 227 | $blue: #056ef0; 228 | $column: 200px; 229 | 230 | .menu_link { 231 | background: $blue; 232 | width: $column; 233 | } 234 | ``` 235 | 236 | ## *Plugins* [postcss-nested](https://github.com/postcss/postcss-nested) 237 | 238 | ```mark_nested 239 | .phone { 240 | &_title { 241 | width: 500px; 242 | @media (max-width: 500px) { 243 | width: auto; 244 | } 245 | } 246 | } 247 | ``` 248 | 249 | ## *Plugins* [postcss-mixins](https://github.com/postcss/postcss-mixins) 250 | 251 | ```mark_mixins 252 | @define-mixin icon $network, $color { 253 | .icon.is-$network { 254 | color: $color; 255 | } 256 | } 257 | 258 | @mixin icon twitter, blue; 259 | @mixin icon youtube, red; 260 | ``` 261 | 262 | ## Maintainability 263 | 264 | - **postcss-nested:** 68 LOC 265 | - **postcss-simple-vars:** 74 LOC 266 | - **postcss-mixins:** 147 LOC 267 | 268 | ## *Impossible with Sass* [autoprefixer](https://github.com/postcss/autoprefixer) 269 | !type with-2-sides 270 | !type with-small-code 271 | !type with-bigger-right 272 | 273 | ```css 274 | :fullscreen a { 275 | transition: transform 1s; 276 | } 277 | ``` 278 | 279 | ```mark_prefixes 280 | :-webkit-full-screen a { 281 | -webkit-transition: -webkit-transform 1s; 282 | transition: transform 1s; 283 | } 284 | :-moz-full-screen a { 285 | transition: transform 1s; 286 | } 287 | :-ms-fullscreen a { 288 | transition: transform 1s; 289 | } 290 | :fullscreen a { 291 | -webkit-transition: -webkit-transform 1s; 292 | transition: transform 1s; 293 | } 294 | ``` 295 | 296 | ## *Impossible with Sass* [cssnext](https://github.com/cssnext/cssnext) 297 | 298 | ```mark_css4 299 | @custom-selector --heading h1, h2, h3, h4, h5, h6; 300 | 301 | .post-article --heading { 302 | margin-top: calc(10 * var(--row)); 303 | color: color( var(--mainColor) blackness(+20%) ); 304 | font-variant-caps: small-caps; 305 | } 306 | ``` 307 | 308 | ## *Impossible with Sass* [cssgrace](https://github.com/cssdream/cssgrace) 309 | !type with-2-sides 310 | 311 | ```css 312 | .icon { 313 | opacity: 0.6; 314 | display: inline-block; 315 | } 316 | ``` 317 | 318 | ```mark_ie 319 | .icon { 320 | opacity: 0.6; 321 | filter: alpha(opacity=60); 322 | display: inline-block; 323 | *display: inline; 324 | *zoom: 1; 325 | } 326 | ``` 327 | 328 | ## *Impossible with Sass* [CSS Modules](https://github.com/css-modules) 329 | !type with-2-sides 330 | 331 | ```mark_data 332 | .title { } 333 | .list { } 334 | .item { } 335 | ``` 336 | 337 | ```mark_data 338 | .__Component_title_dj45 { } 339 | .__Component_list_ha3l { } 340 | .__Component_item_5q67 { } 341 | ``` 342 | 343 | ## *Impossible with Sass* [postcss-colorblind](https://github.com/btholt/postcss-colorblind) 344 | !type with-2-images 345 | 346 | !image colorgood.jpg 347 | !image colorbad.jpg 348 | 349 | ## *Impossible with Sass* [postcss-bem-linter](https://github.com/necolas/postcss-bem-linter) 350 | 351 | Lint Twitter BEM-style [SUIT CSS](https://github.com/suitcss/suit) 352 | 353 | ```css 354 | /** @define Block */ 355 | :root { 356 | --Block-property: value; 357 | } 358 | .Block {} 359 | .Block-other {} 360 | ``` 361 | 362 | ## *Impossible with Sass* [doiuse](https://github.com/anandthakker/doiuse) 363 | 364 | Lint CSS for browser support against Can I Use database 365 | 366 | ```comment 367 | main.css: line 15, col 3 - 368 | CSS user-select: none not supported by: IE (8,9) 369 | main.css: line 32, col 3 - 370 | CSS3 Transforms not supported by: IE (8) 371 | ``` 372 | 373 | ## Hebrew Wikipedia 374 | !type with-image 375 | 376 | !image hewiki.jpg 377 | 378 | ## *Impossible with Sass* [rtlcss](https://github.com/MohammadYounes/rtlcss) 379 | !type with-2-codes 380 | 381 | Mirror styles for Arabic or Hebrew 382 | 383 | ```mark_left 384 | a { 385 | left: 10px; 386 | text-align: left; 387 | } 388 | ``` 389 | 390 | ```mark_right 391 | a { 392 | right: 10px; 393 | text-align: right; 394 | } 395 | ``` 396 | 397 | ## [100+ plugins](https://github.com/postcss/postcss#plugins) 398 | 399 | - Future CSS syntax 400 | - Fallbacks 401 | - Language extensions 402 | - Optimizations 403 | - Shortcuts 404 | - Analytics 405 | 406 | ## Performance 407 | 408 |
    409 | 414 |
    415 |
    libsass
    416 |
    417 |
    136 ms
    418 |
    419 |
    420 |
    Less
    421 |
    422 |
    160 ms
    423 |
    424 |
    425 |
    Stylus
    426 |
    427 |
    167 ms
    428 |
    429 |
    430 |
    Sass
    431 |
    432 |
    1084 ms
    433 |
    434 |
    435 | 436 |
    437 | Source: Preprocessors benchmark 438 |
    439 | 440 | ## Benefits 441 | 442 | 1. Performance 443 | 2. Modularity 444 | 3. Features that are impossible with Sass 445 | 446 | ## *Part 5* Present 447 | !cover present.jpg 448 | !type is-bottom 449 | 450 | ## 850 000 downloads per month 451 | !type shout 452 | 453 | ## PostCSS Users 454 | !type with-companies 455 | 456 | !image google.svg 457 | !image taobao.svg 458 | !image wordpress.svg 459 | !image twitter.svg 460 | 461 | ## Buzz 462 | !type with-article 463 | 464 | !image alistapart.png 465 | 466 | [What Will Save Us from
    the Dark Side of Pre‑Processors?](http://alistapart.com/column/what-will-save-us-from-the-dark-side-of-pre-processors) 467 |
    — A List Apart
    468 | 469 | ## Buzz 470 | !type with-article 471 | 472 | !image benfrain.jpg 473 | 474 | [Breaking up with Sass:
    it’s not you, it’s me](http://benfrain.com/breaking-up-with-sass-postcss/) 475 |
    — Ben Frain, author of «Sass and Compass for Designers»
    476 | 477 | ## Buzz 478 | !type with-article 479 | 480 | !image markotto.jpg 481 | 482 | [Bootstrap 5 will likely be in PostCSS because holy crap that sounds cool](https://twitter.com/mdo/status/591364406816079873) 483 |
    — Mark Otto, Creator of Bootstrap
    484 | 485 | ## *Part 6* Usage 486 | !cover use.jpg 487 | 488 | ## 1. CSS Tool 489 | 490 | - Works with Sass, Stylus, Less and CSS 491 | - Fast and accurate CSS parser 492 | - Source maps 493 | 494 | ## 2. Project without PostCSS 495 | 496 | ```mark_case1 497 | .pipe( sass() ) 498 | .pipe( postcss([ 499 | require('autoprefixer') 500 | ]) ) 501 | ``` 502 | 503 | ## 3. Project with Autoprefixer 504 | 505 | ```mark_case2 506 | .pipe( sass() ) 507 | .pipe( postcss([ 508 | require('autoprefixer'), 509 | require('postcss-easings'), 510 | require('cssnext') 511 | ]) ) 512 | ``` 513 | 514 | ## 4. New Project 515 | 516 | ```mark_case3 517 | .pipe( postcss([ 518 | require('postcss-nested'), 519 | require('postcss-mixins'), 520 | require('postcss-simple-vars'), 521 | require('autoprefixer'), 522 | require('postcss-easings'), 523 | require('cssnext') 524 | ]) ) 525 | ``` 526 | 527 | ## Questions 528 | !cover ask.jpg 529 | !type is-bottom 530 | 531 | ## github.com/postcss/postcss 532 | !type with-links 533 | 534 | * Keynote: [ai.github.io/about-postcss/en](http://ai.github.io/about-postcss/en) 535 | * Twitter: [@postcss](https://twitter.com/postcss) 536 | * Evil Martians: [evl.ms](https://evilmartians.com/) 537 | * Amplifr: [amplifr.com/en](https://amplifr.com/en) 538 | 539 | !image martians.svg 540 | -------------------------------------------------------------------------------- /postcss.ru.md: -------------------------------------------------------------------------------- 1 | # PostCSS: будущее после Sass и Less 2 | 3 | [Андрей Ситник](http://sitnik.ru/), [Злые марсиане](https://evilmartians.com/) 4 | 5 | ## **PostCSS** Будущее после Sass и Less 6 | !type is-title 7 | !image logo.svg 8 | 9 | *Андрей Ситник, Злые марсиане* 10 | 11 | ## 12 | !type with-martians 13 | 14 | !image martians.svg 15 | 16 | ## Работал над 17 | !type with-clients 18 | 19 | !image ebay.svg 20 | !image groupon.svg 21 | !image amplifr.png 22 | 23 | ## Опенсорс 24 | !type with-projects 25 | 26 | !image autoprefixer.svg 27 | !image logo.svg 28 | 29 | ## *Часть 1* Проблема 30 | !cover problem.jpg 31 | 32 | ## Эволюция 33 | 34 | !image evolution.jpg 35 | 36 | ## Как работает эволюция 37 | 38 |
    39 |
    Мутации
    40 |
    41 |
    Отбор
    42 |
    43 |
    Наследование
    44 |
    45 |
    46 | 47 | ## Естественный отбор? 48 | 49 | `` поддерживали **19 лет** 50 | 51 | ## Случайные мутации 52 | 53 |
    54 |
    55 |

    56 | Свобода ничего не стоит, если она не включает в себя свободу ошибаться 57 |

    58 |
    59 |
    — Махатма Ганди
    60 |
    61 | 62 | ## 40% ES6 пришло из CoffeeScript 63 | !type shout 64 | 65 | ## *Часть 2* Препроцессоры 66 | !cover sass.jpg 67 | 68 | ## Шаблонизаторы CSS 69 | 70 | ```mark_template 71 | a { 72 | <%= include clickable %> 73 | color: <%= $link-color %>; 74 | } 75 | ``` 76 | 77 | ## Синтаксические возможности 78 | 79 | 1. Переменные
    `$var: 1` 80 | 2. Примеси
    `@include mixin` 81 | 3. Функции
    `black()` 82 | 83 | ## *Проблема 1* Ограниченность 84 | 85 | ```mark_rem 86 | a { 87 | width: 20rem; 88 | } 89 | ``` 90 | 91 | ## *Проблема 2* Монолитность 92 | 93 | - **Libsass:** 110 файлов, 21 300 строк C++ кода 94 | - **Stylus:** 72 файла, 7 900 строк кода 95 | - **Less:** 105 файлов, 9 800 строк кода 96 | 97 | ## *Проблема 3* Неудобно программировать 98 | !type with-huge-code 99 | 100 | ```sass 101 | !gem compass-core/stylesheets/compass/css3/_transition.scss 102 | ``` 103 | 104 | ## *Часть 3* PostCSS 105 | !cover postcss.jpg 106 | !type is-bottom 107 | 108 | ## Начало 109 | !type with-article 110 | 111 | !image tj.jpg 112 | 113 | Модульный CSS-препроцессинг с Реворком 114 |
    — TJ Holowaychuk, 2013
    115 | 116 | ## Развитие 117 | !type with-difference 118 | 119 |
    120 | 121 | **Rework** 122 | 123 | - первый 124 | - проще, меньше 125 | 126 | **PostCSS** 127 | 128 | - быстрее в 2 раза 129 | - лучше поддержка карт кода 130 | - сохраняет форматирование 131 | - удобнее API 132 | 133 | ## PostCSS 134 | 135 |
    136 |
    137 | CSS 138 | 139 |
    карта кода
    140 |
    141 |
    142 |
    Парсер
    143 |
    Плагин
    144 |
    Плагин
    145 |
    Стригифайр
    146 |
    147 | Новый CSS 148 | 149 |
    новая карта
    150 |
    151 |
    152 |
    153 | 154 | ## Использование 155 | 156 | ```steps:mark_plugin 157 | let postcss = require('postcss'); 158 | 159 | postcss([ plugin1, plugin2 ]) 160 | .process(css) 161 | .then( result => console.log(result.css) ); 162 | ``` 163 | 164 | ## Плагин 165 | 166 | ```steps:js 167 | function (css) { 168 | css.eachDecl( decl => { 169 | decl.value = decl.value.replace(/\d+rem/, rem => { 170 | return 16 * parseFloat(rem) + 'px'; 171 | }); 172 | }); 173 | }; 174 | ``` 175 | 176 | ## Разница 177 | !type with-difference 178 | 179 | **Препроцессор** 180 | 181 | - Монолитный 182 | - Логика прямо в шаблоне 183 | 184 | **PostCSS** 185 | 186 | - Все функции как плагины 187 | - JS трансформирует CSS 188 | 189 | ## Эволюция 190 | 191 |
    192 |
    Пишем плагин
    193 |
    194 |
    Популярность
    195 |
    196 |
    Спецификация
    197 |
    198 |
    199 | 200 | ## *Часть 4* Практика 201 | !cover practice.jpg 202 | 203 | ## *Плагины* [postcss-simple-vars](https://github.com/postcss/postcss-simple-vars) 204 | 205 | ```mark_vars 206 | $blue: #056ef0; 207 | $column: 200px; 208 | 209 | .menu_link { 210 | background: $blue; 211 | width: $column; 212 | } 213 | ``` 214 | 215 | ## *Плагины* [postcss-nested](https://github.com/postcss/postcss-nested) 216 | 217 | ```mark_nested 218 | .phone { 219 | &_title { 220 | width: 500px; 221 | @media (max-width: 500px) { 222 | width: auto; 223 | } 224 | } 225 | } 226 | ``` 227 | 228 | ## *Плагины* [postcss-mixins](https://github.com/postcss/postcss-mixins) 229 | 230 | ```mark_mixins 231 | @define-mixin icon $network, $color { 232 | .icon.is-$network { 233 | color: $color; 234 | } 235 | } 236 | 237 | @mixin icon twitter, blue; 238 | @mixin icon youtube, red; 239 | ``` 240 | 241 | ## Поддерживаемость 242 | 243 | - **postcss-nested:** 68 строки кода 244 | - **postcss-simple-vars:** 74 строки кода 245 | - **postcss-mixins:** 147 строк кода 246 | 247 | ## *Невозможно на Sass* [autoprefixer](https://github.com/postcss/autoprefixer) 248 | !type with-2-sides 249 | !type with-small-code 250 | !type with-bigger-right 251 | 252 | ```css 253 | :fullscreen a { 254 | transition: transform 1s; 255 | } 256 | ``` 257 | 258 | ```mark_prefixes 259 | :-webkit-full-screen a { 260 | -webkit-transition: -webkit-transform 1s; 261 | transition: transform 1s; 262 | } 263 | :-moz-full-screen a { 264 | transition: transform 1s; 265 | } 266 | :-ms-fullscreen a { 267 | transition: transform 1s; 268 | } 269 | :fullscreen a { 270 | -webkit-transition: -webkit-transform 1s; 271 | transition: transform 1s; 272 | } 273 | ``` 274 | 275 | ## *Невозможно на Sass* [cssnext](https://github.com/cssnext/cssnext) 276 | 277 | ```mark_css4 278 | @custom-selector --heading h1, h2, h3, h4, h5, h6; 279 | 280 | .post-article --heading { 281 | margin-top: calc(10 * var(--row)); 282 | color: color( var(--mainColor) blackness(+20%) ); 283 | font-variant-caps: small-caps; 284 | } 285 | ``` 286 | 287 | ## *Невозможно на Sass* [cssgrace](https://github.com/cssdream/cssgrace) 288 | !type with-2-sides 289 | 290 | ```css 291 | .icon { 292 | opacity: 0.6; 293 | display: inline-block; 294 | } 295 | ``` 296 | 297 | ```mark_ie 298 | .icon { 299 | opacity: 0.6; 300 | filter: alpha(opacity=60); 301 | display: inline-block; 302 | *display: inline; 303 | *zoom: 1; 304 | } 305 | ``` 306 | 307 | ## *Невозможно на Sass* [CSS Modules](https://github.com/css-modules) 308 | !type with-2-sides 309 | 310 | ```mark_data 311 | .title { } 312 | .list { } 313 | .item { } 314 | ``` 315 | 316 | ```mark_data 317 | .__Component_title_dj45 { } 318 | .__Component_list_ha3l { } 319 | .__Component_item_5q67 { } 320 | ``` 321 | 322 | ## *Невозможно на Sass* [postcss-colorblind](https://github.com/btholt/postcss-colorblind) 323 | !type with-2-images 324 | 325 | !image colorgood.jpg 326 | !image colorbad.jpg 327 | 328 | ## *Невозможно на Sass* [postcss-bem-linter](https://github.com/necolas/postcss-bem-linter) 329 | 330 | Проверяет БЭМ для Твиттера 331 | (методология [SUIT CSS](https://github.com/suitcss/suit)) 332 | 333 | ```css 334 | /** @define Block */ 335 | :root { 336 | --Block-property: value; 337 | } 338 | .Block {} 339 | .Block-other {} 340 | ``` 341 | 342 | ## *Невозможно на Sass* [doiuse](https://github.com/anandthakker/doiuse) 343 | 344 | Проверяет поддержку свойств в нужных браузерах по Can I Use 345 | 346 | ```comment 347 | main.css: line 15, col 3 - 348 | CSS user-select: none not supported by: IE (8,9) 349 | main.css: line 32, col 3 - 350 | CSS3 Transforms not supported by: IE (8) 351 | ``` 352 | 353 | ## Википедия на иврите 354 | !type with-image 355 | 356 | !image hewiki.jpg 357 | 358 | ## *Невозможно на Sass* [rtlcss](https://github.com/MohammadYounes/rtlcss) 359 | !type with-2-codes 360 | 361 | Изменяет дизайн для арабского и иврита 362 | 363 | ```mark_left 364 | a { 365 | left: 10px; 366 | text-align: left; 367 | } 368 | ``` 369 | 370 | ```mark_right 371 | a { 372 | right: 10px; 373 | text-align: right; 374 | } 375 | ``` 376 | 377 | ## [Ещё более 100 плагинов](https://github.com/postcss/postcss#plugins) 378 | 379 | - Полифилы новых спецификаций 380 | - Поддержка старых браузеров 381 | - Расширения языка 382 | - Оптимизация 383 | - Синтаксический сахар 384 | - Аналитика 385 | 386 | ## Скорость 387 | 388 |
    389 | 394 |
    395 |
    libsass
    396 |
    397 |
    136 мс
    398 |
    399 |
    400 |
    Less
    401 |
    402 |
    160 мс
    403 |
    404 |
    405 |
    Stylus
    406 |
    407 |
    167 мс
    408 |
    409 |
    410 |
    Sass
    411 |
    412 |
    1084 мс
    413 |
    414 |
    415 | 416 |
    417 | Источник: Сравнение препроцессоров 418 |
    419 | 420 | ## Преимущества 421 | 422 | 1. Скорость 423 | 2. Модульность 424 | 3. Функции, невозможные на Sass 425 | 426 | ## *Часть 5* Настоящее 427 | !cover present.jpg 428 | !type is-bottom 429 | 430 | ## 850 000 загрузок в месяц 431 | !type shout 432 | 433 | ## Используют PostCSS 434 | !type with-companies 435 | 436 | !image yandex.svg 437 | !image taobao.svg 438 | !image wordpress.svg 439 | !image twitter.svg 440 | 441 | ## Обсуждение 442 | !type with-article 443 | 444 | !image alistapart.png 445 | 446 | [Что спасёт нас от
    тёмной стороны CSS‑препроцессоров?](http://alistapart.com/column/what-will-save-us-from-the-dark-side-of-pre-processors) 447 |
    — A List Apart
    448 | 449 | ## Обсуждение 450 | !type with-article 451 | 452 | !image benfrain.jpg 453 | 454 | [Расставание с Sass:
    дело во мне, а не в тебе](http://benfrain.com/breaking-up-with-sass-postcss/) 455 |
    — Бен Фрейн, автор «Sass и Compass для дизайнеров»
    456 | 457 | ## Обсуждение 458 | !type with-quote 459 | 460 | !image markotto.jpg 461 | 462 | [Bootstrap 5 будет, скорее всего, на PostCSS, потому что оно выглядит офигенно](https://twitter.com/mdo/status/591364406816079873) 463 |
    — Марк Отто, создатель Bootstrap
    464 | 465 | ## *Часть 6* Применяем 466 | !cover use.jpg 467 | 468 | ## 1. Разработка CSS-инструмента 469 | 470 | - Работает с Sass, Stylus, Less и CSS 471 | - Очень быстрый и точный парсер 472 | - Карты кода 473 | 474 | ## 2. Сайт не использует PostCSS 475 | 476 | ```mark_case1 477 | .pipe( sass() ) 478 | .pipe( postcss([ 479 | require('autoprefixer') 480 | ]) ) 481 | ``` 482 | 483 | ## 3. Сайт уже с Автопрефиксером 484 | 485 | ```mark_case2 486 | .pipe( sass() ) 487 | .pipe( postcss([ 488 | require('autoprefixer'), 489 | require('postcss-easings'), 490 | require('cssnext') 491 | ]) ) 492 | ``` 493 | 494 | ## 4. Новый проект 495 | 496 | ```mark_case3 497 | .pipe( postcss([ 498 | require('postcss-nested'), 499 | require('postcss-mixins'), 500 | require('postcss-simple-vars'), 501 | require('autoprefixer'), 502 | require('postcss-easings'), 503 | require('cssnext') 504 | ]) ) 505 | ``` 506 | 507 | ## Вопросы 508 | !cover ask.jpg 509 | !type is-bottom 510 | 511 | ## github.com/postcss/postcss 512 | !type with-links 513 | 514 | * Презентация: [ai.github.io/about-postcss](http://ai.github.io/about-postcss/) 515 | * ВКонтакте: [vk.com/postcss](https://vk.com/postcss) 516 | * Злые марсиане: [evl.ms](https://evilmartians.com/) 517 | * Амплифер: [amplifr.com](https://amplifr.com/) 518 | 519 | !image martians.svg 520 | -------------------------------------------------------------------------------- /postcss.sass: -------------------------------------------------------------------------------- 1 | @import "evil-front" 2 | 3 | $color: #0080e0 4 | 5 | .slide.shout 6 | background: $color 7 | h2 8 | line-height: 1.4 9 | font-family: 'Open Sans', sans-serif 10 | 11 | .slide.is-title 12 | &::after 13 | visibility: hidden 14 | .list & 15 | visibility: visible 16 | h2 17 | font-size: 32px 18 | strong 19 | display: block 20 | font-weight: normal 21 | font-size: 350% 22 | line-height: 1.35 23 | position: relative 24 | left: -7px 25 | img 26 | position: absolute 27 | top: 105px 28 | right: 130px 29 | width: 150px 30 | em 31 | font-family: "Open Sans Light" 32 | font-style: normal 33 | position: absolute 34 | bottom: 90px 35 | 36 | .slide.with-martians 37 | h2 38 | strong 39 | font-weight: normal 40 | padding-right: 5px 41 | img 42 | display: block 43 | width: 320px 44 | margin: 80px auto 0 auto 45 | 46 | [lang=en] .slide.is-title 47 | h2 strong 48 | top: -5px 49 | font-size: 380% 50 | line-height: 1.3 51 | 52 | .slide.is-bottom 53 | h2 54 | position: absolute 55 | bottom: 10px 56 | 57 | .slide.is-black 58 | h2 59 | +stroke-text(white) 60 | color: black 61 | em 62 | color: black 63 | 64 | .slide.with-2-sides 65 | pre, p 66 | float: left 67 | clear: left 68 | width: 350px 69 | &:nth-of-type(2) 70 | float: right 71 | clear: right 72 | 73 | .slide.with-2-codes 74 | pre 75 | float: left 76 | clear: left 77 | width: 350px 78 | &:nth-of-type(2) 79 | float: right 80 | clear: right 81 | 82 | .slide.with-bigger-right 83 | pre, p 84 | width: 260px 85 | &:nth-of-type(2) 86 | width: 440px 87 | 88 | .slide.with-smaller-right 89 | pre:nth-of-type(2) 90 | font-size: 60% 91 | 92 | .slide.with-huge-code 93 | pre 94 | column-count: 3 95 | font-size: 22% 96 | font-weight: bold 97 | mark.important 98 | padding: 0 99 | code 100 | line-height: 1.3 101 | 102 | .slide.with-small-code 103 | pre 104 | font-size: 70% 105 | code 106 | line-height: 1.8 107 | 108 | .slide.with-smaller-code 109 | pre 110 | font-size: 55% 111 | code 112 | line-height: 1.8 113 | 114 | .slide.with-links 115 | &::after 116 | visibility: hidden 117 | li 118 | font-size: 140% 119 | margin-top: 10px 120 | &::before 121 | visibility: hidden 122 | &:nth-child(3) 123 | margin-top: 100px 124 | &:nth-child(3), &:nth-child(4) 125 | font-size: 130% 126 | img 127 | position: absolute 128 | bottom: 70px 129 | right: 100px 130 | width: 250px 131 | 132 | .slide.with-evolution 133 | strong 134 | color: $color 135 | 136 | .slide.with-difference 137 | .arrow 138 | position: absolute 139 | top: 164px 140 | left: 368px 141 | font-size: 200% 142 | color: $color 143 | p, ul 144 | position: absolute 145 | width: 350px 146 | &:nth-of-type(2) 147 | left: 560px 148 | p 149 | top: 186px 150 | font-size: 130% 151 | ul 152 | top: 282px 153 | 154 | .slide.with-companies 155 | p 156 | padding-top: 20px 157 | display: flex 158 | flex-wrap: wrap 159 | justify-content: center 160 | img 161 | max-height: 90px 162 | max-width: 320px 163 | padding: 40px 164 | 165 | .slide.with-clients 166 | img 167 | display: block 168 | margin: 0 auto 70px auto 169 | &:first-child 170 | width: 200px 171 | margin-top: 75px 172 | margin-bottom: 55px 173 | &:nth-child(2) 174 | width: 300px 175 | &:nth-child(3) 176 | width: 300px 177 | 178 | .slide.with-projects 179 | p 180 | padding-top: 55px 181 | display: flex 182 | flex-wrap: wrap 183 | justify-content: space-around 184 | img 185 | height: 220px 186 | 187 | .slide.with-article, .slide.with-quote 188 | img 189 | display: block 190 | width: 150px 191 | margin: 70px auto 20px auto 192 | border-radius: 50% 193 | p 194 | font-size: 120% 195 | text-align: center 196 | line-height: 1.7 197 | cite 198 | font-size: 70% 199 | position: relative 200 | top: 10px 201 | 202 | .slide.with-image 203 | img 204 | width: 800px 205 | 206 | .slide.with-2-images 207 | img 208 | float: left 209 | width: 390px 210 | position: relative 211 | top: 70px 212 | &:nth-child(2) 213 | float: right 214 | 215 | .evolution 216 | height: 300px 217 | width: 650px 218 | margin: 90px auto 0 auto 219 | position: relative 220 | font-size: 140% 221 | &_top, &_right, &_left 222 | position: absolute 223 | width: 235px 224 | text-align: center 225 | line-height: 1.4 226 | &_top 227 | top: 0 228 | left: 50% 229 | transform: translateX(-50%) 230 | &_right 231 | top: 244px 232 | right: 0 233 | &_left 234 | top: 244px 235 | left: 0 236 | &_first, &_second, &_third 237 | font-size: 170% 238 | position: absolute 239 | color: $color 240 | &_first 241 | top: 30% 242 | left: 65% 243 | transform: rotate(60deg) 244 | &_second 245 | bottom: -34px 246 | left: 55% 247 | transform: translateX(-50%) rotate(180deg) 248 | &_third 249 | top: 30% 250 | right: 65% 251 | transform: rotate(-60deg) 252 | [lang=en] &_second 253 | left: 52% 254 | &.is-postcss 255 | width: 700px 256 | .evolution_second 257 | left: 51% 258 | .evolution_top, .evolution_right, .evolution_left 259 | width: 245px 260 | 261 | .compare 262 | &.is-long 263 | padding-top: 0 264 | &_part 265 | clear: left 266 | padding-top: 15px 267 | &_title 268 | float: left 269 | width: 100px 270 | text-align: right 271 | &_line 272 | float: left 273 | height: 30px 274 | background: #b0b0b0 275 | margin: 10px 0 0 20px 276 | &_value 277 | float: left 278 | font-size: 55% 279 | margin: 11px 0 0 15px 280 | 281 | .compare_part.is-postcss .compare_title 282 | font-weight: bold 283 | .compare_part.is-postcss .compare_line 284 | background: $color 285 | width: 600px * (36 / 1084) 286 | .compare_part.is-libsass .compare_line 287 | width: 600px * (136 / 1084) 288 | .compare_part.is-less .compare_line 289 | width: 600px * (160 / 1084) 290 | .compare_part.is-stylus .compare_line 291 | width: 600px * (167 / 1084) 292 | .compare_part.is-sass .compare_line 293 | width: 600px * (1084 / 1084) 294 | 295 | .postprocessing 296 | position: relative 297 | margin: 0 auto 298 | width: 210px 299 | top: -55px 300 | &_step 301 | +height(50px) 302 | border: 1px solid 303 | text-align: center 304 | position: relative 305 | width: 100% 306 | margin-top: 32px 307 | margin-left: 3px 308 | font-size: 115% 309 | &:after 310 | content: "↓" 311 | font-size: 70% 312 | color: black 313 | position: absolute 314 | top: -46px 315 | left: 97px 316 | &:first-child 317 | margin-top: 0 318 | &:after 319 | display: none 320 | &.is-css 321 | padding: 0 322 | height: auto 323 | line-height: 1.1 324 | border: none 325 | &:after 326 | top: -27px 327 | &.is-important 328 | padding: 0 329 | color: $color 330 | border-width: 4px 331 | margin-left: 0 332 | font-weight: bold 333 | &:after 334 | top: -48px 335 | &_position 336 | position: relative 337 | &_note 338 | position: absolute 339 | top: 11px 340 | left: 15px 341 | font-size: 55% 342 | white-space: nowrap 343 | 344 | .slide .github 345 | border: none 346 | color: gray 347 | font-family: Open Sans, sans-serif 348 | strong 349 | padding: 0 10px 350 | color: black 351 | 352 | .source 353 | position: absolute 354 | bottom: 48px 355 | left: 128px 356 | font-size: 60% 357 | 358 | .slide .copyright 359 | position: absolute 360 | bottom: 15px 361 | right: 30px 362 | font-size: 50% 363 | color: black 364 | border-bottom: none 365 | -------------------------------------------------------------------------------- /postcss_highlighter.rb: -------------------------------------------------------------------------------- 1 | class PostcssHighlighter < Highlighter 2 | def none(code) 3 | code 4 | end 5 | 6 | def default_highlight(code) 7 | code.gsub(/(true|false|null|nil)/,'\0') 8 | .gsub(/"[^"]+"|'[^']+'/, '\0') 9 | .gsub(/(\s|^)(#[^\{].*$)/, '\1\2') 10 | .gsub(/(\(|\[|,|^|\s|=)(\d+(px|deg|%|m?s|))(\)|,|$|\s|;)/, 11 | '\\1\\2\\4') 12 | end 13 | 14 | def css(code) 15 | code.gsub(/^([^\s].*){/, '\1{') 16 | .gsub(/\/\*.*\*\//, '\0') 17 | .gsub(/^ ([^\s:]*):/, ' \1:') 18 | end 19 | 20 | def sass(code) 21 | code.gsub(/\..*/, '\0') 22 | .gsub(/\+.*/, '\0') 23 | .gsub(/(@|\$)[\w-]*/, '\0') 24 | .gsub(/\/\/.*/, '\0') 25 | end 26 | 27 | def mark_rem(code) 28 | css(code).gsub(/\d+rem/, '\0') 29 | end 30 | 31 | def js(code) 32 | default_highlight(code) 33 | .gsub(/(\s|^)(if|var|function|return|=>|let)/, '\1\2') 34 | .gsub(/\/[^<>\/]+\//, '\0') 35 | end 36 | 37 | def mark_postcss(code) 38 | js(code).gsub(/postcss\([^\)]+\)/, '\0') 39 | end 40 | 41 | def mark_webp(code) 42 | css(code.gsub(/\.webp/, '\0')) 43 | end 44 | 45 | def mark_left(code) 46 | css(code.gsub(/left/, '\0')) 47 | end 48 | 49 | def mark_right(code) 50 | css(code.gsub(/right/, '\0')) 51 | end 52 | 53 | def mark_template(code) 54 | code.gsub(/<[^\n]*>/, '\0') 55 | end 56 | 57 | def mark_plugin(code) 58 | js(code).gsub(/plugin\d/, '\0') 59 | end 60 | 61 | def mark_vars(code) 62 | css(code).gsub(/\$\w+/, '\0') 63 | end 64 | 65 | def mark_nested(code) 66 | code.gsub(/([^\s].*)\{/, '\1{') 67 | .gsub(/&|@\w+/, '\0') 68 | end 69 | 70 | def mark_mixins(code) 71 | code.gsub(/@[\w-]+/, '\0') 72 | .gsub(/\$\w+/, '\0') 73 | end 74 | 75 | def mark_css4(code) 76 | css(code).gsub(/var\([^)]+\)/, '\0') 77 | .gsub(/--heading/, '\0') 78 | .gsub(/blackness\([^)]+\)/, '\0') 79 | .gsub(/small-caps/, '\0') 80 | end 81 | 82 | def mark_prefixes(code) 83 | css(code).gsub(/-(webkit|ms|moz|o)-/, '\0') 84 | end 85 | 86 | def mark_data(code) 87 | css(code).gsub(/data:…/, '\0') 88 | end 89 | 90 | def mark_ie(code) 91 | css(code).gsub(/\*\w+|filter/, '\0') 92 | end 93 | 94 | def mark_case1(code) 95 | js(code).gsub(/autoprefixer/, '\0') 96 | end 97 | 98 | def mark_case2(code) 99 | js(code).gsub(/cssnext|postcss-\w+/, '\0') 100 | end 101 | 102 | def mark_case3(code) 103 | js(code).gsub(/postcss-(n|m|s)[\w-]+/, '\0') 104 | end 105 | 106 | def comment(code) 107 | code.gsub(/.*/, '\0') 108 | end 109 | 110 | def mark_context(code) 111 | code.gsub(/^(\s*[^\s].*){/, '\1{') 112 | .gsub(/([^\s:]*):/, '\1:') 113 | .gsub(/@context/, '\0') 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /speech.md: -------------------------------------------------------------------------------- 1 | Hi, my name is Andrey Sitnik. 2 | 3 | --- 4 | 5 | I am from Russia, Saint Petersburg. 6 | 7 | “Winter is comming” is totally about my country. 8 | 9 | --- 10 | 11 | I am lead frontend developer in Evil Martians. 12 | 13 | Unfortunately, right now we have no robots and blaster. 14 | 15 | --- 16 | 17 | Only Rails and React. 18 | 19 | --- 20 | 21 | But we worked on Groupon Russia and eBay Social. 22 | 23 | And my current project to test new technologies is Amplifr.com, 24 | a social media tool. 25 | 26 | --- 27 | 28 | But, I think, you can know me for my open source. 29 | As author of Autoprefixer. 30 | 31 | So today, I will talk about CSS processors. 32 | 33 | How many people here do use Stylus? (Please, raise your hand.) 34 | Less? 35 | Sass? 36 | Autoprefixer? 37 | 38 | If you have used Autoprefixer, you may saw, that it does not work like mixin 39 | libraries, like Compass or Bourbon. 40 | 41 | It doesn’t have mixins at all. You just write selectors and properties. 42 | And it just works — like magic. 43 | 44 | This is because Autoprefixer is only a tip of the iceberg of an entirely new way 45 | to process your CSS. 46 | 47 | In fact, Autoprefixer is not a preprocessor at all. 48 | It is just a plugin for PostCSS, 49 | a tool to extend or even replace CSS preprocessors. 50 | 51 | So today I will tell you about PostCSS. 52 | 53 | --- 54 | 55 | We will start with some theory. 56 | 57 | Why is using CSS processors so important. 58 | And what ideas are behind PostCSS. 59 | 60 | --- 61 | 62 | I belive in evolution. 63 | 64 | I believe that every long-term development is based on evolution. 65 | 66 | We have a great examples right in front of us. The biologically evolution. 67 | We use genetic algorithms in computer science. 68 | Richard Dawkins suggested that even human ideas are created 69 | in same evolution process. 70 | 71 | --- 72 | 73 | And *every* evolution process is based on three steps. 74 | 75 | *Random* mutation. 76 | Natural selection *in the wild*. 77 | And inheritance. 78 | 79 | But do we have these steps in our Web? 80 | Does W3C uses evolution process to develop new specifications? 81 | 82 | Of course, we have a lot of inheritance. 83 | A lot of legacy code and old specifications. 84 | 85 | --- 86 | 87 | Do we really have a selection? 88 | 89 | Blink tag was a *non-standard* tag working *only* in Firefox. 90 | But even in this case Mozilla was forced to support blink tag for *19 years*. 91 | 92 | If some technology becomes a standard, many sites start to use it. 93 | But how we can fix some issue in standard? For we can remove bad ideas? 94 | 95 | --- 96 | 97 | So we have not chance to fail in our standard. 98 | 99 | Will you think about some awesome but litle crazy ideas, 100 | if you must support your mistakes for decades? 101 | 102 | And it is a main problem. Many great ideas looked very crazy at the beginning. 103 | What people thought about iPhone, when it was released? 104 | What people thought about frontend 5 years ago? 105 | What people thought about Web applications 15 years ago? 106 | 107 | Development is about making a mistakes. 108 | There is no great ideas without mistakes. 109 | 110 | And this is why is so important to have have some playground. 111 | 112 | --- 113 | 114 | And this is why preprocessors are a big deal. 115 | They are this kind of a playground. 116 | 117 | Every developer can use own compiler. It is not a global standard. 118 | So we are not afraid to make a mistake. 119 | 120 | We creates CoffeeScript with many crazy ideas. 121 | We got feedback from users. 122 | And then took best ideas to a standard. 123 | 124 | Even the next-next ES7 takes async-await 125 | after it was tested in IcedCoffeeScript. 126 | 127 | And we can get real world feedback before idea becomes a standard. 128 | 129 | --- 130 | 131 | So I am very happy about JavaScript preprocessors. 132 | For example, Elm with time travel debugger. 133 | 134 | But do CSS preprocessors is still this kind of playground? 135 | I didn’t think so. 136 | 137 | --- 138 | 139 | But let’s start from the beginning. Whhat is CSS preprocessor? 140 | 141 | They are like a template language. 142 | Like PHP, but you mix your code with CSS templates. 143 | 144 | --- 145 | 146 | A preprocessor tool like Compass or Bourbon can only create mixins, 147 | functions and variables. 148 | 149 | --- 150 | 151 | And it is a first big problem. 152 | 153 | How we can add polyfill for units? 154 | 155 | Could we create Autoprefixer with mixins? 156 | 157 | --- 158 | 159 | And second problem is a source of the first one. 160 | 161 | Preprocessor codebase is big and very monolithic. 162 | 163 | Can *you* add something to preprocessor? 164 | 165 | Sass team even went further. 166 | To get better performance, they rewrote project to C++ 167 | and now we have even less developers who can add something new. 168 | 169 | --- 170 | 171 | And a last but not least problem. Preprocessors are template language. 172 | 173 | But is it a good language? 174 | 175 | Can you read this code? Me neither. 176 | But it is just a transition mixin from Compass. 177 | 178 | Sass is nice for simple tasks. 179 | But is it easy to code some complicated stuff on Sass? 180 | 181 | --- 182 | 183 | And these are the ideas behind PostCSS. 184 | 185 | Remove limitations of current preprocessors. 186 | Make a playground for crazy ideas. 187 | 188 | --- 189 | 190 | But PostCSS was not the first. 191 | 192 | 3 years ago, TJ Holowaychuk, Stylus maintainer, saw the same problems. 193 | He understood that preprocessor — as an idea — was finished. 194 | 195 | He started a new modular CSS processor tool, Rework. 196 | 197 | When I thought about Autoprefixer, I was looking for some new tool. 198 | Better that Sass. 199 | And when I saw Rework, I thought that it was a revolution. 200 | 201 | --- 202 | 203 | But Autoprefixer quickly became too big for Rework. 204 | We did require better source map support and 205 | more accurate parser to process legacy CSS with hacks. 206 | 207 | And this is how PostCSS was started. 208 | 209 | --- 210 | 211 | So, how does a modular CSS processor work? 212 | 213 | PostCSS core contains only two small parts. CSS parser which returns 214 | tree of node’s objects. And stringifier that converts node tree to CSS string. 215 | 216 | That’s all. By default, PostCSS will not change a byte of your CSS. 217 | It will just parse it, even saves all whitespace. 218 | And converts it back to string. 219 | 220 | All magic happens in plugins. 221 | Plugin code is very simple too. 222 | It is just a JavaScript function, that receives that node tree and changes it. 223 | Read some node, remove unnecessary and add necessary nodes. 224 | And pass changed tree to the next plugin. 225 | 226 | --- 227 | 228 | How does it look like in code? 229 | 230 | You install PostCSS using npm. Set an array with plugins. And process your CSS 231 | through these plugins. 232 | 233 | --- 234 | 235 | Plugin code is simple. 236 | 237 | Do you remember my slide about units in preprocessors? 238 | In PostCSS it is very easy to add new unit. 239 | 240 | This is your plugin’s function. It receives a parsed tree. We iterate through 241 | all declarations. And replace rem unit to pixels. 242 | 243 | That’s all. New plugin with CSS unit polyfill with 1-2-3-4 lines. 244 | 245 | --- 246 | 247 | So, let’s be clear. 248 | What is the difference between preprocessors and PostCSS? 249 | 250 | Preprocessors are a monolithic template language, 251 | which mixes your style and code. 252 | 253 | PostCSS transforms your styles with a small JS function. 254 | All features are in modules. You can use one variable module or the other. 255 | Of none of them if you didn’t want to use variables. 256 | 257 | --- 258 | 259 | So why it is important? Do you remember what I said about evolution? 260 | This plugins are ideal for the evolution process. 261 | 262 | You write a plugin with some crazy idea. For example, a new way to optimize 263 | page load time. 264 | 265 | Then some developers use it. Other developers hate it. 266 | 267 | But emotions don’t matter. If your crazy idea works, you will receive 268 | good feedback. More developers will use it. 269 | 270 | And we will know that this new way or new syntax works before 271 | it becomes a specification. 272 | Even if at the beginning that idea looked really strange. 273 | 274 | --- 275 | 276 | But I only told you about how things are in theory. Current science tells 277 | us that theory is nothing without practical result. 278 | 279 | If PostCSS ideas really do work, we will see new features or awesome benchmark 280 | results. 281 | 282 | So, does PostCSS have these results? 283 | 284 | --- 285 | 286 | First. Of course, we have plugins for variables, … 287 | 288 | --- 289 | 290 | … nesting … 291 | 292 | --- 293 | 294 | … and mixins. 295 | 296 | --- 297 | 298 | But these features are not built-in as part of PostCSS. 299 | They are just a common plugins. 300 | If you didn’t use mixins, you can remove the mixin plugin. 301 | Or even replace it by a plugin with different syntax. 302 | 303 | But it is not about freedom to choose. 304 | Main benefit is that these plugins are really small and simple. 305 | For example, nesting is working because of few functions in 60 lines of JS code. 306 | It is very easy to maintain these features. 307 | 308 | --- 309 | 310 | But, of course, PostCSS is not only about doing Sass job with plugins. 311 | PostCSS is about many other tasks, which are impossible with preprocessors. 312 | 313 | Autoprefixer is the most popular example from this whole new frontend world. 314 | 315 | There are no mixins. You just write CSS like there are no prefixes anymore. 316 | Autoprefixer takes Can I Use data, finds necessary selectors and properties 317 | and generates new CSS with prefixes. Like magic. 318 | 319 | --- 320 | 321 | But we can go further. 322 | 323 | How many people use Babel? … 324 | 325 | It compiles future ES6 JavaScript to old JS for current browsers. 326 | 327 | But why can’t we do so for CSS? Maxime from France wrote cssnext, 328 | a PostCSS plugin to compile future CSS 4 to CSS 3 for current browsers. 329 | 330 | For example, in CSS 4 you can use variables, color transformations, 331 | many shortcuts, or even define your own custom selector. 332 | And all of that will work right now. 333 | 334 | --- 335 | 336 | Next I will tell you very scary story. 337 | China is a big market with a lots of money. 338 | But even IE 6 is still popular there. 339 | Poor Chinese developers. 340 | 341 | So, Alibaba, one of the biggest Chinese IT companies, wrote a PostCSS plugin 342 | to solve their IE problem. It is live previous cssnext but it works 343 | in other way. 344 | 345 | It coverts CSS 3 to CSS 2. Add adds many hacks for old IE. 346 | 347 | --- 348 | 349 | Have you read article “The End of Global CSS”? 350 | There is a idea how to isolate component selector. 351 | Like a BEM but with 100% guarantees. 352 | 353 | This idea was already implemented in webpack. And name has CSS Modules name. 354 | 355 | They transform all you selectors. Add component name and random number. 356 | And, of course, they do it with PostCSS. 357 | 358 | It is a great example of that evolution. 359 | Crazy idea, PostCSS prototype, real feedback and now in webpack and special 360 | GitHub orgranization. 361 | 362 | --- 363 | 364 | Or other example. About 5% of users can’t see some colors. 5% is big number. 365 | It is more, that IE users for some projects. 366 | So we need to test colors combinations. 367 | 368 | This plugin from Netflix replaces all colors to how colorblinds see. 369 | 370 | Of course, there are many other better way to test it. 371 | But it is a good example of PostCSS power. 372 | Maybe it will be more useful in your case. 373 | 374 | --- 375 | 376 | But PostCSS is not only to add something to your styles. 377 | PostCSS working awesome as CSS linter. 378 | 379 | Twitter wrote PostCSS plugin to lint their CSS for Twitter BEM guidelines. 380 | 381 | --- 382 | 383 | But PostCSS linters can be much smarter. 384 | 385 | Doiuse is a PostCSS plugin, like a Autoprefixer. 386 | But it works in a different way. 387 | 388 | It takes Can I Use data and check do all your browsers support CSS that 389 | you write. Maybe you have forgot that IE 9 doesn’t support flexbox? 390 | 391 | --- 392 | 393 | And this is my favorite plugin. 394 | 395 | This is a Hebrew Wikipedia. 396 | 397 | As you know there is right-to-left writing in Arabic world. 398 | Our language affects on our mind, and the way how we think. 399 | 400 | So in right-to-left countries the “future” is not on the right. 401 | Time goes from right to left. 402 | So a progress bar should go in another direction too, from right to left. 403 | 404 | We need to mirror all our of design. But, of course, we don’t want to support 405 | two different CSS files. 406 | 407 | --- 408 | 409 | And Mohammad from Jordan wrote a really awesome plugin. 410 | It mirrors your styles automatically. 411 | It replaces left to right, changes margin values order. 412 | 413 | It works like magic, and so WordPress uses it to convert own styles 414 | for Arabic and Hebrew users. 415 | 416 | --- 417 | 418 | I've mentioned only the most interesting plugins, 419 | which are impossible with Sass. 420 | 421 | But PostCSS has many other plugins from really good developers around the world. 422 | 423 | I will post a link to this presentation on the last slide. 424 | Following this link, you can find many other awesome tools to make 425 | your work better. 426 | 427 | --- 428 | 429 | I’ve shown you that PostCSS can do much more than preprocessors. 430 | Autoprefixer is much smarter that Compass. 431 | And cssnext can even add a new syntax. 432 | 433 | But does it work fast? 434 | 435 | Sass team rewrote their project to C++ and killed compatibility 436 | with Compass to be the fastest preprocessor. 437 | 438 | Can a smarter JavaScript tool be faster than a C++ tool? 439 | 440 | --- 441 | 442 | And this is why modular CSS processing is amazing! 443 | 444 | PostCSS (written in JavaScript) is 4 times faster than libsass written in C++. 445 | 446 | Does somebody use old Ruby Sass? For example, because of Compass? 447 | 448 | By moving to PostCSS you will have more features and 40 times faster 449 | frontend build process. 450 | 451 | --- 452 | 453 | So, what are the benefits of using PostCSS right now? 454 | 455 | It is much faster and has many new features to make your frontend work better. 456 | All of this because of the modular PostCSS way. 457 | 458 | --- 459 | 460 | But I lied to you. Talk title is “Future after Sass”. But PostCSS is not 461 | the future, it is the present. 462 | 463 | --- 464 | 465 | According to npm statistics, we’re close to half a million downloads. 466 | It is more than Babel, Stylus or libsass have. 467 | 468 | --- 469 | 470 | Paul Irish told me that Google uses PostCSS with Autoprefixer. 471 | 472 | WordPress uses PostCSS with Autoprefixer and RTLCSS. 473 | 474 | Taobao, largest e-commerce in China, not only uses PostCSS, but also wrote 475 | many awesome plugins. 476 | 477 | But the most exciting example is Twitter. Nicolas told that Twitter doesn’t use 478 | any preprocessor at all. They moved from Less to Rework postprocessor 479 | and not in the middle of migration to PostCSS only solution. 480 | 481 | And this is why PostCSS is absolutely production ready. 482 | We have many users around the world. 483 | With many different cases. 484 | With hacks and legacy CSS. 485 | And PostCSS parses all this complicated cases. 486 | 487 | --- 488 | 489 | PostCSS became a trend. 490 | 491 | A List Apart wrote an article about “Dark Side” of preprocessors 492 | and that PostCSS can save us from it. 493 | 494 | --- 495 | 496 | Ben Frain, author of “Sass and Compass for designers” wrote a good article 497 | about breaking up with Sass. 498 | 499 | --- 500 | 501 | So, what do I want from you this weekend? 502 | 503 | --- 504 | 505 | If you want to write some CSS tool, linter, library. Look at PostCSS. 506 | 507 | First, PostCSS is better than regexps, because it has very accurate parser 508 | and will generate and update source maps. 509 | 510 | Second, PostCSS is better than a mixins library, because you will have 511 | bigger user base. Mixin library will work only with a single preprocessor. 512 | Autoprefixer or other PostCSS plugin can be executed after preprocessor, 513 | so users could use it in any environment. 514 | 515 | --- 516 | 517 | If you don’t use PostCSS at all, you must add at least Autoprefixer. 518 | 519 | There are many reasons why Autoprefixer is must-have library. 520 | But I will use big names. 521 | 522 | Google recommends to use only Autoprefixer as a tool to handle prefixes. 523 | 524 | --- 525 | 526 | If your project uses only Autoprefixer, you should look at other plugins. 527 | 528 | PostCSS has many plugins. They are as awesome as Autoprefixer. 529 | 530 | --- 531 | 532 | And if you start a new project, you should think about a PostCSS-only solution. 533 | 534 | Twitter is happy with it. 535 | 536 | PostCSS can do variables, nested and mixins better that preprocessors. 537 | So why you need this big and slow tools? 538 | 539 | Because IT is all about simplicity. And one tool is a simpler than two. 540 | 541 | --- 542 | 543 | That’s all. You can find project and github-com-slash-postcss-slash-postcss. 544 | Here are keynotes with all links. 545 | Here are PostCSS Twitter and our company sites. 546 | 547 | Now it is time for your questions. 548 | For every question you will receive small Russian candy. Like this. 549 | --------------------------------------------------------------------------------