简介
每当我开始一个新的项目时,第一件事就是打磨一下CSS语言中的一些粗糙边缘。我使用一套功能性的自定义基线样式来做这件事。
长期以来,我一直使用Eric Meyer的著名的CSS Reset。这是一个坚实的大块的CSS,但在这一点上它有点过时了;它已经有十多年没有更新了,而且从那时起已经发生了很多变化
最近,我一直在使用我自己的自定义CSS重置。它包括我发现的所有小技巧,以改善用户体验和CSS编写经验。
像其他的CSS重设一样,当涉及到设计/化妆品时,它是没有意见的。你可以在任何项目中使用这种重置,无论你要追求什么样的美感。
在本教程中,我们将对我的自定义CSS重置进行考察。我们将深入研究每一条规则,你将了解它的作用以及为什么你可能想要使用它
CSS重置
不多说了,就在这里。
/*
1. Use a more-intuitive box-sizing model.
*/
*, *::before, *::after {
box-sizing: border-box;
}
/*
2. Remove default margin
*/
* {
margin: 0;
}
/*
3. Allow percentage-based heights in the application
*/
html, body {
height: 100%;
}
/*
Typographic tweaks!
4. Add accessible line-height
5. Improve text rendering
*/
body {
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
/*
6. Improve media defaults
*/
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
/*
7. Remove built-in form typography styles
*/
input, button, textarea, select {
font: inherit;
}
/*
8. Avoid text overflows
*/
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
/*
9. Create a root stacking context
*/
#root, #__next {
isolation: isolate;
}
它比较短,但在这个小的样式表中包含了很多东西。让我们开始吧!
一个迂腐的说明
从历史上看,重设CSS的主要目的是确保各浏览器之间的一致性,以及撤销所有的默认样式,创造一个空白的环境。我的CSS重设并没有真正做到这两点。
如今,当涉及到布局或间距时,各浏览器并不存在巨大的差异。总的来说,各浏览器都忠实地执行了CSS规范,而事情也会像你所期望的那样。因此,它不再是必要的了。
我也不认为有必要剥离所有的浏览器默认值。例如,我可能确实需要<em>
标签来设置font-style: italic
。我总是可以在各个项目的样式中做出不同的设计决定,但我认为剥离常识性的默认值没有意义。
我的CSS重设可能不符合 "CSS重设 "的经典定义,但我正在采取这种创造性的自由。
1.盒子大小的模型
流行测试!以可见的粉色边框来衡量,假设没有应用其他的CSS,在下面的情况下,.box
元素的宽度是多少?
html
<style>
.parent {
width: 200px;
}
.box {
width: 100%;
border: 2px solid hotpink;
padding: 20px;
}
</style>
<div class="parent">
<div class="box"></div>
</div>
我们的.box
元素有width: 100%
。因为它的父元素是200px宽,所以100%会解析为200px。
但是它在哪里应用这个200px的宽度呢?默认情况下,它将把这个尺寸应用于内容框。
如果你不熟悉,"内容框 "是盒子模型中的一个矩形,它实际上容纳了内容,在边框和填充物里面。
width: 100%
声明将把.box
'的内容框设置为200px。padding将增加一个额外的40px(每边20px)。边框最后增加4px(每边2px)。当我们进行计算时,可见的粉色矩形将是244px宽。
当我们试图将一个244px的盒子塞进一个200px宽的父体时,它就会溢出。
码上乐园
使用Prettier格式化代码
<style>
.parent {
width: 200px;
border: 2px solid black;
}
.box {
width: 100%;
border: 2px solid hotpink;
padding: 20px;
}
</style>
<div class="parent">
<div class="box"></div>
</div>
这种行为很奇怪,对吗?幸运的是,我们可以改变它,通过设置以下规则。
*, *::before, *::after {
box-sizing: border-box;
}
应用这一规则后,百分比将根据边框来解决。在上面的例子中,我们的粉色框将是200px,而内部的内容框将缩减到156px(200px-40px-4px)。
在我看来,这是一个必须具备的规则。它使CSS的工作方式明显变得更好。
我们使用通配符选择器(*
)将其应用于所有元素和伪元素。与流行的看法相反,这对性能并无坏处。
继承箱体大小
我在网上看到一些建议,用这个来代替。
html {
box-sizing: border-box;
}
*, *:before, *:after {
box-sizing: inherit;
}
如果你试图将一个预先存在的大型项目迁移到使用border-box,这可能是一个有用的策略。如果你要从头开始一个全新的项目,则没有必要这样做。为了保持简单,我把它从我的CSS重置中省略了。
展开来看看这在什么时候会有帮助的例子。
2.删除默认的边距
* {
margin: 0;
}
浏览器会围绕边距做出常识性的假设。例如,一个h1
,默认情况下会比一个段落包含更多的空白。
这些假设在文字处理文档中是合理的,但对于现代网络应用来说,它们可能并不准确。
边距是一个棘手的魔鬼,而且我经常发现自己希望元素在默认情况下没有边距。所以我决定把它全部删除。🔥
如果我确实想给特定的标签添加一些边距,我可以在我的自定义项目样式中这样做。通配符选择器(*
)的特异性极低,所以很容易覆盖这个规则。
3.基于百分比的高度
html, body {
height: 100%;
}
你是否曾经试图在CSS中使用基于百分比的高度,但却发现它似乎没有任何效果?
这里有一个例子。
代码操场
使用Prettier格式化代码
<style>
main {
outline: solid red;
min-height: 100%;
}
</style>
<main>
<p>Hello World</p>
</main>
main
元素有height: 100%
,但该元素根本没有增长!这是不可能的。
这是不可能的,因为在流式布局(CSS的主要布局模式)中,height
和width
的操作原理根本不同。一个元素的宽度是根据它的父元素计算的,但一个元素的高度是根据它的子元素计算的。
作为一个快速演示,在这里我们看到,当我们应用这个规则时,我们的main
元素可以增长。
编码游戏场
使用Prettier格式化代码
<style>
html, body {
height: 100%;
}
main {
outline: solid red;
min-height: 100%;
}
</style>
<main>
<p>Hello World</p>
</main>
如果你使用的是像React这样的JS框架,你可能还希望给这个规则添加第三个选择器:框架使用的根级元素。
例如,在我的Next.js项目中,我将规则更新如下。
html, body, #__next {
height: 100%;
}
为什么不使用vh?
你可能会想:为什么要对基于百分比的高度大惊小怪呢?为什么不使用vh
单位来代替?
问题是,vh
单位在移动设备上不能正常工作;100vh
会占用超过100%的屏幕空间,因为移动浏览器会做那种浏览器UI来来回回的事情。
在未来,新的CSS单元将解决这个问题。在那之前,我继续使用基于百分比的高度。
4 调整行高
body {
line-height: 1.5;
}
line-height
控制一个段落中每行文字之间的垂直间距。默认值在不同的浏览器之间有所不同,但它往往在1.2左右。
这个无单位的数字是一个基于字体大小的比率。它的功能就像em
单位一样。如果line-height
为1.2,每行将比元素的字体大小大20%。
问题就在这里:对于那些有阅读障碍的人来说,这些线条挤得太紧了,使其更难阅读。WCAG标准规定,行高至少应该是1.5。
现在,这个数字确实倾向于在标题和其他大字体的元素上产生相当大的线条。
代码乐园
使用Prettier格式化代码
<style>
* {
line-height: 1.5;
}
</style>
<p>
This paragraph has a 1.5x ratio
for line-height, and it feels
pretty good, right? I think
this text is legible and pleasant.
</p>
<h1>
But it's a bit much on headings!
</h1>
你可能希望在标题上覆盖这个值。我的理解是,WCAG标准是针对 "正文 "文本的,而不是标题。
使用 "计算 "的更聪明的行高
我一直在尝试用另一种方法来管理行高。它是这样的。
* {
line-height: calc(1em + 0.5rem);
}
这是一个相当高级的小片段,它超出了本博客文章的范围,但这里有一个快速的解释。
5.字体平滑化
body {
-webkit-font-smoothing: antialiased;
}
好吧,所以这个有点争议性。
在MacOS电脑上,浏览器默认会使用 "子像素抗锯齿"。这是一种技术,旨在通过利用每个像素内的R/G/B光来使文本更容易阅读。
问题是:那篇文章写于2012年,在高DPI的 "视网膜 "显示器时代之前。今天的像素要小得多,肉眼看不见。
像素LED的物理排列也发生了变化。如果你在显微镜下观察现代显示器,你不会再看到R/G/B线的有序网格。
在2018年发布的MacOS Mojave中,苹果禁用了整个操作系统的子像素抗锯齿。我猜他们意识到,这在现代硬件上弊大于利。
令人困惑的是,Chrome和Safari等MacOS浏览器仍然默认使用子像素抗锯齿。我们需要明确地将其关闭,方法是将-webkit-font-smoothing
设置为antialiased
。
这就是区别。
MacOS是唯一使用子像素抗锯齿的操作系统,因此这一规则对Windows、Linux或移动设备没有影响。如果你是在MacOS电脑上,你可以用实时渲染进行实验。
Code Playground
<p>
This paragraph uses the default subpixel antialiasing.
</p>
<p class="antialiased">
This paragraph does not use subpixel antialiasing.
</p>
6.合理的媒体默认值
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
因此,这里有一个奇怪的事情:图像被认为是 "内联 "元素。这意味着它们应该被用在段落的中间,像<em>
或<strong>
。
这与我在大多数时候使用图片的方式不一致。通常情况下,我对待图片的方式与对待段落、标题或侧边栏的方式相同;它们是布局元素。
但是,如果我们试图在我们的布局中使用内联元素,就会发生奇怪的事情。如果你曾有过一个神秘的4px的间隙,它不是margin或padding或border,它可能是浏览器用line-height
添加的 "内联魔法空间"。
通过在所有图片上默认设置display: block
,我们可以避开一大类古怪的问题。
我还设置了max-width: 100%
。这样做是为了防止大型图片溢出,如果它们被放置在一个宽度不足以容纳它们的容器中。
大多数块级元素会自动增长/收缩以适应它们的父元素,但像<img>
这样的媒体元素很特别:它们被称为替换元素,而且它们不遵循同样的规则。
如果一张图片的 "原生 "尺寸是800×600,那么<img>
元素也将是800px宽,即使我们把它放入一个500px宽的父元素。
这条规则将防止该图像超出其容器,对我来说,这感觉是更合理的默认行为。
7.继承表单控件的字体
input, button, textarea, select {
font: inherit;
}
还有一件奇怪的事情:默认情况下,按钮和输入不会从它们的父级继承排版样式。相反,它们有自己的奇怪的样式。
例如,<textarea>
将使用系统默认的单色字体。文本输入将使用系统默认的无衬线字体。而且,两者都会选择一个微小的字体大小(在Chrome中为13.333px)。
正如你可能想象的那样,在移动设备上很难读到13px的文字。当我们关注一个字体较小的输入时,浏览器会自动放大,从而使文字更容易阅读。
不幸的是,这并不是一个好的体验。
如果我们想避免这种自动缩放行为,输入的字体大小至少要达到1rem / 16px。这里有一个解决这个问题的方法。
input, button, textarea, select {
font-size: 1rem;
}
这解决了自动变焦的问题,但这是个创可贴。让我们从根本上解决这个问题:表单输入不应该有自己的排版样式!
input, button, textarea, select {
font: inherit;
}
font
是一个很少使用的缩写,它设置了一堆与字体有关的属性,比如 , , 和 。通过将其设置为 ,我们指示这些元素与周围环境中的排版相匹配。font-size
font-weight
font-family
inherit
只要我们不为正文选择令人厌恶的小字体,就能一下子解决所有的问题。🎉
8.文字包装
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
在CSS中,如果没有足够的空间在一行中容纳所有的字符,文本会自动换行。
默认情况下,该算法将寻找 "软换行 "的机会;这些是该算法可以分割的字符。在英语中,唯一的软包装机会是空白和连字符,但这因语言不同而异。
如果一行没有任何软换行的机会,而且不合适,它将导致文本溢出。
Code Playground
<div class="wrapper">
<p>
This is a narrow column of text, with a very long word: antidisestablishmentarianism.
</p>
<p>
The same problem happens with URLs: https://www.somewebsite.com/articles/a1b2c3
</p>
</div>
这可能会导致一些恼人的布局问题。这里,它增加了一个水平滚动条。在其他情况下,它可能会导致文本与其他元素重叠,或滑到图像/视频后面。
overflow-wrap
属性让我们可以调整换行算法,并允许它在找不到软换行机会时使用硬换行。
码上乐园
<style>
p {
overflow-wrap: break-word;
}
</style>
<div class="wrapper">
<p>
This is a narrow column of text, with a very long word: antidisestablishmentarianism.
</p>
<p>
The same problem happens with URLs: https://www.somewebsite.com/articles/a1b2c3
</p>
</div>
这两种解决方案都不完美,但至少硬包装不会搞乱布局
你也可以尝试添加hyphens
属性。
p {
overflow-wrap: break-word;
hyphens: auto;
}
hyphens: auto
使用连字符(在支持连字符的语言中)来表示硬包装。这也使得硬包装更加普遍。
如果你有非常窄的文本列,这可能是值得的,但它也可能有点分散注意力。我选择不把它包括在重置中,但它值得试验一下
9.根部堆叠背景
#root, #__next {
isolation: isolate;
}
最后这个是可选的。一般来说,只有当你使用像React这样的JS框架时才需要它。
正如我们在"What The Heck, z-index? "中看到的, isolation
属性允许我们创建一个新的堆叠上下文,而不需要设置一个z-index
。
这是有益的,因为它允许我们保证某些高优先级的元素(模态、下拉、工具提示)将永远显示在我们的应用程序中的其他元素之上。没有奇怪的堆叠上下文错误,没有z-index军备竞赛。
你应该调整选择器以配合你的框架。我们要选择你的应用程序被渲染的顶层元素。例如,create-react-app使用<div id="root">
,所以正确的选择器是#root
。
我们的成品
这里又是CSS的重置,以一种浓缩的便于复制的格式。
/*
Josh's Custom CSS Reset
https://www.joshwcomeau.com/css/custom-css-reset/
*/
*, *::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重置作为NPM包发布,因为我觉得你应该拥有你的重置。把它带入你的项目中,并随着你学习新的东西或发现新的技巧而对它进行调整。如果你愿意,你可以随时制作你自己的NPM包,以方便在你的项目中重复使用。只要记住:你拥有这些代码,它应该和你一起成长。