Skip to content

HyTabNav

Usage examples on this page are written for Lit / plain HTML (<hy-tab-nav>). The same component ships as HyTabNav in @whitespaceux/harmony-react (native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.

Link navigation that looks like tabs: a <nav> landmark of hy-tab-nav-item links, one URL each.

Use it for secondary navigation between pages (a settings section switcher, a top strip that changes the route): the back button works, each item is a link, and the current item carries aria-current="page". hy-tab-group is the in-place counterpart (role="tablist", panels switch without a URL change); hy-toggle-group is a value picker. Pick by one question — does the URL change?

Tabs vs tab nav vs toggle group

In place (no URL)Navigates (URL)
Tab-shapedhy-tab-group / hy-tabhy-tab-nav / hy-tab-nav-item
Value pickerhy-toggle-group

Industry precedent for keeping the two lookalikes apart: Radix Themes Tabs vs TabNav, Primer UnderlinePanels vs UnderlineNav, GOV.UK secondary navigation. There is no role="tablist", no aria-selected and no roving tabindex here — these are links, and Tab moves between them.

What it syncs

emphasis, size and orientation are pushed down to every child item on first render, on prop change and whenever the slotted items change, exactly like hy-tab-group does for its tabs. The chrome is pixel-identical to hy-tab at every rung, so a tab strip and a link strip sitting side by side read as one family.

Narrow regions

By default the items stay on one row, exactly like hy-tab-group. In a region that can get narrow (a resizable rail), set wrap: the item list wraps onto further rows while the action regions stay pinned at the trailing edge of the first row and never shrink — the collapse control is the last thing to go, not the first. Links wrap; tabs don't (GOV.UK service navigation, Bootstrap pills).

Actions

The actions-start / actions-end slots host controls that apply to the whole strip (a collapse button, a global search). They render beside the item list — never inside it — and each region auto-promotes to role="toolbar" when it holds 2 or more controls; name it with actions-start-label / actions-end-label.

Examples

Section switcher (soft pills)

html
<hy-tab-nav label="Settings section" emphasis="soft" size="small">
  <hy-tab-nav-item href="/settings/primitives" active>Primitives</hy-tab-nav-item>
  <hy-tab-nav-item href="/settings/semantics">Semantics</hy-tab-nav-item>
  <hy-tab-nav-item href="/settings/components">Components</hy-tab-nav-item>
  <hy-icon-button slot="actions-end" name="arrow-collapse" label="Hide navigation"></hy-icon-button>
</hy-tab-nav>

Underline strip (the default plain emphasis)

html
<hy-tab-nav label="Project">
  <hy-tab-nav-item href="/overview" active>Overview</hy-tab-nav-item>
  <hy-tab-nav-item href="/activity">Activity</hy-tab-nav-item>
  <hy-tab-nav-item href="/settings">Settings</hy-tab-nav-item>
</hy-tab-nav>

Vertical

html
<hy-tab-nav label="Account" orientation="vertical">
  <hy-tab-nav-item href="/account/profile" active>Profile</hy-tab-nav-item>
  <hy-tab-nav-item href="/account/security">Security</hy-tab-nav-item>
  <hy-tab-nav-item href="/account/billing">Billing</hy-tab-nav-item>
</hy-tab-nav>

Client-side routing

javascript
<hy-tab-nav label="Project" id="project-nav">
<hy-tab-nav-item value="overview" href="/overview" active>Overview</hy-tab-nav-item>
<hy-tab-nav-item value="activity" href="/activity">Activity</hy-tab-nav-item>
</hy-tab-nav>
<script>
document.getElementById('project-nav').addEventListener('activate', (e) => {
e.detail.originalEvent.preventDefault(); // stop the full-page navigation
router.push(e.detail.href);
});
</script>

API

Properties

PropertyAttributeTypeDefaultDescription
labellabelstring''Accessible name of the <nav> landmark — required. A page usually has several navigation landmarks, and the name is how a screen-reader user tells them apart. Dev mode warns when it is empty.
orientationorientation'horizontal' | 'vertical''horizontal'Layout axis propagated to every child item. - horizontal (default) — a row, indicator on the block-end edge - vertical — a column, indicator on the inline-start edge
emphasisemphasis'solid' | 'soft' | 'outlined' | 'plain''plain'Visual chrome weight propagated to every child item. - plain (default) — transparent rest, role underline indicator - soft — role-tinted rounded rectangle on the current item - outlined — boxed/bordered items - solid — fully filled current item
sizesize'small' | 'medium' | 'large''medium'Type + padding rung propagated to every child item. - smalllabel-md type on row-nav-md padding - medium (default) — body-md type on row-nav-lg padding - largebody-lg type on row-nav-lg padding
wrapwrapbooleanfalseLets the item list wrap onto further rows when the region is too narrow for one row. The action regions stay at the trailing edge, centered on the list block, and never shrink. Off by default so the strip stays a pixel lookalike of hy-tab-group, which keeps one row.
actionsStartLabelactions-start-labelstring''Accessible name applied as aria-label to the leading actions region when it auto-promotes to role="toolbar" (2+ controls).
actionsEndLabelactions-end-labelstring''Accessible name for the trailing actions region when it auto-promotes to role="toolbar".

Slots

SlotDescription
defaultThe hy-tab-nav-item elements.
actions-startStrip-wide controls rendered at the leading edge, beside the item list. Auto-promotes to role="toolbar" with 2+ controls.
actions-endStrip-wide controls rendered at the trailing edge. Auto-promotes to role="toolbar" with 2+ controls.

CSS Parts

PartDescription
baseThe <nav> landmark.
listThe container wrapping the items.
actions-startThe leading actions region.
actions-endThe trailing actions region.

CSS Custom Properties

PropertyDescription
--hy-tab-nav-gapGap between items. Default: none in a row (items touch, as in hy-tab-group), var(--hy-gap-layout-2xs) in a column.
--hy-tab-nav-actions-gapGap between the item list and the action regions, and between controls within a region. Default: var(--hy-gap-layout-sm).

Built with Lit. Documented with VitePress.