Tabs
tabs在多个内容面板之间切换,指示条可下划线可实心
用法
基础用法
TabsList 装 TabsTab,每个 TabsPanel 用 value 与之对应;defaultValue 设初始选中页。
<Tabs defaultValue="account" className="w-80">
<TabsList>
<TabsTab value="account">账户</TabsTab>
<TabsTab value="password">密码</TabsTab>
</TabsList>
<TabsPanel value="account">管理你的账户资料与偏好设置。</TabsPanel>
<TabsPanel value="password">在这里修改登录密码。</TabsPanel>
</Tabs>分段药丸皮肤
TabsList 加 variant="solid" 切换为分段药丸轨道,选中态由滑块平滑滑动。
<Tabs defaultValue="account" className="w-80">
<TabsList variant="solid">
<TabsTab value="account">账户</TabsTab>
<TabsTab value="password">密码</TabsTab>
<TabsTab value="team">团队</TabsTab>
</TabsList>
<TabsPanel value="account">账户面板。</TabsPanel>
<TabsPanel value="password">密码面板。</TabsPanel>
<TabsPanel value="team">团队面板。</TabsPanel>
</Tabs>语义色档(tone)
TabsList 加 tone,选中态就带上语义色:solid 是白药丸配语义文字,underline 的下划线也跟着换。默认 neutral 保持库既有的中性选中态,不传就不变。
<Tabs defaultValue="account" className="w-80">
<TabsList variant="solid" tone="brand">
<TabsTab value="account">账户</TabsTab>
<TabsTab value="password">密码</TabsTab>
<TabsTab value="team">团队</TabsTab>
</TabsList>
<TabsPanel value="account">账户面板。</TabsPanel>
<TabsPanel value="password">密码面板。</TabsPanel>
<TabsPanel value="team">团队面板。</TabsPanel>
</Tabs>行内切换器(size=sm)
跟标题、搜索框同行时用 sm:纯文字轨道 28px、tab 24px,md 是 40 / 32。计数 Tag 要自己给 size="sm",否则默认 md 的 24px 会把 tab 顶回去。
<div className="flex items-center gap-2">
<span className="text-sm font-semibold">职称组报表</span>
<Tabs defaultValue="title">
<TabsList variant="solid" size="sm">
<TabsTab value="title">职称订单<Tag size="sm" className="ml-1.5">2</Tag></TabsTab>
<TabsTab value="paper">论文订单<Tag size="sm" className="ml-1.5">7</Tag></TabsTab>
</TabsList>
<TabsPanel value="title">职称订单列表。</TabsPanel>
<TabsPanel value="paper">论文订单列表。</TabsPanel>
</Tabs>
</div>禁用某一页
在 TabsTab 上加 disabled,该页不可点也不可键盘聚焦,其余页正常切换。
<Tabs defaultValue="a" className="w-80">
<TabsList>
<TabsTab value="a">可用</TabsTab>
<TabsTab value="b" disabled>禁用</TabsTab>
<TabsTab value="c">可用</TabsTab>
</TabsList>
<TabsPanel value="a">第一个面板。</TabsPanel>
<TabsPanel value="b">不可达。</TabsPanel>
<TabsPanel value="c">第三个面板。</TabsPanel>
</Tabs>纵向排布
Tabs 加 orientation="vertical",tab 条竖排,方向键上下切换。
<Tabs defaultValue="general" orientation="vertical" className="flex w-96 gap-4">
<TabsList className="flex-col items-stretch border-b-0 border-r border-border">
<TabsTab value="general">通用</TabsTab>
<TabsTab value="security">安全</TabsTab>
<TabsTab value="billing">账单</TabsTab>
</TabsList>
<div className="flex-1">
<TabsPanel value="general">通用设置。</TabsPanel>
<TabsPanel value="security">安全设置。</TabsPanel>
<TabsPanel value="billing">账单设置。</TabsPanel>
</div>
</Tabs>何时用
同一区域内切换若干并列内容面板(账户/密码/团队),内容互斥、平级、无层级。表达页面在站点中的位置用 Breadcrumb;长文内随阅读进度高亮的目录用 Anchor;有序步骤流程用 Stepper。
导入
import { Tabs, TabsList, TabsTab, TabsPanel, tabsListVariants } from "@hulianui/ui"Props
Tabs 透传 Base UI Tabs.Root(value/defaultValue/onValueChange/orientation),默认非受控。皮肤变体在 TabsList 上。
Tabs(根)
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| value | any | - | 受控当前 tab |
| defaultValue | any | - | 非受控初始 tab |
| orientation | "horizontal" | "vertical" | "horizontal" | 方向 |
TabsList
| 名称 | 类型 | 默认 | 说明 |
|---|---|---|---|
| variant | "underline" | "solid" | "underline" | 皮肤:下划滑块 / 实心药丸 |
| size | "sm" | "md" | "md" | 尺寸档,下发给 TabsTab(不必逐个传)。md 是页面级 tab 导航;sm 给「跟标题 / 搜索框同行」的行内切换器。见「尺寸」 |
| tone | "brand" | "success" | "warning" | "danger" | "neutral" | "neutral" | 选中态的语义色档,下发给 TabsTab(不必逐个传)。见「语义色档」 |
| className | string | - | - |
TabsTab 接 value(必填)、disabled、className;TabsPanel 接 value、className。
Events
Tabs(根)
| 事件 | 类型 | 说明 |
|---|---|---|
| onValueChange | (value) => void | 切换回调(透传 Base UI Tabs.Root) |
语义色档
TabsList 的 tone 描述选中意味着什么,取值是「语义 tone SSOT」(见 Button 的 tone)的子集:brand / success / warning / danger / neutral。不收 current —— 那一档是「别设色、跟随容器继承」,tab 条不长在彩色容器里,没有这个场景。
| 皮肤 | 选中文字 | 滑块 |
|---|---|---|
solid | 该 tone 的文字色 | 药丸底保持 bg-surface(白药丸 + 语义字) |
underline | 该 tone 的文字色 | 下划线跟 tone |
<TabsList variant="solid" tone="brand">…</TabsList>- 默认 `neutral` 是「维持现状」,不是「把品牌色换成灰」:选中文字仍是
text-foreground、underline的下划线仍是bg-primary,与加这个 prop 之前逐字相同。存量页面不传tone就一个像素都不动;想要「白药丸 + 品牌蓝字」得显式传tone="brand"。 - 只染选中态:未选中态不受
tone影响,仍是text-muted-foreground→ hovertext-foreground。 - solid 的药丸底不跟 `tone`:语义色铺满药丸会盖掉 tab 文字自身的语义。「浅语义底」(对齐 Button 的
soft)是留给后续的一档。 Segmented有同名同取值的tone:它与 solid tab 条是同一套视觉,选中色必须一致。
尺寸
md(默认)是页面级 tab 导航的尺寸。但 tab 条经常不是导航,而是跟标题、搜索框同行的一个切换器 —— 那一行的既有高度是 28-32px,md 塞不进去:
| 轨道(solid) | tab | |
|---|---|---|
md,纯文字 | 40 | 32 |
md,文字 + 计数 Tag | 44 | 36 |
sm,纯文字 | 28 | 24 |
sm,文字 + Tag size="sm" | 32 | 28 |
// 跟标题同行的行内切换器
<div className="flex items-center gap-2">
<span className="text-sm font-semibold">职称组报表</span>
<Tabs defaultValue="a">
<TabsList variant="solid" size="sm">
<TabsTab value="a">职称订单<Tag size="sm" className="ml-1.5">2</Tag></TabsTab>
<TabsTab value="b">论文订单<Tag size="sm" className="ml-1.5">7</Tag></TabsTab>
</TabsList>
<TabsPanel value="a">…</TabsPanel>
</Tabs>
</div>sm 里的计数 Tag 要自己给 `size="sm"`:Tag 默认 md 是 24px,一颗就把 tab 顶回 32px。组件不会去改子元素显式声明的尺寸 —— 那是从外面穿透改内部件,正是本库禁止消费方做的事。
禁忌 / 坑
- 别在消费侧压 `TabsList` 的高度(
<TabsList className="h-7">):它是inline-flex items-center,强压之后 tab 只是居中溢出,solid 的药丸上下各探出轨道 4px(实测),比高一点更难看。要矮就用size="sm"—— tab 的py与轨道的p必须一起收,只压一层必然探出。
- [[base-ui-tabs-indicator-slider-via-active-tab-css-vars]]:滑块靠 Base UI 写在 indicator 上的
--active-tab-*CSS 变量 + 纯 CSS transition 实现,不引动画库。坑点:激活态钩子是data-active而非data-selected,写错样式不生效;jsdom 单测无 ResizeObserver 也能跑(指示条几何不会真渲染)。
相关
Breadcrumb · Pagination · Anchor · Affix · BackTop · Stepper
Playground
<Tabs defaultValue="account">
<TabsList variant="underline" size="md">
<TabsTab value="account">账户</TabsTab>
<TabsTab value="password">密码</TabsTab>
</TabsList>
<TabsPanel value="account">…</TabsPanel>
<TabsPanel value="password">…</TabsPanel>
</Tabs>