Tabs
Tabs switch between views of the same content, such as Overview, Specs, and Reviews on one product. Primary tabs organize a page's main sections and secondary tabs divide one section further, so the style sets the hierarchy a reader sees. A bar that outgrows its space scrolls with an edge shadow, so you write no overflow handling. Press the tabs below to compare both styles.
Reference docs
Choosing navigation
Two components show a row of destinations to choose from. Each suits a different job:
- Navigation: moving between the app's top-level destinations, as a bar, rail, or drawer, such as Home, Search, Library.
- Tabs: switching between views of the same content, such as Overview, Specs, Reviews on one product.
Two jobs belong elsewhere. Moving through pages of results is a paginator, and a list of commands is a menu.
Choosing a variant
Two kinds of tab set how many levels a bar shows:
- Primary tab: the main sections of a content area, such as Overview, Specs, and Reviews on a product page. It stacks an icon above the label in a 64-pixel tab, or places it beside the label in a 48-pixel tab with
inline-icon. - Secondary tab: the parts of one section, such as Newest and Top rated under Reviews. Its indicator spans the full width of the tab, and an icon sits beside its label.
Built in
andactive-tab-index set and read the selected tab from code.activeTab selects a tab as soon as an arrow key focuses it, instead of waiting for Enter or Space.auto-activatebeforechangefires before the selection moves and can be canceled;changefires after.- Tabs that overflow the bar scroll sideways, with no scrolling code to write.
- The arrow keys, Home, and End move focus between tabs, and Left and Right swap in right-to-left layouts.
- The active indicator slides between tabs, and moves at once under reduced motion.
- A tab sets
andhas-icon for itself from what it holds, so you leave both unset in a browser.icon-only
Using tabs well
- Put primary tabs at the top of a content area, use secondary tabs one level below, and stop at two levels.
- Write each label in one or two words, and let it show in full instead of truncating it.
- Give every tab in a bar an icon, or none of them.
- Use tabs for the views of one content area, and a navigation to move between an app's top-level destinations.
- Leave the active indicator in place: a change of color alone does not mark the selected tab.
These rules follow Material 3 Expressive's guidance for tabs, on material.io.
Tab styles
A primary tab, cw-primary-tabinline-icon. A secondary tab, cw-secondary-tab
Icons
A tab's icon goes in its icon slot. A primary tab stacks it above the label, and inline-icon
A tab sets has-iconicon-only
Icon only
A tab with an icon and no label sizes itself to the icon. Give it an aria-label, as the demo does; Names and states says why.
Panels
The tab bar sets active on the selected tab and fires change, and that is all: it neither shows nor hides content. Show the panel for the selected tab yourself, as this demo does from the index the tab bar is bound to.
Tie each tab to its panel for assistive technology with the attributes that Names and states lists, as the demo does.
Selected tab
active-tab-indexactiveTabactiveTabnull. If the selected tab is removed, the first tab is selected.
The first tab is selected when the bar renders and active-tab-indexactiveselectedactive
Automatic activation
By default the arrow keys move focus and Enter or Space selects the focused tab, so a reader can scan the labels without loading each view. auto-activate
Change events
beforechange fires first, when a click, a key press, or an assignment to active-tab-indexdetail.activeTabIndex names the tab it is moving to. Call preventDefault() on it to leave the selection where it is. change fires after the move and cannot be canceled; it is the event a binding of active-tab-index
The automatic selection of the first tab raises neither event.
tabs.addEventListener("beforechange", (event) => {
if (hasUnsavedEdits) {
event.preventDefault();
}
});
Overflow
When the tabs do not fit, the bar scrolls sideways and shows an edge shadow where more tabs lie beyond. There is nothing to configure. Keep the labels short enough that the most important tabs are visible without scrolling.
Two levels
Primary tabs sit at the top of a content area and secondary tabs subdivide one of its sections. Use one tab bar for each level, and show the secondary bar only inside the panel that owns it. Stop at two levels.
Properties
| Property | Attribute | Type | Description |
|---|---|---|---|
activeTab | Tab | The currently selected tab, null only when there are no tab children. See Selected tab. | |
activeTabIndex | active-tab-index | number | The currently selected tab's index, or -1 when no tab is selected. Assigning an index outside the tabs is ignored. See Selected tab, Change events. |
autoActivate | auto-activate | booleandefault false | Whether the arrow, Home and End keys select the tab they move focus to, rather than only focusing it. A click always selects. See Automatic activation. |
Slots
| Slot | Description |
|---|---|
| (default) | The cw-primary-tab and cw-secondary-tab elements to be shown in the tab bar. |
Events
| Event | Type | Detail | Description |
|---|---|---|---|
beforechange | TabsBeforeChangeEvent | TabsBeforeChangeEventDetail | Fired before the selected tab changes, whether a user interaction like a space/enter key or click, or an author assigning activeTab or activeTabIndex, would change it. It does not fire for the first selection, when no tab was selected before. The event's detail.activeTabIndex carries the index the selection is about to move to. Call preventDefault() to leave the selection where it is. It bubbles and is cancelable. |
change | TabsChangeEvent | TabsChangeEventDetail | Fired after the selected tab changed, whether a user interaction or an author moved it. It is not cancelable. The event's detail.activeTabIndex carries the newly selected index; the target's activeTabIndex or activeTab provide the same information. It bubbles. |
Canceling beforechange also suppresses change.
Props
| Prop | Type | Description |
|---|---|---|
activeTabthrough the ref | Tab | The currently selected tab, null only when there are no tab children. See Selected tab. |
activeTabIndex | number | The currently selected tab's index, or -1 when no tab is selected. Assigning an index outside the tabs is ignored. See Selected tab, Change events. |
autoActivate | booleandefault false | Whether the arrow, Home and End keys select the tab they move focus to, rather than only focusing it. A click always selects. See Automatic activation. |
Children
| Child | Description |
|---|---|
children | The CrPrimaryTab and CrSecondaryTab elements to be shown in the tab bar. |
Events
| Prop | Argument | Description |
|---|---|---|
onBeforeChange | TabsBeforeChangeEvent | Fired before the selected tab changes, whether a user interaction like a space/enter key or click, or an author assigning activeTab or activeTabIndex, would change it. It does not fire for the first selection, when no tab was selected before. The event's detail.activeTabIndex carries the index the selection is about to move to. Call preventDefault() to leave the selection where it is. It bubbles and is cancelable. |
onChange | TabsChangeEvent | Fired after the selected tab changed, whether a user interaction or an author moved it. It is not cancelable. The event's detail.activeTabIndex carries the newly selected index; the target's activeTabIndex or activeTab provide the same information. It bubbles. Its detail carries activeTabIndex. |
Canceling beforechange also suppresses change.
Parameters
| Parameter | Type | Description |
|---|---|---|
AutoActivate | bool?default false | Whether the arrow, Home and End keys select the tab they move focus to, rather than only focusing it. A click always selects. See Automatic activation. |
Value | int? | — |
ValueExpression | Expression<Func<int?>>? | Gets or sets an expression that identifies the bound value. |
Render fragments
| Parameter | Type | Description |
|---|---|---|
ChildContent | RenderFragment? | The CbPrimaryTab and CbSecondaryTab elements to be shown in the tab bar. |
Event callbacks
| Parameter | Type | Description |
|---|---|---|
OnBeforeChange | EventCallback<BeforeChangeEventArgs> | Fired before the selected tab changes, whether a user interaction like a space/enter key or click, or an author assigning activeTab or ActiveTabIndex, would change it. It does not fire for the first selection, when no tab was selected before. The event's detail.activeTabIndex carries the index the selection is about to move to. Call preventDefault() to leave the selection where it is. It bubbles and is cancelable. |
OnChange | EventCallback<TabsChangeEventArgs> | Fired after the selected tab changed, whether a user interaction or an author moved it. It is not cancelable. The event's detail.activeTabIndex carries the newly selected index; the target's ActiveTabIndex or activeTab provide the same information. It bubbles. Its detail carries ActiveTabIndex. |
Canceling beforechange also suppresses change.
Inputs
| Input | Type | Description |
|---|---|---|
activeTabIndex | number | The currently selected tab's index, or -1 when no tab is selected. Assigning an index outside the tabs is ignored. See Selected tab, Change events. |
autoActivate | booleandefault false | Whether the arrow, Home and End keys select the tab they move focus to, rather than only focusing it. A click always selects. See Automatic activation. |
value | number | — |
Content projection
| Content | Description |
|---|---|
content with no slot | The CaPrimaryTab and CaSecondaryTab elements to be shown in the tab bar. |
Outputs
| Output | Event | Description |
|---|---|---|
cwBeforeChange | TabsBeforeChangeEvent | Fired before the selected tab changes, whether a user interaction like a space/enter key or click, or an author assigning activeTab or activeTabIndex, would change it. It does not fire for the first selection, when no tab was selected before. The event's detail.activeTabIndex carries the index the selection is about to move to. Call preventDefault() to leave the selection where it is. It bubbles and is cancelable. |
cwChange | TabsChangeEvent | Fired after the selected tab changed, whether a user interaction or an author moved it. It is not cancelable. The event's detail.activeTabIndex carries the newly selected index; the target's activeTabIndex or activeTab provide the same information. It bubbles. Its detail carries activeTabIndex. |
Canceling beforechange also suppresses change.
Props
| Prop | Type | Description |
|---|---|---|
activeTabIndex | number | The currently selected tab's index, or -1 when no tab is selected. Assigning an index outside the tabs is ignored. See Selected tab, Change events. |
autoActivate | booleandefault false | Whether the arrow, Home and End keys select the tab they move focus to, rather than only focusing it. A click always selects. See Automatic activation. |
Slots
| Slot | Description |
|---|---|
default | The CvPrimaryTab and CvSecondaryTab elements to be shown in the tab bar. |
Events
| Event | Type | Description |
|---|---|---|
beforeChange | TabsBeforeChangeEvent | Fired before the selected tab changes, whether a user interaction like a space/enter key or click, or an author assigning activeTab or activeTabIndex, would change it. It does not fire for the first selection, when no tab was selected before. The event's detail.activeTabIndex carries the index the selection is about to move to. Call preventDefault() to leave the selection where it is. It bubbles and is cancelable. |
change | TabsChangeEvent | Fired after the selected tab changed, whether a user interaction or an author moved it. It is not cancelable. The event's detail.activeTabIndex carries the newly selected index; the target's activeTabIndex or activeTab provide the same information. It bubbles. Its detail carries activeTabIndex. |
Canceling beforechange also suppresses change.
Props
| Prop | Type | Description |
|---|---|---|
activeTabIndex | number | The currently selected tab's index, or -1 when no tab is selected. Assigning an index outside the tabs is ignored. See Selected tab, Change events. |
autoActivate | booleandefault false | Whether the arrow, Home and End keys select the tab they move focus to, rather than only focusing it. A click always selects. See Automatic activation. |
Snippets
| Snippet | Description |
|---|---|
children | The CsPrimaryTab and CsSecondaryTab elements to be shown in the tab bar. |
Events
| Prop | Type | Description |
|---|---|---|
onBeforeChange | TabsBeforeChangeEvent | Fired before the selected tab changes, whether a user interaction like a space/enter key or click, or an author assigning activeTab or activeTabIndex, would change it. It does not fire for the first selection, when no tab was selected before. The event's detail.activeTabIndex carries the index the selection is about to move to. Call preventDefault() to leave the selection where it is. It bubbles and is cancelable. |
onChange | TabsChangeEvent | Fired after the selected tab changed, whether a user interaction or an author moved it. It is not cancelable. The event's detail.activeTabIndex carries the newly selected index; the target's activeTabIndex or activeTab provide the same information. It bubbles. Its detail carries activeTabIndex. |
Canceling beforechange also suppresses change.
Properties
| Property | Attribute | Type | Description |
|---|---|---|---|
active | active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | has-icon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | icon-only | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
inlineIcon | inline-icon | booleandefault false | Puts the icon beside the label instead of above it, which makes the tab shorter. See Icons. |
selected | selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | tabindex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Slots
| Slot | Description |
|---|---|
| (default) | The tab's label content. |
icon | The icon to display in the tab, intended for a cw-icon or equivalent. |
Props
| Prop | Type | Description |
|---|---|---|
active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
inlineIcon | booleandefault false | Puts the icon beside the label instead of above it, which makes the tab shorter. See Icons. |
selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Children
| Child | Description |
|---|---|
children | The tab's label content. |
a child with slot="icon" | The icon to display in the tab, intended for a CrIcon or equivalent. |
Parameters
| Parameter | Type | Description |
|---|---|---|
Active | bool?default false | Whether the tab is the selected one. See Selected tab. |
HasIcon | bool?default false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
IconOnly | bool?default false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
InlineIcon | bool?default false | Puts the icon beside the label instead of above it, which makes the tab shorter. See Icons. |
Selected | bool?default false | Deprecated alias of Active. Use Active. See Selected tab. |
TabIndex | int? | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Render fragments
Once you write Icon or any other fragment as a child element, the main content must go in an explicit <ChildContent> too; markup beside <Icon> is a compile error. Here ChildContent is: The tab's label content.
<CbPrimaryTab>
<Icon><CbIcon>favorite</CbIcon></Icon>
<ChildContent>Main content</ChildContent>
</CbPrimaryTab>
| Parameter | Type | Description |
|---|---|---|
ChildContent | RenderFragment? | The tab's label content. |
Icon | RenderFragment? | The icon to display in the tab, intended for a CbIcon or equivalent. |
Inputs
| Input | Type | Description |
|---|---|---|
active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
inlineIcon | booleandefault false | Puts the icon beside the label instead of above it, which makes the tab shorter. See Icons. |
selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Content projection
| Content | Description |
|---|---|
content with no slot | The tab's label content. |
an element with slot="icon" | The icon to display in the tab, intended for a CaIcon or equivalent. |
Props
| Prop | Type | Description |
|---|---|---|
active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
inlineIcon | booleandefault false | Puts the icon beside the label instead of above it, which makes the tab shorter. See Icons. |
selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Slots
| Slot | Description |
|---|---|
default | The tab's label content. |
icon | The icon to display in the tab, intended for a CvIcon or equivalent. |
Props
| Prop | Type | Description |
|---|---|---|
active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
inlineIcon | booleandefault false | Puts the icon beside the label instead of above it, which makes the tab shorter. See Icons. |
selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Snippets
| Snippet | Description |
|---|---|
children | The tab's label content. |
iconSnippet | The icon to display in the tab, intended for a CsIcon or equivalent. |
Properties
| Property | Attribute | Type | Description |
|---|---|---|---|
active | active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | has-icon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | icon-only | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
selected | selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | tabindex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Slots
| Slot | Description |
|---|---|
| (default) | The tab's label content. |
icon | The icon to display in the tab, intended for a cw-icon or equivalent. |
Props
| Prop | Type | Description |
|---|---|---|
active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Children
| Child | Description |
|---|---|
children | The tab's label content. |
a child with slot="icon" | The icon to display in the tab, intended for a CrIcon or equivalent. |
Parameters
| Parameter | Type | Description |
|---|---|---|
Active | bool?default false | Whether the tab is the selected one. See Selected tab. |
HasIcon | bool?default false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
IconOnly | bool?default false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
Selected | bool?default false | Deprecated alias of Active. Use Active. See Selected tab. |
TabIndex | int? | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Render fragments
Once you write Icon or any other fragment as a child element, the main content must go in an explicit <ChildContent> too; markup beside <Icon> is a compile error. Here ChildContent is: The tab's label content.
<CbSecondaryTab>
<Icon><CbIcon>favorite</CbIcon></Icon>
<ChildContent>Main content</ChildContent>
</CbSecondaryTab>
| Parameter | Type | Description |
|---|---|---|
ChildContent | RenderFragment? | The tab's label content. |
Icon | RenderFragment? | The icon to display in the tab, intended for a CbIcon or equivalent. |
Inputs
| Input | Type | Description |
|---|---|---|
active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Content projection
| Content | Description |
|---|---|
content with no slot | The tab's label content. |
an element with slot="icon" | The icon to display in the tab, intended for a CaIcon or equivalent. |
Props
| Prop | Type | Description |
|---|---|---|
active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Slots
| Slot | Description |
|---|---|
default | The tab's label content. |
icon | The icon to display in the tab, intended for a CvIcon or equivalent. |
Props
| Prop | Type | Description |
|---|---|---|
active | booleandefault false | Whether the tab is the selected one. See Selected tab. |
hasIcon | booleandefault false | Whether an icon is present. Set from the icon slot in a browser; set it by hand only in server rendering. See Icons. |
iconOnly | booleandefault false | Whether the tab has no label and only an icon. Set from the default slot in a browser; set it by hand only in server rendering. See Icons. |
selected | booleandefault false | Deprecated alias of active. Use active. See Selected tab. |
tabIndex | number | The element's position in the tab order: 0 puts it in the order, and -1 leaves it out while script can still focus it. A group, such as a set of tabs, manages it for its members. |
A tabs strip sets active
Snippets
| Snippet | Description |
|---|---|
children | The tab's label content. |
iconSnippet | The icon to display in the tab, intended for a CsIcon or equivalent. |
A value is what the token defaults to; selecting it copies the token's name.
Tokens
| Token | Value |
|---|---|
--ct-primary-tab-active-focus-icon-color | --ct-sys-color-primary --ct-primary-tab-active-focus-icon-color |
--ct-primary-tab-active-focus-label-text-color | --ct-sys-color-primary --ct-primary-tab-active-focus-label-text-color |
--ct-primary-tab-active-hover-icon-color | --ct-sys-color-primary --ct-primary-tab-active-hover-icon-color |
--ct-primary-tab-active-hover-label-text-color | --ct-sys-color-primary --ct-primary-tab-active-hover-label-text-color |
--ct-primary-tab-active-hover-state-layer-color | --ct-sys-color-primary --ct-primary-tab-active-hover-state-layer-color |
--ct-primary-tab-active-hover-state-layer-opacity | --ct-sys-state-hover-state-layer-opacity --ct-primary-tab-active-hover-state-layer-opacity |
--ct-primary-tab-active-icon-color | --ct-sys-color-primary --ct-primary-tab-active-icon-color |
--ct-primary-tab-active-indicator-color | --ct-sys-color-primary --ct-primary-tab-active-indicator-color |
--ct-primary-tab-active-indicator-height | 3px --ct-primary-tab-active-indicator-height |
--ct-primary-tab-active-indicator-radius | 3px 3px 0px 0px --ct-primary-tab-active-indicator-radius |
--ct-primary-tab-active-label-text-color | --ct-sys-color-primary --ct-primary-tab-active-label-text-color |
--ct-primary-tab-active-pressed-icon-color | --ct-sys-color-primary --ct-primary-tab-active-pressed-icon-color |
--ct-primary-tab-active-pressed-label-text-color | --ct-sys-color-primary --ct-primary-tab-active-pressed-label-text-color |
--ct-primary-tab-active-pressed-state-layer-color | --ct-sys-color-primary --ct-primary-tab-active-pressed-state-layer-color |
--ct-primary-tab-active-pressed-state-layer-opacity | --ct-sys-state-pressed-state-layer-opacity --ct-primary-tab-active-pressed-state-layer-opacity |
--ct-primary-tab-container-color | --ct-sys-color-surface --ct-primary-tab-container-color |
--ct-primary-tab-container-end-end-radius | same as --ct-primary-tab-container-radius --ct-primary-tab-container-end-end-radius |
--ct-primary-tab-container-end-start-radius | same as --ct-primary-tab-container-radius --ct-primary-tab-container-end-start-radius |
--ct-primary-tab-container-height | 48px --ct-primary-tab-container-height |
--ct-primary-tab-container-radius | --ct-sys-corner-radius-none --ct-primary-tab-container-radius |
--ct-primary-tab-container-start-end-radius | same as --ct-primary-tab-container-radius --ct-primary-tab-container-start-end-radius |
--ct-primary-tab-container-start-start-radius | same as --ct-primary-tab-container-radius --ct-primary-tab-container-start-start-radius |
--ct-primary-tab-focus-icon-color | --ct-sys-color-on-surface --ct-primary-tab-focus-icon-color |
--ct-primary-tab-focus-label-text-color | --ct-sys-color-on-surface --ct-primary-tab-focus-label-text-color |
--ct-primary-tab-hover-icon-color | --ct-sys-color-on-surface --ct-primary-tab-hover-icon-color |
--ct-primary-tab-hover-label-text-color | --ct-sys-color-on-surface --ct-primary-tab-hover-label-text-color |
--ct-primary-tab-hover-state-layer-color | --ct-sys-color-on-surface --ct-primary-tab-hover-state-layer-color |
--ct-primary-tab-hover-state-layer-opacity | --ct-sys-state-hover-state-layer-opacity --ct-primary-tab-hover-state-layer-opacity |
--ct-primary-tab-icon-color | --ct-sys-color-on-surface-variant --ct-primary-tab-icon-color |
--ct-primary-tab-icon-size | 24px --ct-primary-tab-icon-size |
--ct-primary-tab-label-text-color | --ct-sys-color-on-surface-variant --ct-primary-tab-label-text-color |
--ct-primary-tab-label-text-font | --ct-sys-typescale-title-small-font --ct-primary-tab-label-text-font |
--ct-primary-tab-label-text-line-height | --ct-sys-typescale-title-small-line-height --ct-primary-tab-label-text-line-height |
--ct-primary-tab-label-text-size | --ct-sys-typescale-title-small-size --ct-primary-tab-label-text-size |
--ct-primary-tab-label-text-weight | --ct-sys-typescale-title-small-weight --ct-primary-tab-label-text-weight |
--ct-primary-tab-pressed-icon-color | --ct-sys-color-on-surface --ct-primary-tab-pressed-icon-color |
--ct-primary-tab-pressed-label-text-color | --ct-sys-color-on-surface --ct-primary-tab-pressed-label-text-color |
--ct-primary-tab-pressed-state-layer-color | --ct-sys-color-primary --ct-primary-tab-pressed-state-layer-color |
--ct-primary-tab-pressed-state-layer-opacity | --ct-sys-state-pressed-state-layer-opacity --ct-primary-tab-pressed-state-layer-opacity |
--ct-primary-tab-with-icon-and-label-text-container-height | 64px --ct-primary-tab-with-icon-and-label-text-container-height |
A value is what the token defaults to; selecting it copies the token's name.
Tokens
| Token | Value |
|---|---|
--ct-secondary-tab-active-focus-icon-color | --ct-sys-color-on-surface --ct-secondary-tab-active-focus-icon-color |
--ct-secondary-tab-active-focus-label-text-color | --ct-sys-color-on-surface --ct-secondary-tab-active-focus-label-text-color |
--ct-secondary-tab-active-hover-icon-color | --ct-sys-color-on-surface --ct-secondary-tab-active-hover-icon-color |
--ct-secondary-tab-active-hover-label-text-color | --ct-sys-color-on-surface --ct-secondary-tab-active-hover-label-text-color |
--ct-secondary-tab-active-hover-state-layer-color | --ct-sys-color-on-surface --ct-secondary-tab-active-hover-state-layer-color |
--ct-secondary-tab-active-hover-state-layer-opacity | --ct-sys-state-hover-state-layer-opacity --ct-secondary-tab-active-hover-state-layer-opacity |
--ct-secondary-tab-active-icon-color | --ct-sys-color-on-surface --ct-secondary-tab-active-icon-color |
--ct-secondary-tab-active-indicator-color | --ct-sys-color-primary --ct-secondary-tab-active-indicator-color |
--ct-secondary-tab-active-indicator-height | 2px --ct-secondary-tab-active-indicator-height |
--ct-secondary-tab-active-indicator-radius | 0 --ct-secondary-tab-active-indicator-radius |
--ct-secondary-tab-active-label-text-color | --ct-sys-color-on-surface --ct-secondary-tab-active-label-text-color |
--ct-secondary-tab-active-pressed-icon-color | --ct-sys-color-on-surface --ct-secondary-tab-active-pressed-icon-color |
--ct-secondary-tab-active-pressed-label-text-color | --ct-sys-color-on-surface --ct-secondary-tab-active-pressed-label-text-color |
--ct-secondary-tab-active-pressed-state-layer-color | --ct-sys-color-on-surface --ct-secondary-tab-active-pressed-state-layer-color |
--ct-secondary-tab-active-pressed-state-layer-opacity | --ct-sys-state-pressed-state-layer-opacity --ct-secondary-tab-active-pressed-state-layer-opacity |
--ct-secondary-tab-container-color | --ct-sys-color-surface --ct-secondary-tab-container-color |
--ct-secondary-tab-container-end-end-radius | same as --ct-secondary-tab-container-radius --ct-secondary-tab-container-end-end-radius |
--ct-secondary-tab-container-end-start-radius | same as --ct-secondary-tab-container-radius --ct-secondary-tab-container-end-start-radius |
--ct-secondary-tab-container-height | 48px --ct-secondary-tab-container-height |
--ct-secondary-tab-container-radius | --ct-sys-corner-radius-none --ct-secondary-tab-container-radius |
--ct-secondary-tab-container-start-end-radius | same as --ct-secondary-tab-container-radius --ct-secondary-tab-container-start-end-radius |
--ct-secondary-tab-container-start-start-radius | same as --ct-secondary-tab-container-radius --ct-secondary-tab-container-start-start-radius |
--ct-secondary-tab-focus-icon-color | --ct-sys-color-on-surface --ct-secondary-tab-focus-icon-color |
--ct-secondary-tab-focus-label-text-color | --ct-sys-color-on-surface --ct-secondary-tab-focus-label-text-color |
--ct-secondary-tab-hover-icon-color | --ct-sys-color-on-surface --ct-secondary-tab-hover-icon-color |
--ct-secondary-tab-hover-label-text-color | --ct-sys-color-on-surface --ct-secondary-tab-hover-label-text-color |
--ct-secondary-tab-hover-state-layer-color | --ct-sys-color-on-surface --ct-secondary-tab-hover-state-layer-color |
--ct-secondary-tab-hover-state-layer-opacity | --ct-sys-state-hover-state-layer-opacity --ct-secondary-tab-hover-state-layer-opacity |
--ct-secondary-tab-icon-color | --ct-sys-color-on-surface-variant --ct-secondary-tab-icon-color |
--ct-secondary-tab-icon-size | 24px --ct-secondary-tab-icon-size |
--ct-secondary-tab-label-text-color | --ct-sys-color-on-surface-variant --ct-secondary-tab-label-text-color |
--ct-secondary-tab-label-text-font | --ct-sys-typescale-title-small-font --ct-secondary-tab-label-text-font |
--ct-secondary-tab-label-text-line-height | --ct-sys-typescale-title-small-line-height --ct-secondary-tab-label-text-line-height |
--ct-secondary-tab-label-text-size | --ct-sys-typescale-title-small-size --ct-secondary-tab-label-text-size |
--ct-secondary-tab-label-text-weight | --ct-sys-typescale-title-small-weight --ct-secondary-tab-label-text-weight |
--ct-secondary-tab-pressed-icon-color | --ct-sys-color-on-surface --ct-secondary-tab-pressed-icon-color |
--ct-secondary-tab-pressed-label-text-color | --ct-sys-color-on-surface --ct-secondary-tab-pressed-label-text-color |
--ct-secondary-tab-pressed-state-layer-color | --ct-sys-color-on-surface --ct-secondary-tab-pressed-state-layer-color |
--ct-secondary-tab-pressed-state-layer-opacity | --ct-sys-state-pressed-state-layer-opacity --ct-secondary-tab-pressed-state-layer-opacity |
Names and states
The tab bar is announced as a horizontal tab list and each tab as a tab; both follow the WAI-ARIA Authoring Practices: Tabs pattern.
- Give the bar an
aria-label. It names the tab list. - A tab's accessible name is its label text. An icon-only tab has no name, because the icon is hidden from assistive technology, so give that tab its own
aria-label. - The selected tab is announced as selected, and the others as not selected.
- The tab bar does not link tabs to panels or hide anything. Give each tab
aria-controls, give each panelrole="tabpanel"andaria-labelledby, and hide the panels that are not selected, as Panels shows. - A tab has no
disabled,href, or badge. For links between destinations, use Navigation.
Keyboard and focus
| Key | Action |
|---|---|
| Tab | Moves focus into the bar at the selected tab, and out of the bar from it. |
| Left Arrow | Moves focus to the previous tab, wrapping from the first to the last. Moves to the next tab in a right-to-left layout. |
| Right Arrow | Moves focus to the next tab, wrapping from the last to the first. Moves to the previous tab in a right-to-left layout. |
| Home | Moves focus to the first tab. |
| End | Moves focus to the last tab. |
| Enter or Space | Selects the focused tab. |
The component handles all of these keys itself. The arrow, Home, and End keys do nothing with fewer than two tabs, and Up Arrow and Down Arrow do nothing at any count.
- The bar is a single Tab stop: Tab enters it at the selected tab, and returns there after focus leaves.
- The arrow keys move focus without selecting. With
, a tab is selected as soon as it takes focus.auto-activate - A focus ring appears inside the tab on keyboard focus, and not on a pointer click.
Pointer and touch
- By default a tab is 48 pixels tall, or 64 pixels with an icon stacked above its label, and the tabs share the width of the bar. Both are well above the 24 by 24 pixels that WCAG 2.5.8 asks for.
- A press anywhere inside a tab, on its icon or its label, selects that tab.
Display settings
- In forced colors, the active indicator uses the system text color.
- Under reduced motion, the active indicator moves between tabs at once instead of sliding, and the bar scrolls without smooth scrolling.
- In a right-to-left layout, Left Arrow moves to the next tab and Right Arrow to the previous one.
Standards
With its defaults, the tab bar meets three WCAG 2.2 success criteria:
Three more depend on how you use it:
- 1.1.1 Non-text Content (A): give an icon-only tab an
aria-label, and give a slotted image an alt. - 1.3.1 Info and Relationships (A): link each tab to its panel with
aria-controls,role="tabpanel", andaria-labelledby. - 4.1.2 Name, Role, Value (A): give the bar an
aria-label, and give an icon-only tab its ownaria-label.