定义更好用的CSS Reset

什么是CSS Reset

如果你不定义html元素的默认css样式,那么浏览器自己就会用它自己的默认样式;但由于不同浏览器的默认样式是不一样的,会导致你开发的页面在不同的浏览器上会看上去不太一样;所以,为了避免页面在不同的浏览器上有不同的效果,我们通常需要显示地对html元素的css样式进行一些默认设置,来将“浏览器自带的设置”重置掉,这个就叫做CSS Reset

在一开始,CSS的工作方式没有完全标准化,所以每个浏览器(IE,Chrome,Firefox,Safari)都实现了它认为正确的东西。

各个浏览器将自认为合适的“边框”,“填充”,“边距”,“字体”,“行高”等样式加载到一些基础元素(input,div,a,span)中,以赋予页面独特的感觉。

如下图,可以看到,很基础的html元素在IE,Chrome,Firefox,Opera中的显示不尽相同。

所以,为了避免页面在不同的浏览器上有不同的效果,我们通常需要显示地对html元素的css样式进行一些默认设置,来将“浏览器自带的设置”重置掉,这个就叫做CSS Reset

使用CSS Reset的弊端

其实也有很多“不建议使用CSS Reset”的声音,主要有以下几个原因

  1. 所有的html元素都需要重新设置,会增加css文件的大小
  2. 如果重置掉一些基础属性,开发时会容易忘掉补充这些属性,反而引入显示混乱的问题
  3. 重置掉一些属性会影响用户通过键盘快捷键操作

辩证的判断是否需要CSS Reset

从历史上看,CSS Reset的主要目标是确保浏览器之间的一致性,并撤消所有默认样式,创建一个空白板。

但是如今浏览器在布局或间距方面并没有太大的差异。总的来说,浏览器忠实地实现了CSS规范,并且一切都如你所愿,所以不再那么需要了。

总之,我们应该根据实际的需求辩证的判断是否需要CSS Reset,保持简洁,保持高效即可。

下面列出一个比较合适的CSS Reset模板,不会完全重置浏览器的默认样式而带来不便,同时能够避免一些常见的浏览器兼容性的问题。

一个合适的CSS Reset模板

之前一直有一个2008年的模板被大家奉为经典,但是由于浏览器的更新换代,以及这个模板迟迟没有更新,导致这个模板已经不是那么好用了。

下面是一个参考的更新版本的CSS Reset模板,并会针对模板的内容逐一进行讲解为什么这么做。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
/*
1. 使用更直观的盒模型(box-sizing)
*/
*, *::before, *::after {
box-sizing: border-box;
}

/*
2. 取消默认margin
*/
* {
margin: 0;
}

/*
3. 允许通过百分比设置应用的高度
*/
html, body {
height: 100%;
}

/*
4. 更合适的行高
5. 优化text的渲染平滑度
*/
body {
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}

/*
6. 更合理的多媒体默认展示方式
*/
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}

/*
7. 避免奇怪的默认文字风格
*/
input, button, textarea, select {
font: inherit;
}

/*
8. 避免换行布局混乱
*/
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}

/*
9. 叠层处理
*/
#root, #__next {
isolation: isolate;
}

1. 使用更直观的盒模型(box-sizing)

1
2
3
*, *::before, *::after {
box-sizing: border-box;
}

先看一个例子,如上图

  • parent的width是200px
  • box的width是100%,border是2px,padding是20px

但由于浏览器的默认是:box-sizing: content-box,即设定元素的宽度是不包括padding和border的,导致box的宽度是200px + 2px * 2 + 20px * 2 = 244px,超出parent很多,这个行为其实很违反常理,如果继续使用content-box这个模型,开发者在定义宽度时需要进行很精细的计算,稍有不慎就会导致布局混乱。

如下图,通过设置全局的box-sizing为border-box模型,即设定元素的宽度是包含padding和border的,就会避免这类情况。(图中box的内容区域大小为:200px - 2px * 2 - 20px * 2 = 156px)

由于border-box是一个十分必要的设置,所以我们通过使用*来将这个效果应用到所有的元素和伪元素上。

2. 取消默认margin

1
2
3
* {
margin: 0;
}

不同的浏览器对于margin都有不同的定义,可能很合理,但是并不适用于现代应用。

所以将margin都置为0,如果需要的话,*的优先级很低,可以很方便的对其进行覆盖。

3. 允许通过百分比设置应用的高度

1
2
3
html, body {
height: 100%;
}

你是否曾经想通过百分比来设置一个元素的高度?但是发现并没有效果。

如下图:

虽然我们对main元素设置了100%的高度,但是完全没有用。

这是由于Flow流模型布局的缘故,height和width是有着不同的渲染逻辑的。width宽度是基于它的parent的,而height高度是基于它的children的。

如下图,通过将html、body的height设置为100%后可以看到main能够如预期一样生效。

为什么不用vh:目前vh在手机端并没有很好的适配,所以在没有解决这个问题前考虑仍然使用百分比。

4. 更合适的行高

1
2
3
body {
line-height: 1.5;
}

line-height控制着一段话中文本之间的纵向间距,大部分浏览器的默认值是1.2。

这个没有单位的数字表示着相对字号的大小。1.2则表示行高是字号的120% 。

1.2这个值在大部分的情况下其实会显得略为拥挤,而基于WCAG (Web Content Accessibility Guidelines) 的标准,建议line-height至少是1.5。

5. 优化text的渲染平滑度

1
2
3
body {
-webkit-font-smoothing: antialiased;
}

这是一个历史问题,并且是针对Mac操作系统的(对Linux,Windows,手机都没有影响)。

在过去(2012年),Mac操作系统上面的浏览器会默认使用“subpixel antialiasing”这个亚像素抗锯齿的属性,Mac操作系统看到这个属性后,通过调整RGB像素灯光来让文字阅读起来更加舒服,来提升用户体验,相关文章

而现在,有了视网膜(retina)屏幕,像素更小了,肉眼几乎不可见,并且像素的物理排列也发生了变化。同时,在Mac Mojave这个2018发布的版本中,苹果官方已经禁用了“subpixel antialiasing”这个属性。

但奇怪的是,Mac操作系统上面的Chrome和Safari浏览器仍然默认使用“subpixel antialiasing”这个属性,所以我们要把它关闭。

如图,这个属性只能在mac上,发现不使用“subpixel antialiasing”效果反而会更好。

6. 更合理的多媒体默认展示方式

1
2
3
4
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}

理由有两个。

  1. <img>默认被算作inline(行内)元素,这个很奇怪,不符合我们使用的常规,所以将其设置为block
  2. 大部分block类型的元素的宽度会跟随parent变化,但是<img>不会,它不走寻常路:如果你有一个宽度800px的图片,即使parent只有500px,它仍然会按照800px显示,所以为了防止img溢出,设置了max-width: 100%这个规则

7. 避免奇怪的默认文字风格

1
2
3
input, button, textarea, select {
font: inherit;
}

这个也是一个很奇怪的现象:在默认的情况下,button和inputs并不会继承parent的样式,并且有着自己独特的样式。

比如textarea默认会使用monospace字体,inputs默认会使用sans-serif字体,并且二者都会使用microscopically-small字号(13.333px)。

然而13px过于小了,如果放在手机上,浏览器会自动放大来定位到输入框,体验很不好,如图:

虽然通常直接设置特定元素的字号能够解决这个问题,但认为那不过是权宜之计,像font-size, font-weight, font-family这种涉及到整体页面风格的内容大部分情况下应该具有一致性,所以将其设为”inherit“,让其跟着环境走认为是比较合理的。

8. 避免换行布局混乱

1
2
3
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}

在CSS规范中,如果一行放不下文本的内容时,会尝试进行自动换行。

在默认情况下,换行算法会试图使用”优雅“的换行方式,比如遇到”空格“或者”连字符“;但是如果一段超长的文本中没有这些字符,就不会触发默认的自动换行算法,进而导致溢出的布局混乱,如图:

通过设置overflow-wrap: break-word可以让文本在没有空间时将一个单词截断进行强制换行,虽然少了”优雅“换行的机制,但是至少我们能优先避免文本溢出的布局混乱这个更加影响用户体验的问题。

9. 叠层处理

1
2
3
#root, #__next {
isolation: isolate;
}

最后一个是可选项,只有当你使用了前端框架时可能会需要,比如React。

这个isolation: isolate配置可以免除担心z-index的顺序设置,确保高优先级的组件能够显示在正确的层级上。

最终配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
*, *::before, *::after {
box-sizing: border-box;
}
* {
margin: 0;
}
html, body {
height: 100%;
}
body {
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
}
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
#root, #__next {
isolation: isolate;
}

最后强调一点,这个模板是最基础的,每个人应该拥有自己的CSS Reset,随着你自己的需求不断更新迭代持续维护。

参考资料

https://www.webfx.com/blog/web-design/should-you-reset-your-css/
https://stackoverflow.com/questions/11578819/css-reset-what-exactly-does-it-do
https://www.joshwcomeau.com/css/custom-css-reset/