CSS原生嵌套功能完全指南:告别SCSS与Less
你还在用SCSS或Less只是为了写嵌套CSS吗?浏览器现在已经原生支持了
为什么我们需要CSS嵌套
过去写CSS非常痛苦,选择器重复率很高。看下面这个例子:
/* 老写法:重复写.card 5次 */
.card { background: white; }
.card .title { font-size: 18px; }
.card .title:hover { color: blue; }
.card .body { padding: 16px; }
.card .body p { line-height: 1.6; }为了解决这个问题,我们引入了SCSS:
/* SCSS嵌套写法 */
.card {
background: white;
.title {
font-size: 18px;
&:hover { color: blue; }
}
.body {
padding: 16px;
p { line-height: 1.6; }
}
}现在原生CSS也支持嵌套了,不需要任何构建工具,直接在浏览器里写。
浏览器兼容性
| 浏览器 | 支持版本 | 发布时间 |
|---|---|---|
| Chrome | 112+ | 2023年4月 |
| Firefox | 117+ | 2023年8月 |
| Safari | 16.5+ | 2023年5月 |
| Edge | 112+ | 2023年4月 |
2024年起可以放心在生产环境使用,全球覆盖率超过90%。
基础语法
原生CSS嵌套与SCSS基本一致,有两种写法:
/* 写法1:直接嵌套,推荐使用,Chrome 120+ 不需要写&符号 */
.card {
background: white;
padding: 16px;
.title {
font-size: 18px;
color: #333;
}
}
/* 写法2:用&表示父选择器,Chrome 112+ 就支持 */
.btn {
background: blue;
color: white;
&:hover {
background: darkblue;
}
&.active {
background: green;
}
}注意:Chrome 112到119版本必须使用&符号前缀,Chrome 120以上版本子元素可以省略&符号。
实战1:组件样式封装
告别选择器重复,把一个组件的所有样式写在一起:
/* 传统写法:样式散乱 */
.nav { display: flex; }
.nav ul { list-style: none; }
.nav ul li { padding: 8px 16px; }
.nav ul li a { text-decoration: none; color: #333; }
.nav ul li a:hover { color: blue; }
.nav ul li.active a { color: blue; font-weight: bold; }
/* 原生嵌套:清晰聚合 */
.nav {
display: flex;
ul {
list-style: none;
margin: 0;
padding: 0;
li {
padding: 8px 16px;
a {
text-decoration: none;
color: #333;
&:hover {
color: blue;
}
}
&.active a {
color: blue;
font-weight: bold;
}
}
}
}效果:代码行数相同,但结构清晰很多。
实战2:状态变体管理
用&符号管理同一组件的所有状态:
.btn {
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
/* 颜色变体 */
&.primary {
background: #007bff;
color: white;
}
&.danger {
background: #dc3545;
color: white;
}
&.outline {
background: transparent;
border: 2px solid #007bff;
color: #007bff;
}
/* 尺寸变体 */
&.sm {
padding: 4px 8px;
font-size: 12px;
}
&.lg {
padding: 12px 24px;
font-size: 18px;
}
/* 状态 */
&:hover {
opacity: 0.85;
transform: translateY(-1px);
}
&:disabled {
opacity: 0.5;
cursor: not-allowed;
transform: none;
}
}实战3:响应式嵌套
可以把媒体查询直接嵌套在选择器里面,这个功能非常实用。
/* 旧写法:媒体查询散落各处 */
.card { width: 100%; }
@media (min-width: 768px) {
.card { width: 50%; }
}
@media (min-width: 1024px) {
.card { width: 33.33%; }
}
/* 新写法:响应式就在元素旁边 */
.card {
width: 100%;
padding: 12px;
font-size: 14px;
@media (min-width: 768px) {
width: 50%;
padding: 16px;
font-size: 15px;
}
@media (min-width: 1024px) {
width: 33.33%;
padding: 20px;
font-size: 16px;
}
.title {
font-size: 16px;
@media (min-width: 768px) {
font-size: 20px;
}
}
}媒体查询不再满文件乱飞,直接跟着元素走。
实战4:与is()或where()组合使用
嵌套加伪类的组合写法,可以让代码更精简:
/* 用is()合并多个选择器 */
.article {
h1, h2, h3 {
font-weight: bold;
line-height: 1.3;
}
/* 等价但更简洁的写法 */
:is(h1, h2, h3) {
font-weight: bold;
line-height: 1.3;
}
/* 暗色模式嵌套 */
@media (prefers-color-scheme: dark) {
background: #1a1a1a;
color: #e0e0e0;
a {
color: #64b5f6;
}
}
}
/* 表单验证加has()加嵌套 */
.form-group {
label { color: #333; }
input {
border: 1px solid #ccc;
&:focus { border-color: #007bff; outline: none; }
&:invalid { border-color: #dc3545; }
&:valid { border-color: #28a745; }
}
/* has()联动父元素,Chrome 105以上版本支持 */
&:has(input:invalid) {
label { color: #dc3545; }
.error-msg { display: block; }
}
}完整可运行Demo
直接复制下面的代码到HTML文件里就可以运行:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS原生嵌套功能演示</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: sans-serif; background: #f0f2f5; padding: 20px; }
/* CSS原生嵌套完整示例 */
.card-list {
display: grid;
gap: 16px;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
.card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
transition: transform 0.2s, box-shadow 0.2s;
position: relative;
&:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.15);
}
.card-header {
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
h3 {
font-size: 18px;
margin-bottom: 4px;
}
span {
font-size: 12px;
opacity: 0.8;
}
}
.card-body {
padding: 16px 20px;
p {
color: #666;
line-height: 1.6;
font-size: 14px;
}
}
.card-footer {
padding: 12px 20px;
border-top: 1px solid #f0f0f0;
display: flex;
gap: 8px;
.btn {
padding: 6px 14px;
border-radius: 6px;
border: none;
cursor: pointer;
font-size: 13px;
transition: opacity 0.2s;
&:hover { opacity: 0.85; }
&.primary {
background: #667eea;
color: white;
}
&.outline {
background: transparent;
border: 1px solid #667eea;
color: #667eea;
}
}
}
/* 标记为热门的卡片特殊样式 */
&.hot {
.card-header {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
&::after {
content: '🔥 热门';
position: absolute;
top: 12px;
right: 12px;
background: rgba(255,255,255,0.9);
padding: 2px 8px;
border-radius: 10px;
font-size: 11px;
color: #f5576c;
}
}
}
}
/* 响应式嵌套 */
@media (max-width: 480px) {
.card-list {
grid-template-columns: 1fr;
}
}
</style>
</head>
<body>
<div class="card-list">
<div class="card">
<div class="card-header">
<h3>CSS Nesting</h3>
<span>Chrome 112以上版本,无需构建工具</span>
</div>
<div class="card-body">
<p>原生CSS终于支持嵌套了,告别SCSS,直接在浏览器里写嵌套选择器。</p>
</div>
<div class="card-footer">
<button class="btn primary">立即尝试</button>
<button class="btn outline">查看文档</button>
</div>
</div>
<div class="card hot">
<div class="card-header">
<h3>CSS has()</h3>
<span>Chrome 105以上版本,父元素选择器</span>
</div>
<div class="card-body">
<p>CSS史上最强父选择器,可根据子元素状态选中父元素,不需要JavaScript。</p>
</div>
<div class="card-footer">
<button class="btn primary">立即尝试</button>
<button class="btn outline">查看文档</button>
</div>
</div>
<div class="card">
<div class="card-header">
<h3>CSS @layer</h3>
<span>Chrome 99以上版本,层叠层管理</span>
</div>
<div class="card-body">
<p>彻底解决第三方CSS优先级冲突问题,组件化CSS的终极方案。</p>
</div>
<div class="card-footer">
<button class="btn primary">立即尝试</button>
<button class="btn outline">查看文档</button>
</div>
</div>
</div>
</body>
</html>与SCSS的区别
| 特性 | CSS Nesting | SCSS Nesting |
|---|---|---|
| 需要构建工具 | 不需要 | 需要 |
| 使用&引用父选择器 | 支持 | 支持 |
| 省略&写子元素 | Chrome 120以上版本支持 | 一直支持 |
| 嵌套@media | 原生支持 | 支持 |
| 变量或mixin | 不支持,用CSS变量代替 | 支持 |
| 运行时性能 | 浏览器原生解析 | 需要编译为普通CSS |
对于新项目或轻量项目,可以直接用原生CSS嵌套,零依赖。对于大型项目,SCSS的mixin和函数仍有价值,可以混合使用。
降级处理
/* 检测浏览器是否支持CSS嵌套 */
@supports (selector(&)) {
/* 支持嵌套的浏览器 */
.card {
.title { color: blue; }
}
}
/* 不支持时的降级写法 */
@supports not (selector(&)) {
.card .title { color: blue; }
}也可以使用postcss-nesting自动编译降级。
总结
| 改变 | 说明 |
|---|---|
| 无需SCSS | 小项目不再需要Sass构建步骤 |
| 组件化 | 样式和组件结构保持一致,易于维护 |
| 响应式就近 | 媒体查询嵌套在元素内,不再满文件乱飞 |
| 状态聚合 | hover、focus、disabled等状态全在一处 |
| 兼容性 | Chrome 112以上,Firefox 117以上,Safari 16.5以上 |
CSS嵌套是CSS近年来最重要的生产力提升之一,现在就可以在新项目里用起来。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!