2026 年,CSS 迎来了一场静默却深刻的革命。曾经需要 Sass、Less 等预处理器才能实现的功能——嵌套语法、变量、混入——如今已被原生 CSS 逐一吸收。更令人兴奋的是,容器查询和 :has() 选择器的全面浏览器支持,让 CSS 第一次真正拥有了”组件级响应式”和”父选择器”能力。这意味着我们终于可以告别沉重的构建工具链,回归 CSS 的本来面貌。
3| 3| 4| 4| 5| 5| 6| 6|本文将带你深入这三个改变游戏规则的特性,看看它们如何让前端开发变得更简洁、更强大,以及为什么 2026 年可能是预处理器时代走向终结的起点。
7| 7| 8| 8| 9| 9| 10|
一、原生嵌套:告别预处理器依赖的第一步
15| 13| 16| 14| 17| 15| 18| 16|CSS Nesting Module 在 2023 年首次进入主流浏览器,到 2026 年已获得全面支持(含 Safari、Firefox 的完整兼容)。这意味着你不再需要 Sass 或 Less 就能在样式表中使用嵌套语法,显著减少了代码的层级重复。
19| 17| 20| 18| 21| 19| 22| 20|原生嵌套的语法非常直观,与 Sass 高度相似,但有一些关键区别需要注意:
23| 21| 24| 22| 25| 23| 26| 24|/* 原生 CSS 嵌套 */
27| 25|.card {
28| 26| padding: 24px;
29| 27| border-radius: 12px;
30| 28| background: #fff;
31| 29|
32| 30| & .title {
33| 31| font-size: 1.5rem;
34| 32| font-weight: 700;
35| 33| color: #1a1a2e;
36| 34| }
37| 35|
38| 36| & .body {
39| 37| margin-top: 12px;
40| 38| line-height: 1.6;
41| 39|
42| 40| & p {
43| 41| margin-bottom: 8px;
44| 42| }
45| 43| }
46| 44|
47| 45| &:hover {
48| 46| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
49| 47| }
50| 48|}
51| 49|
52| 50|
53| 51|
54| 52|与 Sass 嵌套相比,原生 CSS 嵌套有一个重要区别:& 符号的使用更加严格。在原生语法中,& 必须放在选择器的开头位置(如 &:hover、&.active),而 Sass 允许 & 出现在任意位置(如 .parent &)。这个限制看似是缺点,实际上鼓励了更清晰、更可维护的嵌套结构。
更值得注意的是性能层面。原生嵌套在浏览器引擎层面直接解析,不需要额外的编译步骤。这意味着开发时的样式更新可以即时生效,构建产物也更小——没有了预处理器生成的冗余选择器,CSS 文件体积平均减少 15%-20%。对于追求极致性能的团队来说,这是一个不可忽视的优势。
59| 57| 60| 58| 61| 59| 62| 60|当然,原生 CSS 变量(Custom Properties)早已替代了 Sass 变量的角色,而 @layer 规则则提供了比 Sass 的 @import 更优雅的样式分层方案。当这些原生能力组合在一起时,预处理器的存在理由正在被一条条剥离。

二、容器查询:响应式设计的范式转移
71| 67| 72| 68| 73| 69| 74| 70|如果要用一句话概括容器查询的意义,那就是:响应式设计终于从”视口级”进化到了”组件级”。
75| 71| 76| 72| 77| 73| 78| 74|过去十年,我们用 @media 做响应式设计,判断的是浏览器视口的宽度。但问题在于,同一个组件可能出现在页面的不同位置——侧边栏里、主内容区、弹窗中——它们所处的容器宽度截然不同,却不得不共享同一套基于视口的响应式规则。这导致组件难以真正复用。
容器查询彻底改变了这一点。它允许你基于组件的父容器尺寸来调整样式,而不是整个视口:
83| 79| 84| 80| 85| 81| 86| 82|/* 声明容器 */
87| 83|.sidebar {
88| 84| container-type: inline-size;
89| 85| container-name: sidebar;
90| 86|}
91| 87|
92| 88|/* 基于容器宽度响应 */
93| 89|@container sidebar (min-width: 400px) {
94| 90| .product-card {
95| 91| display: flex;
96| 92| flex-direction: row;
97| 93| gap: 16px;
98| 94| }
99| 95|
100| 96| .product-card .image {
101| 97| width: 200px;
102| 98| flex-shrink: 0;
103| 99| }
104| 100|}
105| 101|
106| 102|/* 容器较窄时切换为纵向布局 */
107| 103|@container sidebar (max-width: 399px) {
108| 104| .product-card {
109| 105| display: flex;
110| 106| flex-direction: column;
111| 107| gap: 8px;
112| 108| }
113| 109|
114| 110| .product-card .image {
115| 111| width: 100%;
116| 112| }
117| 113|}
118| 114|
119| 115|
120| 116|
121| 117|这段代码的精妙之处在于:无论这个商品卡片被放到哪个容器中——侧边栏、主内容区、还是弹窗——它都能根据实际可用空间自动调整布局。你不再需要为每个使用场景写专门的样式覆盖,组件真正实现了”一次编写,到处适配”。
122| 118| 123| 119| 124| 120| 125| 121|容器查询还催生了一个实用的配套属性:container-type: size。它允许子元素基于容器的尺寸使用百分比单位(包括高度),解决了长久以来”父元素高度依赖子元素内容、子元素百分比高度依赖父元素”的死循环问题。对于构建复杂的自适应布局,这是一个不可或缺的工具。
三、:has() 选择器:CSS 终于有了”关系选择器”
130| 126| 131| 127| 132| 128| 133| 129|在 CSS 的漫长历史中,有一个问题困扰了开发者二十多年:如何根据子元素的状态来设置父元素的样式?由于 CSS 的渲染模型是从父到子单向传递的,”父选择器”一度被认为是不可能实现的特性。
134| 130| 135| 131| 136| 132| 137| 133|:has() 选择器的出现打破了这个限制。它本质上是一个”关系选择器”,允许你基于元素内部或后续的子元素状态来匹配该元素本身:
/* 卡片中有图片时,增加内边距 */
142| 138|.card:has(img) {
143| 139| padding: 0;
144| 140|}
145| 141|
146| 142|.card:has(img) .content {
147| 143| padding: 16px;
148| 144|}
149| 145|
150| 146|/* 表单字段有错误信息时,边框变红 */
151| 147|.form-field:has(.error-message) {
152| 148| border-color: #e74c3c;
153| 149|}
154| 150|
155| 151|.form-field:has(.error-message) label {
156| 152| color: #e74c3c;
157| 153|}
158| 154|
159| 155|/* 导航项包含活跃子菜单时高亮 */
160| 156|.nav-item:has(> .submenu .active) {
161| 157| background: var(--accent-color);
162| 158|}
163| 159|
164| 160|/* 段落后面紧跟图片时,减少底部间距 */
165| 161|p:has(+ img) {
166| 162| margin-bottom: 0;
167| 163|}
168| 164|
169| 165|
170| 166|
171| 167|这些示例展示了 :has() 的强大之处。过去要实现这些效果,你需要在 JavaScript 中手动检查 DOM 结构并添加/移除 class,既增加了运行时开销,也引入了额外的复杂度。现在,一切都在 CSS 层面声明式地完成。
关于性能,一个常见的担忧是::has() 会不会导致重绘性能问题?实际上,现代浏览器引擎(特别是 Chromium 113+ 和 Safari 17+)对 :has() 做了大量优化,在大多数实际场景中性能表现良好。但确实需要注意避免在深层嵌套结构中使用过于复杂的 :has() 链式选择器,比如 .container:has(.child:has(.grandchild:has(.active))) 这类写法可能会影响渲染性能。
最佳实践是将 :has() 用于结构性的条件样式(如”有图片就调整布局”、”有错误就变红”),而不是用于高频变化的交互状态。对于后者,传统的 class 切换方案仍然是更好的选择。

结语:回归原生的勇气
188| 182| 189| 183| 190| 184| 191| 185|CSS 在 2026 年终于”成年”了。原生嵌套让我们告别预处理器的编译依赖,容器查询让组件级响应式成为现实,:has() 选择器补齐了 CSS 选择器体系的最后一块拼图。这三个特性合在一起,构成了一个完整的”后预处理器时代”技术栈。
当然,从”使用预处理器”到”回归原生”的迁移不会一蹴而就。Sass 在函数式编程能力(如循环、条件判断)上仍然有优势,大型项目中短期内仍会保留它。但对于新项目而言,直接使用原生 CSS 已经是完全可行的选择——更少的依赖、更快的构建、更清晰的调试体验。
196| 190| 197| 191| 198| 192| 199| 193|技术的进步往往不是惊天动地的革命,而是一个个”终于不需要了”的瞬间叠加。当你发现某个工具的存在不再是必需,而只是习惯时,也许就是放手的时候了。
200| 194| 201| 195|