23 种 CSS 设计风格如何按成本实现并兼容兜底

把 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 让文字对比度、焦点状态依赖透明度和发光效果

我的判断是:视觉风格只是增强层,兜底的目标不是复刻每个像素,而是保住速度、可读性和操作确定性;最贵的效果只在能证明收益时启用。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容