跳到主要内容

CSS显式默认值:inherit,initial,unset和revert

近年来,CSS 添加了一些全局关键词:inheritinitialunsetrevert 。理论上,它们可以应用于除 none 以外的任何 CSS 属性。它们在 CSS 中也被称为 CSS 显式默认值,其中 inheritinitialunset 是在级联层 Level3 (Cascading Level 3)规范中定义的,而 revert 是在级联层 Level4 (Cascading Level 4)中添加的。在 CSS 中,它们可以在属性的默认值或重置属性的值方面提供很多细微的控制。

今天,我们就来简单了解一下这些特殊的 CSS 关键词。同时,我们也将探讨何时使用它们的效果最好,以及它们之间存在的一些重要的区别。

CSS 的基础知识

在我们深入探讨 inheritinitialunsetrevert 等关键词之前,我们有必要先了解一些 CSS 的基础知识,比如 CSS 属性的初始值、浏览器默认样式表、继承和非继承等。

那我们就先从 CSS 属性的初始值开始!

CSS 属性的初始值

在 CSS 中,每一个 CSS 属性都会有一个属于自己的初始值,即由客户端(比如,浏览器)定义的值。这个值通常在 CSS 规范中为该属性定义。简单地说,你在 W3C 规范中查询任何 CSS 属性,它都会具有一个初始值。如下图所示:

img

即,规范中介绍属性参数时,会有一个名为 “Initial” 选项,该选项对应的值就是 CSS 属性的初始值,也就是 initial 对应的值。例如 font-size 的初始值是 medium ,即:

.font-size {
font-size: initial;
}

/* 等同于 */
.font-size {
font-size: medium;
}

这是很重要的,因为在大多数情况下,我们都希望将属性的值重置为其初始值

浏览器默认(User-Agent)样式表

在所有的 CSS 属性都设置完初始样式之后,紧接着浏览器会加载自身的样式表。该样式表与 CSS 属性的初始值没有任何关系。例如,h1 元素,浏览器给它设置的默认样式如下:

/* user agent stylesheet */
h1 {
display: block;
font-size: 2em;
margin-block-start: 0.67em;
margin-block-end: 0.67em;
margin-inline-start: 0px;
margin-inline-end: 0px;
font-weight: bold;
}

HTML 元素是没有初始样式值的!上面所示代码中 <h1> 标签元素的基础样式是从“用户代理样式表”(user agent stylesheet)中获取的,而不是 CSS 属性的初始值 initial

两种属性类型:继承和非继承

CSS 中有两组属性:

  • 继承属性组:由默认情况下从父元素继承定义的属性组成;它们主要是排版属性(如 font-sizecolor 等)。你可以点击这里查询可继承的 CSS 属性组
  • 非继承属性组:由其余的属性组成,它们不受父元素的定义影响(比如 marginpadding 等)。

同样的,在 W3C 描述每个属性的时候,都有明确的描述,该属性是哪种类型的属性:

img

正如上图所示,font-size 属性的 “Inherited” 参数值为 “yes” 时,表示该属性是一个可继承属性;dispalybackground-color 属性的 “Inherited” 参数值为 “no”,表示它们是一个非可继承属性。

CSS 的可继承属性是有一种机制的。简单地说,每个 HTML 元素默认有每个 CSS 属性的定义初始值。初始值是一个不可继承的属性,如果级联无法计算元素的值,则会显示为默认值。

可以被继承的属性会向下级联,子元素将获得一个计算值,该计算值代表其父元素的值。这意味着,如果父元素的 font-weight 属性设置为 bold,所有子元素都会是粗体,除非它们的 font-weight 属性被设置为不同的值,或者用户代理样式表为该元素设置了一个 font-weight 值。

也就是说:

  • 当元素的一个继承属性没有指定值时,则取父元素的同属性的计算值,只有文档根元素取该属性的概述中给定的初始值;
  • 当元素的一个非继承属性没有指定值时,则取属性的初始值。

处理 CSS 继承的机制

在 CSS 中提供了几个属性值,可以用来处理 CSS 属性的继承。这几个属性值就是 initialinheritunsetrevert。其实除了这四个属性值之外,还有一个 all 属性值。虽然这几个属性值主要用来帮助大家处理 CSS 属性继承的,但他们之间的使用,还是有一定的差异化。

先用一张图来阐述它们之间的差异:

img

接下来我们一看看这几个属性值的实际使用以及对应的差异化。

initial 的作用是什么?

最容易理解的 CSS 值是 initial。这个值只是将 CSS 属性重置为其初始值,这个初始值在该属性的 W3C 规范中指定。但使用它时,你可能会感到困惑,因为属性的初始 CSS 值并不总是你想象的那样,它通常与浏览器的默认样式不同。

事实上,从 W3C 规范中获取的 CSS 属性初始值也较为混乱。其中有些是合理的,有些是不合理的。例如,CSS 的 float 的初始值是 nonebackground-color 的初始值是 transparent ,它们是属性合理的一类。但有一些基本上是任意的。比如 CSS 的 display 属性,为什么初始值就是 inline ,而不是 block 呢?我也不知道当时 CSS 工作小组为什么要这样设计 display 属性,我们只知道 W3C 规范也给 display 定义了一个初始值,虽然 inine 有点奇怪,但 block 同样奇怪。

也就是说,无论如何,initial 关键字**都要**将属性恢复到规范中**定义**的初始值,无论是否合理

即使 W3C 对 CSS 属性的初始值定义有些混乱或者不合理,但是在一些具体场景中,将 CSS 的属性的值设置为 initial 是非常有益的。比如,你想删除所有特定于浏览器的样式时,使用 initial 值就是非常好的选择。

我们来看一个具体的示例。假设我们有一个<p> 元素:

<p>👧🏼欢迎来到现代 CSS 的世界中🎫</p>

这个 <p> 元素是一个块元素,即 display: block

为了好看,咱们添加一点修饰的样式代码:

p {
background: #f36;
padding: 2rem;
font-size: clamp(2rem, 5cqw + 2.25rem, 3rem);
color: #fff;
text-shadow: 1px 1px 0.0125em rgb(0 0 0 / 0.5);
}

你在浏览器中看到的效果如下:

img

Demo 地址:https://codepen.io/airen/full/eYPMarJ

如果我们希望 p 元素变成行内元素时,按照我们以前的处理方式,需要手动处理浏览器默认样式(User-Agent 用户代理样式),也就是显示的重置:

p {
dispaly: inline;
}

blockinline 效果对比如下:

img

Demo 地址:https://codepen.io/airen/full/VwEXOGQ

前面提到过 inlinedisplay 的初始值(也就是默认值),而在规范中也提到过: 你在元素样式的设置中显示的设置某个属性的值为 initial 时,其实就表示设置了该属性的默认值。 也就是说,我们可以给 display 设置 initial 关键词:

p {
display: initial;

/* 等同于将 diplay 重置为 inline */
display: inline;
}

这个时候得到的效果其实和使用 display:inline 是一样的:

img

Demo 地址:https://codepen.io/airen/full/poxLmBj

使用开发者工具查看相应的计算值,不难发现,display 设置为 initial 时,会覆盖用户代理的样式值 block

img

接下来,我们再来看一个继承属性 color 的示例。

<p>👧🏼欢迎来到<strong>现代 CSS </strong>的世界中🎫</p>
p {
background: #f36;
padding: 2rem;
font-size: clamp(2rem, 5cqw + 2.25rem, 3rem);
color: #fff;
text-shadow: 1px 1px 0.0125em rgb(0 0 0 / 0.5);
}

p strong {
color: initial;
}

示例中的 color 属性是一个可继承属性,所以 <p> 元素的后代元素 <strong> 也会继承 <p> 元素中设置的color: #fff 值。如果我们显式的在 strong 中设置 color 的值为 initial 时,那么 strongcolor将重置为默认值。由于我们没有设置默认的 color 颜色,那么这个时候,浏览器将会把一个计算值赋予成color 的初始值:

img

Demo 地址:https://codepen.io/airen/full/abRYgmE

inherit 的作用是什么?

在 CSS 中,许多属性会从其父元素继承值。例如,font-family(字体系列)、color(颜色)和许多其他属性默认情况下会继承其值。这就是为什么你可以将一个 div 元素的文本颜色(color)设置为绿色,并使该 div 的所有子元素文本也都变为绿色。通过将 CSS 属性的值设置为 inherit,你告诉浏览器将该 CSS 属性的值设置为与其父元素的属性值相等。如果父元素也继承该属性的话,它将一直向上寻找,直到找到某个父元素上设置的具体值。

举个例子,如果我给一个 div 元素设置了 5px 的实线蓝色边框(即 border: 5px solid blue),并在该 div 内部放置了一个子元素(比如 p ),那么我们知道边框只会显示在父 div 上,而不会显示在子元素上。

<div>
<p>👧🏼欢迎来到<strong>现代 CSS </strong>的世界中🎫</p>
</div>
div {
border: 5px solid #09f;
padding: 1rem;
}

p {
background: #f36;
padding: 2rem;
font-size: clamp(2rem, 5cqw + 2.25rem, 3rem);
color: #fff;
text-shadow: 1px 1px 0.0125em rgb(0 0 0 / 0.5);
}

img

Demo 地址:https://codepen.io/airen/full/vYVRqqB

然而,如果我在子元素 p 上将 border 属性设置为 inherit,那么该子元素也将具有 5px 的实线蓝色边框:

p {
border: inherit;
}

img

Demo 地址:https://codepen.io/airen/full/NWOYQWp

这是一个非常有用的属性值,因为某些元素(如按钮)在浏览器样式表中有特定的 font-family 设置,但你很可能希望继承该 font-family 以与站点的其余部分保持一致。这就是为什么我几乎总是将按钮的 font-family 设置为 inherit 的原因。

:where(button) {
font-family: inherit;
}

需要记住的重要一点是,inherit 属性值使元素从其父元素继承其值,而不是从级联中继承其值。比如上面这个示例,如果我们在 p 元素上未显式设置 border 属性的值,即使你在 strong 元素中显式设置 border 的值为 inherit ,它也不会继承其祖父元素 div 的边框样式:

div {
border: 5px solid #09f;
}

div strong {
border: inherit;
}

img

Demo 地址:https://codepen.io/airen/full/jOezgVY

这个示例说明:尽管元素自身显式的设置了 inherit 关键词,但是,如果其父元素没有明确指定样式,那么其最终效果将和 revert 的效果一致。即继承的是其父元素的计算值,也就是浏览器默认样式(User Agent Stylesheet)

前面我们提到过,CSS 的属性会可继承不可继承两种类型,对于可继承属性,它是会自动继承其父元素对应的属性值。比如 color 属性,默认情况之下会继承其父元素的 color 值。来看一个具体的例子:

<div class="box"><strong>Color</strong>是一个可继承属性</div>
.box {
color: #fff;
}

理论上,你是不需要在 <strong> 元素上显式设置 color 属性的值,它的文本颜色也是 #fff 。虽然说,color 属性是可继承属性,但也并不意味着它总是会继承其父元素的 color 属性值。因为,有的时候它和客户端(比如,浏览器)设置的默认值也是有关系的。比如,将上面示例中的 <strong> 换成 <a> 元素,要是你没有将 <a> 元素的 color 属性值设置为 inherit ,即使 color 是可继承属性,它的颜色也不会是 #fff (不会继承其父元素的 color 属性值)。这是因为浏览器自动将 <a> 元素的 color 值设置为蓝色。

<div class="box">
<a href="">我是一个链接元素</a>,我的 color 并不会自动继承父元素的 color 值
</div>
.box {
color: #fff;
}

img

这种现象在 CSS 中很常见,比如 mark 标签也和 a 标签类似。但是,要是你在 amark 元素上显式将 color 属性的值设置为 inherit 时,结果就会不一样了:

.box a,
.box mark {
color: inherit;
}

它会明确告诉浏览器,amark 的文本颜色 color 要继承其父元素 .boxcolor 值。在我们这个示例中,amark 的文本颜色都将变成白色(#fff):

img

Demo 地址:https://codepen.io/airen/full/XWxqzmM

无论如何,这就是 inherit 关键字的工作原理。在字体大小或颜色方面使用它可能偶尔是一个好主意,但其他情况下少使用。并且请记住继承和非继承属性之间的差异,稍后这将变得很重要。

unset 的作用是什么?

unset 是一个有趣的属性值,因为它会执行与 initialinherit 相同的操作。换句话说,unset 关键词可以重置可继承和不可继承的属性。

  • 如果一个 CSS 属性本来就是继承的,比如 colorfont-family等,那么它(unset)将像 inherit 一样执行。
  • 如果一个 CSS 属性本来就是不可继承的,比如 displayborder等,那么它(unset)将像 initial 一样执行。

也就是说,在任何属性上使用 unset 关键词将会应用其适当的重置关键词。例如:

.box {
max-width: unset; /* unset = initial = none */
font-size: unset; /* unset = inherit = 父元素的 font-size 值 */
}

起初 unset 关键词似乎并不是很有用,因为你可以手动将 inheritinitial 作为属性的重置值,但是随着 all 属性出现,unset 就变得非常有用。就拿上面的代码为例,现在你可以直接将 all 设置为 unset 即可:

.box {
all: unset;
}

/* 等同于 */
.box {
max-width: unset; /* unset = initial = none */
font-size: unset; /* unset = inherit = 父元素的 font-size 值 */
}

通过使用 all,我们可以将元素的所有内容重置为初始值或确保它们继承,这在从头开始创建 HTML 元素的样式时非常理想。例如,如果你想从一个按钮(<button>)元素中删除所有浏览器特定的样式,这就非常有用了。

或者说,一个元素运用了很多属性,你希望能够确保它们全部重置,而无需逐个进行操作。例如,你正在使用一个第三方库(比如 Bootstrap)中的按钮 .button ,但你在某个场景下,需要重置第三方库赋予的所有样式,那么使用 all:unset 就非常有用。

.btn-secondary {
all: unset;
}

img

Demo 地址:https://codepen.io/airen/full/PoyeOgq

不过,这几乎是 unset 的唯一用例。

然而,在某些情况下,将 CSS 恢复到其默认值是不够的。例如,如果我们将一个 <div> 元素的 display 属性重置为 unset,我们得到的初始值(initial)将是 inline 而不是 block。示例:

div {
display: unset; /* = initial = inline */
}

此时,inline 的值将应用于 div 元素。

事实上,客户端(比如浏览器)在渲染 HTML 元素时,对于块元素(例如,<div><p> 等),display 属性的初始值为 block,但对于内联元素(例如 <span><a>),display 的初始值为 inline。如果仅是将元素的 display 属性被设置为 unset,它将最终以 inline 的方式渲染,这是因为初始值(initial)没有使用浏览器的 User-agent-stylesheet 中的默认样式。

revert 的作用是什么?

作为一名 Web 开发者,我想你在对一个 Web 应用或页面开始编写 CSS 的时候,都会先重置客户端(如浏览器)所提供的默认样式。那么,关键词 revert 就显得尤其的重要,因为它允许将 CSS 属性的值重置回浏览器特定的样式。例如,一个元素的 CSS 样式已经被大量修改,你想将其中的一部分或全部样式更改回浏览器默认样式,那么可以将属性值设置为 revert

我并不经常使用这个功能,因为我更喜欢删除所有浏览器样式,但在使用 all: unset 后,将一些样式更改回浏览器特定的样式可能会很有用。revert 关键字首先确定浏览器的 User-agent-stylesheet (浏览器加载的默认 CSS 文件)是否为特定的 HTML 元素创建特殊样式。如果是,则将重置为这些默认样式。例如:

div {
display: revert; /* = block */
}

span {
display: revert; /* = inline */
}

table {
display: revert; /* = table */
}

如果 User-agent-stylesheet (浏览器加载的默认 CSS 文件)中没有为该元素定义样式,则 revert 关键字的行为将类似于 unset 值。如果属性属于继承组,则将其重置为 inherit 值;否则,将其重置为 initial

也就是说,revert 也区分了可继承属性和不可继承属性:

  • 如果一个属性通常是可继承的,revert 的含义就是继承
  • 如果一个属性通常不可继承,revert 将还原为浏览器样式表中指定的值

如果你想将应用程序中的每个元素,使其回到浏览器的默认样式,则可以选择使用 all: revert ,即:

* {
all: revert;
}

实际上,应用程序内的每个元素都像是从头开始一样!

revert vs. unset

revertunset 非常像,唯一的区别在于 revert 会把 CSS 属性值重置为 User-agent-stylesheet (浏览器加载的默认 CSS 文件)中对应的值,例如:

/* unset */
div {
display: unset; /* = initial = inline */
}

h1 {
font-weight: unset; /* = inherit = 其父元素的 font-weight 的值 */
font-size: unset; /* = inherit = 其父元素的 font-size 的值 */
}

/* revert */
div {
display: revert; /* = block = User-agent-stylesheet */
}

h1 {
font-weight: revert; /* = bold = User-agent-stylesheet */
font-size: revert; /* = 2em = User-agent-stylesheet */
}

总之,unsetrevert 的主要区别在于 revert 考虑了用户定义和开发者编写的样式,而 unset 只考虑继承。因此,如果你想将属性重置为其继承(inherit)或初始(initial)值,并考虑用户定义或开发者编写的样式,请使用 revert。如果你只想将属性重置为其继承(inherit)或初始(initial)值,并且不需要考虑用户定义或开发者编写的样式,请使用 unset

initial VS. inherit VS. unset VS. revert

我们花了一些篇幅向大家阐述了 initialinheritunsetrevert 几个关键词的作用和功能是什么以及何时使用哪个关键词。但我还是希望一个示例,向大家阐述它们之间的差异之处。比如,下面这个示例:

<div>
<h2>Title</h2>
</div>
div {
color: red;
border: 5px solid green;
margin: 10px;
padding: 10px;
}

h2 {
color: blue;
}

我们分别在 divh2 元素上应用了一些简单的 CSS 样式。在浏览器中呈现的效果如下图所示:

img

Demo 地址:https://codepen.io/airen/full/OJBZZwm

其中 color 是可继承属性,bordermarginpadding 是不可继承属性。例如,div 元素各属性的具体描述如下所示:

属性名初始值是否可继承User Agent Style(浏览器默认样式)开发者编写样式
color取决于用户代理blackred
border-colorcurrentColorNogreen
border-widthmediumNo5px
border-style取决于计算值Nosolid
margin0No10px
padding0No10px

h2 元素各属性具体描述如下所示:

属性名初始值是否可继承User Agent Style(浏览器默认样式)开发者编写样式
color取决于用户代理blackblue
border-colorcurrentColorNoNo
border-widthmediumNoNo
border-style取决于计算值NoNo
margin00.83em 0pxNo
padding0NoNo

如果给 h2 元素添加下面这段代码:

h2 {
color: inherit;
border: inherit;
margin: inherit;
padding: inherit;
}

img

Demo 地址:https://codepen.io/airen/full/YzJLvVQ

很明显,h2 元素继承了其父元素 divcolorbordermarginpadding 。即:

属性名开发者编写样式(值)计算值继承值来源
colorinheritred父元素 div
border-colorinheritgreen
border-widthinherit5px
border-styleinheritsolid
margininherit10px
paddinginherit10px

将上面代码中的 inherit 值替换为 initial ,即 h2 元素中的 colorborderpaddingmargin 都为初始值:

h2 {
color: initial;
border: initial;
margin: initial;
padding: initial;
}

img

Demo 地址:https://codepen.io/airen/full/QWZrxQo

属性名初始值开发者编写样式(值)计算值值来源
colorCanvasTextinitialblack初始值
border-colorcurrentColorinitialcurrentColor初始值
border-widthmediuminitial5px初始值
border-stylenoneinitialnone初始值
margin0initial0初始值
padding0initial0初始值

继续把 h2 中的 colorpaddingmarginborder 属性的值重置为 unset

h2 {
color: unset;
border: unset;
margin: unset;
padding: unset;
}

你将看到的效果如下:

img

Demo 地址:https://codepen.io/airen/full/LYgmrgL

前面说过了,unset 会根据属性是否为可继承属性来做判断。如果属性是可继承属性,那么 unset 相当于 inherit,比如 color ,则 h2color 会继承父元素 divcolor 值;如果属性是不可继承属性,那么 unset 相当于 initial ,比如 borderpaddingmargin 都是不可继承属性,所以 h2 上的 borderpaddingmargin 都会取它们各自的初始值 initial

属性名初始值是否可继承User Agent Style(浏览器默认样式)开发者编写样式计算值值来源
colorCanvasTextblackunsetred继承父元素 divcolor
border-colorcurrentColorNounsetcurrentColor初始值
border-widthmediumNounsetmedium初始值
border-stylenoneNounsetnone初始值
margin00.83em 0pxunset0初始值
padding0Nounset0初始值

最后再来看 revert 关键词,把 h2 中的 colorbordermarginpadding 等属性的值设置为 revert

h2 {
color: revert;
border: revert;
margin: revert;
padding: revert;
}

此时看到的效果如下图所示:

img

Demo 地址:https://codepen.io/airen/full/OJBZwVq

revert 会将属性重置为其父级自然继承的值。在我们的例子中,它从父级 div 继承了 color。如果该属性不是可继承属性,则 revert 会将级联值回退到先前的级别。如果存在用户代理或用户默认样式,则将属性设置为默认值。在我们的例子中,它将 h2margin 设置为其用户代理默认值 0.83em。如果没有默认样式,则将值设置为其初始值。这适用于我们示例中的边框属性。

属性名初始值是否可继承User Agent Style(浏览器默认样式)开发者编写样式计算值值来源
colorCanvasTextblackrevertred继承父元素 divcolor
border-colorcurrentColorNorevertcurrentColor初始值
border-widthmediumNorevertmedium初始值
border-stylenoneNorevertnone初始值
margin00.83em 0pxrevert0.83em 0px浏览器默认样式
padding0Norevert0初始值

简而言之,initial 将值设置为属性的定义初始值,inherit 将值设置为父元素的值,unset 将属性值恢复到继承值或初始值,revert 将属性重置为父级自然继承的值。

all 的作用是什么?

有时候我需要为网站的特定组件设计样式,但我需要重用已经以不同方式进行样式化的类名。当无法使用代码拆分时,这可能导致从其他源继承某些样式,我需要为我的组件覆盖这些样式。例如,假设我们有一个使用 Materialize 进行样式化的按钮,但我需要创建一个完全不同样式的按钮:

img

Demo 地址:https://codepen.io/airen/full/oNaddvV

.unstyled,
.unstyled:hover {
color: revert;
background: revert;
transition: initial;
cursor: initial;
text-decoration: revert;
text-align: initial;
letter-spacing: initial;
font-size: revert;
outline: initial;
border: initial;
border-radius: initial;
display: initial;
line-height: initial;
padding: initial;
text-transform: initial;
vertical-align: initial;
box-shadow: initial;
}

Materialize 的按钮有许多样式,虽然最好的方法是使用不同的类来移除它们,但可能存在这样的情况,你没有这个选项,只能选择逐行调整每个属性或使用 all 属性。all 属性将允许你一次性重置多行:

.unstyled-all,
.unstyled-all:hover {
all: revert;
}

all 属性将所有属性的值重置为给定的值。它目前接受四个值:initialinheritrevertunset。这些值也可以用作任何其他 CSS 属性的值,以将其重置为特定状态,但如果你确实需要重置所有内容,可以使用 all

需要再次强调的是,all 可能不是从元素中删除样式的最佳方法。更有效的方法是更新你的选择器,使样式不会被覆盖或使用代码拆分。我只建议在受到应用样式方式和顺序限制时使用 all

另外,需要注意的是,all 在 CSS 中有时候是一个属性,比如这里说的就是属性,但有的时候它还是 CSS 中某些属性的值。比如我们常在 transition 中用到的 all,那这个时候就是属性值。到目前为止,CSS 中的 all 属性也得到了众多浏览器的支持。

综合案例:创建重置 CSS

现在 CSS 有了这些新的重置功能,即 initialinheritunsetrevertall ,使得我们创建一个重置 CSS 样式比以前简单得多。

在大部分情况下,我们希望将大多数属性重置为它们的默认初始值或 CSS 的内在行为,使用 unset 值。但正如我们上面所看到的,我们还想保留来自客户端(User-agent-stylesheet)的样式,比如 display 属性。那么我们就可以这样做:

/* 重置除了 `display` 属性之外的所有 User-Agent-Stylesheet 样式 */
* {
all: unset;
display: revert;
}

看上去很完美,但还有一些细节需要处理。

当一些特殊的 HTML 元素(例如,<img><video><svg><canvas><iframe> )的 widthheight 属性被 all: unset 重置时,这些元素上的 widthheight 属性的效果也被重置。将会造成这些元素的大小属性不再起作用。

为了解决这个问题,你可以使用一些 CSS 伪类,比如前面课程《CSS 选择器 :has() 和 :not() 的组合》中提到的 :not() 伪类函数。即,将这些元素(比如,imgvideosvgcanvasiframe 等)当作 :not() 伪类函数的参数:

*:not(img, video, svg, canvas, iframe):not(svg *) {
all: unset;
display: revert;
}

但是,使用 :not() 伪类函数会产生一个意想不到的后果,它会增加选择器权重,因此可能会覆盖项目中稍后定义的样式。例如:

*:not(img, svg, video, iframe, canvas):not(svg *) {
all: unset;
display: revert;
}

body {
width: 100vw;
min-height: 100vh;
font-family: "Exo", "Bungee Shade", cursive, Arial, sans-serif;
background-color: #557;
color: #fff;
display: grid;
place-content: center;
padding: 1rem;
}

div {
color: red;
font-size: clamp(2rem, 3vw + 2.5rem, 3rem);
}

img

Demo 地址: https://codepen.io/airen/full/BaqVoOB

其中 *:not(img, svg, video, iframe, canvas):not(svg *) 的权重是 (0, 0, 2) ,而 bodydiv 的权重是 (0, 0, 1)

img

这就是 bodydiv 选择器对应的样式没有起作用的原因。如果你不希望这种现象出现,可以使用 CSS 的 :where() 伪类选择器,将 *:not(img, svg, video, firame, canvas):not(svg *) 选择器权重除至最低,即 (0 , 0, 0) 。你可以像下面这样做:

*:where(:not(img, svg, video, iframe, canvas):not(svg *)) {
all: unset;
display: revert;
}

如果你不想重置一些表单元素,则可以将其 all 属性设置为 revert 。比如 @Elad Shechter 的 《The New CSS Reset》就有这些关键词的使用:

*:where(
:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)
) {
all: unset;
display: revert;
}

a,
button {
cursor: revert;
}

textarea {
white-space: revert;
}

meter {
-webkit-appearance: revert;
appearance: revert;
}

:where(pre) {
all: revert;
}

::placeholder {
color: unset;
}

::marker {
content: initial;
}

:where(dialog:modal) {
all: revert;
}

上面代码来源于:https://elad2412.github.io/the-new-css-reset/

当然,你也可以采用更为激进的做法,将 all 属性的值设置为 initial 。其中差别是,initial 使用 CSS 规范上基于每个属性定义的初始值,而 revert 是用户代理样式表根据 CSS 选择器设置的默认值。例如,display 的初始值是 inline (即 initial = inline),而普通用户代理样式表将块元素(比如 div)的默认 display 值设置为 block ,将 table 元素的 display 值设置为 table ,等等。

小结

在 CSS 中,有一些全局关键字( initialinheritunsetrevert)可以用于设置属性的默认值或重置属性的值。虽然它们都可以用于任何属性,但它们之间存在一些重要的区别:

  • initial 会将属性设置为其初始值,即由浏览器定义的值。这个值通常在 CSS 规范中为该属性定义。
  • inherit 将属性设置为其父元素的值。如果没有继承值,则将使用该属性的初始值。
  • unset ,如果属性可以继承,则将属性设置为其继承值(等同于 inherit)。否则,将使用该属性的初始值(等同于 initial )。这意味着 unset 允许继承,并且适用于将属性重置为其默认值,同时仍然允许从父元素继承。
  • revertunset 类似,如果属性可以继承,则将属性设置为其继承值。否则,将使用该属性的初始值。不同之处在于,revert 还考虑了任何用户定义的样式或作者样式表可能应用于元素。如果没有用户定义或作者样式,则 revert 的行为与 unset 相同。

img

all 既可以是一个值,也可以是一个属性。只不过,我们这节课所探讨的 all 只是一个属性。或者更确切地说,是元素上所有 CSS 属性的集合。它的值可以是前面所讨论的关键字中的任何一个,并允许你将该关键字应用于所有 CSS 属性。

除此之外,在 W3C 的 CSS Cascading and Inheritance Level 5 规范中新增了一个 revert-layer 关键词。它可以让你回滚到之前的层级中指定的样式。理想情况下,revert-layer 关键词适用于在一个层级内部应用于属性。然而,如果将 revert-layer 关键词设置在一个层级外的属性上,那么该属性的值将回滚到用户代理样式表(或用户样式,如果存在)建立的默认值。因此,在这种情况下,revert-layer 关键词的行为类似于 revert 关键词。

这节课没有对 revert-layer 做过多阐述,我们将会在介绍 @layer 的时候,详细阐述该关键词的具体使用。

最后,再次强调一下,initialinheritunsetrevertall 要是用好了的话,可以使你的代码更为简洁和灵活性更强,甚至起到事半功倍的效果。