[TOC]
网页的第二个组件就是在层叠样式表(CSS)中包含的表现信息。Web浏览器成功实现CSS后,整整一代web开发者对他们网站的外观和体验拥有了全部的控制权。
正如网页信息在语义方面由 [HTML 标记](http://coderlmn.github.io/code-standards/#markup) 描述, CSS 从表现方面则是通过对视觉属性的定义来描述网页。 CSS 的强大之处在于,这些属性可以混合并通过各种标示符匹配,它可以通过样式规则的分层(”层叠“)来控制页面的布局和视觉特征。
### 编码总体原则
* 从外部文件加载CSS,尽可能减少文件数。加载标签必须放在文件的 HEAD 部分。
* 用 LINK 标签加载,[永远不要用@import](http://blog.amodernfable.com/2008/01/21/thoughts-on-linking-to-stylesheets/)。
加载样式表
~~~
<link rel="stylesheet" type="text/css" href="myStylesheet.css" />
~~~
不要用内联式样式
~~~
<p style="font-size: 12px; color: #FFFFFF">This is poor form, I say</p>
~~~
* 不要在文件中用内联式引入的样式,不管它是定义在样式标签里还是直接定义在元素上。这样会很难追踪样式规则。
* 使用 [normalize.css](http://necolas.github.com/normalize.css/) 让渲染效果在不同浏览器中更一致。
* 使用类似 [YUI fonts.css](http://developer.yahoo.com/yui/fonts/) 的字体规格化文件。
* 定义样式的时候,对样式在页面只出现一次的元素用id,其他的用class。
* 理解 [层叠和选择器的明确度](http://www.stuffandnonsense.co.uk/archives/css_specificity_wars.html) ,这样你就可以写出非常简洁且高效的代码。
* 编写性能优化的选择器。尽可能避免使用开销大的CSS选择器。例如,避免 * 通配符选择器,也不要叠加限定条件到 ID 选择器(例如 `div#myid`)或 class 选择器(例如 `table.results`)上。这对于速度至上并包含了成千上万个DOM元素的web应用来说尤为重要。更多相关内容请参阅 MDN 上的这篇 [《编写高效CSS》](https://developer.mozilla.org/en/Writing_Efficient_CSS)。
### CSS盒子模型
深入学习和理解CSS及基于浏览器的盒子模型,对于掌握CSS布局的基础是非常必要的。
![CSS Box Model](https://box.kancloud.cn/2015-08-05_55c1b96cecc5d.png "CSS Box Model")
3D CSS 盒子模型示意图, 由 [Jon Hicks](http://hicksdesign.co.uk/boxmodel/) 绘制。
### CSS 校验
我们一般不用 [W3C 校验器](http://jigsaw.w3.org/css-validator/)。
### CSS 格式
最低要求:选择器单独占一行,每个属性占一行。属性声明要有缩进。
作为提高的要求,关联或孩子样式要增加2-4个空格的缩进。这样有利于分层查看和组织,产生(对某些人来说)可读性更好的样式表。
另外,在给一个样式指定多个选择器的时候,把每个选择器单独放一行是个好主意。这样可以避免一行变得太长,也能提高可读性及版本控制流程。
~~~
.post-list li a{
color:#A8A8A8;
}
.post-list li a:hover{
color:#000;
text-decoration:none;
}
.post-list li .author a,
.post-list li .author a:hover{
color:#F30;
text-transform:uppercase;
}
~~~
在多个开发者协作环境下,避免用单行CSS格式,因为这样会给版本控制带来问题。
#### 字母排序
如果你对性能情有独钟, [对CSS属性进行字母排序有利于在GZIP压缩中识别大量可重复的特征](http://www.barryvan.com.au/2009/08/css-minifier-and-alphabetiser/)。
### Classes vs. IDs
对于所用的样式只出现一次的元素,给它设一个id属性。这个属性只会应用于该元素,不会用到其他地方。Class属性则可以用到多个具有相同样式属性的元素上。具有相同外观和表现的元素可以具有相同的class名。
~~~
<ul id="categories">
<li class="item">Category 1</li>
<li class="item">Category 2</li>
<li class="item">Category 3</li>
</ul>
~~~
### 选择器命名的惯例
无论是 ID 还是 class,对任何东西最好总是根据 **它是什么** 而不是 **它看上去是什么样子** 来命名。 比如一个页面上的特别提示的 class 名是 `bigBlueText` (大蓝字),可它的样式早就被改成红色小字体,这个名字就没意义了。使用更聪明的惯例如 `noteText` (提示文字)就好多了,因为即使视觉样式改变了,它也还是管用的。
### 选择器
[CSS3 选择器](http://www.w3.org/TR/2009/PR-css3-selectors-20091215/) 规格引入了一整套对于更好地选择元素极其有用的 [CSS 选择器](http://www.w3.org/TR/css3-selectors/#selectors)。
#### 伪类
[伪类](http://www.w3.org/TR/css3-selectors/#pseudo-classes) 使你能动态地修饰网页内容的样式。有些伪类从CSS1 (`:visited, :hover`等) 和 CSS2 (`:first-child, :lang`)那时候开始就有了。CSS3又往列表里加入了16个新的伪类,这些伪类对于动态地修饰网页内容的样式特别有用。 [学习如何深度使用伪类](http://www.smashingmagazine.com/2011/03/30/how-to-use-css3-pseudo-classes/)。
#### 组合及属性选择器
[组合选择器](http://www.w3.org/TR/css3-selectors/#combinators) 提供了为特定元素选择其后代元素、孩子元素或兄弟元素的快捷方式。
[属性选择器](http://www.w3.org/TR/css3-selectors/#attribute-selectors) 适用于具有特定属性 和/或 特定值的元素。正则表达式的知识对属性选择大有帮助。
#### 明确度
浏览器会通过 [计算选择器的明确度](http://www.w3.org/TR/2009/PR-css3-selectors-20091215/) 来决定应用哪个CSS规则。如果两个选择器都适用于同样的元素,**具有更高明确度的那个会胜出**。
ID 选择器比属性选择器明确度高,class 选择器比任何数量的元素选择器明确度高。尽量使用 ID 选择器来提高明确度。有时候我们可能会想方设法给一个元素应用一条CSS规则,但用尽方法也不能如愿。这种情况有可能是因为我们使用的选择器比另外一个的明确度低,所以明确度高的另一个选择器里的属性就比我们想应用的选择器优先了。这种情况在更大或更复杂的样式表里更为常见。在小一些的项目里,通常这不是大问题。
##### 计算明确度
当你在一个很大很复杂的样式表上干活的时候,知道如何计算选择器的明确度会有很大帮助,会节约你的时间,并让你的选择器更有效率。
明确度的计算方式是对你的CSS的各种组件计数,并用 (a,b,c,d) 格式表达出来。
* 元素,伪元素: d = 1 – (0,0,0,1)
* 类,伪类,属性: c = 1 – (0,0,1,0)
* Id: b = 1 – (0,1,0,0)
* 内联样式: a = 1 – (1,0,0,0)
不过,也许使用现成的明确度计算器更好一些。
* [明确度计算器](http://specificity.keegan.st/)
* [你应该了解的关于明确度的一些事](http://www.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/)
* [IE 明确度 bugs](http://www.brunildo.org/test/IEASpec.html#a)
使用 `!important` 会覆盖掉所有的明确度,不管它有多高。因此我们倾向于避免使用它。大部分时候是没必要用它的。即使你需要覆盖某个你访问不到的样式表里的选择器,往往也会有其他的办法去覆盖。尽可能避免使用它。
### 像素 vs. Em
我们使用 `px` 作为定义 `font size` 的度量单位,因为它能提供对文本的绝对控制。我们知道为字体大小使用 `em` 单位一度很流行,这样可以解决 IE6 无法改变基于像素的文本大小的问题。不过,现在所有的主流浏览器(包括 IE7 和 IE8)都支持基于像素单位的文本大小 和/或 整页缩放。既然 IE6 被广泛认为已废弃,用像素定义文本尺寸更好。另外,无单位的 `line-height` 也应该优先考虑,因为它不会从父元素继承一个百分比值,而是基于 `font-size` 的一个乘数。
正确
~~~
#selector {
font-size: 13px;
line-height: 1.5; /* 13 * 1.5 = 19.5 ~ Rounds to 20px. */
}
~~~
不正确
~~~
/* Equivalent to 13px font-size and 20px line-height, but only if the browser default text size is 16px. */
#selector {
font-size: 0.813em;
line-height: 1.25em;
}
~~~
### IE Bugs
不可避免地,当所有其他浏览器看起来都正常工作的时候,各种版本的IE浏览器就会冒出一些莫名其妙的bug,让部署一拖再拖。虽然我们鼓励排除问题,产出无需打补丁就能在所有浏览器上运行的代码,有时候为了在样式表中使用CSS钩子,还是有必要用到CSS `if IE` 条件注释。[从 paulirish.com 了解更多信息。](http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/)
修复 IE
~~~
<!--[if lt IE 7 ]> <body class="ie6"> <![endif]-->
<!--[if IE 7 ]> <body class="ie7"> <![endif]-->
<!--[if IE 8 ]> <body class="ie8"> <![endif]-->
<!--[if IE 9 ]> <body class="ie9"> <![endif]-->
<!--[if !IE]><!--> <body> <!--<![endif]-->
~~~
~~~
.box { float: left; margin-left: 20px; }
.ie6 .box { margin-left: 10px; }
~~~
如果你在用HTML5(以及 [HTML5 Boilerplate](http://coderlmn.github.io/code-standards/#h5bp)), 我们推荐使用 [Modernizer](http://www.modernizr.com/) JavaScript库和下列模式:
~~~
<!--[if lt IE 7]> <html class="no-js ie ie6" lang="en"> <![endif]-->
<!--[if IE 7]> <html class="no-js ie ie7" lang="en"> <![endif]-->
<!--[if IE 8]> <html class="no-js ie8" lang="en"> <![endif]-->
<!--[if IE 9]> <html class="no-js ie9" lang="en"> <![endif]-->
<!--[if gt IE 9]><!--><html class="no-js" lang="en"><!--<![endif]-->
~~~
### 速记格式
一般情况下要优先使用CSS速记格式,一是因为它的简洁,二是用它也可以扩充已有的值,例如margin和padding的情况。 开发者必须注意TRBL 缩写,它表示元素的各边按顺时针方向定义的顺序:上、右、下、左。如果bottom没有定义,它就会从top继承值。同理,如果left未定义,它从right继承值。如果只有top的值有定义,所有的边都会继承那一个值。
下面是关于减少样式表代码冗余和使用CSS速记格式的更多内容:
* [http://qrayg.com/journal/news/css-background-shorthand](http://qrayg.com/journal/news/css-background-shorthand)
* [http://sonspring.com/journal/css-redundancy](http://sonspring.com/journal/css-redundancy)
* [http://dustindiaz.com/css-shorthand](http://dustindiaz.com/css-shorthand)
### 图片
* 对于(用于背景的)重复图片,要使用 [比 1x1 像素大的图片](http://www.iandevlin.com/blog/2010/03/webdev/fading-issue-with-repeating-background-transparent-image-in-internet-explorer)
* 永远不要用空白图片。
* 多使用 [CSS精灵(sprites)](http://coderlmn.github.io/code-standards/#_leverage_css_sprites)。它会使悬停状态更简单,改善页面加载速度,并减少二氧化碳的排放。
* 一般情况下,所有的图片都应该切成带透明背景(PNG8),并裁减成紧密贴合图片外边框。
* 不过,logo必须总是带有背景遮片,并在裁减内容之外留出内边框。
### 颜色管理
* 确认团队所有成员都有一致的颜色管理设置。
* 任意两台显示器显示的颜色很可能会有所不同,但必须使用sRGB颜色作为缺省配置。
* 当你在Photoshop打开文件时,要注意颜色配置警告,当Photoshop建议把图片转换到另一个配置时,要通知其他团队成员。
* 永远不要把颜色配置嵌入到图片里。
* 当你从Photoshop保存图片时,务必去掉"Embed Color Profile"选项的勾选。
* * *
### 通用的文本和字体样式
#### 标题
* 要给 `h1-h6` 标题 -- 包括作为链接的标题 -- 定义缺省样式。在你的CSS文档顶部定义它们,在必要时修改它们以保持整个站点的一致性。
* 标题必须有层次,能表明从大到小不同级别的重要性,h1具有最大的字体大小。
* SEO:要大致地了解页面的层次组织和阅读效果,在开发者工具里关闭CSS效果,你会看到一个基于文字的视图,包括所有的 `h1-h6` , `strong`, `em` 等标签。
#### 链接
* 必须定义链接的缺省样式,样式要和主要的文字样式不同,载悬停状态下也要有不同的样式。
* 当给链接加下划线样式时,使用 `border-bottom` 并用 `text-decoration: none;` 加点内边框。这样看起来更好一些。
## Web字体
近些年来越来越流行使用web上的定制字体和字型。随着本地浏览器对其支持度的攀升,以及一些支持它的服务和API的出现,这个领域发展的势头很猛。这些方法都各有利弊。项目启动前最好是在技术和版权限制方面先做一些研究,以便为特定项目选择合适的方法。
所有这些方法都有代码开销、开发时间和性能(时间计算和用户感受)的不足。你需要熟悉这些问题,并和团队成员及用户沟通,这样会减少项目后期的大量问题。
下面列出一些内嵌定制字体的流行手段,按我们实施时的优先级排序。
### @font-face
[@font-face 规则](http://www.w3.org/TR/2011/WD-css3-fonts-20110324/#font-face-rule) 允许你自定义字体。它最早是在CSS2 规范里定义的,但从CSS2.1被删除了。现在,它是CSS3草案中的推荐稿。
对于web定制字体,我们的首选和最偏爱的选择都是 `@font-face` ,就是因为它被列入了CSS字体模块工作草案的一部分,这意味着它会随着浏览器支持的提升变得越来越流行,并且随着它不断改善变得更稳定,使用起来也更简单。
就现在而言,使用 `@font-face` 的时候,建议为每种字体格式定义它的source。这很重要 -- 如果你想让它在大多数浏览器中有效 -- 虽然这不是使用它的必要条件。
在规范中包括的字体格式有:
* **woff**: WOFF (Web Open Font 格式)
* **ttf**: TrueType
* **ttf**, **otf**: OpenType
* **eot**: 嵌入式 OpenType
* **svg**, **svgz**: SVG 字体
#### 防弹 @font-face
为了实现完全的浏览器兼容性,可以使用 Fontsprings 的新版 [防弹 @font-face 语法](http://www.fontspring.com/blog/further-hardening-of-the-bulletproof-syntax) (2011年2月21日的最新版本)。
~~~
@font-face {
font-family: 'MyFontFamily';
src: url('myfont-webfont.eot'); /* IE9 Compat Modes */
src: url('myfont-webfont.eot?iefix') format('eot'), /* IE6-IE8 */
url('myfont-webfont.woff') format('woff'), /* Modern Browsers */
url('myfont-webfont.ttf') format('truetype'), /* Safari, Android, iOS */
url('myfont-webfont.svg#svgFontName') format('svg'); /* Legacy iOS */
font-weight: <font-weight>;
font-style: <font-style>;
// etc.
}
~~~
这里有一个使用上述技术实现的 [演示](http://www.thecssninja.com/demo/css_fontface/) 。
#### 跨浏览器兼容
Safari, IE 6-9, IE 9 兼容模式, Firefox, Chrome, iOS, Android, Opera
#### 阻止兼容模式
有时候 IE 会在用户不知道的情况下自作主张切换到兼容模式。要阻止你的站点缺省进入兼容模式,可以在站点的`<head>` 部分加入下列代码:
~~~
<meta http-equiv="X-UA-Compatible" content="IE=edge">
~~~
* * *
#### 有关 @font-face 的提示
* IE 6–8 只会接受打包成 EOT 的 TrueType 字体。
* font-weight 和 font-style 在 `@font-face` 里有不同的含义。带有 `font-weight:bold;` 的声明意味着它是这种字型的粗体版本,而不是直接给文本加粗。
* [@font-face 的一些坑](http://paulirish.com/2010/font-face-gotchas/)
**优点**
* 易于实施
* 多种多样APIs
* 可定制
* 易于加入元素中
* 除了 CSS 之外无需其他库或接口
* 被列入了CSS字体模块3工作草案的一部分
**不足**
* 使用不当的时候,支持它的浏览器有限
* 某些现代浏览器(Chrome, Opera)的老版本并不总是能正常渲染。文本可能会出现毛刺。**我本人没有亲自证实过,不确定现在这个问题是否还存在。
* * *
### Google WebFonts API 和 Font 加载器
使用 [Google Webfonts](https://code.google.com/apis/webfonts/) 有两套可选方案。这两套方案当然也各有其弊端,但它们也可以用得和 `@font-face` 一样好,这全取决于项目的需要。
#### Webfonts API
[Google的 Webfonts API](https://code.google.com/apis/webfonts/docs/getting_started.html) 本质上和 `@font-face` 做的是同样的事情,只是它把所有困难的工作都帮你做好了,提供了更广泛的浏览器支持。这个方案主要的缺点是它使用的字体库非常小。使用它的时候你只需要引入下面这套样式表并指定字体名。
~~~
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Font+Name">
~~~
然后给你想应用的选择器定义一个样式即可:
~~~
CSS selector {
font-family: 'Font Name', serif;
}
~~~
### Webfont 加载器
Google 提供的另一个备选方案是 [Webfont 加载器](https://code.google.com/apis/webfonts/docs/webfont_loader.html) ,它是一个 JavaScript 库,提供比字体 API 更多的控制。你也可以像Typekit那样使用多套web字体。 使用这套方案需要把下面的script引入你的页面:
~~~
<script type="text/javascript">
WebFontConfig = { google: { families: [ 'Tangerine', 'Cantarell' ]} };
(function() {
var wf = document.createElement('script');
wf.src = ('https:' == document.location.protocol ? 'https' : 'http') +
'://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})();
</script>
~~~
用这种方式引入 webfont.js 文件速度更快,如果没有用到前面的Ajax接口的话。否则你应该用下面的方法:
~~~
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">
google.load("webfont", "1");
google.setOnLoadCallback(function() {
WebFont.load({ google: { families: [ 'Tangerine', 'Cantarell' ]} });
});
</script>
~~~
通过使用这套 Webfont 加载器,你具备更多的定制能力,包括使用更多的字体,而不局限于不大的Google Webfont字库。不过,这种方案需要加载Javascript,这就是为了便利付出一些其他的牺牲了。
**优点**
* 非常易于实施
* 广泛的浏览器支持
* 可与 Typekit 组合
* 如果使用 font 加载器,可以定制
* API 和 `@font-face` 功能相同
**不足**
* API只提供很小的字体库
* Webfont 加载器需要 JavaScript 才能工作
* 大部分浏览器会先加载页面的其他部分,在这些字体所在部分留下空白,或在有回退选项存在的时候显示回退样式,直到页面加载完成为止。
* webfont 库里的一些字体在 Windows 下渲染效果不佳
* * *
### Cufon
如果你选择使用 Cufon,我们强烈推荐你使用 [Cufon 压缩版](http://cufon.shoqolate.com/js/cufon-yui.js)。你会需要用 [生成器](http://cufon.shoqolate.com/generate/) 来转换你的字体。
~~~
<script src="cufon-yui.js" type="text/javascript"></script>
<script src="YourFont.font.js" type="text/javascript"></script>
<script type="text/javascript">
Cufon.replace('h1'); // Works without a selector engine
Cufon.replace('#sub1'); // Requires a selector engine for IE 6-7
</script>
~~~
我们推荐慎重使用 Cufon ,因为如果应用到大量的文本上,它会产生很多开销。访问 [Cufon Wiki](https://github.com/sorccu/cufon/wiki/) 可以获取更多相关信息。
**优点**
* 广泛的浏览器支持
* 在支持它的浏览器中渲染效果好
* 可定制
* 易于实施
**不足**
* 需要 JavaScript 才能工作
* 使用该方法的文本不能被选中
* 并不是适用于所有文字
* 定制可能会很复杂
* 并不总是容易应用到多个元素,特别是在增加类似悬停的效果的时候
* * *
### Typekit
当选择给网站添加定制字体的时候,使用 [Typekit](https://typekit.com/) 有它不容忽视的优势。它具备很强的平台集成,并且是可扩展和流行的服务。 它可以和 Google Webfonts 一起使用,并且易于加入到以 WordPress, Posterous, Typepad, 和其他类似的 CMS 实现的网站上。
但是,要完整地应用 [Typekit 也不是无本生意](https://typekit.com/plans)。如果你需要把它用到超过2个站点,或用在一个浏览量很高的站点上,你需要每年付49.99美元费用,对于百万以上浏览量的站点费用会加倍。不过,如果你的网站有这么大浏览量,对这点成本你应该是不差钱的。如果不是这样,你可能需要重新考虑你的商业模式了。
**优点**
* 庞大的字体库,包括 Adobe 字体
* 易于实施
* 和Google Webfont API 以及 博客平台 集成
* 免费计划有限制但不会过期
**不足**
* 需要 JavaScript 才能工作
* 免费计划能用到的字体库是有限制的
* 免费和最便宜的计划只允许用到1-2个网站,每个网站只能用2-5种字体。
* 用到超过一个网站就需要付费了
* * *
### 可扩展 Inman Flash 替换 (sIFR)
我们不推荐使用这种方法,但是因为它的广泛使用,我们觉得还是有必要介绍它,以便你在选择定制web字体方法时做出胸有成竹的决定。
即使它在web设计师中广为流行,而且它在大多数浏览器中也有很好的支持,使用它的缺点还是大过它的便利性。 弃用 sIFR 的最大也是最明显的原因是它需要使用Flash这一事实。而且,即便是为了让Flash正常工作就需要 JavaScript,而且在你使用此方法渲染的文本能在页面上可见之前,相关的脚本必须都先被加载。更不用说,它会增加页面加载时间,并会让一个慢网站变得更慢。
我们会让你对这些问题算算细账。
**优点**
* 文本可以被选中
* 大多数浏览器都支持
* 在支持它的浏览器上渲染得还不错
**不足**
* 它用到了 Flash
* 需要 JavaScript 来让 Flash 正常工作
* 它是 Flash!
* 脚本加载完之前,文字不会出现
* ...还有,它是 Flash...
* * *
### 字体版权
即使你可以把任何字体转换为web字体文件,你还是应该确认这样做是否合法。很多厂商更新了他们关于字体在web上使用的条款。查看 [字体版权和保护的细则](http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&item_id=UNESCO_Font_Lic) 获取更多信息。
* * *
### 字体描述及字体文件格式
* [CSS 2 字体](http://www.w3.org/TR/1998/REC-CSS2-19980512/fonts.html#font-descriptions) – 1998年5月 (已废弃)
* [CSS 3 字体](http://www.w3.org/TR/css3-fonts/) – 2009 工作草案
* [CSS 字体模块](http://www.w3.org/TR/css3-fonts/) – W3C 工作草案 2011年5月
* [WOFF Font Format](http://www.w3.org/TR/WOFF/) – 工作草案 2010
* [SVG 字体格式](http://www.w3.org/TR/SVG/fonts.html)
* [嵌入式 OpenType (EOT) 文件格式](http://www.w3.org/Submission/EOT/)
* [微软 OpenType 规范](http://www.microsoft.com/typography/otspec/)
* [OpenType 特性文件规范](http://www.adobe.com/devnet/opentype/afdko/topic_feature_file_syntax.html#9.e)
* [苹果 TrueType 手册](http://developer.apple.com/fonts/TTRefMan/)
### 使用 CSS3
利用CSS3规范中增加的特性,你可以做很多新奇的事情,不过里边很多新特性还没有得到所有主流浏览器的完全支持。不过这也不是说它们现在就不能用。对于这些支持不好的特性,有一些回退的库可用,或者有一些其他补丁,用来填补在浏览器对新特性缺乏支持时出现的空白。
还有一些浏览器特定的属性或前缀也可以用来修饰样式。为跨浏览器支持起见,我们推荐使用[Prefixr.com](http://prefixr.com/) 来确保你加入了所有针对不同浏览器的前缀属性。