基础
层级
哪些东西会浮起并投下阴影,哪些控件浮在对话之上却不带阴影,弹出层如何定位,以及焦点环。
控件和卡片平铺在页面上。只有短暂浮在页面之上的东西(菜单、弹出层、对话框、轻提示)才投下阴影,而且它们投下的都是同一种柔和阴影。浮在对话之上、把人带到对话中某处的控件保持平铺(浮动控件)。
原则
- 控件和卡片不加阴影。 控件靠墨色边框来标记,不靠阴影,即使它浮在内容之上也是如此。有两个例外,都是暂时离开原位的卡片:被拖动的卡片和展开的评论卡片。
- 所有浮起的东西用同一种柔和阴影。
- 硬朗的贴纸阴影只用于品牌插画。
Token
| Token | 浅色 | 深色 |
|---|---|---|
| elevation-floatControls never cast a shadow; only overlays float. | ||
| elevation-sticker |
| Token | 浅色 | 深色 |
|---|---|---|
| focus-ringThe focus indicator. It shows only after keyboard navigation: styles.css empties it while the root carries data-input-modality="pointer", which installInputModality sets after a click or a tap. |
| Token(工具类) | 用于 |
|---|---|
elevation-float(shadow-float) | 菜单、下拉选择列表、抽屉、底部面板、对话框、个人资料卡、工具提示、轻提示、消息的悬停工具栏和选中文字的工具栏(浮在所操作消息之上的弹出层)、被拖动中的卡片(它被从看板上提起;留在原处的卡片不投阴影),以及文档边栏中展开的评论卡片(它被从卡片列中提起,其他卡片为它让开位置;评论) |
elevation-sticker(shadow-sticker) | 仅限品牌插画:个人资料上和初始设置最后一步的 99px 头像,以及创建服务器页面上新服务器的预览(品牌)。其他地方都不用 |
focus-ring(shadow-focus) | 所有能用键盘到达的元素,在键盘到达它时 |
弹出层
菜单、下拉选择和筛选列表、个人资料卡在打开它们的元素旁边打开。每个组件都遵循同样的规则,所以弹出层不会超出窄屏,也不会让页面在人的眼前移动。
- 弹出层浮在整个页面之上。它们从不占用布局空间。
- 弹出层碰到窗口边缘时,移动它或翻到另一侧;不要让它超过剩余的空间。
- 弹出层打开时、有人在里面输入时,不要滚动或移动页面。
- 弹出层始终跟着打开它的元素。弹出层开着时页面在下面滚动或移动(无论是整个窗口滚动,还是窗口里的某个面板滚动),弹出层都随触发它的元素一起移动;停在原地的弹出层会指向滚到那个位置的别的东西。右键菜单例外:它属于打开它的那个点,而不属于某个元素(ContextMenu)。
浮动控件
对话中的跳转胶囊,Back to Bottom、"3 New Messages" 和 "Next Unread Thread (3)",浮在消息列表的底部或顶部(MessageList)。它们是唯一浮在内容之上的控件,而且不带阴影。
- 浮动控件按它本来的控件画:一个
outline样式的 Button,墨色边放在不透明的layer-card上。不透明的卡片让它在下面滚过的消息之上依然清晰,墨色边像其他地方一样把它标记为控件。 - 不要给它加柔和阴影。阴影的意思是「这东西暂时浮在页面上,用完就会消失」:菜单、弹出层、浮在所操作消息之上的工具栏。跳转胶囊属于对话本身,列表在它下面滚动时它待在原处,无处可跳时自己消失;加了阴影,对话看起来就像有个弹出层开着。
- 不要用浮动控件承载页面上的操作。它只用于在它所浮的列表中移动;操作应放在头部或工具栏里(FloatButton)。
叠加的浮层
模态表面(对话框、警告对话框、模态面板)用遮罩压暗它下面的一切,这样唯一能用的就是最上面那个。当一个模态表面从另一个打开时,这一点也必须成立:下面的表面要退后一步,否则白色对话框叠在白色面板上,没有任何东西说明两者中哪个是当前可用的。
- 在每个模态表面的正下方放一层遮罩,包括从另一个模态表面打开的那个。遮罩才是说明「这个在最上面」的东西;只有阴影说明不了。
- 所有遮罩画法相同:
layer-backdrop,在另一个模态表面之上用layer-backdrop-nested,不加模糊,也不加自己的色调,无论它在对话框、抽屉、底部面板还是搜索浮层下面(stdrc,2026-10-21)。它如何出现跟随它的表面(动效)。 - 第一个表面用
layer-backdrop。从它打开的表面用更浅的layer-backdrop-nested(两种主题下都是 40%),这样下面的表面变暗,而页面不会变黑。浅色模式下,一层遮罩让白色页面剩下约 35% 的亮度,两层完整的遮罩会只剩 12%;用嵌套遮罩则保留约 21%。深色模式下,第一层遮罩是 60%,嵌套的 40% 依然能让下面的深色表面明显退后,又不会和页面融成一片。 - 模态表面最多叠两层。要打开第三层时,替换最上面那层,或者把流程移到一个页面里。
- 从面板打开的面板是例外:下面那个面板自己退后(Drawer)。
raft-ui 会自动处理这件事。模态的 Dialog、AlertDialog 或 Drawer 会告诉它根节点下的所有内容它已打开,方式与 Base UI 判断对话框是否嵌套相同,所以在它下面打开的对话框、警告对话框或面板会画出嵌套遮罩。非模态的不算:它下面不会被压暗。Base UI 默认不给嵌套对话框画遮罩,这就是从面板打开的确认框曾经叠在一个未压暗的面板上的原因。
焦点环
focus-ring 是先一道 2px 的表面色间隙,再一道 2px 的墨色。它是墨色,不是黄色:黄色的焦点环在黄色外框和黄色贴纸上会看不见。
- 给每个能用键盘到达的元素一个可见的焦点环。
- 只在键盘导航之后显示,点击或轻触之后不显示(状态)。根节点标有
data-input-modality="pointer"时,这个 token 本身会被清空,所以shadow-focus自动遵循这条规则。 - 贴着容器边缘的元素,比如菜单的搜索行,改用向内的 2px 墨色描边。它不是这个 token,所以要加上
keyboard:变体(focus-visible:keyboard:inset-ring-2)来遵循同一条规则。 - 贴纸获得焦点时保留它自己的墨色边(提及标记、选中的侧边栏行、筛选),把焦点环画在它外面。不要用焦点环替换这条边。
- 复合控件的哪一部分显示焦点环,见状态。