From 019cf5e3c27e1e9244a6bdb056d5f659c485314f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=83=E4=BF=9D=E7=BF=95Will?= Date: Thu, 25 Jul 2013 17:50:46 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B9=81=E9=AB=94=E5=8C=96=E7=AC=AC=E4=B8=80?= =?UTF-8?q?=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1084 ++++++++++++++++++++++++++--------------------------- 1 file changed, 542 insertions(+), 542 deletions(-) diff --git a/README.md b/README.md index c8c9cda..eb80129 100644 --- a/README.md +++ b/README.md @@ -1,542 +1,542 @@ -# 通用 CSS 笔记、建议与指导 - ---- - -在参与规模庞大、历时漫长且人手众多的项目时,所有开发者遵守如下规则极为重要: - -+ **保持 CSS 的可维护性** -+ **保持代码清晰易懂** -+ **保持代码的可拓展性** - -为了实现这一目标,我们要采用诸多方法。 - -本文档第一部分将探讨语法、格式以及 CSS 分析;第二部分将从方法论、思维框架以及编写与规划 CSS 的态度入手。 - -## 目录 - -* CSS 文档分析 - * 总览 - * 单一文件与多文件 - * 目录 - * 章节标题 -* 代码顺序 -* 规则解析 -* 命名规范 - * JavaScript 钩子 - * I18n -* 注释 - * 注释的拓展用法 - * 准修饰选择器 - * 代码标签 - * 继承标记 -* 编写 CSS -* 添加新部分 -* 面向对象 CSS -* 布局 -* 界面尺寸 - * 字号调节 -* 简写 -* IDs -* 选择器 - * 过修饰选择器 - * 选择器性能 -* 选择器继承 -* `!important` -* 魔数与绝对比例 -* 条件判断 -* Debugging -* 预处理 - -注:ID 不能包含汉字,所以在 Github Markdown 中暂时无法实现跳转。 - ---- - -## CSS 文档分析 - -无论编写什么文档,我们都应当尽力维持统一的风格,包括统一的注释、统一的语法与统一的命名规范。 - -### 总则 - -尽量将行宽控制在 80 字节以下。渐变(gradient)相关的语法以及注释中的 URL 等可以算作例外,毕竟这部分我们也无能为力。 - -我倾向于用 4 个空格而非 Tab 缩进,并且将声明拆分成多行。 - -### 单一文件与多文件 - -有些人喜欢将样式写成一个大文件,这并不赖,而且如果你按照下文的规则来编写的话也不会遇到什么问题。我在迁移至 Sass 之后,开始将样式拆分成众多小文件。这也不赖。无论你采用什么方式,下文中的规则都将适用。这两种写法仅仅在目录以及区块标题上有所差异。 - -### 目录 - -在 CSS 的开头,我会写一份目录,例如: - - /*------------------------------------*\ - $CONTENTS - \*------------------------------------*/ - /** - * CONTENTS............You’re reading it! - * RESET...............Set our reset defaults - * FONT-FACE...........Import brand font files - */ - -这份目录可以告诉其他开发者这个文件中具体含有哪些内容。这份目录中的每一项都与其对应的区块标题相同。 - -如果你在维护一份规模较大的单文件 CSS,对应的区块将也在同一文件中。如果你是在编写一组小文件,那么目录中的每一项应当对应相应的 @include 语句。 - -### 区块标题 - -目录应当对应区块的标题。请看如下示例: - - /*------------------------------------*\ - $RESET - \*------------------------------------*/ - -区块标题前缀 `$` 可以让我们使用([Cmd|Ctrl]+F)命令查找 `$[SECTION-NAME]`,同时 **将搜索范围限制在区块标题中**。 - -如果你在维护一份大文件,那么在区块之间空 5 行,如下: - - /*------------------------------------*\ - $RESET - \*------------------------------------*/ - [Our - reset - styles] - - - - - - /*------------------------------------*\ - $FONT-FACE - \*------------------------------------*/ - -在大文件中快速翻动时这些大块的空档有助于区分区块。 - -如果你在维护多份、以 @include 连接的 CSS 的话,在每份文件头加上标题即可,不必这样空行。 - -## 顺序 - -尽量按照特定顺序编写规则,这将确保你充分发挥 CSS 缩写中第一个 C 的意义:cascade,层叠。 - -一份规划良好的 CSS 应当按照如下排列: - -1. **Reset** 万物之根源 -2. **元素类型** 没有设置 class 的 `h1`、`ul` 等 -3. **对象以及抽象内容** 最一般、最基础的设计模式 -4. **子元素** 由对象延伸出来的所有拓展及其子元素 -5. **修补** 针对异常状态 - -如此一来,当你依次编写 CSS 时,每个区块都可以自动继承在它之前区块的属性。这样就可以减少代码相互抵消的部分,减少某些特殊的问题,构成设计更理想的 CSS 结构。 - -关于这方面的更多信息,强烈推荐 Jonathan Snook 的 [SMACSS](http://smacss.com)。 - -## CSS 规则集分析 - - [selector]{ - [property]:[value]; - [<- Declaration ->] - } - - [选择器]{ - [属性]:[值]; - [<- 声明 ->] - } - -编写 CSS 样式时,我习惯遵守这些规则: - -* class 名称以连字符(-)连接,除了下文提到的 BEM 命名法; -* 缩进 4 空格; -* 声明拆分成多行; -* 声明以相关性顺序排列,而非字母顺序; -* 有前缀的声明适当缩进,对齐其值; -* 缩进样式从而反映 DOM; -* 保留最后一条声明结尾的分号。 - -例如: - - .widget{ - padding:10px; - border:1px solid #BADA55; - background-color:#C0FFEE; - -webkit-border-radius:4px; - -moz-border-radius:4px; - border-radius:4px; - } - .widget-heading{ - font-size:1.5rem; - line-height:1; - font-weight:bold; - color:#BADA55; - margin-right:-10px; - margin-left: -10px; - padding:0.25em; - } - -我们可以发现,`.widget-heading` 是 `.widget` 的子元素,因为前者比后者多缩进了一级。这使得开发者在阅读这些样式时可以快速获取信息。 - -我们还可以发现 `.widget-heading` 的声明是根据其相关性排列的:`.widget-heading` 是文字元素,所以我们先添加字体相关的样式声明,接下来是其它的。 - -以下是一个没有拆分成多行的例子: - - .t10 { width:10% } - .t20 { width:20% } - .t25 { width:25% } /* 1/4 */ - .t30 { width:30% } - .t33 { width:33.333% } /* 1/3 */ - .t40 { width:40% } - .t50 { width:50% } /* 1/2 */ - .t60 { width:60% } - .t66 { width:66.666% } /* 2/3 */ - .t70 { width:70% } - .t75 { width:75% } /* 3/4*/ - .t80 { width:80% } - .t90 { width:90% } - -在这个例子(来自[inuit.css’s table grid system](https://github.com/csswizardry/inuit.css/blob/master/inuit.css/partials/base/_tables.scss#L88))中,将 CSS 放在一行内可以使得代码更紧凑。 - -## 命名规范 - -一般情况下我都是以连字符(-)连接 class 的名字(例如 `.foo-bar` 而非 `.foo_bar` 或 `.fooBar`),不过在某些特定的时候我会用 BEM(Block, Element, Modifier)命名法。 - -BEM 命名法可以使得选择器更规范,更清晰,更具语义。 - -该命名法按照如下格式: - - .block{} - .block__element{} - .block--modifier{} - -其中: - -* `.block` 代表某个基本的抽象元素; -* `.block__element` 代表 `.block` 这一整体的一个子元素; -* `.block--modifier` 代表 `.block` 的某个不同状态。 - -打个比方: - - .person{} - .person--woman{} - .person__hand{} - .person__hand--left{} - .person__hand--right{} - -这个例子中我们描述的最基本元素是一个人,然后这个人可能是一个女人。我们还知道人拥有手,这些是人体的一部分,而手也有不同的状态,如同左手与右手。 - -这样我们就可以根据亲元素来规定选择器的命名空间并传达该选择器的职能,它是一个子元素(`__`)还是不同状态(`--`)? - -由此,`.page-wrapper` 是一个独立的选择器。这是一个符合规范的命名,因为它不是其它元素的子元素或其它状态;然而 `.widget-heading` 则与其它对象有关联,它应当是 `.widget` 的子元素,所以我们应当将其重命名为 `.widget__heading`。 - -BEM 命名法虽然不太好看,而且相当冗长,但是它使得我们可以通过名称快速获知元素的功能和元素之间的关系。与此同时,BEM 语法中的重复部分非常有利于 gzip 的压缩算法。 - -无论你是否使用 BEM 命名法,你都应当确保 class 命名得当,力保一字不多、一字不少;将元素命名抽象化以提高复用性(例如 `.ui-list`,`.media`)。由此延伸出去的元素命名则要尽量精准(例如 `.user-avatar-link`)。不用担心 class 名的数量或长度,因为写得好的代码 gzip 也能有效压缩。 - -### HTML 中的 class - -为了确保易读性,在 HTML 标记中用两个空格隔开 class 名,例如: - -
- -增加的空格应当可以使得在使用多个 class 时更易阅读与定位。 - -### JS 钩子 - -**千万不要把 CSS 样式用作 JavaScript 钩子。**把 JS 行为与样式混在一起将无法对其分别处理。 - -如果你要把 JS 和某些标记绑定起来的话,写一个 JS 专用的 class。简单地说就是划定一个前缀 `.js-` 的命名空间,例如 `.js-toggle`,`.js-drag-and-drop`。这意味着我们可以通过 class 同时绑定 JS 和 CSS 而不会因为冲突而引发麻烦。 - - - - -上面的这个标记有两个 class,你可以用其中一个来给这个可排序的表格栏添加样式,用另一个添加排序功能。 - -### I18n - -虽然我(该 CSS Guideline 文档原作者 Harry Roberts)是个英国人,而且我一向拼写 colour 而非 color,但是为了统一性,我认为在 CSS 中用美式拼法更佳。CSS 以及其它多数语言都是以美式拼法编写,所以如果在 `.colour-picker{}` 中写 `color:red` 就缺乏统一性。我以前主张同时用两种拼法,例如: - - .color-picker, - .colour-picker{ - } - -但是我最近参与了一份规模庞大的 Sass 项目,这个项目中有许多的颜色变量(例如 `$brand-color`,`$highlight-color` 等等),每个变量要维护两种拼法实在辛苦,要查找并替换时也需要两倍的工作量。 - -所以为了统一性,把所有的 class 与变量都以你参与的项目的惯用拼法命名即可。 - -## 注释 - -我使用行宽不超过 80 字节的块状注释: - - /** - * This is a docBlock style comment - * - * This is a longer description of the comment, describing the code in more - * detail. We limit these lines to a maximum of 80 characters in length. - * - * We can have markup in the comments, and are encouraged to do so: - * -
-

Lorem

-
- * - * We do not prefix lines of code with an asterisk as to do so would inhibit - * copy and paste. - */ - - - /** - * 这是一个文档块(DocBlock)风格的注释。 - * - * 这里开始是描述更详细、篇幅更长的注释正文。当然,我们要把行宽控制在 80 字以内。 - * - * 我们可以在注释中嵌入 HTML 标记,而且这也是个不错的办法: - * -
-

Lorem

-
- * - * 如果是注释内嵌的标记的话,在它前面不加星号,否则会被复制进去。 - */ - -在注释中应当尽量详细描述代码,因为对你来说清晰易懂的内容对其他人可能并非如此。每写一部分代码就要专门写注释以详解。 - -### 注释的拓展用法 - -注释有许多很先进的用法,例如: - -* 准修饰选择器 -* 代码标签 -* 继承标记 - -#### 准修饰选择器 - -你应当避免过分修饰选择器,例如如果你能写 `.nav{}` 就尽量不要写 `ul.nav{}`。过分修饰选择器将影响性能,影响 class 复用性,增加选择器私有度。这些都是你应当竭力避免的。 - -不过有时你可能希望告诉其他开发者 class 的使用范围。以 `.product-page` 为例,这个 class 看起来像是一个根容器,可能是 `html` 或者 `body` 元素,但是仅凭 `.product-page` 则无法判断。 - -我们可以在选择器前加上准修饰(即将前面的类型选择器注释掉)来描述我们规划的 class 作用范围: - - /*html*/.product-page{} - -这样我们就能准确获知该 class 的作用范围而不会影响复用性。 - -其它例子如: - - /*ol*/.breadcrumb{} - /*p*/.intro{} - /*ul*/.image-thumbs{} - -这样我们就能在不影响代码私有度的前提下获知 class 作用范围。 - -#### 代码标签 - -如果你写了一个新规则的话,可以在它上面加上标签,例如: - - /** - * ^navigation ^lists - */ - .nav{} - - /** - * ^grids ^lists ^tables - */ - .matrix{} - -这些标签可以使得其他开发者快速找到相关代码。如果一个开发者需要查找和列表相关的部分,他只要搜索 `^lists` 就能快速定位到 `.nav`,`.matrix` 以及其它相关部分。 - -#### 继承标记 - -将面向对象的思路用于 CSS 编写的话,你经常能找到两部分 CSS 密切相关(其一为基础,其一为拓展)却分列两处。我们可以用继承标记来在原元素和继承元素之间建立紧密联系。这些在注释中的写法如下: - -在元素的基本样式中: - - /** - * Extend `.foo` in theme.css - */ - .foo{} - -在元素的拓展样式中: - - /** - * Extends `.foo` in base.css - */ - .bar{} - -这样一来我们就能在两块相隔很远的代码间建立紧密联系。 - ---- - -## 编写 CSS - -之前的章节主要探讨如何规划 CSS,这些都是易于量化的规则。本章将探讨更理论化的东西,也将探讨我们的态度与方法。 - -## 编写新组件 - -编写新组件时,要在着手处理 CSS **之前** 写好 HTML 部分。这可以令你准确判断哪些 CSS 属性可以继承,避免重复浪费。 - -先写标记的话,你就可以关注数据、内容与语义,在这之后再添加需要的 class 和 CSS 样式。 - -## 面向对象 CSS - -我以面向对象 CSS 的方式写代码。我把组件分成结构(对象)与外观(拓展)。正如以下分析(注意这个只是笔记而非例子): - - .room{} - - .room--kitchen{} - .room--bedroom{} - .room--bathroom{} - -我们在屋子里有许多房间,它们都有共同的特点:它们都包含地板、天花板、墙壁和门。这些共享的部分我们可以放到一个抽象的 `.room{}` class 中。不过我们还有其它与众不同的房间:一个厨房可能有地砖,卧室可能有地毯,洗手间可能没有窗户但是卧室会有,每个房间的墙壁颜色也许也会不一样。面向对象 CSS 的思路使得我们把相同部分抽象出来组成结构部分,然后用更具体的 class 来拓展这些特征并添加特殊的处理方法。 - -所以比起编写大量的特殊模块,应当努力找出这些模块中重复的设计模式并将其抽象出来,写成一个可以复用的 class,将其用作基础然后编写其它拓展模块的特殊情形。 - -当你要编写一个新组件时,将其拆分成结构和外观。编写结构部分时用最通用 class 以保证复用性,编写外观时用更具体的 class 来添加设计方法。 - -## 布局 - -所有组件都不要声明宽度,而由其亲元素或格栅系统来决定。 - -**坚决不要** 声明高度。高度应当仅仅用于尺寸已经固定的东西,例如图片和 CSS Sprite。在 `p`,`ul`,`div` 等元素上不应当声明高度。如果需要的话可以写 `line-height`,这个更加灵活。 - -格栅系统应当当作书架来理解。是它们容纳内容,而不是把它们本身当成内容装起来,正如你先搭起书架再把东西放进去。比起声明它们的尺寸,把格栅系统和元素的其它属性分来开处理更有助于布局,也使得我们的前端工作更高效。 - -你在格栅系统上不应当添加任何样式,他们仅仅是为布局而用。在格栅系统内部再添加样式。在格栅系统中任何情况下都不要添加盒模型相关属性。 - -## UI 尺寸 - -我用很多方法设定 UI 尺寸,包括百分比,`px`,`em`,`rem` 以及干脆什么都不用。 - -理想情况下,格栅系统应当用百分比设定。如上所述,因为我用格栅系统来固定栏宽和页宽,所以我可以不用理会元素的尺寸。 - -我用 rem 定义字号,并且辅以 px 以兼容旧浏览器。这可以兼具 em 和 px 的优势。下面是一个非常漂亮的 Sass Mixin,假设你在别处声明了基本字号(base-font-size)的话,用它就可以生成 rem 以及兼容旧浏览器的 px。 - - @mixin font-size($font-size){ - font-size:$font-size +px; - font-size:$font-size / $base-font-size +rem; - } - -我只在已经固定尺寸的元素上使用 px,包括图片以及尺寸已经用 px 固定的 CSS Sprite。 - -### 字号 - -我会定义一些与格栅系统原理类似的 class 来声明字号。这些 class 可以用于双重标题分级,关于这点请阅读 [Pragmatic, practical font-sizing in CSS](http://csswizardry.com/2012/02/pragmatic-practical-font-sizing-in-css)。 - -## 简写 - -**CSS 简写应当谨慎使用。** - -编写像 `background:red;` 这样的属性的确很省事,但是你这么写的意思其实是同时声明 `background-image:none; background-position:top left; background-repeat: repeat; background-color:red;`。虽然大多数时候这样不会出什么问题,但是哪怕只出一次问题就值得考虑要不要放弃简写了。这里应当将其改为 `background-color:red;`。 - -类似的,像 `margin:0;` 这样的声明的确简洁清爽,但是还是应当 **尽量写清楚**。如果你只是想修改底边的 `margin`,最好具体一些,写成 `margin-bottom:0;`。 - -反过来,你需要声明的属性也要写清楚,不要因为简写而波及其它属性。例如如果你只想改掉底部的 `margin`,那就不要用会把其它边距也清零的 `margin:0`。 - -简写虽然是好东西,但是注意切勿滥用。 - -## ID - -在我们开始处理选择器之前,牢记这句话: - -**在 CSS 里坚决不要用 ID。** - -在 HTML 里 ID 可以用于 JS 以及锚点定位,但是在 CSS 里只要用 class,一个 ID 也不要用。 - -Class 的优势在于复用性,而且私有度也并不高。私有度非常容易导致问题,所以将其降低就尤为重要。ID 的私有度是 class 的 **255** 倍,所以在 CSS 中坚决不要使用。 - -## 选择器 - -务必保持选择器简短高效。 - -通过页面元素位置而定位的选择器并不理想。例如 `.sidebar h3 span{}` 这样的选择器就是定位过于依赖相对位置,所以很难把 span 移到 h3 和 sidebar 外面并保持其样式。 - -结构复杂的选择器将会影响性能。选择器结构越复杂(如 `.sidebar h3 span` 为三层,`.content ul p a` 是四层),浏览器的消耗就越大。 - -尽量使得样式不依赖于其定位,尽量保持选择器简洁清晰。 - -作为一个整体,选择器应当尽量简短(例如只有一层结构),但是 class 名则不应当过于简略,例如 `.user-avatar` 就远比 `.usr-avt` 好。 - -**牢记:** class 无所谓是否语义化;应当关注它们是否合理。不要强调 class 名要符合语义,而要注重使用合理且不会过时的名称。 - -### 过度修饰的选择器 - -由前文所述,过度修饰的选择器并不理想。 - -过度修饰的选择器是指像 `div.promo` 这样的。很可能你只用 `.promo` 也能得到相同的效果。当然你可能偶尔会需要用元素类型来修饰 class(例如你写了一个 `.error` 而且想让它在不同的元素类型中显示效果不一样,例如 `.error{ color:red; }` `div.error{ padding:14px;}`),但是大多数时候还是应当尽量避免。 - -再举一个修饰过度的选择器例子,`ul.nav li a{}`。如前文所说,我们马上就可以删掉 `ul` 因为我们知道 `.nav` 是个列表,然后我们就可以发现 `a` 一定在 `li` 中,所以我们就能将这个选择器改写成 `.nav a{}`。 - -### 选择器性能 - -虽然浏览器性能日渐提升,渲染 CSS 速度越来越快,但是你还是应当关注效率。使用间断、没有嵌套的选择器,不把全局选择器(`*{}`)用作核心选择器,避免使用日渐复杂的 CSS3 新选择器可以避免这样的问题。 - -译注,核心选择器:浏览器解析选择器为从右向左的顺序,最右端的元素是样式生效的元素,是为核心选择器。 - -## 使用 CSS 选择器的目的 - -比起努力运用选择器定位到某元素,更好的办法是直接给你想要添加样式的元素直接添加一个 class。我们以 `.header ul{}` 这样一个选择器为例。 - -假定这个 `ul` 就是这个网站的全站导航,它位于 header 中,而且目前为止是 header 中唯一的 `ul` 元素。`.header ul{}` 的确可以生效,但是这样并不是好方法,它很容易过时,而且非常晦涩。如果我们在 header 中再添加一个 `ul` 的话,它就会套用我们给这个导航部分写的样式,哪怕我们设想的不是这个效果。这意味着我们要么要重构许多代码,要么给后面的 `ul` 新写许多样式来抵消之前的影响。 - -你的选择器必须符合你要给这个元素添加样式的原因。思考一下, **「我定位到这个元素,是因为它是 `.header` 下的 `ul`,还是因为它是我的网站导航?」**这将决定你应当如何使用选择器。 - -确保你的核心选择器不是类型选择器,也不是高级对象或抽象选择器。例如你在我们的 CSS 中肯定找不到诸如 `.sidebar ul{}` 或者 `.footer .media{}` 这样的选择器。 - -表达清晰:直接找到你要添加样式的元素,而非其亲元素。不要想当然地认为 HTML 不会改变。 **用 CSS 直接命中你需要的元素,而非投机取巧。** - -完整内容请参考我的文章 [Shoot to kill; CSS selector intent](http://csswizardry.com/2012/07/shoot-to-kill-css-selector-intent/) - -## `!important` - -只在起辅助作用的 class 上用 `!important`。用 `!important` 提升优先级也可以,例如如果你要让某条规则 **一直** 生效的话,可以用 `.error{ color:red!important; }`。 - -避免主动使用 `!important`。例如 CSS 写得很复杂时不要用它来取巧,要好好整理并重构之前的部分,保持选择器简短并且避免用 ID 将效果拔群。 - -## 魔数与绝对定位 - -魔数(Magic Number)是指那些「凑巧有效果」的数字,这东西非常不好,因为它们只是治标不治本而且缺乏拓展性。 - -例如 `.dropdown-nav li:hover ul{ top:37px; }` 把下拉菜单移动下来远非良策,因为这里的 37px 就是个魔数。37px 会生效的原因是因为这时 `.dropbox-nav` 碰巧高 37px 而已。 - -这时你应该用 `.dropdown-nav li:hover ul{ top:100%; }`,也即无论 `.dropbox-down` 多高,这个下拉菜单都会往下移动 100%。 - -每当你要在代码中放入数字的时候,请三思而行。如果你能用一个关键字(例如 `top:100%` 意即「从上面拉到最下面」)替换之,或者有更好的解决方法的话,就尽量避免直接出现数字。 - -你在 CSS 中留下的每一个数字,都是你许下而不愿遵守的承诺。 - -## 条件判断 - -专门为 IE 写的样式基本上都是可以避免的,唯一需要为 IE 专门处理的是为了处理 IE 不支持的内容(例如 PNG)。 - -简而言之,如果你重构 CSS 的话,所有的布局和盒模型都不用额外兼容 IE。也就是说你基本上不用 `` 或者类似的兼容 IE 的写法。 - -## Debugging - -如果你要解决 CSS 问题的话, **先把旧代码拿掉再写新的** 。如果旧的 CSS 中有问题的话,写新代码是解决不了的。 - -把 CSS 代码和 HTML 部分删掉,直到没有 BUG 为止,然后你就知道问题出在哪里了。 - -有时候写上一个 `overflow:hidden` 或者其它能把问题藏起来的代码的确效果立竿见影,但是 overflow 方面可能根本就没问题。所以 **要治本,而不是单纯治标**。 - -## 预处理器 - -我用 Sass。使用时应当 **灵活运用** 。用 Sass 可以令你的 CSS 更强大,但是不要嵌套得太复杂。在 Vanilla CSS 中,只在必要的地方用嵌套即可,例如: - - .header{} - .header .site-nav{} - .header .site-nav li{} - .header .site-nav li a{} - -这样的写法在普通 CSS 里完全用不到。以下为 **不好的** Sass 写法: - - .header{ - .site-nav{ - li{ - a{} - } - } - } - -如果你用 Sass 的话,尽量这么写: - - .header{} - .site-nav{ - li{} - a{} - } +# CSS 筆記、建議與指導方針總整理 + +--- + +在參與大規模、歷時漫長且人手眾多的專案時,所有網頁開發人員都能遵守以下原則極為重要: + ++ **維持 CSS 樣式的可維護性 (maintainable)** ++ **維持撰寫風格清晰明瞭並具可讀性 (readable)** ++ **維持 CSS 樣式的延展性 (scalable)** + +為了達成上述原則,我們必須使用許多方法才能達成這個目標。 + +本文第一部分將探討語法、格式以及 CSS 剖析;第二部分將從方法論(approach)、思維框架(mindframe)以及編寫與架構 CSS 的態度著手。 + +## 本文內容 + +* 剖析 CSS 文件 + * 總覽 + * 單一檔案與多檔案 + * 目錄大綱 + * 區段標題 +* 樣式載入順序 +* 規則解析 +* 命名規範 + * JavaScript 鉤子 + * I18n +* 註釋 + * 註釋的拓展用法 + * 准修飾選取器 + * 原始碼標籤 + * 繼承標記 +* 編寫 CSS +* 編寫新組件 +* 物件導向 CSS (OOCSS) +* 版面配置 +* 界面尺寸 + * 字號調節 +* 簡寫 +* IDs +* 選取器 + * 過修飾選取器 + * 選取器性能 +* 選取器繼承 +* `!important` +* 魔數與絕對比例 +* 條件判斷 +* 偵錯(Debugging) +* 預處理 + +註:ID 不能包含漢字,所以在 Github Markdown 中暫時無法實現跳轉。 + +--- + +## 剖析 CSS 文件 + +無論編寫什麼文件,我們都應當盡力維持統一的風格,包括統一的註釋、統一的語法與統一的命名規範。 + +### 總則 + +盡量將行寬控制在 80 字節以下。漸變(gradient)相關的語法以及註釋中的 URL 等可以算作例外,畢竟這部分我們也無能為力。 + +我傾向於用 4 個空格而非 Tab 縮進,並且將聲明拆分成多行。 + +### 單一檔案與多檔案 + +有些人喜歡將樣式寫成一個大文件,這並不賴,而且如果你按照下文的規則來編寫的話也不會遇到什麼問題。我在遷移至 Sass 之後,開始將樣式拆分成眾多小文件。這也不賴。無論你採用什麼方式,下文中的規則都將適用。這兩種寫法僅僅在目錄以及區段標題上有所差異。 + +### 目錄大綱 + +在 CSS 的開頭,我會寫一份目錄,例如: + + /*------------------------------------*\ + $CONTENTS + \*------------------------------------*/ + /** + * CONTENTS............You』re reading it! + * RESET...............Set our reset defaults + * FONT-FACE...........Import brand font files + */ + +這份目錄可以告訴其他網頁開發人員這個文件中具體含有哪些內容。這份目錄中的每一項都與其對應的區段標題相同。 + +如果你在維護一份規模較大的單文件 CSS,對應的區塊將也在同一文件中。如果你是在編寫一組小文件,那麼目錄中的每一項應當對應相應的 @include 語句。 + +### 區段標題 + +目錄應當對應區塊的標題。請看如下範例: + + /*------------------------------------*\ + $RESET + \*------------------------------------*/ + +區段標題前綴 `$` 可以讓我們使用([Cmd|Ctrl]+F)命令查找 `$[SECTION-NAME]`,同時 **將搜尋範圍限制在區段標題中**。 + +如果你在維護一份大文件,那麼在區塊之間空 5 行,如下: + + /*------------------------------------*\ + $RESET + \*------------------------------------*/ + [Our + reset + styles] + + + + + + /*------------------------------------*\ + $FONT-FACE + \*------------------------------------*/ + +在大文件中快速翻動時這些大塊的空檔有助於區分區塊。 + +如果你在維護多份、以 @include 連接的 CSS 的話,在每份文件頭加上標題即可,不必這樣空行。 + +## 樣式載入順序 + +盡量按照特定順序撰寫樣式規則,這將確保你充分發揮 CSS 縮寫中第一個 C 的意義:Cascade,串聯。 + +一份規劃良好的 CSS 應當按照如下順序載入或撰寫: + +1. **Reset** 重置所有樣式為 0 +2. **元素類型** 沒有設定 class 的 `h1`、`ul` 等元素,也就是元素的預設樣式 +3. **物件以及抽像內容** 最一般、最基礎的設計模式 +4. **元件** 從物件組合而成的完整元件 +5. **修補** 針對異常狀態 + +如此一來,當你依次編寫 CSS 時,每個區塊都可以自動繼承在它之前區塊的屬性。這樣就可以減少原始碼相互抵消的部分,減少某些特殊的問題,構成設計更理想的 CSS 結構。 + +關於這方面的更多信息,強烈推薦 Jonathan Snook 的 [SMACSS](http://smacss.com)。 + +## CSS 規則集分析 + + [selector]{ + [property]:[value]; + [<- Declaration ->] + } + + [選取器]{ + [屬性]:[值]; + [<- 聲明 ->] + } + +編寫 CSS 樣式時,我習慣遵守這些規則: + +* class 名稱以連字符(-)連接,除了下文提到的 BEM 命名法; +* 縮進 4 空格; +* 聲明拆分成多行; +* 聲明以相關性順序排列,而非字母順序; +* 有前綴的聲明適當縮進,對齊其值; +* 縮進樣式從而反映 DOM; +* 保留最後一條聲明結尾的分號。 + +例如: + + .widget{ + padding:10px; + border:1px solid #BADA55; + background-color:#C0FFEE; + -webkit-border-radius:4px; + -moz-border-radius:4px; + border-radius:4px; + } + .widget-heading{ + font-size:1.5rem; + line-height:1; + font-weight:bold; + color:#BADA55; + margin-right:-10px; + margin-left: -10px; + padding:0.25em; + } + +我們可以發現,`.widget-heading` 是 `.widget` 的子元素,因為前者比後者多縮進了一級。這使得網頁開發人員在閱讀這些樣式時可以快速獲取信息。 + +我們還可以發現 `.widget-heading` 的聲明是根據其相關性排列的:`.widget-heading` 是文字元素,所以我們先新增字體相關的樣式聲明,接下來是其它的。 + +以下是一個沒有拆分成多行的例子: + + .t10 { width:10% } + .t20 { width:20% } + .t25 { width:25% } /* 1/4 */ + .t30 { width:30% } + .t33 { width:33.333% } /* 1/3 */ + .t40 { width:40% } + .t50 { width:50% } /* 1/2 */ + .t60 { width:60% } + .t66 { width:66.666% } /* 2/3 */ + .t70 { width:70% } + .t75 { width:75% } /* 3/4*/ + .t80 { width:80% } + .t90 { width:90% } + +在這個例子(來自[inuit.css』s table grid system](https://github.com/csswizardry/inuit.css/blob/master/inuit.css/partials/base/_tables.scss#L88))中,將 CSS 放在一行內可以使得原始碼更緊湊。 + +## 命名規範 + +一般情況下我都是以連字符(-)連接 class 的名字(例如 `.foo-bar` 而非 `.foo_bar` 或 `.fooBar`),不過在某些特定的時候我會用 BEM(Block, Element, Modifier)命名法。 + +BEM 命名法可以使得選取器更規範,更清晰,更具語義。 + +該命名法按照如下格式: + + .block{} + .block__element{} + .block--modifier{} + +其中: + +* `.block` 代表某個基本的抽像元素; +* `.block__element` 代表 `.block` 這一整體的一個子元素; +* `.block--modifier` 代表 `.block` 的某個不同狀態。 + +打個比方: + + .person{} + .person--woman{} + .person__hand{} + .person__hand--left{} + .person__hand--right{} + +這個例子中我們描述的最基本元素是一個人,然後這個人可能是一個女人。我們還知道人擁有手,這些是人體的一部分,而手也有不同的狀態,如同左手與右手。 + +這樣我們就可以根據親元素來規定選取器的命名空間並傳達該選取器的職能,它是一個子元素(`__`)還是不同狀態(`--`)? + +由此,`.page-wrapper` 是一個獨立的選取器。這是一個符合規範的命名,因為它不是其它元素的子元素或其它狀態;然而 `.widget-heading` 則與其它對像有關聯,它應當是 `.widget` 的子元素,所以我們應當將其重命名為 `.widget__heading`。 + +BEM 命名法雖然不太好看,而且相當冗長,但是它使得我們可以通過名稱快速獲知元素的功能和元素之間的關係。與此同時,BEM 語法中的重複部分非常有利於 gzip 的壓縮算法。 + +無論你是否使用 BEM 命名法,你都應當確保 class 命名得當,力保一字不多、一字不少;將元素命名抽像化以提高復用性(例如 `.ui-list`,`.media`)。由此延伸出去的元素命名則要盡量精準(例如 `.user-avatar-link`)。不用擔心 class 名的數量或長度,因為寫得好的原始碼 gzip 也能有效壓縮。 + +### HTML 中的 class + +為了確保易讀性,在 HTML 標記中用兩個空格隔開 class 名,例如: + +
+ +增加的空格應當可以使得在使用多個 class 時更易閱讀與定位。 + +### JS 鉤子 + +**千萬不要把 CSS 樣式用作 JavaScript 鉤子。**把 JS 行為與樣式混在一起將無法對其分別處理。 + +如果你要把 JS 和某些標記綁定起來的話,寫一個 JS 專用的 class。簡單地說就是劃定一個前綴 `.js-` 的命名空間,例如 `.js-toggle`,`.js-drag-and-drop`。這意味著我們可以通過 class 同時綁定 JS 和 CSS 而不會因為衝突而引發麻煩。 + + + + +上面的這個標記有兩個 class,你可以用其中一個來給這個可排序的表格欄新增樣式,用另一個新增排序功能。 + +### I18n + +雖然我(該 CSS Guideline 文件原作者 Harry Roberts)是個英國人,而且我一向拼寫 colour 而非 color,但是為了統一性,我認為在 CSS 中用美式拼法更佳。CSS 以及其它多數語言都是以美式拼法編寫,所以如果在 `.colour-picker{}` 中寫 `color:red` 就缺乏統一性。我以前主張同時用兩種拼法,例如: + + .color-picker, + .colour-picker{ + } + +但是我最近參與了一份規模龐大的 Sass 專案,這個專案中有許多的顏色變量(例如 `$brand-color`,`$highlight-color` 等等),每個變量要維護兩種拼法實在辛苦,要查找並替換時也需要兩倍的工作量。 + +所以為了統一性,把所有的 class 與變量都以你參與的專案的慣用拼法命名即可。 + +## 註釋 + +我使用行寬不超過 80 字節的塊狀註釋: + + /** + * This is a docBlock style comment + * + * This is a longer description of the comment, describing the code in more + * detail. We limit these lines to a maximum of 80 characters in length. + * + * We can have markup in the comments, and are encouraged to do so: + * +
+

Lorem

+
+ * + * We do not prefix lines of code with an asterisk as to do so would inhibit + * copy and paste. + */ + + + /** + * 這是一個文件塊(DocBlock)風格的註釋。 + * + * 這裡開始是描述更詳細、篇幅更長的註釋正文。當然,我們要把行寬控制在 80 字以內。 + * + * 我們可以在註釋中嵌入 HTML 標記,而且這也是個不錯的辦法: + * +
+

Lorem

+
+ * + * 如果是註釋內嵌的標記的話,在它前面不加星號,否則會被複製進去。 + */ + +在註釋中應當盡量詳細描述原始碼,因為對你來說清晰易懂的內容對其他人可能並非如此。每寫一部分原始碼就要專門寫註釋以詳解。 + +### 註釋的拓展用法 + +註釋有許多很先進的用法,例如: + +* 准修飾選取器 +* 原始碼標籤 +* 繼承標記 + +#### 准修飾選取器 + +你應當避免過分修飾選取器,例如如果你能寫 `.nav{}` 就盡量不要寫 `ul.nav{}`。過分修飾選取器將影響性能,影響 class 復用性,增加選取器私有度。這些都是你應當竭力避免的。 + +不過有時你可能希望告訴其他網頁開發人員 class 的使用範圍。以 `.product-page` 為例,這個 class 看起來像是一個根容器,可能是 `html` 或者 `body` 元素,但是僅憑 `.product-page` 則無法判斷。 + +我們可以在選取器前加上准修飾(即將前面的類型選取器註釋掉)來描述我們規劃的 class 作用範圍: + + /*html*/.product-page{} + +這樣我們就能準確獲知該 class 的作用範圍而不會影響復用性。 + +其它例子如: + + /*ol*/.breadcrumb{} + /*p*/.intro{} + /*ul*/.image-thumbs{} + +這樣我們就能在不影響原始碼私有度的前提下獲知 class 作用範圍。 + +#### 原始碼標籤 + +如果你寫了一個新規則的話,可以在它上面加上標籤,例如: + + /** + * ^navigation ^lists + */ + .nav{} + + /** + * ^grids ^lists ^tables + */ + .matrix{} + +這些標籤可以使得其他網頁開發人員快速找到相關原始碼。如果一個網頁開發人員需要查找和列表相關的部分,他只要搜尋 `^lists` 就能快速定位到 `.nav`,`.matrix` 以及其它相關部分。 + +#### 繼承標記 + +將面向對象的思路用於 CSS 編寫的話,你經常能找到兩部分 CSS 密切相關(其一為基礎,其一為拓展)卻分列兩處。我們可以用繼承標記來在原元素和繼承元素之間建立緊密聯繫。這些在註釋中的寫法如下: + +在元素的基本樣式中: + + /** + * Extend `.foo` in theme.css + */ + .foo{} + +在元素的拓展樣式中: + + /** + * Extends `.foo` in base.css + */ + .bar{} + +這樣一來我們就能在兩塊相隔很遠的原始碼間建立緊密聯繫。 + +--- + +## 編寫 CSS + +之前的章節主要探討如何規劃 CSS,這些都是易於量化的規則。本章將探討更理論化的東西,也將探討我們的態度與方法。 + +## 編寫新組件 + +編寫新組件時,要在著手處理 CSS **之前** 寫好 HTML 部分。這可以令你準確判斷哪些 CSS 屬性可以繼承,避免重複浪費。 + +先寫標記的話,你就可以關注數據、內容與語義,在這之後再新增需要的 class 和 CSS 樣式。 + +## 物件導向 CSS (OOCSS) + +我以物件導向 CSS 的方式寫原始碼。我把組件分成結構(對像)與外觀(拓展)。正如以下分析(注意這個只是筆記而非例子): + + .room{} + + .room--kitchen{} + .room--bedroom{} + .room--bathroom{} + +我們在屋子裡有許多房間,它們都有共同的特點:它們都包含地板、天花板、牆壁和門。這些共享的部分我們可以放到一個抽像的 `.room{}` class 中。不過我們還有其它與眾不同的房間:一個廚房可能有地磚,臥室可能有地毯,洗手間可能沒有窗戶但是臥室會有,每個房間的牆壁顏色也許也會不一樣。物件導向 CSS 的思路使得我們把相同部分抽像出來組成結構部分,然後用更具體的 class 來拓展這些特徵並新增特殊的處理方法。 + +所以比起編寫大量的特殊模塊,應當努力找出這些模塊中重複的設計模式並將其抽像出來,寫成一個可以復用的 class,將其用作基礎然後編寫其它拓展模塊的特殊情形。 + +當你要編寫一個新組件時,將其拆分成結構和外觀。編寫結構部分時用最通用 class 以保證復用性,編寫外觀時用更具體的 class 來新增設計方法。 + +## 版面配置 + +所有組件都不要聲明寬度,而由其親元素或格柵系統來決定。 + +**堅決不要** 聲明高度。高度應當僅僅用於尺寸已經固定的東西,例如圖片和 CSS Sprite。在 `p`,`ul`,`div` 等元素上不應當聲明高度。如果需要的話可以寫 `line-height`,這個更加靈活。 + +格柵系統應當當作書架來理解。是它們容納內容,而不是把它們本身當成內容裝起來,正如你先搭起書架再把東西放進去。比起聲明它們的尺寸,把格柵系統和元素的其它屬性分來開處理更有助於版面配置,也使得我們的前端工作更高效。 + +你在格柵系統上不應當新增任何樣式,他們僅僅是為版面配置而用。在格柵系統內部再新增樣式。在格柵系統中任何情況下都不要新增盒模型相關屬性。 + +## UI 尺寸 + +我用很多方法設定 UI 尺寸,包括百分比,`px`,`em`,`rem` 以及乾脆什麼都不用。 + +理想情況下,格柵系統應當用百分比設定。如上所述,因為我用格柵系統來固定欄寬和頁寬,所以我可以不用理會元素的尺寸。 + +我用 rem 定義字號,並且輔以 px 以兼容舊瀏覽器。這可以兼具 em 和 px 的優勢。下面是一個非常漂亮的 Sass Mixin,假設你在別處聲明了基本字號(base-font-size)的話,用它就可以生成 rem 以及兼容舊瀏覽器的 px。 + + @mixin font-size($font-size){ + font-size:$font-size +px; + font-size:$font-size / $base-font-size +rem; + } + +我只在已經固定尺寸的元素上使用 px,包括圖片以及尺寸已經用 px 固定的 CSS Sprite。 + +### 字號 + +我會定義一些與格柵系統原理類似的 class 來聲明字號。這些 class 可以用於雙重標題分級,關於這點請閱讀 [Pragmatic, practical font-sizing in CSS](http://csswizardry.com/2012/02/pragmatic-practical-font-sizing-in-css)。 + +## 簡寫 + +**CSS 簡寫應當謹慎使用。** + +編寫像 `background:red;` 這樣的屬性的確很省事,但是你這麼寫的意思其實是同時聲明 `background-image:none; background-position:top left; background-repeat: repeat; background-color:red;`。雖然大多數時候這樣不會出什麼問題,但是哪怕只出一次問題就值得考慮要不要放棄簡寫了。這裡應當將其改為 `background-color:red;`。 + +類似的,像 `margin:0;` 這樣的聲明的確簡潔清爽,但是還是應當 **盡量寫清楚**。如果你只是想修改底邊的 `margin`,最好具體一些,寫成 `margin-bottom:0;`。 + +反過來,你需要聲明的屬性也要寫清楚,不要因為簡寫而波及其它屬性。例如如果你只想改掉底部的 `margin`,那就不要用會把其它邊距也清零的 `margin:0`。 + +簡寫雖然是好東西,但是注意切勿濫用。 + +## ID + +在我們開始處理選取器之前,牢記這句話: + +**在 CSS 裡堅決不要用 ID。** + +在 HTML 裡 ID 可以用於 JS 以及錨點定位,但是在 CSS 裡只要用 class,一個 ID 也不要用。 + +Class 的優勢在於復用性,而且私有度也並不高。私有度非常容易導致問題,所以將其降低就尤為重要。ID 的私有度是 class 的 **255** 倍,所以在 CSS 中堅決不要使用。 + +## 選取器 + +務必維持選取器簡短高效。 + +通過頁面元素位置而定位的選取器並不理想。例如 `.sidebar h3 span{}` 這樣的選取器就是定位過於依賴相對位置,所以很難把 span 移到 h3 和 sidebar 外面並維持其樣式。 + +結構複雜的選取器將會影響性能。選取器結構越複雜(如 `.sidebar h3 span` 為三層,`.content ul p a` 是四層),瀏覽器的消耗就越大。 + +盡量使得樣式不依賴於其定位,盡量維持選取器簡潔清晰。 + +作為一個整體,選取器應當盡量簡短(例如只有一層結構),但是 class 名則不應當過於簡略,例如 `.user-avatar` 就遠比 `.usr-avt` 好。 + +**牢記:** class 無所謂是否語義化;應當關注它們是否合理。不要強調 class 名要符合語義,而要注重使用合理且不會過時的名稱。 + +### 過度修飾的選取器 + +由前文所述,過度修飾的選取器並不理想。 + +過度修飾的選取器是指像 `div.promo` 這樣的。很可能你只用 `.promo` 也能得到相同的效果。當然你可能偶爾會需要用元素類型來修飾 class(例如你寫了一個 `.error` 而且想讓它在不同的元素類型中顯示效果不一樣,例如 `.error{ color:red; }` `div.error{ padding:14px;}`),但是大多數時候還是應當盡量避免。 + +再舉一個修飾過度的選取器例子,`ul.nav li a{}`。如前文所說,我們馬上就可以刪掉 `ul` 因為我們知道 `.nav` 是個列表,然後我們就可以發現 `a` 一定在 `li` 中,所以我們就能將這個選取器改寫成 `.nav a{}`。 + +### 選取器性能 + +雖然瀏覽器性能日漸提升,渲染 CSS 速度越來越快,但是你還是應當關注效率。使用間斷、沒有嵌套的選取器,不把全局選取器(`*{}`)用作核心選取器,避免使用日漸複雜的 CSS3 新選取器可以避免這樣的問題。 + +譯注,核心選取器:瀏覽器解析選取器為從右向左的順序,最右端的元素是樣式生效的元素,是為核心選取器。 + +## 使用 CSS 選取器的目的 + +比起努力運用選取器定位到某元素,更好的辦法是直接給你想要新增樣式的元素直接新增一個 class。我們以 `.header ul{}` 這樣一個選取器為例。 + +假定這個 `ul` 就是這個網站的全站導航,它位於 header 中,而且目前為止是 header 中唯一的 `ul` 元素。`.header ul{}` 的確可以生效,但是這樣並不是好方法,它很容易過時,而且非常晦澀。如果我們在 header 中再新增一個 `ul` 的話,它就會套用我們給這個導航部分寫的樣式,哪怕我們設想的不是這個效果。這意味著我們要麼要重構許多原始碼,要麼給後面的 `ul` 新寫許多樣式來抵消之前的影響。 + +你的選取器必須符合你要給這個元素新增樣式的原因。思考一下, **「我定位到這個元素,是因為它是 `.header` 下的 `ul`,還是因為它是我的網站導航?」**這將決定你應當如何使用選取器。 + +確保你的核心選取器不是類型選取器,也不是高級對像或抽像選取器。例如你在我們的 CSS 中肯定找不到諸如 `.sidebar ul{}` 或者 `.footer .media{}` 這樣的選取器。 + +表達清晰:直接找到你要新增樣式的元素,而非其親元素。不要想當然地認為 HTML 不會改變。 **用 CSS 直接命中你需要的元素,而非投機取巧。** + +完整內容請參考我的文章 [Shoot to kill; CSS selector intent](http://csswizardry.com/2012/07/shoot-to-kill-css-selector-intent/) + +## `!important` + +只在起輔助作用的 class 上用 `!important`。用 `!important` 提升優先級也可以,例如如果你要讓某條規則 **一直** 生效的話,可以用 `.error{ color:red!important; }`。 + +避免主動使用 `!important`。例如 CSS 寫得很複雜時不要用它來取巧,要好好整理並重構之前的部分,維持選取器簡短並且避免用 ID 將效果拔群。 + +## 魔數與絕對定位 + +魔數(Magic Number)是指那些「湊巧有效果」的數字,這東西非常不好,因為它們只是治標不治本而且缺乏延展性。 + +例如 `.dropdown-nav li:hover ul{ top:37px; }` 把下拉菜單移動下來遠非良策,因為這裡的 37px 就是個魔數。37px 會生效的原因是因為這時 `.dropbox-nav` 碰巧高 37px 而已。 + +這時你應該用 `.dropdown-nav li:hover ul{ top:100%; }`,也即無論 `.dropbox-down` 多高,這個下拉菜單都會往下移動 100%。 + +每當你要在原始碼中放入數字的時候,請三思而行。如果你能用一個關鍵字(例如 `top:100%` 意即「從上面拉到最下面」)替換之,或者有更好的解決方法的話,就盡量避免直接出現數字。 + +你在 CSS 中留下的每一個數字,都是你許下而不願遵守的承諾。 + +## 條件判斷 + +專門為 IE 寫的樣式基本上都是可以避免的,唯一需要為 IE 專門處理的是為了處理 IE 不支持的內容(例如 PNG)。 + +簡而言之,如果你重構 CSS 的話,所有的版面配置和盒模型都不用額外兼容 IE。也就是說你基本上不用 `` 或者類似的兼容 IE 的寫法。 + +## 偵錯(Debugging) + +如果你要解決 CSS 問題的話, **先把舊原始碼拿掉再寫新的** 。如果舊的 CSS 中有問題的話,寫新原始碼是解決不了的。 + +把 CSS 原始碼和 HTML 部分刪掉,直到沒有 BUG 為止,然後你就知道問題出在哪裡了。 + +有時候寫上一個 `overflow:hidden` 或者其它能把問題藏起來的原始碼的確效果立竿見影,但是 overflow 方面可能根本就沒問題。所以 **要治本,而不是單純治標**。 + +## 預處理器 + +我用 Sass。使用時應當 **靈活運用** 。用 Sass 可以令你的 CSS 更強大,但是不要嵌套得太複雜。在 Vanilla CSS 中,只在必要的地方用嵌套即可,例如: + + .header{} + .header .site-nav{} + .header .site-nav li{} + .header .site-nav li a{} + +這樣的寫法在普通 CSS 裡完全用不到。以下為 **不好的** Sass 寫法: + + .header{ + .site-nav{ + li{ + a{} + } + } + } + +如果你用 Sass 的話,盡量這麼寫: + + .header{} + .site-nav{ + li{} + a{} + }