[译]原生CSS嵌套使用

本文翻译自 CSS Nesting,作者:Ahmad Shadeed, 略有删改。

如果你是一个前端开发人员,那么你应该使用过CSS预处理器以及预处理器中的嵌套特性。它一直是一个受欢迎的功能,我一直都在使用CSS预处理器。

今年所有的主流浏览器都支持原生CSS嵌套:Chrome、Firefox和Safari。这是一个重要的CSS功能,这将使编写CSS更加容易。在本文中我将记录到目前为止我所学到的关于CSS嵌套的知识,并与您分享我的发现,包括使用案例和示例。

介绍

CSS嵌套是许多开发人员期待已久的功能之一。我们曾经依赖于CSS预处理器,如Sass或Less。让我们来回顾一下以前的做法:

请看下面的例子。我们有一个图标嵌套在选择器.nav__item中。

css 复制代码
.nav__item {
  .icon {
    display: flex;
    padding: 1rem;
  }
}

以上是一个Sass语法的代码。编译后,它在浏览器中看起来像下面这样:

css 复制代码
.nav__item .icon {
  display: flex;
  padding: 1rem;
}

使用原生CSS嵌套,相同的CSS将按原样工作。这里有一个图,显示了原生CSS嵌套和浏览器DevTools之间的比较。

请注意浏览器显示CSS的方式与CSS代码中的显示方式几乎一致。

如果这个CSS是用Sass编译的,浏览器会显示如下:

CSS嵌套的好处

在我看来,嵌套 CSS 有一些很合理的理由,使其变得有用:

  • 更容易阅读CSS
  • 风格一致
  • 确定特定样式的作用域
  • 对没有类或ID的HTML元素添加样式

CSS嵌套规则

为了让你了解CSS嵌套,我将尝试提供不同CSS问题的案例,以及嵌套如何帮助解决这些问题。

首先,你需要学习与符号&。有多数情况下,这个符号是必要的。

嵌套没有类或ID的元素

在这个例子中,<a>元素是通过.nav__item设置样式。要使CSS有效,使用&符号是可选的。

css 复制代码
nav__item {
  & a {
    display: block;
    padding: 1.5rem 1rem;
  }
}

/* 等同于 */
.nav__item a {
}

您也可以选择不使用&符号:

css 复制代码
.nav__item {
  a {
    display: block;
    padding: 1.5rem 1rem;
  }
}

/* 等同于 */
.nav__item a {
}

请注意这是一个最近的更新,被称为宽松的CSS嵌套。它适用于最新的Chrome Canary和Safari技术预览版。

使用类嵌套元素

以前面的例子为例,但假设<a>元素有一个HTML类。

css 复制代码
.nav__item {
  .link {
    display: block;
    padding: 1.5rem 1rem;
  }
}

/* 等同于 */
.nav__item .link {
}

这里不需要使用&,只有类名称也正常生效。

嵌套CSS组合器

CSS原生嵌套的一个好处是可以使用组合器。让我们来看一些例子。

在下面的例子中,我想选择.nav__item的每个元素,它前面有另一个具有相同类的元素。因此我使用了相邻兄弟选择器。

在原生CSS嵌套中,我们可以使用&符号来模拟这种效果。请注意我重复使用了它两次。

css 复制代码
.nav__item {
  & + & {
    border-left: 2px solid;
  }
}

魔术发生在第二次重复的&。在这里浏览器将理解我想使用相邻的兄弟选择器。让我给你看一个图表来说明这一点:

另一个例子是嵌套子组合器。它可以选择元素的直接子元素。

css 复制代码
.nav__item {
  > a {
    padding: 1rem;
  }
}

嵌套示例:Active, Focus, Hover

:active:focus:hover是通过用户操作激活的CSS伪类。

使用CSS嵌套,可以一次嵌套它们以避免代码重复。让我们以:hover为例:

css 复制代码
button {
  &:hover {
    background-color: var(--bg-color);
  }

  &:focus {
    outline: solid 2px;
  }
}

使用预处理器进行嵌套时的区别在于浏览器将像这样渲染它:

css 复制代码
button:hover {
  background-color: var(--bg-color);
}

button:focus {
  outline: solid 2px;
}

让我们来看看原生CSS嵌套在Chrome、Safari和Firefox中是如何呈现的。

嵌套示例:Post Content

第一个尝试CSS嵌套的例子之一是对帖子的正文内容设置样式。想象一下,一篇文章有正文标题、文本、图像、引用等等。

标题

我们倾向于像下面这样设计标题:

css 复制代码
.post-content h1,
.post-content h2,
.post-content h3,
.post-content h4 {
  /* styles here */
}

使用CSS嵌套,它更简单:

css 复制代码
.post-content {
  h1,
  h2,
  h3,
  h4 {
    color: var(--heading-color);
    font-weight: var(--heading-font-bold);
    margin-bottom: var(--size-2);
  }
}

我们也可以使用:is()选择器来做同样的事情。

css 复制代码
.post-content {
  :is(h1, h2, h3, h4) {
    color: var(--heading-color);
    font-weight: var(--heading-font-bold);
    margin-bottom: var(--size-2);
  }
}

段落元素

一个常见的情况是设置段落内部的链接样式。在这种情况下,CSS的嵌套功能非常好用。

css 复制代码
.post-content {
  & p {
    color: var(--color-black);

    & a {
      font-weight: bold;
      text-decoration: underline;
    }
  }
}

链接可能也需要悬停或焦点效果。

css 复制代码
.post-content {
  & p {
    color: var(--color-black);

    & a {
      font-weight: bold;
      text-decoration: underline;

      &:hover {
        /* hover styles */
      }
    }
  }
}

我们还可以用在嵌套媒体查询。

css 复制代码
.post-content {
  & p {
    /* base styles */

    @media (min-width: 400px) {
      /* do something */
    }
  }
}

帖子图片

帖子图包含一个图像和一个可选的<figcaption>来显示图像的描述。

在我的示例中,如果<figure>有标题,我需要对它进行不同的样式设置。可以通过嵌套CSS :has() 实现。

css 复制代码
.post-content {
  & figure {
    & img {
      /* the figure's image styles */
    }

    /* changes to the <figure> container, if it has a figcaption element */
    &:has(figcaption) {
      display: flex;
      align-items: start;
    }

    & figcaption {
      /* caption styling */
    }
  }
}

文章列表

我需要为所有列表项添加边框,除了最后一个。为此,我使用了:not()选择器。

css 复制代码
.post-content {
  li {
    &:not(:last-child) {
      border-bottom: 1px solid;
    }
  }
}

要使用:not(),我们需要在它前面附加一个&符号。

自定义标题间距

如果<h3><h4>之后是代码片段,我需要减少它们下方的间距。

css 复制代码
.post-content {
  & h3 + [class*="language-"],
  & h4 + [class*="language-"] {
    margin-top: 0.5rem;
  }
}

嵌套示例:卡片组件

我将演示一个简单的卡片组件,它使用CSS嵌套来实现所需的样式。

假设有一个默认或基本样式的.card元素,我将继续演示CSS嵌套的使用。

css 复制代码
.card {
  /* default card styles */
}

如果容器宽度大于400px,我希望卡片成为一个Flex容器。

css 复制代码
.card {
  /* default card styles */

  /* if the container width is 400px or bigger */
  @container card (min-width: 400px) {
    display: flex;
  }
}

嵌套示例:表单输入

一种常见的情况是对输入的占位符进行样式设置。问题是每个浏览器都有自己的前缀。

由于前缀样式需要一个双冒号,我们需要使用与号&,否则样式将中断。

css 复制代码
input {
  --placeholder-color: #969696;
  /* other styles */

  &::-webkit-input-placeholder {
    color: var(--placeholder-color);
  }

  &::-moz-placeholder {
    color: var(--placeholder-color);
    opacity: 1;
  }

  &:-moz-placeholder {
    color: var(--placeholder-color);
  }
}

我在探索CSS嵌套时发现的错误

使用不带&号的通用选择器

假设我们有一张卡片,我们想选择其中的所有元素。使用CSS原生嵌套,这应该可以工作:

css 复制代码
.card {
  * {
    /* styles here */
  }
}

我发现这在Chrome稳定版中不起作用,但在Chrome Canary 121、Safari 17.1和Firefox 119中工作正常。

css 复制代码
.card {
  & * {
    /* styles here */
  }
}

使用不带&号的自定义属性

在这个问题中,选择一个不带&号的自定义属性将不会呈现预期的结果。

css 复制代码
.card {
  [data-type="featured"] {
    /* styles here */
  }
}

我发现这在Chrome稳定版中不起作用,但在Chrome Canary 121、Safari 17.1和Firefox 119中工作正常。

为了解决这个问题,我们需要附加一个&符号:

css 复制代码
.card {
  &[data-type="featured"] {
    /* styles here */
  }
}

这两个bug都在Chrome Canary中的宽松CSS嵌套版本中得到了修复。

检测CSS嵌套支持

可以使用@supports来检查CSS嵌套支持。在我们的例子中,我们想检查浏览器是否识别&可以使用以下代码。

css 复制代码
@supports selector(&) {
  .post-content {
    & h2 {
      /* styles here. */
    }
  }
}

对我来说,现在还在使用PostCSS嵌套插件,它将原生CSS嵌套编译为普通CSS。一旦它可以安全的使用后,就可以不再使用插件了。

结论

CSS嵌套是一个重要的功能,它将增强我们编写CSS的能力。目前使用嵌套是可行的,但需要注意受众情况,因为目前对支持程度仍然较新。


看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)

相关推荐
passerby60618 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了15 分钟前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅18 分钟前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅40 分钟前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅1 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊1 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte2 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc