组件 · 导航
Tabs
A row of tabs for the sections of one page, each with its own content; the selected tab is a yellow sticker, and the row scrolls when the tabs don't fit.
import { Tabs } from "@botiverse/raft-ui";Tabs switch between the sections of one page, each with its own content: a channel's chat, tasks and files, for example. The selected tab is a yellow sticker, the same "you are here" as a sidebar row or a segmented control. When the tabs don't fit, the row scrolls sideways.
When to use
| Situation | Use |
|---|---|
| Sections of one page, each with its own content | Tabs |
| Another view of the same content | SegmentedControl |
| The steps of a flow, in order | Steps |
| A product's destinations | AppRail, MobileNav |
A place with several kinds of content, such as a channel, shows them as one row of sticker tabs under its header (Navigation).
Anatomy
Tabs: the root. It holds the selected value and theplacement.TabsList: the row of tabs.TabsTab: one tab, with an optional icon before aTabsLabel.TabsPanel: the content of one tab.SortableTabsListandSortableTabsTab: a row whose tabs people can reorder by dragging.useOrderedTabsrestores a saved order.
Variants
| Variant | Use for |
|---|---|
variant="default" | The sticker tab: the selected tab is a yellow sticker. The default |
variant="underline" | Many tabs, or a place that already has a lot of color: the selected tab is ink text with a 2px ink line on the row's bottom edge |
The sticker tab is the default, and the one the product ships: a selected tab says "you are here", like a selected sidebar row or segment, so it takes the same yellow sticker (Navigation). The underline stays a variant. placement puts the row on the top (the default), bottom, left or right of the panels.
Behavior
- When the tabs don't fit, the row scrolls on its own. Tabs never wrap onto a second line and never shrink their text.
- The row shows no scrollbar, and a vertical mouse wheel scrolls it sideways.
- The side that hides tabs fades out over 32px.
- The selected tab always scrolls into view.
A tab's own controls
When a tab's content has controls of its own (a view switch, filters, search, a sort), they go in one toolbar row at the top of the tab's content, under the tabs. Every tab with controls uses the same row, the one the Tasks tab draws (TasksPanelToolbar), so a person who has learned one tab knows where to look in the next:
- The row has its own height and padding, the content margin at its sides and a
line-seambelow it, and it stays pinned while the content scrolls under it. Content never starts flush under the row, and never scrolls up against the tabs. - Filters and search start at the row's left; a SegmentedControl that switches the view goes at its right end. A view switch on the left would sit right under the selected tab, and two yellow stickers stacked one above the other read as the same level.
- A tab with no controls has no row.
Content
- Label tabs with 1 or 2 words in Title Case: Chat, Tasks, Files (Typography).
- Put a count in its own Space Mono element after the label, never in the label's text.
Accessibility
Tabswraps the Base UI Tabs, which provides the tab list, tab and panel semantics and the arrow keys.- Name the
TabsListafter the place it belongs to:aria-label="launch-week". - Give every icon-only tab an
aria-label. - Sortable tabs are reordered with a pointer or by touch; there is no keyboard reordering.
Rules
- Use one row of tabs per screen.
- Make a sticker tab 32px high, with a 44px hit area and 8px of row above and below it, so the selected sticker sits as far from the line above as from the line below. Its width follows its label, and it has no track behind it.
- Make an underline tab 44px high, 20px from the next, with text only.
- Draw a
line-seamunder the row in both variants. When you change the size of the text or the row, check that the selected tab's space above and below is still equal. - Start the first sticker on the content's edge, the same margin as the panel header's first element (Page structure). In dark mode the selected sticker has the die-cut edge like every sticker, so it lines up with the avatar in the header (Dark mode).
API
SortableTabsList
| Prop | 类型 | 默认值 |
|---|---|---|
| A tab was picked up. On a touch screen the long press that armed it may have opened a menu the caller owns: close it here (DS › States › Dragging on a touch screen). | ((event: BaseUIEvent<DragEvent<HTMLDivElement>>) => void) & ((value: T) => void) | |
| (value: T[]) => void | ||
| readonly T[] | ||
| TabsListVariant | "default" | |
| Whether to automatically change the active tab on arrow key focus. Otherwise, tabs will be activated using <kbd>Enter</kbd> or <kbd>Space</kbd> key press. | boolean | false |
| Whether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. | boolean | true |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
SortableTabsTab
| Prop | 类型 | 默认值 |
|---|---|---|
| The value of the Tab. | any | |
| Whether the Tab is disabled. | boolean | |
| Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | boolean | true |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
Tabs
| Prop | 类型 | 默认值 |
|---|---|---|
| T | ||
| (value: T, eventDetails: BaseTabs.Root.ChangeEventDetails) => void | ||
| Side of the panels the tab list sits on. `left` and `right` also make the tabs vertical (arrow up / down) unless `orientation` is set. | TabsPlacement | "top" |
| T | ||
| "default" | "underline" | "default" | |
| The component orientation (layout flow direction). | Orientation | 'horizontal' |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
TabsLabel
接受其元素的全部属性。
TabsList
| Prop | 类型 | 默认值 |
|---|---|---|
| TabsListVariant | "default" | |
| Whether to automatically change the active tab on arrow key focus. Otherwise, tabs will be activated using <kbd>Enter</kbd> or <kbd>Space</kbd> key press. | boolean | false |
| Whether to loop keyboard focus back to the first item when the end of the list is reached while using the arrow keys. | boolean | true |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
TabsPanel
| Prop | 类型 | 默认值 |
|---|---|---|
| Whether to keep the HTML element in the DOM while the panel is hidden. | boolean | false |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) | |
| The value of the TabPanel. It will be shown when the Tab with the corresponding value is active. | any |
Also takes the attributes of its element.
TabsTab
| Prop | 类型 | 默认值 |
|---|---|---|
| The value of the Tab. | any | |
| Whether the Tab is disabled. | boolean | |
| Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). | boolean | true |
| Allows you to replace the component's HTML element with a different tag, or compose it with another component. | ReactElement | ((props, state) => ReactElement) |
Also takes the attributes of its element.
相关组件
改动
- 0.5.36 · ChangedElegant
Tabsunderline: the bar starts on the 20px line at every width (the first icon lands at 32), and the strip has one source now — the indicator defaults tobg-primary-400and the placement blocks no longer draw a static hairline, so the conversation panel, the agent-details strip and the docs read the same look. Brutal underline: the conversation panel drops its asymmetric first-tabpl-[22px]; the strip sits at 16 and reads centred, as the agent-details strip.