把 23 种视觉风格理解成 23 套互不相关的模板,是最容易走偏的地方。落到 CSS,它们大多只是颜色、阴影、圆角、透明度、滤镜、变换和布局等属性的不同组合,真正需要比较的是实现、渲染、兼容和维护成本。
掘金热帖《CSS 复现 23 种设计风格:从拟物到液态玻璃的成本阶梯与兜底写法【实战】》提供了按成本组织内容的思路。 我的结论更直接:普通业务页面优先从 T0、T1 开始;T2、T3 只有在目标设备、背景复杂度和文字可读性都经过验证后再启用。默认样式先保证可读,透明、模糊和高光再通过 @supports 逐层添加,动画按需启用。
横向测试使用同一个卡片组件,包含标题、正文、标签、按钮、表单控件和焦点状态,背景覆盖纯色、图片、视频、动态渐变以及浅色和深色主题。这样比较的重点不是截图是否“像某种风格”,而是效果在哪个能力分支失效、失效后是否还能保持相同的信息层级和操作反馈。
先算清每种风格依赖的 CSS 能力
视觉复杂度不等于浏览器成本。一个多阴影按钮未必比单层渐变昂贵,真正拉开差距的是模糊面积、透明层数量、背景是否持续变化,以及动画是否反复触发绘制。因此不能只按“像不像”分档,而要把成本拆成四项。
| 成本维度 | 需要记录的内容 | 常见问题 |
|---|---|---|
| 实现成本 | CSS 层数、伪元素、重复阴影、渐变数量、选择器复杂度 | 同一组件出现多套近似实现,细节难以统一 |
| 渲染成本 | 模糊面积、叠层数量、滤镜组合、动画频率、持续重绘 | 大面积玻璃、多层透明卡片、视频背景同时出现时明显放大开销 |
| 兼容成本 | backdrop-filter、混合模式、遮罩、3D 变换及厂商前缀 |
不同浏览器进入不同分支,截图和实际效果不一致 |
| 维护成本 | 深浅主题、对比度、减少动态效果、高对比度模式 | 增强效果成为必选项后,辅助功能和性能问题难以收口 |
按这些成本可以把实现划成四档。同一种风格如果降低精度,可能直接降档;例如单层玻璃可以停在 T1,而大面积、多层、带动态折射的玻璃会进入 T3。
| 档位 | 判断标准 | 典型能力 |
|---|---|---|
| T0 基础层 | 只改变信息表达,不依赖持续合成 | 色块、字阶、网格、边框、圆角、简单伪元素 |
| T1 质感层 | 增加静态材质和空间关系 | 多层阴影、渐变、倒角、二维或三维变换 |
| T2 特效层 | 引入透明合成或持续变化 | 模糊、滤镜、混合模式、遮罩、透明叠层、循环动画 |
| T3 合成层 | 多层材质共同参与最终画面 | 折射边缘、高光、动态背景、大面积模糊和透明卡片堆叠 |
以下矩阵按同一组件的实际实现方式整理 23 种风格。矩阵中的档位针对较高完成度版本,单色、纯边框等简化实现可以下沉到更低档位。
| 风格与档位 | 核心属性 | 主要风险 | 兜底结果 |
|---|---|---|---|
| 扁平化 Flat Design(T0) | 色块、字号、留白,不依赖阴影 | 层级完全依赖排版,删掉装饰后可能失去重点 | 直接作为所有增强效果的基线 |
| 极简主义 Minimalism(T0) | 字体层级、间距、少量色彩 | 信息过多时容易缺少视觉分组 | 删除装饰后仍保留阅读顺序 |
| 粗野主义 Brutalism(T0) | 硬边框、原生字体、强对比、偏移阴影 | 窄屏溢出,阴影容易变成视觉噪声 | 去掉阴影,保留硬边和文字对比 |
| 瑞士风 Swiss Style(T0) | 网格、非对称布局、克制排版 | 复杂网格在窄屏下拥挤 | 折叠为单列网格,保留层级 |
| 包豪斯 Bauhaus(T0) | 原色、圆形、方形和几何伪元素 | 装饰与正文争夺注意力 | 复杂几何退化为矩形色块 |
| 孟菲斯 Memphis(T0) | 斜线、波点、高饱和色、几何伪元素 | 图案密度过高会增加视觉噪声 | 减少图案数量,保留配色和构图 |
| 波普艺术 Pop Art(T0) | 粗体字、高对比色、网点或渐变 | 网点资源和装饰可能干扰阅读 | 网点改为纯色背景或边框 |
| 装饰艺术 Art Deco(T0) | 对称布局、几何边线、金属色渐变 | 小屏下边线和装饰过密 | 渐变改为纯色金属边线 |
| 像素风 Pixel Art(T0) | 像素网格、阶梯边框、像素化图像渲染 | 缩放后模糊,位图资源体积上升 | 去掉位图,保留阶梯式边框 |
| 暗色模式 Dark Mode(T0) | 色彩变量、表面层级、状态色 | 阴影失效后层级消失,局部对比不足 | 用表面差和边框承担层级 |
| 拟物化 Skeuomorphism(T1) | 多层渐变、高光、box-shadow |
图层多,跨尺寸和跨主题维护困难 | 合并为单层高光、一条边框和一个阴影 |
| Material Design(T1) | 两层阴影表达不同海拔 | 只靠阴影表达层级时边界不清 | 降为单层阴影或实体边框 |
| Flat 2.0(T1) | 纯色块加伪元素长投影 | 额外伪元素增加绘制和维护项 | 删除长投影,保留纯色块 |
| 新拟物主义 Neumorphism(T1) | 同背景上的双向明暗阴影 | 低对比、状态不明确,键盘焦点容易丢失 | 改用边框、填充差和状态色 |
| Y2K(T1) | 金属渐变、倒角、星芒装饰 | 纹理和星芒容易喧宾夺主 | 删除复杂纹理,保留倒角边框 |
| 等距视角 Isometric(T1) | transform、斜切、层叠平面 |
文本倾斜,点击区域与视觉位置不一致 | 退化为普通卡片或俯视网格 |
| 黏土拟态 Claymorphism(T1) | 大圆角、双阴影、内高光 | 阴影层数多,圆角与内部留白容易冲突 | 保留圆角和一层阴影 |
| Glassmorphism(T2) | 半透明背景、backdrop-filter、半透明边框 |
不支持、性能不足或背景复杂时可读性下降 | 改用高不透明实体表面并保留边缘层级 |
| 赛博朋克 Cyberpunk(T2) | 霓虹阴影、扫描线、故障动画 | 持续动画、闪烁和低对比影响阅读 | 去掉辉光与扫描线,改为静态强调边框 |
| 蒸汽波 Vaporwave(T2) | 粉青渐变、网格、发光、复古动画 | 装饰密集时增加绘制并干扰表单 | 保留静态双色渐变,移除网格和动画 |
| 渐变风格 Gradient(T2) | 线性、径向及多层渐变 | 渐变导致文字对比波动,色彩带明显 | 先声明纯色背景,再叠加渐变 |
| 极光 Aurora(T2) | 多层径向渐变、模糊、位移动画 | 大面积持续重绘,移动端压力较高 | 使用静态径向渐变并关闭模糊 |
| 液态玻璃 Liquid Glass(T3) | 多层透明材质、背景模糊、高光、折射边缘 | 合成层多,文字稳定性和低端设备性能最难保证 | 使用实体玻璃卡片、边框与阴影,取消动态折射 |
拟物化的核心并不是“阴影多”,而是让高光、阴影、边框和渐变共同模拟一种材质。下面的实现已经比完整拟物界面克制,但仍然保留了可辨认的表面关系。
.skeuo-card {
color: #2d1b0e;
background:
linear-gradient(135deg, rgba(255, 255, 255, .3), transparent 42%),
linear-gradient(180deg, #a87342, #79502d);
border: 1px solid #56371f;
border-radius: 12px;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, .35),
0 2px 8px rgba(0, 0, 0, .28);
}
.skeuo-card--low {
background: #865830;
box-shadow: 0 1px 3px rgba(0, 0, 0, .22);
}
低精度版本不是浏览器不支持时自动出现的分支,而是组件设计时就提供的简化方案。拟物化尤其适合票据、账单等局部对象,不适合把整张数据表改造成由几十层材质组成的界面。
Glassmorphism 也不能简化为一个 blur 属性。透明背景决定透射程度,采样区域决定背后内容是否被模糊,边缘负责表达玻璃厚度,阴影则补足悬浮关系;这些因素共同决定最终质感。
.glass-card {
--glass-solid: #f7f8fa;
--glass-tint: rgba(247, 248, 250, .72);
color: #18202a;
background: var(--glass-solid);
border: 1px solid rgba(24, 32, 42, .18);
box-shadow: 0 8px 24px rgba(15, 23, 42, .16);
}
@supports (backdrop-filter: blur(1px) saturate(120%)) {
.glass-card {
background: var(--glass-tint);
backdrop-filter: blur(14px) saturate(120%);
}
}
@supports (-webkit-backdrop-filter: blur(1px) saturate(120%)) {
.glass-card {
background: var(--glass-tint);
-webkit-backdrop-filter: blur(14px) saturate(120%);
}
}
默认背景保持实体高不透明度,只有检测到背景模糊能力后才切换为半透明表面。这里不能照搬某个固定透明度:长文本、业务表格和复杂图片背景需要更实的遮罩,装饰性标签或短按钮才适合更明显的透射效果。
液态玻璃比普通玻璃多了一层边缘、高光和动态背景关系。纯 CSS 可以模拟高光与近似边缘,但不能把这种效果描述成真正的物理折射;下面的版本仍然把实体背景作为兜底。
.liquid-card {
--liquid-solid: #edf2f6;
--liquid-tint: rgba(237, 242, 246, .58);
position: relative;
isolation: isolate;
overflow: hidden;
background: var(--liquid-solid);
border: 1px solid rgba(24, 32, 42, .16);
box-shadow: 0 12px 30px rgba(15, 23, 42, .18);
}
.liquid-card::before {
content: "";
position: absolute;
inset: -30%;
background: linear-gradient(
120deg,
rgba(255, 255, 255, .72),
transparent 38%,
rgba(129, 178, 255, .24)
);
opacity: .45;
pointer-events: none;
}
.liquid-card > * {
position: relative;
z-index: 1;
}
.liquid-card::after {
content: "";
position: absolute;
inset: 0;
border-radius: inherit;
box-shadow:
inset 1px 1px 0 rgba(255, 255, 255, .72),
inset -1px -1px 0 rgba(63, 88, 120, .16);
pointer-events: none;
}
@supports (backdrop-filter: blur(1px)) {
.liquid-card {
background: var(--liquid-tint);
backdrop-filter: blur(16px) saturate(135%);
}
}
@supports (-webkit-backdrop-filter: blur(1px)) {
.liquid-card {
background: var(--liquid-tint);
-webkit-backdrop-filter: blur(16px) saturate(135%);
}
}
@media (prefers-reduced-motion: no-preference) {
.liquid-card--ambient::before {
animation: liquid-drift 12s ease-in-out infinite alternate;
}
}
@keyframes liquid-drift {
from { transform: translate3d(-2%, -1%, 0) scale(1.02); }
to { transform: translate3d(2%, 1%, 0) scale(1.06); }
}
这里的动画只移动高光层,不持续改变模糊半径。它能增加环境感,但不构成真实折射,也能减少背景采样不断失效带来的压力。
每次测试不必保存一张大截图,只要固定五项字段,就能把视觉结果转换成可复用的工程数据。
| 记录字段 | 记录方式 |
|---|---|
| 视觉层数 | 统计背景、伪元素、阴影和透明表面的实际叠加层数 |
| 最大模糊面积 | 记录模糊元素面积及其占视口比例 |
| 动画频率 | 记录循环间隔、触发方式和同时运行的动画数量 |
| 兼容分支 | 标记为标准支持、仅厂商前缀支持或不支持 |
| 对比度修正 | 记录背景透明度、遮罩、文字色和边框色调整 |
在统一组件里测出兼容与性能边界
兼容性结论必须附带测试日期和浏览器版本,但长期复制一张版本表很容易过期。更稳妥的做法是固定目标环境,把完整版本、操作系统和测试日期随每次发布归档。Android WebView 尤其不能只写一个“Android Chrome 版本”,因为它由系统组件和设备更新共同决定。
当前项目资料没有附带这批环境的真实运行日志,因此这里不预填具体版本、绘制耗时或帧率,也不使用“全浏览器支持”这类无法核验的结论。正式发布时,应将下面五个环境全部跑完,并把实际版本、日期和结果写入发布记录。
| 固定测试环境 | 版本与日期必须记录 | 必测能力 | 结果必须记录 |
|---|---|---|---|
| Chromium 桌面端 | 完整浏览器版本、操作系统、日期 | 标准与前缀模糊、filter、mask、混合模式、3D 变换 |
视觉结果、兜底分支、首屏绘制、滚动、焦点和对比度 |
| Firefox 桌面端 | 完整浏览器版本、操作系统、日期 | 标准与前缀模糊、滤镜、遮罩和混合模式 | 同上 |
| Safari 桌面端 | Safari 版本、macOS 版本、日期 | 标准与前缀模糊、滤镜、遮罩、3D 变换 | 同上 |
| iOS Safari | iOS 版本、设备型号、日期 | 前缀与标准分支、滤镜、视频背景表现 | 同上,并记录页面滚动和表单焦点 |
| Android WebView | System WebView 或 Chrome 版本、Android 版本、设备型号、日期 | 标准与前缀模糊、滤镜能力、低端设备滚动表现 | 同上 |
浏览器能力可以在测试页面中直接探测。下面代码只用于记录环境,不通过 User-Agent 推断能力;User-Agent 只作为版本归档信息保留。
const features = [
["backdrop-filter", CSS.supports(
"backdrop-filter",
"blur(1px) saturate(120%)"
)],
["-webkit-backdrop-filter", CSS.supports(
"-webkit-backdrop-filter",
"blur(1px) saturate(120%)"
)],
["filter", CSS.supports("filter", "blur(1px)")],
["mask-image", CSS.supports(
"mask-image",
"linear-gradient(#000, #000)"
)],
["mix-blend-mode", CSS.supports(
"mix-blend-mode",
"multiply"
)],
["transform-3d", CSS.supports(
"transform",
"translate3d(0, 0, 0)"
)]
];
console.table(
features.map(([feature, supported]) => ({
feature,
supported
}))
);
console.info("testedAt:", new Date().toISOString());
console.info("userAgent:", navigator.userAgent);
CSS.supports() 只能回答“能否解析这项能力”,不能回答设备是否跑得动,也不能保证文字对比度合格。性能测试应把同一组件放入逐步增加负担的场景,而不是一次把所有效果全部打开。
| 测试场景 | 固定变量 | 主要观察项 | 出现压力后的处理 |
|---|---|---|---|
| 小卡片、静态纯色背景 | 一个玻璃层、固定模糊值 | 首屏绘制、滚动是否出现额外等待 | 保留模糊,缩小受影响区域 |
| 大面积玻璃导航栏 | 与小卡片相同的模糊值 | 模糊面积扩大后的滚动表现 | 改为局部模糊或实体表面 |
| 多层透明卡片重叠 | 相同背景和卡片尺寸 | 透明层叠后的绘制与文字稳定度 | 减少同时透明的卡片数量 |
| 玻璃覆盖图片或视频 | 相同组件尺寸 | 背景变化是否导致持续重新采样 | 在内容区增加实体遮罩或取消模糊 |
| 滚动时同时运行模糊、发光和故障动画 | 相同滚动距离和设备 | 滚动流畅度、动画稳定性 | 关闭非必要动画,减少合成层 |
| 中低端手机及 WebView | 相同代码和网络条件 | 首屏绘制、输入响应、滚动 | 优先使用 T0、T1 兜底 |
排查“效果没有出现”时,顺序应当是:先确认标准属性是否受支持,再检查厂商前缀,最后确认背景采样区域、层叠上下文和父级裁剪是否改变了结果。外观看起来像“模糊失效”,不代表浏览器一定不支持该属性。
排查“效果存在但页面变慢”时,则要分开检查四个变量:模糊面积是否扩大、玻璃层是否重叠、背景是否持续变化、动画是否频繁改变需要重新计算的属性。只观察最终帧率,很容易把这四类问题混成一句“显卡性能不足”。
扫描线、发光和故障效果还要分别运行正常动画与减少动态效果状态。前者用于确认视觉与性能,后者用于确认用户关闭动态效果后仍能获得完整信息。故障效果只做小幅位移,不依赖高频闪烁;所有关键状态都应由文字、边框或图标共同表达。
按能力缺失逐级替换而不破坏信息层级
玻璃效果最稳妥的顺序不是先判断浏览器版本,而是先建立不依赖增强能力的默认状态,再依次加入半透明背景、背景模糊、高光和动态效果。缺少哪一层,就停在哪一层。
下面的组件同时处理浅色、深色、焦点、减少动态效果和高对比度模式。兜底与增强只改变背景、滤镜和装饰,不改变盒模型,因此不会出现因增强失败导致的尺寸跳动。
.glass-panel {
--panel-solid: #f7f8fa;
--panel-tint: rgba(247, 248, 250, .78);
--panel-border: rgba(24, 32, 42, .18);
--panel-text: #18202a;
--panel-focus: #075bd8;
color: var(--panel-text);
background: var(--panel-solid);
border: 1px solid var(--panel-border);
box-shadow: 0 2px 8px rgba(15, 23, 42, .12);
}
@supports (backdrop-filter: blur(1px) saturate(120%)) {
.glass-panel {
background: var(--panel-tint);
backdrop-filter: blur(14px) saturate(120%);
}
}
@supports (-webkit-backdrop-filter: blur(1px) saturate(120%)) {
.glass-panel {
background: var(--panel-tint);
-webkit-backdrop-filter: blur(14px) saturate(120%);
}
}
@media (prefers-color-scheme: dark) {
.glass-panel {
--panel-solid: #171b22;
--panel-tint: rgba(23, 27, 34, .82);
--panel-border: rgba(255, 255, 255, .2);
--panel-text: #f3f5f7;
--panel-focus: #8ab4ff;
}
}
.glass-panel--reading {
--panel-tint: rgba(247, 248, 250, .92);
}
@media (prefers-color-scheme: dark) {
.glass-panel--reading {
--panel-tint: rgba(23, 27, 34, .92);
}
}
.glass-panel :where(a, button, input, select, textarea):focus-visible {
outline: 2px solid var(--panel-focus);
outline-offset: 3px;
}
@media (forced-colors: active) {
.glass-panel {
color: CanvasText;
background: Canvas;
border-color: CanvasText;
box-shadow: none;
backdrop-filter: none;
-webkit-backdrop-filter: none;
}
.glass-panel::before,
.glass-panel::after {
content: none;
}
.glass-panel :where(a, button, input, select, textarea):focus-visible {
outline-color: Highlight;
}
}
属性缺失时,替换关系应保持简单。
| 缺失或不适合的能力 | 替代方案 | 不能牺牲的内容 |
|---|---|---|
backdrop-filter |
提高表面不透明度,使用实体背景 | 边缘层级、边框和阴影 |
filter |
保留纯色或静态渐变 | 文字与背景的分离度 |
mix-blend-mode、mask |
使用渐变、透明边框或伪元素轮廓 | 内容边界和视觉分组 |
| 3D 变换 | 取消倾斜,改为二维卡片或俯视网格 | 点击区域与内容的对应关系 |
| 循环动画 | 保留静态构图或第一帧 | 信息、状态和操作反馈 |
| 阴影表达层级 | 增加实体边框、表面色差或状态色 | 控件边界和焦点可见性 |
扫描线和故障位移也应该有明确的静态版本。下面这段只移动装饰层,不改变布局;关闭动态效果后,扫描线停在静态位置,故障文字恢复原位。
.cyberpunk__scan {
animation: cyber-scan 2.4s linear infinite;
}
.cyberpunk__glitch {
animation: cyber-glitch 4s ease-in-out infinite;
}
@keyframes cyber-scan {
from { transform: translateY(-120%); }
to { transform: translateY(120%); }
}
@keyframes cyber-glitch {
0%, 90%, 100% { transform: translate3d(0, 0, 0); }
93% { transform: translate3d(-2px, 0, 0); }
96% { transform: translate3d(2px, 0, 0); }
}
@media (prefers-reduced-motion: reduce) {
.cyberpunk__scan,
.cyberpunk__glitch,
.aurora__glow,
.liquid-card--ambient::before {
animation: none !important;
}
}
性能兜底不应通过设备名称猜测能力。更可靠的做法是缩小模糊面积、减少同时透明的层数、取消持续动画,并避免直接改变模糊半径。transform 和 opacity 可以用于有限的环境动效,但不能替代对滚动性能的验证。
对比度、焦点环、文字层级和减少动态效果都应成为发布门槛,而不是视觉验收完成后的附加检查。半透明表面必须与实际背后的图片、视频或业务内容一起判断,不能只在纯色背景上确认。
| 页面场景 | 建议档位 | 需要避免 |
|---|---|---|
| 后台、表单、文档、表格 | T0 或 T1 | 大面积玻璃、持续故障效果、仅靠阴影表达状态 |
| 品牌落地页、背景可控的活动页 | T1 或 T2 | 在未验证的设备上同时启用多层透明和循环动画 |
| 移动端 WebView、低端设备优先 | T0 或 T1 | 大面积模糊、相互重叠的玻璃卡片 |
| 高对比度和严格无障碍场景 | T0 或 T1 | 让文字对比度、焦点状态依赖透明度和发光效果 |
我的判断是:视觉风格只是增强层,兜底的目标不是复刻每个像素,而是保住速度、可读性和操作确定性;最贵的效果只在能证明收益时启用。
[email protected]
。
我们承诺在核实信息后,将第一时间删除相关内容或断开链接。



















暂无评论内容