26 March 2014
号外号外:我的新书《现代JavaScript库开发:原理、技术与实战》出版啦!!!快点我查看
号外号外:一组小而美的JavaScript迷你库!!!快点我查看
号外号外:猿辅导招聘前端,后端,客户端啦!地点:北京!!!快点我查看

你是否觉得 CSS 不再跟以前一样了呢?最近几年成了热门话题,许多聪明的人也在谈论它。CSS 远不止是前端开发者应该用来美化网页的小玩意儿。我们关心性能,想要创作出更好的网站。

在这篇文章里,我会分享最近几个月学习 CSS 相关的知识和我个人对编写 CSS 代码确切的看法。作为程序员,我真的对每样东西的结构部分很感兴趣。我觉得编写 CSS 的方式应该改变并对此深入研究。我寻找好的处理方式,最佳准则(best principles)和新的工作流程(workflows)。

这篇文章就像是在 CSS 世界里旅行的总结。很多人说编写 CSS 不是真正的编程。我并不认同,它同样充满乐趣和挑战性。

CSS 预处理器(CSS Preprocessors)

当一个程序员开始写 CSS 时发生了什么 当一个程序员开始写 CSS 时发生了什么:你 -> 编写代码 -> 预处理器 -> CSS 代码 -> 网页

好吧,让我们面对它。这世上编写纯的 CSS 不是件有趣的事情。预处理器使用一些类似 CSS 语法,神奇地生成有效的 CSS 代码。在你和最终发送给浏览器的样式之间,它添加了一个中间层。这没有听起来那么坏,因为预处理器提供了一些真正实用的特性。

合并(Concatenation)

我认为能使你的文件合并在一起是最有价值的事情之一。我确信,你了解当在你的 .css 文件用 @import时,实际上告诉浏览器“麻烦你顺便也捎带这个文件”。 确实如此,发送新的请求,这有点不好,因为你可能会有非常多的文件。发送额外的请求会降低应用的性能。如果你使用 CSS 预处理器,这个问题将会解决。它们会很容易地把你所有的样式编译到单一的 .css 文件。

扩展 (Extending)

主要有两个 CSS 预处理器 —— LESSSASS。它们都支持扩展。没错,工作方式略有不同,不过想法(idea)是一样的。你写一个有一串属性的基本类(通常称作 mixin),之后把这些属性导入到另一个选择器。例如:

// less
.bordered(@color: #000) {
    border: dotted 2px @color;
}
.header { .bordered; }
.footer { .bordered(#BADA55); }

// compiles to
.header {
    border: dotted 2px #000000;
}
.footer {
    border: dotted 2px #bada55;
}

这里有个问题,如果你定义了一个没有参数的 mixin,也就是说像这样:

.bordered {
    border: dotted 2px #000;
}

它会原样编译到 CSS 文件里,不管你是否使用到。就像这样,因为这是有效的选择器。在 SASS 里,我们会多一点点灵活性。分别是 mixinsextendsplaceholders (如果你想了解它们准确的不同之处,我强烈推荐这篇文章。让我们看下 SASS 及其编译后的结果:

// sass
@mixin bordered($color: #000) {
    border: dotted 2px $color;
}
.header { @include bordered; }
.footer { @include bordered(#BADA55); }

// compiles to
.header {
    border: dotted 2px black; 
}
.footer {
    border: dotted 2px #bada55; 
}

看起来几乎和 LESS 相同,但如果我们看下第二个用例,定义一个占位符(a place holder):

// sass
%bordered {
    border: dotted 2px #000;
}
.header { 
    @extend %bordered; 
}
.footer { 
    @extend %bordered; 
}

// compiles to
.header, .footer {
    border: dotted 2px #000; 
}

有两个很好的事情发生。首先,这里不会编译 .bordered 类(there is no .bordered class compiled)。第二,SASS 合并了选择器,这让我们的 CSS 更短一些。

配置 (Configuration)

LESS 和 SASS 都支持定义变量。你可以稍后再访问这些变量,使用它们作为属性的值。

// sass
$brand-color: #009f0A;
...
h1 {
    color: $brand-color;
}

这是个好的特性,因为你可能会在同一个地方,存储一些像公司的颜色或网格宽度之类重要的东西。如果你想要修改,可以不用检查一边所有的代码。

另一个方便的用法是插入变量。下面的例子演示这种方法:

// sass
@mixin border($side) {
    border-#{$side}: solid 1px #000;
}
.header {
    @include border("left");
}

// compiles to
.header {
    border-left: solid 1px #000; 
}

反对预处理器 (Against the preprocessors)

  • 预处理器是一个工具,也就是说,你必须多做一件事,把它添加到把你的开发环境中。你可能想要把它整合进你的应用里,当然这需要额外编写代码。

  • 如果你不想让你的代码跟预处理器的弄乱,那么你很有可能需要一个监听工具。另一个用来监听你文件的工具,一旦文件有更新就会生成编译后的版本。如果是这样的话,那么每次当你开始开发项目的时候都要运行这个监听工具。也许你会优化这个过程所需的时间,但它还是需要你多留一份心。

  • 许多开发者总是只盯着他们的 .less 或者 .sass 文件。但编译后的文件才是重要的。你的 SASS 代码可能很优雅并优化过的,但这并不意味着你最后得到同样优美的 CSS 代码。你可能会有真正需要关心的特定问题。因此,定期地检查编译后的版本。

BEM

BEM 代表块 Block 元素 Element 修饰符 Modifier BEM代表块(Block),元素(Element),修饰符(Modifier)。

好吧,我找到一个可以玩的新工具。预处理器也许能够节省大量的时间,但是单独使用它们不能写出好的结构。我开始思考的第一件事是命名规范。让我们看下面 HTML 代码:

<header class="site-header">
    <div class="logo"></div>
    <div class="navigation"></div>
</header>

样式可能跟这个类似:

.site-header { ... }
.logo { ... }
.navigation { ... }

当然,这会奏效。但有个问题,阅读这个 CSS 你不能理解它,例如, logo 属于 header。你也许有另一个小的 logo 图片用在 footer。下一个逻辑步骤是写一个后代选择器。

.site-header .logo { ... }

然而使用这种选择器不是个好主意,因为这把样式紧绑到特定的标记层次(it tights styles to specific tags hierarchy)。如果我把 logo 移到 header 标签外面会怎样呢?这样式会失效。另一个你能做的是把 site-header 添加到 logo 类的名字中:

.site-header-logo { ... }

这很好,不言自明(self explanatory)。但这不是在所有的情况下都奏效。以后,在十二月份可能想要使用圣诞节版的 logo。那么,我不能写成:

.site-header-logo-xmas { ... }

因为我的逻辑是写一个选择器能够匹配嵌套在 HTML 里的标记。

BEM 是这种情况的解决方案。它意思是块(Block),元素(Element),修饰符(Modifier)和创建一些你可以遵循的规则。使用 BEM,我们小小的例子会变成这样:

.site-header { ... } /* block */
.site-header__logo { ... } /* element */
.site-header__logo--xmas { ... } /* modifier */
.site-header__navigation { ... } /* element */

也就是说,.site-header 是我们的块(our block)。logo 和 导航(navigation)这个块的元素(elements),logo 的 xmas 版本是修饰符(modifier)。也许这看起来简单,但这真的强大。一旦你开始使用它会发现让你更好的结构化。反对的理由主要是 BEM 的语法。没错,这看来确实有点难看,但我准备好对这个好系统的命名妥协。

(值得阅读:这里这里 )

OOCSS

面向对象的 CSS 面向对象的 CSS

我找到了 BEM 就能准确地命名我的类,然后就开始思考构造(composition)。也许我第一次阅读的文章是关于面向对象的 CSS。面向对象编程有时是关于添加抽象并且 CSS 能够支持它。是否使用预处理器,你都应该了解 OOCSS 。作为码农(coder),我发现这个理念真的跟平时编程很接近,例如 JavaScript 。这是两个主要原则(principles):

结构和表层分开 (Separate structure and skin)

让我们用下面的例子:

.header {
    background: #BADA55;
    color: #000;
    width: 960px;
    margin: 0 auto;
}
.footer {
    background: #BADA55;
    text-align: center;
    color: #000;
    padding-top: 20px;
}

这里有些样式是重复的。我们可以把它们提取到另一个类,如下:

.colors-skin {
    background: #BADA55;
    color: #000;
}
.header {
    width: 960px;
    margin: 0 auto;
}
.footer {
    text-align: center;
    padding-top: 20px;
}

这样,我们就有了可扩展的 colors-skin 对象。HTML 代码也许跟下面的类似:

<div class="header colors-skin"> ... </div>
<div class="colors-skin"> ... </div>
<div class="footer colors-skin"> ... </div>

这种改变有几个好处:

  • 我们有一个可以使用多次的类。
  • 如果我们需要修改,只需修改一处。
  • 我们移除了 CSS 文件里的重复项,这让文件更小。

容器和内容分开 (Separate container and content)

这里的理念是,不管每个元素放在哪里,都应该被应用同一种样式。因此,你应该避免跟下面类似的选择器用法:

.header .social-widget {
    width: 250px;
}

这是因为如果你把 .social-widget 移到 .header 容器(container)外面,宽度(width)就会不相同。一般来说这不是好的做法,尤其是给整个页面到处都使用的部分添加样式。这个原则鼓励使 CSS 模块化,我强烈建议利用充足的时间尝试下。就我个人而言,遵守这个原则意味着能够编写出更好的 CSS。

框架 (The framework)

如果你在 GitHub 打开 OOCSS repository 会看到一个框架。是的,这个框架用到面向对象的 CSS 理念,并且有一堆很酷的组件可以立即使用(ready-to-use)。某些时候我并不喜欢框架。如果你思考一下的话会发现,framework 这个单词有部分 frame 和 work,你确实在条条框框(frame)里工作(work)。你确实要对框架妥协并且必须遵守它的规则。我更愿意使用微框架(micro-frameworks)或者只提供最基本特性的工具。当然我不是打算重新发明轮子,但我总是试着在两者之间取得平衡。经常这样,现成可用(ready-to-use)的解决方案会带来混乱复杂的系统。我的建议是,干一件事只为了一个特定的目的。如果你可能多地包含方方面面,你的下场将会是……你懂的,一个框架。

但是,我强烈推荐你查看下 OOCSS 框架。这是一个独特的知识(It’s an unique piece of knowledge),也许会满足你的需求。Nicole Sullivan 托管这个 repository (仓库)。她是 OOCSS 的创始人(She is a pioneer in OOCSS),如果你有空的话我建议你查看下这个 presentations/talks

SMACSS

可量化和模块化 CSS 架构 Scalable and Modular Architecture for CSS 可量化和模块化的 CSS 架构

另一个流行的理念是 SMACSS。SMACSS 表示可量化(Scalable)和模块化(Modular)的 CSS 架构。Jonathan Snook 为 CSS 开发者介绍了风格指南(introduces something like style guide for the CSS developers)。这个想法是把你的应用分成以下几种类型:

  • 基本(Base)—— 为一些简单的选择器设置默认的基本样式。例如 clearfix (清除浮动)。
  • 布局(Layout)—— 定义网格(grids)。
  • 模块(Module)—— 一组元素组合成模块。例如 header 和 sidebar。
  • 状态(State)—— 包括了元素不同的状态。如果特定的对象是隐藏的,点击的(pressed),扩展的(expanded)等等……则定义相应的规则。
  • 主题(Theme)—— 更多地面向视觉部分。跟状态类型相似。 我没有使用 SMACSS 的经验,但它非常受欢迎,并确实提倡了好的想法。它更像是一个理念而不是框架,这非常好。因此,你不会受严格的规则,类或者是组件( components)束缚。

原子设计 (Atomic design)

原子设计 原子(Atoms) -> 分子(molecules) -> 组织(organisms) -> 模板(Templates) -> 网页(Pages)

了解 OOCSS 和 SMACSS 之后我寻找一个恰当的象征(metaphor),很快我看到了 Atomic Design 这篇文章,很好地展示 原子设计( Atomic Design) 这个好的理念。作者是 Brad Frost,是个知名的 Web 开发者,主要从事自适应式(responsive)和移动终端方面的工作。

这个理念真的很有趣。效仿一些化学技术,物质的基本单位是原子。Brad 把这移到 CSS,我们的网页是由原子构成的。一个原子是这样的:

<label>Search the site</label>

或者

<input type="text" placeholder="enter keyword" />

也就是说,原子包含 DOM 元素一些基本的样式。例如调配颜色(color palette),字体大小或者转换(transitions)。稍后这些部分会合并成一个分子(molecules)。例如:

<form>
    <label>Search the site</label>
    <input type="text" placeholder="enter keyword" />
    <input type="submit" value="search" />
</form>

这样 form 元素包含了几个原子。像这样的抽象化带来灵活性,因为我们也许会用同样的原子构建另一个分子。这样(Together with that),我们能在不同的地方(contexts)重复使用同样的 form

Brad 并没有止步于此。分子构成了组织(organisms)。按照同样的方法,我们可以写成如下,把它称作组织(organisms):

<header>
    <div class="logo">
    <nav>
        <ul>
            <li><a href="#">Home</a></li>
            <li><a href="#">About</a></li>
            <li><a href="#">Contacts</a></li>
        </ul>
    </nav>
    <form>
        <label>Search the site</label>
        <input type="text" placeholder="enter keyword" />
        <input type="submit" value="search" />
    </form>
</header>

这个理念的下一个是模板(templates)。这没有跟化学直接相关,而是放到 Web 环境中(web context)。一旦我们开始合并不同的组织,就是在构造模板。之后这些模板形成了最终的网页。

你可能已经使用相似的方法开发你的应用软件。然而,以合理的方式命名会带来好的架构。你和所有你的团队队员在开发中会明白一些事情。把一件东西分成原子和分子是挺重要的一部分,因为这会改善 Web 应用程序的开发过程和维护。

OrganicCSS

原子设计 有机的 CSS Organic CSS

几个月前我写了一篇关于 Organic 的文章。这是一个很小的 JavaScript 应用程序框架。它更像是设计模式,我个人很喜欢它。我甚至在几个项目里使用了 Organic 并且它干得非常好。如果你对它感兴趣,我强烈建议你阅读这篇博文

当我看 Brad Frost 的文章时,我已经对相似的理念非常熟悉,因为我了解 Organic 。Brad 的 Atomic Design 十分出色,但我决定更进一步,基于原子设计(Atomic Design)理念试着写自己的微框架。我选择 SASS 作为预处理器并在 Github 创建了仓库

原子 (Atoms)

让我们从框架最小的部分开始 —— 原子。在维基百科的定义是 *原子是一种元素能保持其化学性质的最小单位。*。在 CSS 的环境(context)中,我认为是一个属性和它的值。例如:

margin-top: 24px;

添加原子只是直接在类里面写样式,这种方式不是我想要的。因此如果写成下面这样:

body {
    margin-top: 24px;
}
header {
    margin-top: 24px;   
}

预处理器就会保持这个样子。我想要的最后结果是:

body, header {
    margin-top: 24px;
}

SASS 使用占位符(place holders)实现这个效果。也就是说:

%margin-top-24 {
    margin-top: 24px;
}
body {
    @extend %margin-top-24; 
}
header {
    @extend %margin-top-24; 
}

因此,我必须使用占位符(placeholders)。这也就意味着我必须有大量预定义的并且我能使用的占位符(placeholders)。就在那时,我决定这个框架将只包含原子。也许还有一些类似通常的 reset.css,网格定义等等。我想要为 CSS 开发写一些基本的东西(I wanted to write something which acts as a base for the CSS development)。也许一个又一个项目之后我将会看到一些可以放进核心的模式,但刚开始我想保持 repo(仓库)整洁简单。为了使一些东西始终如一的,我为定义原子创建 mixin。如下:

@include define-atom("block") {
    display: block;
}
@include define-atom("font-family") {
    font-family: Georgia;
}

使用这种方式我创建了一堆容易应用到每个项目的原子。你可以在这里查阅。我从别的框架里使用了一些最佳做法,所以并不是全归功于我。这里是个在分子里混合原子的 mixin:

@mixin header { /* <- molecule called 'header' 称作 `header` 的分子*/
    @include atoms((
        block,
        clearfix,
        font-family
    ));
}

分子 (Molecules)

分子是需要样式的 DOM 元素,但是没有子代。或者是没有直接联系的子代。例如 <img src="logo.jpg" /> 可以是分子。如果你觉得在网页里识别出分子,只需思考下什么是有原子构成的。如果一些元素是由其他分子构成的,那么很可能是一个(细胞器)。下面几行展示的是如何定义一个分子:

@mixin login-box { 
    @include atoms((
        block,
        font-size-20,
        margin-top-23,
        bold
    ));
}

我面前有些有趣的东西。让我们看下 body 标记。这是什么?是一个分子或者其他东西吗?没错,它通过原子需要一些样式,但通常包含了其他分子。它应该是其他东西。我做出决定,CSS 应该是主角。也就是说,如果 body 标记需要一些原子提供样式,那就是分子,理论上,我不该给它附加上任何其他的分子。这看起来有点不切实际,但在大多数情况下,这会阻止你使用后代选择器,这个好兆头。

细胞器 (Organelles)

一旦你能够识别出哪个 DOM 元素是分子,你将会知道什么细胞器。例如,典型的 form 元素是一个细胞器的好例子。它包含了分子像 labelinput 或者 textarea

.login-form {
    @include label;
    @include input;
    @include textarea;
}

这个框架中,细胞器(Organelles)是第一个跟当前应用紧密相关的 。原子和分子在不同项目中会有所改变,而细胞器却不会。

更多的抽象 (More abstractions)

你也许经常想要在其他地方合并几个细胞器。如果是这种情况,添加其他抽象:

Atom → Molecule → Organelle → Cell → Tissue → Organ → Sys → Organism

你选择什么来让你的 CSS 结构化,这是个问题。目前为止,我只在一个项目里使用过 OrganicCSS,但我能说,它使项目变清晰了。我把不同的元素放在它们各自的目录(文件夹)里并像这样命名类,例如,分子在一个“molecules”目录(文件夹),里面的文件命名为“header_molecule.scss”我才能容易地找到正在编写的是哪一个。例如,如果有一个叫 header 的细胞器,我简单地改成 o-header。然后,当我看 HTML 代码时可以是了解到,这个元素的 CSS 样式文件在 organelles 文件夹。

总结

这个一个有趣的旅行。我不知道是否会在将来使用 OrganicCSS,但这不是重点。我学到的东西才是重要的。我知道必须改变我的 CSS 开发过程,我做到了。我确信我们应该谈论更多 CSS 的结构。如你所见,在文章里我们有很多好的资源。我们只是必须找到它们,学习它们干了什么和如何运行的。只有我们能决定是否使用它们。更好的是,当你了解了整个项目才能创作出能更好满足需求的东西。

  • 原文:http://funwo.tk/2013-09-08-starting-to-write-css-cn.html
  • 英文:http://davidwalsh.name/starting-css

原文网址:http://yanhaijing.com/css/2014/03/26/starting-to-write-css/

微信公众号:颜海镜
关注微信公众号 颜海镜
微信支付二维码
赞赏支持 微信扫一扫


暂无评论,抢沙发!!!