一个 React 标签页组件,采用 Liquid Glass 标签栏、滑动指示器,并支持键盘导航。
Tabs 用于在同一页面的一组相关面板之间切换。标签栏共享一块玻璃表面,半透明指示器会跟随当前标签移动。
标签页通过 items 数组传入。每个条目的 value 会把标签按钮与对应面板关联起来,content 则渲染在该面板中。非受控用法使用 defaultValue,受控用法使用 value 与 onValueChange。
标签按钮可以同时包含文字与图标。默认会激活第一个可用的标签。
支持 xs / sm / md / lg / xl,默认 md。标签高度、内边距、文字、图标、间距与面板文字会一起缩放。
value 与 onValueChange 让当前标签完全由外部状态控制。把 value 设为 null 可以渲染没有激活标签的状态。
Active tab: Overview
使用 orientation="vertical" 切换为左右布局。方向键会跟随方向变化,因此纵向标签使用 ArrowDown 与 ArrowUp 移动。
在组件上设置 disabled 会禁用所有标签;在单个 item 上设置 disabled 只会禁用该标签。禁用标签仍然可见,并保留在标签栏中。
切换到其他标签时,面板默认会卸载。给条目设置 keepMounted 可以让面板保留在 DOM 中,从而在切换标签后保留本地状态。
默认情况下,方向键只移动焦点,需要按 Enter 或 Space 才会激活标签。设置 activateOnFocus 后,焦点移动时会直接切换面板。
Active tab: Overview. Focus the list and press an arrow key to switch without pressing Enter.
标签默认由 label 命名。只渲染图标时,请提供 aria-label 或 aria-labelledby。
Tabs 渲染由 tab 按钮组成的 tablist 与关联的 tabpanel。激活的标签暴露 aria-selected,每个面板通过 aria-controls 与 aria-labelledby 关联。
方向键沿布局方向移动焦点,Home 与 End 跳到首尾;activateOnFocus 为 false 时,Enter 或 Space 激活当前焦点标签。请用 aria-label 或 aria-labelledby 为标签栏命名;纯图标标签需要自己的名称。键盘细节见可访问性指南。
Tabs
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
items | readonly TabsItemProps[] | — | 组件渲染的标签与面板。 |
value | any | — | 受控的当前值。设为 null 时不激活任何标签。 |
defaultValue | any | 第一个可用项 | 非受控时的初始值。 |
onValueChange | (value: any, details) => void | — | 当前值变化时调用。 |
orientation | "horizontal" | "vertical" | "horizontal" | 布局与键盘方向。 |
activateOnFocus | boolean | false | 方向键移动焦点时是否立即激活标签。 |
loopFocus | boolean | true | 焦点到达末尾后是否回到第一个标签。 |
disabled | boolean | false | 禁用所有标签。 |
size | "xs" | "sm" | "md" | "lg" | "xl" | "md" | 标签高度、内边距、文字、图标与间距。 |
aria-label | string | — | 标签栏的无障碍名称。 |
aria-labelledby | string | — | 用外部元素命名标签栏。 |
aria-describedby | string | — | 外部说明文字的 ID。 |
render | ReactElement | ((props, state) => ReactElement) | — | 替换根元素。 |
ref | Ref<HTMLDivElement> | — | 转发到根元素的 ref。 |
className | string | — | 合并到根元素的 Tailwind 类名。 |
style | CSSProperties | — | 内联样式,合并到与 className 相同的元素。 |
ui | { root?: ClassValue; list?: ClassValue; glass?: ClassValue; overlay?: ClassValue; indicator?: ClassValue; tab?: ClassValue; icon?: ClassValue; label?: ClassValue; panels?: ClassValue; panel?: ClassValue; panelContent?: ClassValue } | — | 按 data-slot 组织的内部槽位类名。 |
TabsItem
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | any | — | 关联标签与面板的值。 |
label | ReactNode | — | 标签内的可见文字。 |
icon | ReactNode | — | 文字前的装饰性图标。 |
content | ReactNode | — | 标签激活时显示的面板内容。 |
keepMounted | boolean | false | 其他标签激活时是否保留面板 DOM。 |
disabled | boolean | false | 禁用该标签。 |
aria-label | string | — | 仅图标标签的无障碍名称。 |
aria-labelledby | string | — | 用外部元素命名仅图标标签。 |
nativeButton | boolean | true | 标签是否渲染原生 <button>。 |
render | ReactElement | ((props, state) => ReactElement) | — | 替换标签元素。 |
ref | Ref<HTMLElement> | — | 转发到标签元素的 ref。 |
className | string | — | 合并到标签按钮的 Tailwind 类名;面板请使用 ui.panel。 |
style | CSSProperties | — | 内联样式,合并到与 className 相同的元素。 |
ui | { tab?: ClassValue; icon?: ClassValue; label?: ClassValue; panel?: ClassValue; panelContent?: ClassValue } | — | 按 data-slot 组织的该标签内部槽位类名。 |
根 ui 会先合并到每个标签与面板,再合并该 item 的 ui。item 的 className 只作用于标签按钮;面板请通过 ui.panel 设置。
| 属性 | 说明 |
|---|---|
data-active | 标签处于激活状态时存在。 |
data-disabled | 组件或标签禁用时存在。 |
data-orientation | 表示方向(horizontal 或 vertical)。 |
data-activation-direction | 表示新激活标签相对上一个标签的方向。 |
data-hidden | 面板已挂载但未激活时存在。 |
data-starting-style | 面板开始进入动画时存在。 |
data-ending-style | 面板退出动画时存在。 |
data-slot | 标记内部部件(root、list、glass、overlay、indicator、tab、icon、label、panels、panel、panel-content)。 |
组件使用共享玻璃材质。可以在父级调整 --ui-glass-* token,用 className 控制布局,并通过 ui 按 data-slot 调整内部槽位。组件级数值位于 src/theme/tabs.ts。Token 分层与预设见主题指南。