与Elad Shechter关于“新CSS重置”的访谈

Avatar of Elad Shechter
Elad Shechter

DigitalOcean为您的旅程的每个阶段提供云产品。立即开始使用200美元免费额度!

大家好!Elad联系我,向我展示了他的新CSS重置项目,名为the-new-css-reset。它非常有趣!我认为与大家分享它的一个好方法不仅是指出它,而且还要问Elad一些关于它的问题,供大家阅读。

以下是重置的完整代码

/*** The new CSS Reset - version 1.2.0 (last updated 23.7.2021) ***/

/* Remove all the styles of the "User-Agent-Stylesheet", except for the 'display' property */
*:where(:not(iframe, canvas, img, svg, video):not(svg *)) {
  all: unset;
  display: revert;
}

/* Preferred box-sizing value */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/*
  Remove list styles (bullets/numbers)
  in case you use it with normalize.css
*/
ol, ul {
  list-style: none;
}

/* For images to not be able to exceed their container */
img {
  max-width: 100%;
}

/* Removes spacing between cells in tables */
table {
  border-collapse: collapse;
}

/* Revert the 'white-space' property for textarea elements on Safari */
textarea {
  white-space: revert;
}

首先,当谈到“CSS重置”时,我们有两种方法

  • Nicolas Gallagher的Normalize.css是一种温和的方法。Normalize.css修复了不同浏览器实现之间的差异。
  • Eric Meyer的CSS Reset是一种强硬的方法,它认为在大多数情况下,我们不需要来自浏览器的基本样式,例如我们从像<h1><h6>这样的元素获得的font-size值,或者<ul><ol>列表元素的默认样式。例如,我们仅出于语义含义使用列表,因为它还有助于提高可访问性和SEO。

我非常喜欢Normalize.css。我认为它在任何项目中都是必不可少的,即使你更喜欢CSS Reset的想法。

为什么Normalize.css如此重要?Normalize.css会触及CSS Reset不会触及的Shadow DOM元素。当查看Normalize.css时,你会发现一些特殊的伪类,例如::-moz-focus-inner::-webkit-file-upload-button等等。它涵盖了很多方面,这就是我相信Normalize.css在任何项目中都是必不可少的的原因。

我也喜欢强硬的CSS Reset。我认为在大多数情况下,我们不需要浏览器的基本样式,如果我们在特定位置需要它,我们会根据我们的需要定义它。这让我想到我同时使用Normalize.css和CSS Reset。因此,Normalize.css首先加载,然后是强硬的CSS Reset。

那么,为什么我们需要一个新的CSS重置呢?我们现有的CSS重置是基于旧的CSS功能构建的。但在过去的几年里,我们获得了专门用于重置CSS中内容的新功能,这让我开始思考,现在我们可以使用这些新的尖端CSS功能创建更有效的CSS重置。

在我看来,这里最关键的部分是第一个规则集。让我们从第一个CSS属性和值开始:all: unset;。这是在这个CSS重置中承担重任的部分,对吧?它是如何工作的?

all是最特殊的CSS属性,因为它允许我们一次性重置CSS中存在的所有属性。

该属性接受几个关键字。两个基本关键字是initialinherit;还有两个更智能的关键字,分别是unsetrevert。要了解all: unset的作用,我们需要了解CSS属性的基本行为。

在CSS中,我们有两组属性

  • **继承属性组:**这些属性默认具有继承性——主要是排版属性。
  • **非继承属性组:**所有其他默认不继承的属性,例如包括paddingbordermargin的盒子模型属性。

与排版属性一样,当我们尝试重置它们时,我们希望保持inherit行为。因此,我们可以使用inherit关键字值。

/* Will get values from the parent element value */
font-size: inherit;  
line-height: inherit;
color: inherit;

对于非继承属性组中的其他属性,在大多数情况下,我们希望获得它们的初始值。值得一提的是,initial关键字对不同属性的计算方式不同。

max-width: initial; /* = none */ 
width: initial; /* auto */
position: initial; /* = static */

在我们理解了基本知识以及inheritinitial关键字值之后,我们理解如果我们想一起重置所有属性,我们不能直接将它们与all属性一起使用。这是因为,如果我们将所有属性重置为初始值,即all: initial,我们就会丢失继承属性组上的继承行为。假设我们将所有属性重置为inherit值。在这种情况下,所有属性都会继承——即使是盒子模型属性,这也是我们想要避免的。

这就是我们拥有unset值的原因。unset根据属性类型重置属性。如果我们将其用于继承属性,则等于inherit;如果我们将其用于自然的非继承属性,则等于initial

max-width: unset; /* = initial = none */
font-size: unset; /* = inherit = get parent element value */ 

这让我们回到了我的CSS重置的主要功能。all: unset的作用是将所有继承属性重置为inherit值,并将非继承属性组中的所有其他属性重置为其initial值。

此操作删除了浏览器添加的所有默认用户代理样式表样式。为了理解这些实质性的新CSS功能,所有这些都发生在我只对所有HTML元素执行一个操作时。

/* 
  Reset all: 
  - Inherited properties to inherit value
  - Non-inherited properties to initial value
*/
* {
  all: unset;
}

然后你接着使用display: revert;——all: unset;是否对display属性进行了不希望的操作?

简短回答:是的。display属性表示我们确实希望从用户代理样式表中获取的基本结构。正如我们在大多数属性中看到的那样,unset值对我们来说做得很好,并且我们在一项操作中重置了所有属性。

现在,要了解revert关键字值对display属性的独特作用,让我们谈谈我们从浏览器获得的两种类型的样式。我们从浏览器获得的样式由两层构建

  • **第1层,CSS initial值:**正如我们已经看到的,第一层是CSS中所有属性的initial值,包括某些属性上的inherit行为。
  • **第2层,用户代理样式表:**这些是浏览器为特定HTML元素定义的样式。

在大多数情况下,当我们想要重置某些内容时,我们想要删除第2层的样式。当我们使用all: unset重置时,我们删除了用户代理样式表的所有样式。

但是display属性是特殊的。正如我们已经看到的,CSS中的每个属性只有一个初始值。这意味着如果我们将display属性重置为其初始值,例如在<div>元素或任何其他HTML元素上,它始终返回inline值。

继续这种逻辑,我们将<div>元素连接到默认的display: block声明,这是我们从浏览器获得的。但是我们只有因为第2层,用户代理样式表,定义了它们,才获得这种行为。它基于与标题元素上的font-size更大(从<h1>到<h6>)相同的理念,而不是任何其他HTML元素。

div { 
  display: unset; /* = inline */ 
}
span { 
  display: unset; /* = inline */ 
}
table { 
  display: unset; /* = inline */ 
}
/* or any other HTML element will get inline value */

当然,这是不希望的行为。display属性是我们希望从浏览器获取的唯一例外。因此,我使用唯一的关键字值revert来从用户代理样式表中恢复默认的display值。

revert值是唯一的。首先,它检查用户代理样式表中是否存在特定HTML元素上特定属性的默认样式,如果找到,则使用它。如果找不到,revert的工作方式类似于unset值,这意味着如果该属性默认是继承属性,则使用inherit值;如果不是,则使用initial值。

所有CSS重置关键字的示意图

然后这两个规则位于一个规则集中,该规则集使用一个选择器来选择几乎所有内容。看起来你似乎正在通过通用标签选择器(*)选择页面上的所有内容,但随后删除了少量内容。对吗?为什么针对这些特定内容?

当我开始构思“新CSS重置”时,我没想到会需要例外。在我的想象中,它要简单得多。

但是当我开始创建体验时,我用我的新CSS重置(没有所有例外)替换了我的旧CSS重置,并且我看到一些破坏了我测试过的旧项目的东西。

主要出现问题的是可以通过 `width` 和 `height` 属性获取尺寸的元素——例如 `<iframe>`、`<canvas>`、`<img>`、`<svg>` 和 `<video>`。不幸的是,当我重置所有内容时,这些元素的宽度和高度由 `auto` 值定义,该值优先级更高,并会消除元素 `width` 和 `height` 属性的效果。

这可能是个问题,因为当我们通过 HTML 的 `width` 和 `height` 属性添加尺寸时,我们希望精确的尺寸来自 HTML 元素。我们更倾向于从 HTML 获取尺寸,而不是从 CSS 获取,因为当尺寸来自 CSS 时,在页面加载时可能会导致故障。

我发现的唯一能够去除所有这些特定元素重置效果的方法是将它们放在 `:not()` 选择器下。在这种情况下,我的新 CSS 重置是有害的,而不是有帮助的,因此我删除了对这些特定元素的影响。

在重置中,保持最低的特殊性似乎很重要,这样你就不会发现自己在与重置本身作斗争。这是 `:where()` 背后的理念吗?

是的,`:where()` 的理念是去除特殊性。我们不需要在 CSS 中描述更高的特殊性,仅仅是为了覆盖 CSS 重置。

总的来说,我认为我们很快就会看到更多使用 `:where()` 包裹元素以去除其特殊性的案例,而不仅仅是替换多个选择器。

看起来里面有一些对 `<svg>` 子元素的额外特殊处理。这是怎么回事?

第二个案例 `:not(svg *)` 使用单独的 `:not()` 是因为它是针对不同的问题。修改 SVG 内部元素可能会破坏视觉图像,而这是浏览器没有合理理由中断的事情之一。

让图像成为图像。我说。

在大型重置部分之后,它进入了一些更主观的片段。例如,浏览器之间对 `box-sizing` 的初始值没有分歧,但无论如何你都在更改它。我个人很喜欢这个,但我很好奇关于什么内容应该包含在重置中,什么内容不应该包含的理念。

总的来说,我认为 CSS 重置是一个见仁见智的事情。例如,Eric Meyer 的 CSS 重置选择删除特定事物的样式,而其他事物,例如 `display` 属性,则保持不变,正如你已经看到的,我完全同意这一点。

关于 `box-sizing`,是的,这是主观的。我做 Web 开发已经 15 年了。在这段时间里,我看到许多 Web 开发人员难以理解 `box-sizing` 的默认行为,而我过去已经习惯了。多年前,当人们谈论将其添加到 CSS 重置时,许多在行业工作多年的 Web 开发人员都害怕这种变化,因为总的来说,人们害怕改变。

但如今,我几乎看不到任何项目没有将所有元素重置为 `box-sizing: border-box` 的。浏览器的引擎无法修复默认 `box-sizing: content-box` 的笨拙行为,因为如果它们这样做,就会破坏对旧网站的支持。但是对于新项目,必须包含这部分内容,因为我们只能自己解决这个问题。

再说一次,这完全是主观的。

另外两个规则集,删除列表样式和折叠边框,也包含在 Eric Meyer 的重置中,因此它们已经存在很长时间了!从列表样式开始,我可以看到希望将它们清除,因为列表通常用于不需要标记的事情,例如导航。但在今天,这感觉有点有争议,因为 `list-style: none;` 也会在 iOS 上删除列表的语义。对此有什么顾虑吗?

简短的回答:没有。我这边没有顾虑。原因如下。

如果我们选择不重置 `list-style`,则意味着我们无法将列表元素用于导航。这也意味着我们不会在任何其他浏览器中获得任何语义。

现在,如果我需要在大多数浏览器获得这些语义和没有浏览器获得这些语义之间做出选择,我将选择前者,因为获得这些语义的浏览器比失去它的浏览器更多。

你能看到自己随着时间的推移不断添加内容吗?比如,如果你发现自己一遍又一遍地在项目中做同样的事情?为图像设置 `max-width` 感觉就像这样。再说一次,这并不是浏览器现在存在分歧的东西,但几乎每个项目都会这样做。

当然可以。如果这个重置缺少我没有考虑到的东西,我会添加它并发布新版本。但它需要像你提到的 `max-width` 的例子一样,没有很好的理由让我们希望图像溢出其容器。

你见过这个新的 层叠层 功能吗?你认为它将来会如何影响 CSS 重置?

在你问我之前,我还没有想过这个问题。层叠层模块是一个令人兴奋的功能。它还没有任何支持,但大多数浏览器引擎已经将此功能置于一个标志之下,这意味着我们很有可能在一年后看到此功能在所有常青浏览器中得到支持。

对于那些还没有听说过层叠层的人来说,其理念是 `@layer` 可以覆盖样式而无需创建更强的特殊性,因为每个加载在其之后的层都自动比之前的层更强。

当此功能到来时,我们将首先加载“重置”层。例如:首先是 Normalize.css,然后是 the-new-css-reset,然后是项目的 `@layer` 样式。

@layer normalize; /* Create 1st layer named “normalize” */
@layer the-new-css-reset; /* Create 2nd layer named “the-new-css-reset” */
@layer project-styles; /* Create 3rd layer named “project-styles” */

这应该确保底层始终胜过顶层。这也意味着像我所做的那样使用 `:where()` 去除特殊性将不再必要。

`@layer` 是即将出现在 CSS 中的最令人兴奋的未来功能之一,感谢 Miriam Suzanne,她一如既往地出色地完成了工作。

感谢您抽出时间,Elad!