What it is
A tab bar switches between sections of a single subject, in place, without leaving the page. It is page furniture: it belongs to the header and it changes what fills the body beneath it. It is not navigation between different subjects, and it is not a filter. If the sections have different subjects it is navigation; if they are the same records narrowed differently it is filtering.Live example
Exports
Vocabulary
Say “selected”, not “active”. A tab is selected; a session is active. The distinction matters
because a page can have a selected tab whose contents are still loading, and “active” reads as a
claim about the content rather than the control.
Choose Tabs when
- One subject has two or more sections a reader moves between, and each section stands alone.
- A reader needs one section at a time, and comparing sections side by side is not the job.
- The sections are peers. No section is a step toward another.
Choose something else when
Anatomy
The bar draws its own bottom rule. That is why a header carrying a tab bar suppresses the page
divider rather than drawing both — TITAN-VIEW-12.
Variants, sizes, and states
Orientation is the first decision, and the two are different components in everything but name. Horizontal is a tab bar. Vertical is a navigation rail — it has its own minimum row width, a selected row with a fill and a leading edge marker, a rule down its trailing side, and an indented mode for nested rows. This page documents the horizontal bar. What the rail is for is undecided — see TITAN-GAP-29. How the bar distributes its tabs — three published behaviours, and the system has not stated which is the default choice:
What happens when the tabs do not fit —
variant="scrollable" with three settings for the
scroll affordance:
States a tab carries: unselected, selected, hover, pressed, focused, and disabled. The design
library also defines an error display for a tab, across every interaction state. Code has no
error treatment for a tab — verified against the theme overrides, the token set, the shared
package, and the legacy stylesheets. See
TITAN-GAP-30.
Edge and failure states
Tokens
Three component tokens exist, and all three serve the vertical rail:tabs-vertical-min-width,
tabs-icon-gap, and tabs-indented-font-size. The horizontal bar carries no component tokens of
its own — its rule comes from the border tokens and its label from the type scale, and its
internal padding comes from no token at all.
Composition
The bar goes in the header. A tabbed page carries its bar in the header’s second row and suppresses the page divider; the header’s second row holds a tab bar or a filter row and never both. That is a view-tier decision and it is documented with the page frame, including the constraint thatHeader cannot hold the bar today
(TITAN-DIV-16).
Each tab needs a panel, and the pairing is manual. The shipped TabPanel defaults to naming a
tab that nothing produces, so set the ids yourself on both sides:
TabIconLabel gives a rail row an icon, a truncating label, and a
trailing accessory; TabMenu gives a row its own overflow menu. A published story assembles a
section header with expand and collapse, indented sub-rows, a status chip on a row, and per-row
menus. That is a navigation pattern, not a component, and nothing states when to reach for it
— see TITAN-GAP-29.
Content
- A tab label names a section, not an action. “Routing”, not “Configure routing”.
- One to three words. Labels do not truncate gracefully and the bar has finite width.
- Sentence case — unconfirmed, not a settled rule. Capitalization and punctuation lists tab labels explicitly as an open gap, not a confirmed content type. Treat this as this page’s own best guess until that page’s gap closes.
- No counts in labels unless the count is the point. “Members” is a section; “Members (147)” commits the label to staying accurate.
- Never number the tabs. Numbering implies order, and tabs are peers. A sequence is a wizard.
Accessibility
The bar needs an accessible name. GiveTabs an aria-label describing what it divides —
“Campaign sections”, not “Tabs”. Without it a screen-reader user hears a tab list with no
indication of what it belongs to.
Each tab and its panel reference each other. The tab carries id and aria-controls; the
panel carries id and aria-labelledby pointing back. The frame does not wire this and the
shipped panel wires it wrong — TITAN-DIV-14.
Keyboard. The bar is a single tab stop. Arrow keys move between tabs, Home and End jump to
the ends. A bar of nine sections costs one stop on the way to the body, not nine.
A tab label is not a heading. The section a tab reveals still needs its own heading — a reader
outlining the page by heading level does not see tab labels.
An icon-only tab must carry an explicit accessible name. A published story does this with
aria-label on each tab. Without it the tab announces as “tab” and nothing else.
A disabled tab is not focusable and announces nothing. A section a reader cannot open, with no
stated reason, is indistinguishable from a bug.
Constraints
Known issues
Tabs: open issues
Divergences, open decisions, and undocumented gaps for Tabs.
Why it works this way
The bar belongs to the header, not to the body. A section is part of what the page is, so the control that switches sections sits with the page’s identity. A bar floating between two regions has to be re-justified on every page. This is the view tier’s decision and the reasoning is recorded with the page frame. Selection is addressable. A tab that changes only local state cannot be linked, cannot survive a reload, and is lost to the back button — and the shipped panel unmounts the section it leaves, so unsaved work in it goes too. Making each section a URL turns a tab bar into navigation the browser understands (TITAN-VIEW-14). Peers, not steps. The strongest reason to keep filters and sequences out of a tab bar is that both imply the sections are not peers — a filter says some of these are subsets, a step says one comes before another. Once either is in the bar, a reader can no longer tell what a tab means, and every other rule on this page is arguing about something that has already been lost.Status and changelog
Related
- Views overview — where the bar goes, and what fills the panel
- SideNav — the question the vertical orientation raises
- Accordion · Wizard — the two nearest neighbours
- Form validation — the case that needs a tab-level error