介绍
如果你是一名使用过 CSS 预处理器的前端开发人员,那么你很可能已经接触或使用过嵌套功能。它一直是一个很受欢迎的功能,对我来说,它也是促使我使用 CSS 预处理器的原因之一。.
今年,所有主流浏览器(Chrome、Firefox 和 Safari)都已支持原生 CSS 嵌套。这是 CSS 的一项核心特性,能够简化 CSS 的编写。在本文中,我将记录我目前对 CSS 嵌套的理解,并与大家分享我的发现、应用案例和示例。.
除了热情和专注之外,没有其他先决条件。.
CSS嵌套一直是许多开发者期待已久的功能。过去我们依赖于Sass或Less等CSS预处理器。下面我简单介绍一下:
请看以下示例。我们有一个位于 .nav__item 选择器中的图标。.
.nav__item {
.icon {
display: flex;
padding: 1rem;
}
}以上代码是有效的 Sass 代码。编译后,在浏览器中将显示如下内容:
.nav__item .icon {
display: flex;
padding: 1rem;
}使用原生 CSS 嵌套,同样的 CSS 代码可以直接生效。下图展示了原生 CSS 嵌套和浏览器开发者工具的对比。.
注意浏览器显示 CSS 的方式与 CSS 中的显示方式几乎相同。.
如果将这段 CSS 代码编译成 Sass,浏览器会显示如下内容:
CSS嵌套的优势
我认为嵌套 CSS 有其合理用途:
- CSS 更易于阅读。.
- 将风格组合在一起
- 限制特定风格
- 对没有类或 ID 的 HTML 元素进行样式设置。.
CSS嵌套规则
为了帮助大家了解 CSS 嵌套,我将尝试提供各种 CSS 问题的可视化示例,以及嵌套如何帮助解决这些问题。.
首先,你需要了解符号“&”。在很多情况下,这个符号都至关重要。.
嵌套没有类名或 ID 的元素
在这个例子中,元素 <a> 通过 .nav__item 设置样式。使用 <head> 标签对于 CSS 的有效性是可选的。.
.nav__item {
& a {
display: block;
padding: 1.5rem 1rem;
}
}
/* Same as: */
.nav__item a {
}您也可以选择不勾选:
.nav__item {
a {
display: block;
padding: 1.5rem 1rem;
}
}
/* Same as: */
.nav__item a {
}请注意,这是最近更新的内容,称为“宽松 CSS 嵌套”。它适用于最新的 Chrome Canary 和 Safari 技术预览版。您可以查看 Adam Argyle 撰写的关于宽松嵌套的文章。.
嵌套带有类名的元素
考虑与之前相同的例子,但假设元素 <a> 它有一个HTML类。.
.nav__item {
.link {
display: block;
padding: 1.5rem 1rem;
}
}
/* Same as: */
.nav__item .link {
}这里不需要使用符号,类名就可以了。.
嵌套 CSS 组合器
CSS原生嵌套的优势之一在于可以使用组合器。让我们来看一些例子。.
在下面的示例中,我想选择所有类名为 `.nav__item` 且其前面还有一个具有相同类名的元素的元素。为此,我使用了相邻兄弟选择器。.
在原生 CSS 嵌套中,我们可以使用 & 符号来模拟嵌套。请注意,我重复使用了两次。.
.nav__item {
& + & {
border-left: 2px solid;
}
}
神奇之处在于 & 符号的第二次迭代。在这里,浏览器意识到我想使用相邻兄弟选择器。下面我举个例子来说明:
嵌套的另一个例子是子元素组合器。它可以选择一个元素的直接子元素。.
.nav__item {
> a {
padding: 1rem;
}
}和符号
.nav__item {
& a {
color: blue;
}
}这是最初 CSS 嵌套规范中的要求。在 Safari TP 179+ 和 Chrome Canary 120 中,嵌套元素不再需要 & 符号。.
因此,以下方法有效:
.nav__item {
a {
color: blue;
}
}唯一的问题是,您必须恢复到规范的先前版本,该版本必须包含标记和标记。.
嵌套示例:活动、聚焦、悬停
:active、:focus 和 :hover 是 CSS 伪类,由用户操作激活。.
通过 CSS 嵌套,我们可以一次性嵌套所有样式,避免代码重复。以 `:hover` 为例:
button {
&:hover {
background-color: var(--bg-color);
}
&:focus {
outline: solid 2px;
}
}使用预处理器进行嵌套时的区别在于,浏览器会按如下方式渲染:
button:hover {
background-color: var(--bg-color);
}
button:focus {
outline: solid 2px;
}让我们来看看 CSS 嵌套在 Chrome、Safari 和 Firefox 中的显示效果。.
关于 CSS 嵌套的 DevTools 用户体验,我有一些想法,稍后会在文章中讨论。.
嵌套示例:发布内容
测试嵌套 CSS 的最早例子之一是为文章正文内容设置样式。想象一下,一篇文章包含标题、正文、图片、引用等等。.
标题
我们通常采用以下这种标题格式:
.post-content h1,
.post-content h2,
.post-content h3,
.post-content h4 {
/* styles here */
}使用嵌套 CSS 就容易多了:
.post-content {
h1,
h2,
h3,
h4 {
color: var(--heading-color);
font-weight: var(--heading-font-bold);
margin-bottom: var(--size-2);
}
}我们也可以使用 :is() 选择器来实现同样的功能。.
.post-content {
:is(h1, h2, h3, h4) {
color: var(--heading-color);
font-weight: var(--heading-font-bold);
margin-bottom: var(--size-2);
}
}段落元素
常见的例子是给段落内的链接设置样式。在这种情况下,CSS 嵌套非常有效。.
.post-content {
& p {
color: var(--color-black);
& a {
font-weight: bold;
text-decoration: underline;
}
}
}链接可能还需要悬停或聚焦效果。.
.post-content {
& p {
color: var(--color-black);
& a {
font-weight: bold;
text-decoration: underline;
&:hover {
/* hover styles */
}
}
}
}我们还可以嵌套媒体查询。.
.post-content {
& p {
/* base styles */
@media (min-width: 400px) {
/* do something */
}
}
}在某些情况下,内容管理系统 (CMS) 可能是一个要素。 <p> 将其包裹在另一个元素中,并且出于样式考虑,它应该仅用于直接元素。 <p> 打造你的风格。.
.post-content {
/* Select the direct <p> elements */
> p {
/* base styles */
}
}引用块
在这个例子中,引用拥有自己的自定义样式和元素 <p> 在引用中,选择将底部边距重置为零。.
.post-content {
& blockquote {
/* custom quote styling */
& p {
margin-bottom: 0;
}
}
}后形
帖子表单包含一张图片和一个
在我的例子中,如果
.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() 选择器。.
.post-content {
li {
&:not(:last-child) {
border-bottom: 1px solid;
}
}
}要使用 :not(),我们需要在其前面添加一个 & 符号。.
自定义标题间距
以下距离应为 <h3> 和 <h4> 如果其中一条评论后面跟着代码片段,我会减少评论的金额。.
.post-content {
& h3 + [class*="language-"],
& h4 + [class*="language-"] {
margin-top: 0.5rem;
}
}正如你在这个实际例子中看到的,使用 CSS 嵌套很简单,特别是如果你有 CSS 预处理器方面的经验。.
嵌套示例:卡片组件
我将展示一个简单的卡片组件,它使用 CSS 嵌套来实现所需的样式。.
假设有一个具有默认或基本样式的 .card 元素,我将演示嵌套 CSS 的用法。.
.card {
/* default card styles */
}
嵌套容器查询
如果容器宽度大于 400px,我希望卡片转换为弹性容器。.
.card {
/* default card styles */
/* if the container width is 400px or bigger */
@container card (min-width: 400px) {
display: flex;
}
}设置段落元素样式
我想通过以下方式使用段落元素 <h3> 这样,我就可以给元素添加边距和内边距了。 <p> 我来补充。如果没有这个元素,用户界面就不会有额外的间距。.
.card__content {
& h3 + p {
border-top: 1px solid #000;
padding-top: 0.5rem;
margin-top: 0.5rem;
}
}当容器宽度为 400 像素或更大时,.card__content 元素也应转换为弹性容器。.
.card__content {
& h3 + p {
border-top: 1px solid #000;
padding-top: 0.5rem;
margin-top: 0.5rem;
}
@container card (min-width: 400px) {
display: flex;
flex-direction: column;
justify-content: center;
}
}嵌套示例:表单输入
一个常见的例子是设置输入框占位符的样式。问题在于每个浏览器厂商都有自己的前缀(哦,现在都2023年了)。.
由于前缀样式需要冒号,因此我们必须使用 & 符号,否则样式将失效。.
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 和直接编写前缀样式有什么区别。.
/********** Option 1: native nesting **********/
input {
&::-webkit-input-placeholder {
color: var(--placeholder-color);
}
}
/********** Option 2: without nesting **********/
input::-webkit-input-placeholder {
color: var(--placeholder-color);
}两者之间没有区别。它们都具有相同的属性(0、1、1)。.
嵌套示例:通过父元素设置元素样式
我们可以使用 CSS 嵌套来根据子元素所在的位置改变其样式。例如,如果一个 `.button` 元素位于一个 `.box` 父元素中,它应该占据整个宽度。.
<div class="box">
<h2>Get access to all features</h2>
<p>Create an account now and get access to all exclusive features.</p>
<a href="/offer" class="button">Create an account</a>
</div>.button {
.box & {
width: 100%;
}
}
/* equivalent to */
.box .button {
}我在探索 CSS 嵌套时遇到的错误
使用无符号全局选择器
假设我们有一个卡片,并且想要选中卡片内的所有元素。使用 CSS 原生嵌套,以下代码应该可以实现:
.card {
* {
/* styles here */
}
}我注意到这在 Chrome 稳定版中不起作用,但在 Chrome Canary 121、Safari 17.1 和 Firefox 119 中运行良好。.
解决方法是添加标点符号。.
.card {
& * {
/* styles here */
}
}使用无符号数据属性
在这个问题中,选择没有勾选标记的数据属性并不能得到预期的结果。.
.card {
[data-type="featured"] {
/* styles here */
}
}我注意到它在 Chrome 稳定版中无法正常工作,但在 Chrome Canary 121、Safari 17.1 和 Firefox 119 中运行良好。.
要解决这个问题,我们需要添加一个逗号:
.card {
&[data-type="featured"] {
/* styles here */
}
}这两个错误都在 Chrome Canary 版本中针对宽松的 CSS 嵌套进行了修复。.
识别 CSS 嵌套支持
`@supports` 可以用来检查 CSS 嵌套是否受支持。在本例中,我们要检查浏览器是否识别 `&` 符号。.
@supports selector(&) {
.post-content {
& h2 {
/* styles here. */
}
}
}您可以在 Bramus 的 Codepen 示例中探索其他识别支撑的方法。.
今天我会使用 PostCSS 嵌套插件,它可以将嵌套 CSS 编译成普通 CSS。等到安全后,我会移除这个插件。.
CSS嵌套的DevTools用户体验
我不太喜欢目前浏览器开发者工具中 CSS 嵌套的用户体验。.
我曾撰写过一篇单独的文章,探讨了我对 DevTools 中 CSS 嵌套实现方式的一些建议。.
结果
CSS嵌套是一项重要的特性,它显著提升了CSS的编写方式。目前,嵌套功能已经可以使用,但由于其支持尚处于起步阶段,因此在实际应用中需要谨慎考虑受众群体。.
























