Creating Style Sheets

顾轶灵 @ EFE

从理解设计开始

样式表的意义?

「还原设计意图」

「设计稿发给你了,看一下大概需要几天?」

但是……设计稿 ≠ 设计意图

  • 「这个设计图是 960px 宽的,在宽屏下要怎么显示啊?是定宽还是自适应啊?」
  • 「点击量好高,消费金额显示不下了啊,整个卡片布局都得修改了。」
  • ……

(kuā)(zhāng)的故事。

工作流程

设计师 设计稿 工程师 页面样式 用户 设计意图

Point(s) vs. Function

还原设计 ~ 曲线拟合

编写样式代码

CSS is Powerful but “Simple”

Cascading Style Sheets (CSS) is a simple mechanism for adding style (e.g., fonts, colors, spacing) to Web documents.

不是传统意义上的「编程语言」

a {
    color: #69c !important;
}
重要?不重要?

「但是 Web 发展到现在,与当初已经有了很大的变化。」

  • 普通用户 → 专业工程师
  • Web pages → Web apps
  • 模块化、工程化
  • 可维护性

CSS 的(cáo)(diǎn)

语法限制

Too simple!

.post {
    background-color: white;
    color: rebeccapurple;
}

.post h1, .post h2, .post h3,
.post h4, .post h5, .post h6 {
    /* reversed colors */
    background-color: rebeccapurple;
    color: white;
}

DRY? No.

复用机制

  • 继承
  • 共享声明块

继承

button {
    font-family: inherit;
}
  • 仅针对单条声明
  • 继承关系来自文档结构而非 CSS 本身

非理想的样式继承机制

共享声明块

  • 选择器组

    .title,
    .comment {
        color: #333;
    }
    
  • 在文档中添加用来复用样式的内容

    <h1 class="post-title block">...<h1>
    ...
    <article class="post block">...<article>
    ...
    <article class="comment block">...<article>
    

「样式类」

  • clearfix, cf
  • float-right, fr, pull-right, ...
  • red, mr10, ...
  • ...

样式层的抽象和复用

没有!

Bug or feature?

On the other hand, CSS stops short of even more powerful features that programmers use in their programming languages: macros, variables, symbolic constants, conditionals, expressions over variables, etc. That is because these things give power-users a lot of rope, but less experienced users will unwittingly hang themselves; or, more likely, be so scared that they won't even touch CSS. It's a balance. And for CSS the balance is different than for some other things.

Bug or feature?

Adding any form of macros or additional scopes and indirections, including symbolic constants, is not just redundant, but changes CSS in ways that make it unsuitable for its intended audience. Given that there is currently no alternative to CSS, these things must not be added.

但是……

Document statuses: WD - LC - CR - PR - REC

Endless road.

可似乎有点遥远……

「CSS 框架」

  • Bootstrap
  • Semantic UI
  • Foundation
  • ...

CSS 方法论/架构/最佳实践

  • BEM
  • OOCSS
  • SMACSS
  • ...

预处理器的崛起

The superman logo is a diamond shaped polygon with an “S” in it.

预处理器 / DSL

Sass logo

Less logo

Stylus logo

预处理器能做什么?

  • 提供语法糖增强语法
  • 将重复的值抽象为变量
  • 将重复的样式抽象出来进行混入或继承
  • ...

提供了在纯样式层提高可维护性的机制

eg. 嵌套语法 (WHATWG<h1> 推荐样式)

/* heading.less */
article, aside, nav, section {
    h1 {
        margin-top: 0.83em; margin-bottom: 0.83em; font-size: 1.50em;
    }
    & & h1 {
        margin-top: 1.00em; margin-bottom: 1.00em; font-size: 1.17em;
    }
    & & & h1 {
        margin-top: 1.33em; margin-bottom: 1.33em; font-size: 1.00em;
    }
    & & & & h1 {
        margin-top: 1.67em; margin-bottom: 1.67em; font-size: 0.83em;
    }
    & & & & & h1 {
        margin-top: 2.33em; margin-bottom: 2.33em; font-size: 0.67em;
    }
}

via 14.3.7 Sections and headings

编译后

headings.css文件大小为47229B

47KB? Are you serious?

See compiled code.

eg. 抽象样式

.border-radius(@radius) {
    -webkit-border-radius: @radius;
       -moz-border-radius: @radius;
            border-radius: @radius;
}

.badge(@color, @radius: 5px) {
    .border-radius(@radius);
    background-color: lighten(@color, 20%)
    color: @color;
}

@color-alert: red;
@color-warning: yellow;
@color-success: lightgreen;

.alert   { .badge(@color-alert);   }
.warning { .badge(@color-warning); }
.success { .badge(@color-success); }

预处理器工作流程

  1. DSL 源码 → 动态 AST
  2. 动态 AST → 静态 AST
  3. 静态 AST → CSS

开发者工作流程

DSL生产环境 CSS

预处理器拯救世界?

Vendor-prefix 的尴尬

/* Bootstrap's mixin */
.border-radius(@radius) {
    -webkit-border-radius: @radius;
       -moz-border-radius: @radius;
            border-radius: @radius;
}

如果不同项目需要支持不同浏览器?

Autoprefixer (2013)

输入 (标准 CSS)
.badge {
  border-radius: 5px;
}

配置条件 > 0% (尽可能兼容)
.badge {
  -webkit-border-radius: 5px;
     -moz-border-radius: 5px;
          border-radius: 5px;
}
配置条件 > 1% (市场占有率大于 1%)
.badge {
  border-radius: 5px;
}

Autoprefixer

  • 不依赖 DSL,书写标准 CSS
  • 数据来自 Can I Use (GitHub),由社区更新
  • 根据目标环境配置按需生成前缀

预处理器的局限

  • 提供整套方案,模块化能力不足
  • DSL 决定了功能范围,用户自定义能力有限
  • 功能缺失怎么办?——feature request

「后处理器」 (非官方)

  • 直接处理标准的 CSS 代码
  • 模块化,一次只做一件事
  • 按需自由组合模块,灵活调用
  • 逻辑通过编程语言描述,不借助 DSL

适合处理兼容性问题、进行 polyfill、优化输出

广义上「后处理」也属于预处理的范畴,其实 CSS 压缩等已经是其中之一

后处理器工作流程

  1. 标准 CSS → CSS AST
  2. 对 CSS AST 进行后处理 (不同后处理模块的核心功能)
  3. CSS AST → CSS 代码

开发者工作流程

标准 CSS (甚至草案)生产环境 CSS

「后处理框架」

  1. 将 CSS 解析为 AST
  2. 操作 AST
  3. 将 AST 格式化输出

  • rework (2012) - 「CSS 预处理插件框架」
  • postcss (2013) - 「通过 JS 插件转换 CSS」

如何选择?

前后结合的方案

解决兼容性问题不是样式本身的复用

  • 预处理器处理样式层的抽象、复用,减少冗余
  • 后处理器处理兼容、优化

前后结合的工作流

DSL标准 CSS生产环境 CSS

Bootstrap

// Vendor Prefixes
//
// All vendor mixins are deprecated as of v3.2.0 due to the introduction of
// Autoprefixer in our Gruntfile. They will be removed in v4.
via bootstrap/less/mixins/vendor-prefixes.less

est 2.0 (EFE Less mixin library)

.border-radius(@radius: @default-border-radius) when not (@use-autoprefixer) {
    -webkit-border-radius: @radius;
       -moz-border-radius: @radius;
            border-radius: @radius;
}
.border-radius(@radius: @default-border-radius) when (@use-autoprefixer) {
    border-radius: @radius;
}
.border-radius(@radius-x, @radius-y) {
    .border-radius(@radius-x ~"/" @radius-y);
}

有趣的现象

  • 预处理器越来越「后」
  • 「后处理器」越来越「前」

eg. Less 插件

  • 可以调用「后处理器」:Autoprefixer、clean-css、...
  • 可以操作 DSL 的 AST
  • 可以对 DSL 代码进行预处理
  • ...

用来增强 DSL 的表达能力受限的不足

eg. postcss 模块

用来增强原生 CSS 抽象能力的不足、弥补语法缺陷

结果

  • 「后处理器」不再提「后处理」 - postcss
  • 大家都叫「预处理」 - rework、Myth

优化与工具

The Flash's logo is a lightning bolt in a circle.

CSS 优化有多重要?

  • JS 可以异步加载
  • 多媒体资源可以异步加载 (图片/视频/音频/...)
  • CSS 往往是关键路径

流量 ===

减小数据量

  • 源码压缩
  • 二进制压缩

简单压缩

  • 空格/换行
  • rebeccapurple#639, ...
  • 0px0
    0.5.5
  • from0%
    100%to
  • bold700, normal400
  • 1000ms1s

高级压缩

(需要解析 CSS 语法)

  • 去重
    @keyframes, 重复属性, ...
  • 合并
    相邻的相同选择器/规则集/@media

CSS Shrink

提高 gzip 率

提高冗余

冗余 → 有序 → 低信息量

提高冗余
.post a {
  color: #6a0;
  text-decoration: underline;
  padding: 3px;
  background-color: rgba(255, 255, 255, .1);
  border-radius: 3px;
}

.comment {
  margin: 5px 0;
  padding: 3px;
  background-color: rgba(255, 255, 255, .1);
  text-decoration: underline;
  color: #cc9900;
  border-radius: 3px;
}

164 Bytes zipped

给属性排个序
.post a {
  padding: 3px;
  border-radius: 3px;
  background-color: rgba(255, 255, 255, .1);
  color: #6a0;
  text-decoration: underline;
}

.comment {
  margin: 5px 0;
  padding: 3px;
  border-radius: 3px;
  background-color: rgba(255, 255, 255, .1);
  color: #cc9900;
  text-decoration: underline;
}

158 Bytes zipped

属性排序

CSScomb

Mixins?

访问 DSL 的 AST

「Used CSS」

只传输会被使用的 CSS 代码

Removing unused CSS using PhantomJS.
UnCSS 工作流程

减少请求数

  • 小图片内嵌入 CSS (datauri)
  • <link><style> (?)
  • CSS Sprites (?)
  • 字体图标
  • ...

谨慎使用内嵌(可能降低缓存效果)

避免大文件和顺序依赖

  • 图片压缩!
  • <link> > @import
  • 并行下载 (limit)、domain sharding
Chrome now support parallel downloads.
Chrome 下并行下载文件 (network timing)

「关键路径」CSS

critical

Critical's workflow
Advanced CSS Performance Tooling

选择器性能 (Old school)

  • 避免 *
  • 去掉不必要的约束
  • 减少嵌套
  • 越靠右越精确
  • ...

多年前的建议?

选择器性能

CSS selector matching is now reasonably fast for the absolute majority of common selectors that used to be slow at the time of the profiler implementation. This time is also included into the Timeline "Recalculate Style" event.

As such, I believe the CSS selector profiler is not as useful as it [might have been] used to and can safely be dropped. This will also reduce the fraction of developers trying to micro-optimize already fast selectors.

Chrome 30 起去掉了 Selector Profiler

动画性能

  • 触发硬件加速
    translateZ(), translate3d()
  • 低消耗变换
    translate(), scale(), rotate(), opacity
  • will-change

Layout, paint, composite

High Performance Animations/CSS Triggers/will-change

总结

  • 尽可能用工具增强可维护性
  • 在性能没有瓶颈时不要牺牲可维护性
  • 加载速度优化 > 性能优化

Q ? A : Thanks