Skip to content

Tabs

An accessible tab interface with roving tabindex, arrow-key navigation, and horizontal or vertical orientation. Tab labels accept plain strings or snippets.

Import

import { Tabs } from "@hyzer-labs/ui"

Demo

A stable distance driver for windy rounds: predictable fade, without fighting you on hyzer lines.

<!-- The panel snippet receives the item — render per-item content -->
<Tabs items={discTabs} ariaLabel="Disc details">
	{#snippet panel(item)}
		<p>{discContent[item.id]}</p>
	{/snippet}
</Tabs>

Props

NameTypeDefaultNote
itemsTabItem[]Required. See TabItem below.
ariaLabelstringRequired. Labels the tablist.
defaultTabstringID of the initially active tab.
orientation'horizontal' | 'vertical''horizontal'
activation'auto' | 'manual''auto'auto activates on arrow-key focus; manual waits for Enter/Space.
panelSnippet<[TabItem]>Required. Renders each panel.
onChange(activeId: string) => void
classstringMerged after the hz-tabs class.

TabItem

NameTypeDefaultNote
idstringRequired. Must be unique.
labelstring | SnippetRequired. String for plain text; snippet for inner markup.
disabledbooleanfalse

Theme hooks

What this component promises your CSS. The reference theme styles exactly these — from @layer hz-theme, so your unlayered rules win. See Styling Components for the how.

Root class: .hz-tabs

Data attributes

HookValuesStyles
data-orientation'horizontal' | 'vertical'Vertical moves the list border and the active indicator to the inline edge. Default horizontal.
data-state'active' | 'inactive'On .hz-tabs-trigger. The panel carries it too, but its visibility runs off the native hidden attribute.
data-disabledpresent on disabled tabsOn .hz-tabs-trigger. Disabled tabs stay focusable (aria-disabled, not the native attribute).

Part classes

HookValuesStyles
.hz-tabs-listchild elementThe tablist. Scrolls horizontally.
.hz-tabs-triggerchild elementOne tab. The theme owns the whole button reset here, so an unthemed Tabs renders native buttons. The active indicator is an inset box-shadow, deliberately: the list scrolls, and any overlap trick that adds layout size produces a phantom scrollbar. Keep that property if you restyle it.
.hz-tabs-panelchild elementOne panel.

Accessibility

Implements the WAI-ARIA tabs pattern: role='tablist'/'tab'/'tabpanel', aria-selected, and roving tabindex. Tab is one stop for the whole tablist, and arrow keys move between triggers, with Home/End jumping to the first/last.

Panels are only their own tab stop when they contain no focusable content. ariaLabel is required to name the tablist.

Disabled tabs stay focusable via arrows but carry aria-disabled='true' and never activate.

References: APG Tabs pattern