帮助选择 CSS 嵌套的语法
CSS 工作组正在继续讨论在 CSS 中定义嵌套的最佳方式。如果您是 CSS 编写者,我们希望能得到您的帮助。
嵌套是 Sass 等工具中非常受欢迎的功能。它可以节省 Web 开发人员重复编写相同选择器的时间,并能使代码更清晰、更易于理解。
未嵌套的 CSS
.class1 {
color: green;
}
.class1 .class2 {
border: 5px solid black;
}
Sass 中的嵌套
.class1 {
color: green;
.class2 {
border: 5px solid black;
}
}
每个人都希望 CSS 嵌套能使用 Sass 那种简单的语法。然而,由于浏览器解析引擎的工作方式,这是不可能的。如果它们看到 element:pseudo
这样的序列,它们会将整个样式规则解析为 property: value
声明。
因此,关于替代方案的漫长讨论开始了。今年夏天早些时候,CSSWG 在选项1、选项2和选项3之间进行了辩论。其中,选项3获胜。从那时起,又提出了两个选项,选项4和选项5。如果您还记得 53+个问题 中讨论的各种细节,请将所有那些旧的想法放在一边。目前,我们只在这篇文章中描述的选项3、4和5之间进行辩论,如下面的示例所示。
为了帮助我们决定选择哪个选项,我们希望您阅读这些示例并回答一个问题调查。
请查看这些示例,思考您如何编写和维护样式表,并深入思考您偏好哪种语法。然后投票选出您认为最佳的那个。
在这三个选项中,&
符号是一个标记,表示“将嵌套外部的选择器放在 [此处]”。例如……
这个……
.foo {
& .bar {
color: blue;
}
}
……变成这个。
.foo .bar {
color: blue;
}
还有这个……
.foo {
.bar & {
color: blue;
}
}
……变成这个。
.bar .foo {
color: blue;
}
明白了么?
在下面的许多示例中,&
是可选的。有些开发人员会使用它,因为它有助于使他们的代码更具可读性。其他开发人员则会选择省略它,特别是在从非嵌套上下文中复制粘贴代码时。当省略 &
时,外部选择器会作为祖先添加到开头。(当我们调查作者时,他们对可选 &
的使用意见各占 50%。)
但选项 3 *(且仅限选项 3)*的关键技巧是——如果您使用的是元素选择器(如 p
、article
或 div
),则 &
是必需的。如果您使用的是任何其他选择器,如类或 ID,您可以选择省略 &
。最简单的记忆方式是:选择器不能以字母开头。它必须以符号开头。CSSWG 中的一些人认为这会很容易记住和做到。其他人则想知道,鉴于 CSS 中放置在其他任何地方的样式规则都没有这样的语法限制,这是否会造成混淆,以一种难以调试的方式使开发人员陷入困境。
在比较这些选项时,请考虑您的整个团队将如何处理嵌套代码。考虑将代码从一个项目复制粘贴到另一个项目会是什么样子。哪个选项能让在嵌套上下文内外编码变得容易?哪个能最容易地阅读、编写和编辑大量的 CSS?
哪个对 CSS 的未来最有利?三十年后,当人们编写 CSS 时——当今天的习惯和期望完全被遗忘,当未来几代人从未听说过 Sass 时——哪个选项能让编写这种语言变得轻松优雅?
请务必仔细阅读*所有*示例。某个选项在一个示例中可能显得是您的最爱,但在另一个示例中却存在您不喜欢的问题。
比较选项
- 选项 5:顶层 @nest:嵌套样式规则在专用的、独立的 at-rule 中声明,该规则只接受样式规则。声明可以使用 & { .. } 进行嵌套。
- 选项 4:后缀块:样式规则允许在声明块后添加一个可选的第二个块,该块只包含样式规则。
- 选项 3:非字母开头:嵌套样式规则可以直接添加到声明块中,但不能以字母开头。
示例 A:基础
未嵌套的 CSS
article {
font-family: avenir;
}
article aside {
font-size: 1rem;
}
选项 5
@nest article {
& {
font-family: avenir;
}
aside {
font-size: 1rem;
}
}
选项 4
article {
font-family: avenir;
} {
aside {
font-size: 1rem;
}
}
选项 3
article {
font-family: avenir;
& aside {
font-size: 1rem;
}
}
示例 B:带语法变体
开发人员喜欢以许多不同的方式来组织他们的 CSS——使用制表符或空格,将 {
}
大括号与规则放在同一行或分开的行上,按特定顺序排列规则。编写嵌套 CSS 时也会如此。示例 B 阐述了每种选项下格式化嵌套代码的几种可能变体。这完全取决于您的个人偏好。
未嵌套的 CSS
.foo {
color: red;
}
.foo .bar {
color: blue;
}
.foo p {
color: yellow;
}
选项 5
@nest .foo {
& {
color: red;
}
.bar {
color: blue;
}
p {
color: yellow;
}
}
或者你可以使用这种语法
@nest .foo {{
color: red; }
.bar {
color: blue;
}
p {
color: yellow;
}
}
选项 4
.foo {
color: red;
} {
.bar {
color: blue;
}
p {
color: yellow;
}
}
或者你可以这样格式化
.foo {
color: red; } {
.bar {
color: blue;
}
p {
color: yellow;
}
}
选项 3
.foo {
color: red;
.bar {
color: blue;
}
& p {
color: yellow;
}
}
或者你可以使用这种语法
.foo {
color: red;
& .bar {
color: blue;
}
& p {
color: yellow;
}
}
示例 C:在元素选择器内重新嵌套
对于选项 3,有一种情况仅使用 &
是不够的——当我们希望 &
引用的父选择器出现在嵌套选择器*之后*时。由于我们不能以 &
开头,我们需要使用类似 :is()
或 :where()
的方式,以便以符号而不是字母开头。
未嵌套的 CSS
a:hover {
color: hotpink;
}
aside a:hover {
color: red;
}
选项 5
@nest a:hover {
& {
color: hotpink;
}
aside & {
color: red;
}
}
选项 4
a:hover {
color: hotpink;
} {
aside & {
color: red;
}
}
选项 3
a:hover {
color: hotpink;
:is(aside) & {
color: red;
}
}
示例 D:零未嵌套声明 + 各种选择器
未嵌套的 CSS
:has(img) .product {
margin-left: 1rem;
}
:has(img) h2 {
font-size: 1.2rem;
}
:has(img) > h3 {
font-size: 1rem;
}
:has(img):hover {
box-shadow: 10px 10px;
}
a:has(img) {
border: none;
}
选项 5
@nest :has(img) {
.product {
margin-left: 1rem;
}
h2 {
font-size: 1.2rem;
}
> h3 {
font-size: 1rem;
}
&:hover {
box-shadow: 10px 10px;
}
a& {
border: none;
}
}
选项 4
:has(img) {} {
.product {
margin-left: 1rem;
}
h2 {
font-size: 1.2rem;
}
> h3 {
font-size: 1rem;
}
&:hover {
box-shadow: 10px 10px;
}
a& {
border: none;
}
}
选项 3
:has(img) {
.product {
margin-left: 1rem;
}
& h2 {
font-size: 1.2rem;
}
> h3 {
font-size: 1rem;
}
&:hover {
box-shadow: 10px 10px;
}
:is(a&) {
border: none;
}
}
示例 E:嵌套中的嵌套
未嵌套的 CSS
table.colortable td {
text-align: center;
}
table.colortable td .c {
text-transform: uppercase;
}
table.colortable td:first-child,
table.colortable td:first-child + td {
border: 1px solid black;
}
table.colortable th {
text-align: center;
background: black;
color: white;
}
选项 5
@nest table.colortable {
@nest td {
& {
text-align: center;
}
.c {
text-transform: uppercase;
}
&:first-child,
&:first-child + td {
border: 1px solid black;
}
}
th {
text-align: center;
background: black;
color: white;
}
}
选项 4
table.colortable {} {
td {
text-align: center; }{
.c {
text-transform: uppercase;
}
&:first-child,
&:first-child + td {
border: 1px solid black;
}
}
th {
text-align: center;
background: black;
color: white;
}
}
选项 3
table.colortable {
& td {
text-align: center;
.c {
text-transform: uppercase;
}
&:first-child,
&:first-child + td {
border: 1px solid black;
}
}
& th {
text-align: center;
background: black;
color: white;
}
}
示例 F:与媒体查询集成
未嵌套的 CSS
ol, ul {
padding-left: 1em;
}
@media (max-width: 30em){
.type ul,
.type ol {
padding-left: 0;
}
}
选项 5
@nest ol, ul {
& {
padding-left: 1em;
}
@media (max-width: 30em){
.type & {
padding-left: 0;
}
}
}
选项 4
ol, ul {
padding-left: 1em;
} {
@media (max-width: 30em){
.type & {
padding-left: 0;
}
}
}
选项 3
ol, ul {
padding-left: 1em;
@media (max-width: 30em){
.type & {
padding-left: 0;
}
}
}
示例 G:与层叠层集成
未嵌套的 CSS
@layer base {
html {
width: 100%;
}
@layer support {
html body {
min-width: 100%;
}
}
}
选项 5
@layer base {
@nest html {
& {
width: 100%;
}
@layer support {
body {
min-width: 100%;
}
}
}
}
选项 4
@layer base {
html {
width: 100%;
} {
@layer support {
body {
min-width: 100%;
}
}
}
}
选项 3
@layer base {
html {
width: 100%;
@layer support {
& body {
min-width: 100%;
}
}
}
}
现在您已经有机会理解了这三个选项的示例,或许还自己尝试了一些,您认为未来 CSS 的编写方式应该是什么样的?
哪个选项对 CSS 的未来最有利?
-
选项 59%
-
选项 45%
-
选项 386%
如果您想进一步描述您的想法,请在 Twitter 上回复 @webkit,或在 Mastodon 上回复 @jensimmons@front-end.social。
感谢您的反馈。设计一门编程语言需要考虑很多因素,不仅仅是像这样的调查结果。但了解 Web 开发人员在阅读示例后的想法将有助于我们正在进行的讨论。
请将这份调查转发给您认识的其他 CSS 编写者,发布到社交媒体,帮助我们传播消息。发表意见的人越多越好。
我们特别希望听到来自从事各种项目的开发人员的意见——从网站到 Web 应用,从原生应用到数字标牌,再到印刷书籍。无论是大型项目还是小型项目,庞大或微小的团队,全新的代码库还是几十年前的代码库。无论构建在何种框架、CMS、构建系统之上……动态或静态,服务器端渲染或客户端渲染……全球各地的人们使用 CSS 的方式真是*太多*了。我们需要考虑所有这些情况。