周末打开 Chrome,发现版本号又跳了一位。很多人可能没太在意,但 2025 年对前端来说确实是个分水岭——过去三年里 CSS 新增的特性数量,比前面十年加起来还多;而 Chrome 作为事实上的标准推进主力,几乎每个月都在把以前只能由 JavaScript 硬扛的能力,下沉成几个 CSS 关键字。这篇文章不打算写成趋势报告,就是周末闲下来,结合我自己项目里摸过踩过的东西,聊一聊 2025 年的 CSS 到底变了什么,Chrome 在这条路上推了什么,以及所谓“声明式 Web”是怎么从概念变成每天都能写到的代码。如果你还在观望新特性,或者被各种框架轮换搞得有点焦虑,这期杂谈值得你泡杯茶慢慢看。
1. 为什么偏偏是 2025 年:CSS 终于长成一门“应用语言”
1.1 Chrome 的版本节奏,决定了一件事能不能用
以前聊 Chrome 版本号,更多是吐槽它“数字涨得快、升级比吃饭还勤”;但到了 2025 年,版本节奏反而成了好事。Chrome 稳定版基本维持 8 周一个大版本,每个大版本都带一份清晰的新特性清单,其中 CSS 占据的篇幅越来越长。更关键的是,很多特性不再需要去 chrome://flags 手动开启实验开关,落地即默认支持。
判断一个 CSS 特性能不能放心用,我现在的习惯是先看 Chrome 的发布说明,再对照 Web Platform Dashboard 里的 Baseline 状态。2025 年的标志性事实是:容器查询(Container Queries)、:has()、CSS 原生嵌套、子网格(Subgrid)、视图过渡(View Transitions)这些名字,全部进入了“广泛可用”区间。换句话说,它们不是“未来技术”,而是你现在打开 Chrome 就能直接放进生产环境的基础设施。
1.2 声明式 Web:一次“少写 JS”的集体回归
声明式意味着你告诉浏览器要什么,而不是一步步指挥它怎么做。HTML 是声明式的,CSS 也是声明式的。过去二十年,为了做出交互效果,我们不得不转向命令式:监听事件、更新 DOM、计算位置、管理状态,一层层叠加下来,JavaScript 就越写越重。结果就是包体积膨胀、长任务卡顿、体验反而离原生 App 越来越远。
2025 年“声明式 Web”重新成为关键词,不是因为理念多先进,而是 CSS 终于把过去 JS 被迫承担的工作接了回去。一个典型例子是滚动驱动动画。以前做“元素滚到可视区中间就出现”这种效果,要监听 scroll、不停地 requestAnimationFrame、手动计算进度;现在一个 animation-timeline 属性就能表达:
.card { animation: scale-in linear both; animation-timeline: view(); }这段代码的意思是:动画进度跟随元素在视口中的可见位置,滚到中间时放大,滚出视口时缩小。没有一行 JS,效果是原生的、流畅的,而且不需要担心 scroll 监听器造成的性能损耗。
1.3 三个地基能力,撑起这一波爆发
为什么偏偏是 2025 年爆发?因为下面三块地基在这几年陆续成熟了。
第一块是@property。它允许开发者注册一个带类型和初始值的自定义属性,并像普通属性那样参与计算和动画。以前渐变角度没法做过渡动画,因为浏览器不知道渐变里的角度如何插值;有了@property,把--angle声明成syntax: '<angle>',就能放心地在 keyframes 里动它。
第二块是容器查询单位,比如cqw、cqi。以前相对单位只有vw/vh这类视口单位,CSS 的响应式被迫绑定在浏览器窗口宽度上;现在单位可以相对组件所在的容器,这才真正匹配“组件化开发”的思考方式。
第三块是浏览器合成器能力的增强。动画和过渡可以跑在合成线程,主线程被解放出来,复杂动效不再意味着卡顿。这三块合在一起,让 CSS 不再只是“静态样式表”,而是一套能描述动态行为、可组合的语言。
2. 2025 年最值得上手的 CSS 新能力
2.1 容器查询:组件终于能感知自己的“容器”
容器查询的价值,一句话就能说明白:以前响应式只能靠视口宽度,同一个组件换个位置就可能不管用;现在组件可以根据自己所在容器的大小来调整样式。
.card { container-type: inline-size; } @container (min-width: 480px) { .card__title { font-size: 2rem; } }我实际用过一次之后,就再也回不去了。之前做卡片组件库,为了兼容侧边栏里的窄卡片和主内容区的宽卡片,得给组件加一堆variantprops,逻辑里全是if width < 480 then ...。有了容器查询,组件自己就能感知宽度,不需要外部传入任何状态。
需要注意container-type: inline-size会建立一个包含上下文,子元素的cqw单位会相对这个容器计算。如果要继续使用百分比高度,可能需要配合container-type: size,但后者会让容器的尺寸被内容撑开这件事失效,慎用。
2.2 :has():终于能选“父元素”了
:has()在 2023 年进入正式基线,这两年在生产里已经非常稳了。它解决的是前端长期以来的痛点:根据子元素的状态,改变父容器甚至兄弟元素的样式。
.card:has(.featured-image) { grid-template-columns: 1fr 1fr; } .form-field:has(input:invalid) { border-color: #e11; }我经常用它替代 JS 的“监听 DOM 变化再切换类名”逻辑。比如表单校验,以前要监听 input 的 input 事件去判断是否显示错误态,现在直接一条:has(input:invalid)就完事,纯声明式,样式归样式,逻辑归逻辑。
性能方面也不必太担心。Chrome 对:has()的选择器匹配做了专门优化,只要不是写特别复杂的嵌套,日常场景的性能损耗可以忽略不计。
2.3 CSS 原生嵌套:终于不用再为了嵌套去依赖预处理器
CSS 嵌套语法和 Sass 很像,但它是浏览器原生解析的,不经过编译。
.card { border-radius: 16px; &:hover { transform: translateY(-4px); } .badge { background: #f2f2f2; } }我最大的感受是调试链路变短了。以前用 Sass,浏览器 DevTools 里显示的是编译后的扁平选择器,你得手动去 sourcemap 里找源文件;原生嵌套直接在浏览器里的样式面板看到嵌套结构,定位问题快得多。
注意:嵌套选择器对 specificity(优先级)的计算和 Sass 不同,嵌套层级越深,权重越高。所以即使现在能嵌套了,也不建议无脑套四五层,否则还是会遇到选择器优先级打架的老问题。
2.4 子网格:对齐不再靠魔法数字
子网格让我这种经常和表格类布局打交道的人非常开心。以前要在两个不同的 grid 容器里让列宽对齐,只能靠固定宽度或者百分比,改一处就得改好几处。现在直接在子级里写:
.parent { display: grid; grid-template-columns: 1fr 2fr 1fr; } .child { display: grid; grid-template-columns: subgrid; }子级的列自动跟随父级的列轨,不需要知道具体的宽度值。做表单项的 label/input 对齐、卡片内多列对齐,都是一句subgrid的事。
目前 Chrome、Safari、Firefox 都支持了,我在自己维护的组件库里已经默认使用。唯一要留意的是,subgrid只能作用于 grid 容器,Flex 布局里用不了,所以布局选型时要想清楚。
2.5 锚点定位:气泡浮层的终极方案
工具提示(tooltip)、下拉菜单、弹层定位,这是前端老油条都烦过的事。以前要么用第三方库计算位置,要么靠 CSSposition: absolute猜一个大概角度再微调,换了位置就容易穿帮。
锚点定位(Anchor Positioning)提供了一个全新的思路:
.anchor { anchor-name: --popup; } .popup { position: fixed; position-try-fallbacks: flip-block, flip-inline; inset-area: top span-right; }这段代码的意思是:.popup锚定在.anchor上,优先显示在锚点上方的右侧;如果空间不够,浏览器自动翻转方向。这彻底解决了“弹层超出边界”这类问题,不需要 JS 参与几何运算。
我试着在一个内部后台系统的工具提示里用了这个方案,效果非常丝滑,以后脱离第三方定位库完全可行。
2.6 View Transitions:页面切换也能声明式搞定
过去做 SPA 页面切换动画,要引入 framer-motion 这类库,或者自己写路由监听、加载状态、进出场动画。View Transitions API 把这件事变成了浏览器能力。
::view-transition-old(root) { animation: fade-out 0.3s; } ::view-transition-new(root) { animation: fade-in 0.3s; }多页应用(MPA)可以在 2025 年的 Chrome 里直接用view-transition开关,实现类似原生 App 的页面过渡。单页应用也可以在 DOM 更新前后调用document.startViewTransition(),然后靠 CSS 去描述动画。
我个人的结论是:页面级的转场以后会逐步变成浏览器默认能力,前端需要关注的不再是怎么做动画,而是怎么设计过渡语义。
3. 声明式 Web 的实战:那些曾经以为必须靠 JS 的效果
3.1 涟漪光圈扩散:纯 CSS 也能模拟水波纹
“css 涟漪光圈扩散”这个搜索词今年很热,因为它确实是设计系统里的高频需求。以前要么引组件库,要么自己写 JS 监听点击、插入 span、加类、动画结束再移除节点。其实伪元素加一个 keyframes 就够了。
.ripple-button { position: relative; overflow: hidden; -webkit-tap-highlight-color: transparent; } .ripple-button::after { content: ''; position: absolute; left: 50%; top: 50%; width: 200px; height: 200px; margin: -100px 0 0 -100px; border-radius: 50%; background: radial-gradient( circle, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.2) 60%, transparent 70% ); opacity: 0; transform: scale(0); transition: opacity 0.6s ease, transform 0.6s ease; pointer-events: none; } .ripple-button:active::after { transform: scale(2.5); opacity: 0.9; transition: 0s; }这段代码在按钮按下时,会从中心扩散一个光圈,效果和 Material Design 波纹很接近。除了最后光圈散开时让位给过渡动画,整个过程没有 JS。如果你希望波纹从鼠标点击的位置开始扩散,留三行 JS 设置--x、--y自定义属性即可,但大多数纯展示场景,上面已经够用。
3.2 流光边框:conic-gradient + border-box 双层背景
“流光边框 css”是今年被问爆的另一个效果。核心思路是:让边框区域的渐变背景持续转动,同时内容区保持纯色覆盖。
@property --angle { syntax: '<angle>'; initial-value: 0deg; inherits: false; } .glow-border { position: relative; border: 3px solid transparent; border-radius: 16px; background: linear-gradient(#fff, #fff) padding-box, conic-gradient(from var(--angle), #ff6b6b, #ffd93d, #6bcb77, #4fc3f7, #ff6b6b) border-box; animation: spin 3s linear infinite; } @keyframes spin { to { --angle: 360deg; } }原理可以拆成两层理解。padding-box区域是内容背景,用纯白盖住内侧;border-box区域是边框背景,用conic-gradient铺满一圈渐变色;border: 3px solid transparent让边框显现的是背景层而不是描边颜色。最后靠@property让--angle能被动画成 0deg 到 360deg,实现持续旋转的流光感。没有@property的时候,这个动画很难纯 CSS 做出来,只能塞 GIF 或者 JS 改内联样式。
3.3 字体渐变与“屏幕穿出”效果
字体渐变算老技术了,但经典永不过时。
.gradient-text { background: linear-gradient(90deg, #ff6b6b, #ffc371, #4fc3f7); -webkit-background-clip: text; background-clip: text; color: transparent; }大标题上用这个,比单色字有质感得多。再进一步,可以把渐变背景尺寸拉大,然后循环移动background-position,做出流光动态字体,这在营销页和品牌页里非常加分。
至于“css 能实现屏幕穿出来的效果吗”,这个问题要看你怎么定义“穿出来”。如果只是视觉上让元素浮出屏幕,用 3D 变换加透视就能做到:
.card-3d { transform: perspective(600px) rotateX(15deg) translateZ(50px); }我做过一个弹窗悬浮效果,父层设置perspective,子层用translateZ(80px),视觉上已经很有“从背景里浮起来”的感觉,全程没有 JS。如果要做类似苹果官网那种跟随鼠标的 3D 倾斜,才需要监听鼠标位置去更新旋转角度,那属于合理使用 JS 的场景。
3.4 数字加载动画与倒计时
以前做数字从 0 滚到目标值的动画,首选requestAnimationFrame或者setInterval,再更新textContent。其实有个更声明式的思路:利用@property的整数插值能力。
@property --num { syntax: '<integer>'; initial-value: 0; inherits: false; } .counter { width: 100px; animation: count-num 2s ease-out forwards; font: 3rem monospace; counter-reset: n var(--num); } .counter::after { content: counter(n); } @keyframes count-num { to { --num: 99; } }浏览器会对--num做整数插值,伪元素的content读取counter(n)显示当前值。数字从 0 平滑滚到 99,不需要 JS,也不会像 JS 定时器那样出现掉帧。这种写法很适合仪表盘、统计面板的入场数字。
需要注意@property不支持calc()直接计算,但可以通过多个自定义属性组合使用来解决。复杂的业务数字还是建议 JS 控制,展示型动画纯 CSS 更省心。
3.5 五种布局方式与原子化 CSS 的身位
经常看到有人问“HTML CSS 五种布局方式”,其实指的是:流式布局(normal flow + float)、Flexbox、Grid、绝对定位布局、多列布局。这是 CSS 发展时间线上层层叠加的产物。
2025 年我个人的选择习惯是:默认 Grid,局部一维排列用 Flex,特殊的悬浮定位用 absolute,文本分栏用columns,float 只用于处理图片文字环绕。布局方案没有绝对优劣,关键是每种方案的适用边界要清楚。
原子化 CSS 这几年热度不减,Tailwind 为代表的工具把“一个类只干一件事”推到极致。但原子化 CSS 和原生 CSS 新特性并不是二选一的关系。你在 Tailwind 里照样可以写:has(),照样能搭配容器查询;只是原子类让代码更符号化,原生 CSS 让样式更接近“意图”。我现在的组件库是:基础样式用原生特性封装,业务页面用原子类拼接,两者互补。
4. 声明式 Web 对开发方式的影响
4.1 性能:少写 JS 就是最直接的优化
页面性能优化的尽头是什么?我的答案是“减少主线程的工作量”。JS 执行、DOM 操作、样式计算都在主线程上排队,而 CSS 的动画和交互大部分可以交给渲染进程的合成线程。2025 年你能做的很大一部分性能优化,就是把“没必要让 JS 做的事”放回 CSS。
比如前面说的滚动驱动动画,如果沿用旧方案,一个页面有十个滚动动画就得挂十个 scroll 监听器,还要自己在回调里做防抖、算位置;换成animation-timeline后,滚动监听交给浏览器。肉眼可见的长任务少了,页面流畅度提升是实打实的。
4.2 可维护性:把样式意图交给 CSS
CSS 更接近“表达意图”,JS 更接近“描述过程”。一个交互效果如果用 CSS 描述,后续维护时只需要找样式,不需要翻遍 JS 逻辑。:has()和容器查询带来的变化,是从根上减少了一大批状态类命名:以前要维护.is-active、.has-error、.is-expanded这一堆类名的增删,现在选择器可以直接绑定状态。少一层状态同步,就少一堆 bug 来源。
4.3 框架怎么选:Tailwind、CSS Modules、原生 CSS
2025 年还多了一个选项:浏览器原生的 CSS 越来越强,很多以前必须借助框架的场景现在不需要了。我的建议是,按项目复杂度来分:
- 简单页面、营销落地页:直接原生 CSS + 少量 CSS 变量,够用。
- 组件库、设计系统:用 CSS Modules 或原生 CSS,配合容器查询、
:has()做组件化封装。 - 大型业务应用:原子化 CSS 能快速迭代,但要在团队里约定规则,避免类名爆炸。
框架始终是工具,不是信仰。今年如果你还在纠结“要不要上某个新框架”,不如先把 Chrome 里已经默认支持的 CSS 能力列一遍,大概率能省掉不少依赖。
4.4 什么场景仍然需要 JavaScript
声明式 Web 并不是要消灭 JavaScript,而是把 JS 还给真正需要逻辑的地方。数据请求、状态管理、路由跳转的鉴权、复杂表单校验逻辑、与后端通信、大量 DOM 增删改,这些依然是 JS 的领地。
遇到需要精确计算物理效果(如拖动、惯性、手势冲突处理)或者需要监听长时间持续的状态流时,也建议老老实实写 JS。CSS 再强,也不适合承担完全的“应用逻辑层”职责。2025 年的最佳实践是:能声明就声明,该命令就命令,两边各干各擅长的活。
5. 常见问题与排查技巧实录
5.1 Chrome 版本太旧,新特性用不了
如果你在用 Windows 7 或 Windows 8.1,那么浏览器版本会被卡在 Chrome 109,因为这是官方支持这两个系统的最后一个大版本。很多 2023 年之后的 CSS 特性自然用不了。
先到chrome://version查版本号。如果确实需要兼顾老系统用户,可以用特性查询降级:
@supports (font-size: clamp(1rem, 2vw, 2rem)) { .title { font-size: clamp(1rem, 2vw, 2rem); } } /* 旧浏览器走这里 */ .title { font-size: 1.4rem; }@supports是一个很老但可靠的方法,碰到“理论上兼容,实际环境太旧”的情况,它就是你的安全网。
5.2 硬件加速开启后光标变白、滚动变慢
“Chrome 启用硬件加速后光标变白”这个现象,本质上是显卡驱动和 Chrome 合成器之间的兼容性问题。显卡驱动没跟上 Chrome 的渲染管线更新,导致光标位图读取异常。
我的处理顺序是:先关闭硬件加速试试,如果问题消失,说明是驱动问题,去更新显卡驱动;更新后重新打开硬件加速,一般能恢复。如果更新驱动不能解决,再考虑在 Chrome 的启动参数里禁用特定 GPU 特性,但这种情况建议直接用内置的 “不拦截 / 阻止该网站使用 GPU” 站点设置去隔离问题网站。
滚动慢和视频卡顿也多因 GPU 合成异常导致,同样是先确认驱动版本,再决定是关硬件加速还是换浏览器通道试试。不要一上来就关闭硬件加速,那会让所有页面都回到 CPU 渲染,体验更差。
5.3 插件装不上:注意 Manifest V3 和版本匹配
“因为使用了不受支持的清单版本”这类扩展安装报错,一般是扩展还在用 Manifest V2(MV2)。Chrome 在 2023 年开始逐步淘汰 MV2,到 2025 年新版本的扩展商店基本不再接受 MV2 扩展。如果你在公司内部需要安装老扩展,要么找团队升级到 MV3,要么用浏览器策略放宽限制,但这不是长久之计。
另外,下载扩展时记得看商店标注的“支持的 Chrome 版本区间”,在chrome://extensions/里打开开发者模式也能看到更多错误信息。大多数“装不上”的问题,不是版本太旧就是版本太新,先把扩展来源和版本对清楚。
5.4 账号无法登录与 HSTS 状态的排查
账号登录不上,第一步打开 DevTools 的 Network 面板,看请求是否被拦截或重定向。常见的现象是登录请求被缓存了旧的重定向,或者当前域名的 HSTS 状态异常。
HSTS 是浏览器的一项安全机制,它强制浏览器对某个域名使用 HTTPS,访问chrome://net-internals/#hsts可以查看当前域名的 HSTS 状态,也可以查询某域名下是否存在未过期的 STS 策略。遇到“明明访问的是 HTTP,却被强制跳转 HTTPS 且证书不对”时,去这个面板里查一下,再决定要不要清理状态。日常开发如果需要在本地调试 HTTPS 场景,建议用本地证书工具,而不是轻易去操作 HSTS 配置。
5.5 验证 CSS 新特性的几个实用工具
我平时验证一个属性在当前 Chrome 能不能用,有三个顺手的方式。
一是直接打开 DevTools,在 Styles 面板里输入属性,如果显示无效,就说明当前浏览器不支持或不接受这种写法。
二是在控制台里快速跑一条CSS.supports():
CSS.supports('selector(:has(*))'); // true/false它能精确判断某个选择器、属性、函数在当前 Chrome 版本里的支持状态。
三是@supports,我想在代码里做降级时直接写在样式表里,比在控制台里看结果更实用。这三个工具组合起来,基本不会踩“写完才发现当前 Chrome 用不了”的坑。
一点周末的碎碎念
做了这么多年前端,我很少见到 CSS 像 2025 年这样集中爆发。Chrome 的作用不只是实现了几个属性,它把整套“声明式”的思维方式推到了前台:能用样式解决的问题,不写 JS;能在浏览器层面完成的效果,不引依赖。对我个人来说,真正的进步不是学会几个新属性,而是开始用新的眼光拆解问题——拿到一个交互效果,先问一句“这是不是本来就可以用 CSS 表达?”这会让代码干净不少,也会让你重新找回写页面的乐趣。如果你也在 2025 年的项目里试过这些新特性,欢迎把自己的体验和坑分享出来,我这个周末的杂谈就当是个开场。