bq-tab must always be placed inside a bq-tab-group. The group manages tab state, keyboard navigation, and propagates size, orientation, and placement to all child tabs automatically.When to use
Use tabs when
- Content can be clearly organized into a small number of distinct, peer-level sections
- Users need to compare or switch between sections without losing page context
- Space is limited and displaying all sections at once would clutter the layout
- Each section can stand independently without requiring the user to read others first
Do not use tabs when
- The number of tabs exceeds seven — prefer a sidebar navigation or dropdown instead
- Content across sections is sequential and users must read them in order — prefer a stepper
- Sections contain very different content types that do not relate to the same subject
- The goal is to navigate to a new page or route — prefer anchor links or a navigation component
Anatomy
| Part | Element | Description |
|---|---|---|
| 1 | Active tab | The currently selected tab button, visually distinguished by brand color and the selected indicator |
| 2 | Inactive tab | A tab button that is not currently selected; clicking it activates it and deactivates the previous tab |
| 3 | Selected indicator | The underline beneath the active tab that marks the current selection; exposed as the underline shadow part |
| 4 | Text label | The visible label inside each tab button; placed in the default slot and exposed as the text shadow part |
Design guidelines
Orientation
Size
small
Compact tabs with reduced padding. Use in dense interfaces or data-heavy views where space is constrained.
Setting
size on bq-tab-group propagates the value to all child bq-tab elements automatically. You do not need to set size on each tab individually.Placement
Useplacement="start" (default) to left-align horizontal tabs or top-align vertical tabs. Use placement="end" to right-align horizontal tabs or bottom-align vertical tabs.
Usage
Default
Options
With icons
Tabs support an optional icon displayed before the label via theicon slot.
Sizes
Vertical
Setorientation="vertical" on bq-tab-group to stack tabs in a column beside the content area.
No divider
Usedisable-divider to hide the underline separator between the tab list and the content area.
Best practices
DoKeep tab labels short and parallel — prefer noun phrases like “Overview”, “Details”, or “Settings” rather than full sentences.
Don’tDon’t use tabs with only one or two items — a single visible section or a toggle is simpler and less visually heavy.
DoAlways provide a
controls attribute on each bq-tab pointing to the id of its associated panel element to meet ARIA requirements.Don’tDon’t mix tabs of different sizes in the same tab group. Set
size on bq-tab-group and let it propagate uniformly.DoUse consistent tab labels across your interface — if some tabs have icons, apply icons to all tabs in the same group.
Don’tDon’t use tabs for actions or commands. Tabs are for navigation between content views, not for triggering operations like “Save” or “Delete”.
DoChoose the orientation that matches your layout — use horizontal tabs for top-of-content placement and vertical tabs for sidebar-style navigation.
Don’tDon’t rely solely on the active-indicator underline for visual differentiation — ensure sufficient color contrast on the active tab label as well.
Accessibility
bq-tab-group and bq-tab implement the ARIA Tabs pattern:
- The tab list container has
role="tablist". - Each
bq-tabrenders as a<button role="tab">witharia-selectedandaria-disabledset automatically. - The
controlsprop maps toaria-controls, linking each tab to its panel. Always provide a matchingidon the panel element androle="tabpanel"for correct screen reader announcements. - Keyboard navigation is handled by the tab group:
ArrowRight/ArrowDown— move focus to the next enabled tabArrowLeft/ArrowUp— move focus to the previous enabled tab
- Focus management follows the roving
tabindexpattern: only the active tab is in the tab order; other tabs are reachable via arrow keys. - Disabled tabs have
aria-disabled="true"anddisabledset on the underlying button, removing them from interaction.
- Provide a meaningful
controlsvalue on eachbq-tabthat matches theidof the corresponding panel. - Add
role="tabpanel"to each panel element. - When using icons without a visible label, add a descriptive
aria-labelto thebq-tabso screen readers can identify the tab.
API reference
bq-tab-group
Properties
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
debounceTime | debounce-time | Delay in milliseconds before bqChange fires | number | 0 |
disableDivider | disable-divider | If true, the underline divider is hidden | boolean | false |
orientation | orientation | Layout direction of the tab list | "horizontal" | "vertical" | "horizontal" |
placement | placement | Alignment of tabs within the group | "start" | "end" | "start" |
size | size | Size applied to all child tabs | "small" | "medium" | "large" | "medium" |
value | value | The tab-id of the currently selected tab | string | — |
Events
| Event | Description | Type |
|---|---|---|
bqChange | Fired when the selected tab changes | CustomEvent<{ target: HTMLBqTabElement; value: string }> |
Slots
| Slot | Description |
|---|---|
| (default) | The bq-tab items to render inside the group |
Shadow parts
| Part | Description |
|---|---|
base | The outer <div> wrapper |
tabs | The <div role="tablist"> holding all tab buttons |
bq-tab
Properties
| Property | Attribute | Description | Type | Default |
|---|---|---|---|---|
active | active | If true, the tab is rendered in its active state | boolean | false |
controls (required) | controls | The id of the panel element this tab controls | string | — |
disabled | disabled | If true, the tab is non-interactive | boolean | false |
orientation | orientation | Layout direction — propagated automatically by bq-tab-group | "horizontal" | "vertical" | "horizontal" |
placement | placement | Alignment — propagated automatically by bq-tab-group | "start" | "end" | "start" |
size | size | Size — propagated automatically by bq-tab-group | "small" | "medium" | "large" | "medium" |
tabId (required) | tab-id | Unique identifier used to match the tab to its panel | string | — |
Events
| Event | Description | Type |
|---|---|---|
bqClick | Fired when the tab is clicked or activated | CustomEvent<HTMLBqTabElement> |
bqFocus | Fired when the tab receives focus | CustomEvent<HTMLBqTabElement> |
bqBlur | Fired when the tab loses focus | CustomEvent<HTMLBqTabElement> |
bqKeyDown | Fired when a key is pressed while the tab is focused | CustomEvent<KeyboardEvent> |
Methods
| Method | Description | Signature |
|---|---|---|
vClick() | Simulates a click on the underlying <button> element | vClick() => Promise<void> |
vFocus() | Sets focus on the underlying <button> element | vFocus() => Promise<void> |
vBlur() | Removes focus from the underlying <button> element | vBlur() => Promise<void> |
Slots
| Slot | Description |
|---|---|
| (default) | The tab label text |
icon | An optional icon to display before the label |
Shadow parts
| Part | Description |
|---|---|
base | The <button> element |
content | The <div> wrapping the icon and text |
icon | The <div> holding the slotted icon |
text | The <div> holding the slotted label text |
underline | The element that renders the active-state underline indicator |
CSS custom properties
Resources
Interactive playground
Explore Tab variants and states in Storybook
Source code
View the component source on GitHub