区域级的加载覆盖层:放进一个 relative 的父容器,默认 absolute inset-0
铺满同宽高;半透明背景 + backdrop-blur 的磨砂玻璃,底下的内容隐约可见——
语义是「内容还在、正在刷新」,不是空白骨架屏。
保持渲染、切换 loading,进出两个方向都有 150ms 交叉淡入
(motion-reduce 豁免)。隐藏靠 visibility 一起过渡:退场时淡完才消失,
消失后离开无障碍树——不会有幽灵的「Loading」播报,也不会挡任何点击。
使用
import { LoadingOverlay } from '@gedatou/cadenza-ui'<div className="relative overflow-hidden rounded-xl border p-4">
{/* 内容 */}
<LoadingOverlay loading={isLoading} />
</div>宿主容器
父容器要记两件事:relative(忘了它,覆盖层会铺到更外层的定位祖先上去);
圆角容器再加 overflow-hidden。覆盖层自带 rounded-[inherit]——这不是装饰
而是必需:Chromium 的 backdrop-filter 不吃祖先的圆角裁切,只有元素自身的
圆角能约束磨砂范围,否则四角会凸出方形缺口(在真实 Button 上放大验证过,宿主加
transform / isolation 都救不了)。宿主的 overflow-hidden 负责另一半——把覆盖层
画出的背景裁进宿主的可见形状。库内的内嵌实例(表格卡片、Button)都已按此处理。
自定义内容
不传 children 就是居中一个 Spinner(英文 aria 兜底、
零可见文案)。传了就完全替换——自带文案时让 Spinner 装饰化(aria-hidden),
语义交给文字:
内嵌实例
它就是库内加载态的默认视觉:DataTable 和
InfiniteSelect 的一切 isLoading、
Button 的 pending,渲染的都是这同一层——
首屏给最低高度防坍缩,刷新/进行中盖住旧内容(表格和下拉由业务层配 react-query 的
placeholderData 保住旧数据即可触发)。
Button 的实例传 rounded-none backdrop-blur-none、磨砂改由标签的内容滤镜
承担——按钮尺度下 backdrop 模糊核会沿边角涂出晕染,模糊内容本身观感相同而边缘
像素级干净,这是真机放大验证过的结论。
内嵌实例的定制:表格和下拉走各自的 slotted 标记部件
DataTableLoadingOverlay / InfiniteSelectLoadingOverlay(props 就是本组件的,
loading 除外——那是基座的接线),样例见两个组件页的「加载中」小节;Button 的
定制走「不传 pending、children 自己画」这条路,见
Button 页。
无障碍
- 挡鼠标不挡键盘:Tab 仍能聚焦到覆盖层底下的控件(Mantine / antd 同样如此)。
要彻底封锁,给内容区上
inert——那是调用方的决定,本组件不替你做。 - 加载期间光标是
wait(覆盖层自带,含其内所有后代)。 - 建议给被刷新的区域标
aria-busy,让辅助技术知道这块内容暂不可靠。
状态与 className
底下是纯 div,不是 Base UI 部件,没有任何 state:className 只收字符串,改磨砂
程度(backdrop-blur-*)、背景浓度(bg-background/*)、z-* 都从这一个口子进。
覆盖层带 data-slot="loading-overlay",需要从外部定位时当选择器用。
Props
顺序规则:必填 → 非受控默认值 → 受控值 → 回调 → 行为开关 → 外观 → className。
LoadingOverlayProps 类型一并导出。