news 2026/9/23 17:16:43

Material Web 组件之 Elevation 完全指南:用 `<md-elevation>` 实现 0-5 级阴影体系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Material Web 组件之 Elevation 完全指南:用 `<md-elevation>` 实现 0-5 级阴影体系
  • UI组件
  • 前端
  • 设计系统

【免费下载链接】material-web

Material Design Web Components

项目地址:https://gitcode.com/gh_mirrors/ma/material-web
点击查看免费下载

Elevation(高度)是 Material Design 中两个表面沿 z 轴方向的相对距离,Material Web Components 通过<md-elevation>自定义元素与--md-elevation-levelCSS 自定义属性,把 M3 的六档阴影体系(0~+5)完整落地到 Web 平台。读完本篇,你将掌握如何用一行标记为任意容器自动铺满的投影、通过 transition 属性在悬停/按压等交互状态间平滑切换阴影、如何用颜色 token 定制阴影色,并理解底层双阴影合成(key + ambient shadow)的实现原理。

Elevation 是什么

根据 M3 规范 的定义,Elevation 是两个表面沿 z 轴(垂直于屏幕的方向)的相对距离。Material 的阴影系统被刻意限制为只有少数几个档位,这一"创意约束"迫使你在设计 UI 的阴影叙事(elevation story)时做出深思熟虑的取舍,而不是随意堆砌阴影。

Material 使用六档高度等级,每档对应一个 dp(密度无关像素)数值,命名反映了元素相对 UI 表面的距离:0、+1、+2、+3、+4、+5。其中元素的静止(resting)状态可以使用 0 到 +3 级,而+4 和 +5 级专门保留给用户交互状态,例如 hover(悬停)和 dragged(拖拽)。这意味着你应当把最高的两级高度当作"交互反馈"手段,而不是让普通静态卡片常年停留在高位。

在 Material Web Components 中,这一套体系由一个轻量的自定义元素承载:

// elevation/elevation.ts @customElement('md-elevation') export class MdElevation extends Elevation { static override styles: CSSResultOrNative[] = [styles]; }

元素内部实现非常精简,internal/elevation.ts 只做两件事:在connectedCallback里设置aria-hidden="true"(避免 VoiceOver 把它识别为与内容并列的"分组"),并渲染出一个<span class="shadow">作为阴影承载节点。也就是说,<md-elevation>本身没有任何可见内容,纯粹是一层可感知的投影层

基础用法:为任意表面添加高度

Elevation 可以通过--md-elevation-levelCSS 自定义属性在 0 到 5 之间取值。投影会自动填满最近的position: relative元素的尺寸与形状(圆角会被继承)。

<style> .surface { position: relative; border-radius: 16px; height: 64px; width: 64px; --md-elevation-level: 3; } </style> <div class="surface"> <md-elevation></md-elevation> <!-- Content --> </div>

使用上有三个关键点:

  1. 宿主必须有定位上下文:阴影节点通过position: absolute; inset: 0铺满,而它的定位基准是最近的position: relative祖先,因此承载表面必须设置position: relative(源码见 internal/_elevation.scss 中的:host, .shadow, .shadow::before, .shadow::after样式块)。
  2. 圆角自动继承:阴影使用border-radius: inherit,所以只要给宿主设置圆角,阴影就会跟随宿主形状;每档阴影的具体圆角是否叠加由 Material 规范决定,本组件实现中直接继承宿主的圆角值。
  3. <md-elevation>可以出现在内容任意位置:组件使用display: flex; pointer-events: none,不会拦截鼠标事件,也不会影响内容的可点击性。

从源码的阴影公式(internal/_elevation.scss)可以看到,阴影是两层 box-shadow 合成的

  • .shadow::beforekey shadow(主投影)opacity: 0.3,负责给出物体的"着地感";
  • .shadow::afterambient shadow(环境投影)opacity: 0.15,负责大范围的环境光晕。

每档等级对应的 key shadow 目标值(源码注释中给出)为:

LevelKey box-shadowAmbient box-shadow
00px 0px 0px 0px0px 0px 0px 0px
10px 1px 2px 0px0px 1px 3px 1px
20px 1px 2px 0px0px 2px 6px 2px
30px 1px 3px 0px0px 4px 8px 3px
40px 2px 3px 0px0px 6px 10px 4px
50px 4px 4px 0px0px 8px 12px 6px

实现上,Sass 用一组clamp(0, var(--_level) - N, M)的分段函数把连续的--_level映射到离散的 y / blur / spread 值,再乘以1px得到最终 box-shadow。这种"用 CSS 计算把 0~5 离散值换算成阴影参数"的手法,正是组件支持动画的关键——因为阴影参数是直接由 CSS 变量实时推导的,改变--md-elevation-level时无需重建任何样式。

动画:在高度等级间平滑过渡

在表面切换高度等级时,可以给宿主添加transition-durationtransition-easing-function(或transition-timing-function)属性,阴影就会平滑过渡。组件内部已经把transition-durationtransition-propertytransition-timing-function全部设置为inherit,因此宿主上的过渡设置会被阴影层完整继承

<style> .surface { /* ... */ transition-duration: 250ms; transition-timing-function: ease-in-out; --md-elevation-level: 3; } .surface:active { --md-elevation-level: 0; } </style> <div class="surface"> <md-elevation></md-elevation> <!-- Content --> </div>

要点说明:

  • 组件默认在:host.shadow::before/::after上设置transition-property: box-shadow, opacity,确保只有阴影相关的属性参与过渡,避免不必要的重排;
  • 上面示例利用:active状态实现"按压时降至 0 级"的物理反馈,这正符合 M3 中"静止状态 0~+3、交互状态 +4~+5"的用法延伸——反过来,hover 时升到 +4/+5 也同样是推荐的交互模式;
  • 过渡时长(如 250ms)与缓动函数(如 ease-in-out)完全由你在宿主上控制,组件不做硬编码。

仓库的演示代码也验证了这一模式,elevation/demo/stories.ts 中每个 demo 盒子都设置了transition-duration: 250ms; transition-timing-function: ease-in-out;,而 elevation/demo/demo.ts 通过levelknob 让你可以在 0~5 之间实时拖动切换(渲染时用Math.min(Math.max(level, 0), 5)把输入夹在合法区间内),直观观察阴影过渡效果。

无障碍

Elevation 是一个纯视觉组件,不承载任何语义与交互信息,因此没有无障碍方面的顾虑。组件实现中唯一与无障碍相关的处理,是在挂载时为自身设置aria-hidden="true",避免屏幕阅读器把阴影层误读为内容分组(源码注释见 internal/elevation.ts)。在使用时,你无需为投影层提供任何 ARIA 属性或替代文本。

Theming:用 Material 主题定制阴影

Elevation 支持 Material theming,可以从颜色维度进行定制。它遵循与其它组件一致的 token 体系(Sass 侧见 elevation/_elevation.scss 的@forward './internal/elevation' show theme,样式侧见 internal/_elevation.scss 的theme($tokens)mixin),支持且仅支持两个 token:

Token默认值
--md-elevation-level0
--md-elevation-shadow-color--md-sys-color-shadow

这两个 token 的定义来自 tokens/_md-comp-elevation.scss:

  • level取自md-sys-elevation.level0,默认即为 0,对应"无投影";
  • shadow-color取自md-sys-color.shadow,也就是系统色板中的 shadow 色(tokens/_md-sys-color.scss 中注册的'shadow'token),默认通常接近黑色。

值得留意的是,tokens/_md-sys-elevation.scss 中有一处针对 Web 平台的特殊处理:注释明确指出"Web 端的高度等级应使用等级数字而非 dp 值"(level0~level5 被映射为整数 0~5),这正是你在页面上看到--md-elevation-level: 3而不是12dp之类写法(level3 对应的 dp 值)的原因。

主题化示例:自定义阴影颜色

<style> .surface { position: relative; border-radius: 16px; height: 64px; width: 64px; } :root { --md-elevation-level: 5; --md-sys-color-shadow: #006A6A; } </style> <div class="surface"> <md-elevation></md-elevation> <!-- Content --> </div>

这个例子做了两件事:

  • 通过:root--md-elevation-level提升到 5(所有表面默认获得最高级投影);
  • 把系统 token--md-sys-color-shadow覆盖为青绿色#006A6A,让阴影呈现品牌色。

因为组件样式中的阴影颜色读取的是var(--_shadow-color),而_shadow-color又回落到var(--md-elevation-shadow-color, ...),最终落到--md-sys-color-shadow,所以在:root覆盖任何一个环节都能全局生效——这也解释了为何示例中只需要改--md-sys-color-shadow一个变量。若只想改变单个表面的阴影色,把--md-elevation-shadow-color直接写在对应类上即可。

若使用 Sass 构建,还可以通过组件暴露的theme($tokens)mixin 以编程方式注入 token:

// 需要引入 tokens 模块与 elevation 模块 // @use 'material-web/tokens'; // @use 'material-web/elevation'; // .surface { @include elevation.theme((level: 5, shadow-color: #006A6A)); }

thememixin 的 token 白名单定义在 tokens/_md-comp-elevation.scss 的$supported-tokens中,传入白名单之外的 token 会在编译期直接@error报错。)

API 一览

<md-elevation>的公共 API 极为精简,不暴露任何属性(property)、属性(attribute)与方法,所有能力都通过 CSS 自定义属性对外提供:

  • --md-elevation-level:取整数值 0~5,控制高度等级,默认0
  • --md-elevation-shadow-color:控制阴影颜色,默认引用--md-sys-color-shadow

配合使用的还有宿主上的两个标准 CSS 属性:transition-durationtransition-easing-function(组件用transition-timing-functioninherit完成继承),用于开启阴影动画。

测试方面,elevation/elevation_test.ts 通过createTokenTests(MdElevation.styles)对组件的样式 token 做一致性校验,确保暴露的 token 与主题体系声明保持一致。

总结

  • 是 M3 六档高度体系的 Web 实现:用--md-elevation-level: 0~5控制,自动铺满并继承最近position: relative宿主的尺寸与圆角;
  • 静止用低档(0~+3),交互用高档(+4~+5):这是 Material 对高度体系的创造性约束,也是最容易出彩的设计决策点;
  • 动画零成本:给宿主加transition-duration/transition-easing-function,配合状态切换改变--md-elevation-level即可获得流畅的阴影过渡,因为底层阴影参数由 CSS 变量实时推导;
  • 主题化只碰颜色:两个 token 中--md-elevation-shadow-color默认指向--md-sys-color-shadow,改一个系统变量即可全局换色;
  • 纯视觉、零负担:组件不拦截事件、无无障碍问题,天然适合作为任意表面的一层"投影叠加层"。
  • UI组件
  • 前端
  • 设计系统

【免费下载链接】material-web

Material Design Web Components

项目地址:https://gitcode.com/gh_mirrors/ma/material-web
点击查看免费下载
上一篇:capa入门教程:5步学会分析可疑可执行文件
下一篇:Awesome Go的CQRS模式:命令查询职责分离

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 17:16:40

3个关键优化点,搞定quadrature性能,保姆级教程

3个关键优化点,搞定quadrature性能,保姆级教程 报错一堆看不懂 StackTrace,CPU 飙到 100% 还没算出结果?别慌,今天这篇保姆级教程,专门拆解数值积分里的性能黑洞。…

作者头像 李华
网站建设 2026/9/23 17:16:40

搞懂博客和微博的区别,3个最佳实践避坑指南

搞懂博客和微博的区别,3个最佳实践避坑指南 复制来的代码跑不通,报错信息一堆,不知道从哪下手调?别急,这往往不是代码本身的问题,而是你对底层机制的理解出了偏差。在技术选型和内容输出的最佳实践中,搞清“长文”与“短文”的边界,比盲目堆砌功能更重要。就像写代码,你得知道哪段逻辑该放在核心模块,哪段只是日…

作者头像 李华
网站建设 2026/9/23 17:16:33

3个真实案例拆解无人机比赛开发最佳实践

3个真实案例拆解无人机比赛开发最佳实践 刚学完Python或C++语法,看着无人机比赛的规则文档两眼一抹黑?别慌。这就是典型的“会敲代码,不会搭项目”的困境。在无人机竞速或自主飞行比赛中,语法只是入场券, 最佳实践 才是让你从模拟器崩溃飞到赛道夺冠的关键。…

作者头像 李华
网站建设 2026/9/23 17:16:24

行圆汽车性能优化:吃透3道高频面试题

行圆汽车性能优化:吃透3道高频面试题 刚毕业那会儿,我在面试游戏开发岗时被问懵了。面试官问:“行圆汽车”在渲染管线里怎么优化?我愣在原地,脑子里一片空白。那一刻我才意识到,很多看似专业的名词,其实是把基础原理包装了一下。…

作者头像 李华
网站建设 2026/9/23 17:16:09

5分钟吃透看脸时代源码解析,新手避坑指南

5分钟吃透看脸时代源码解析,新手避坑指南 官方文档往往厚达数百页,术语堆砌让人头大,读完还是懵。很多开发者卡在第一步,根本抓不住核心逻辑,导致项目进度停滞。别慌,今天不念经,直接切入【看脸时代】的底层脉络,用【源码解析】的方式把复杂问题拆成积木块。…

作者头像 李华