Tab Layout
An ARIA tablist with active-tab state, roving keyboard navigation (Arrow / Home / End, skipping disabled tabs), badges, and a swapping panel. A Lit web component.
Preview
<esa-tab-layout id="tabs">
<div slot="panel-0">Overview content — summary metrics and status.</div>
<div slot="panel-1">Activity content — recent events log.</div>
<div slot="panel-2">Settings content — configuration form.</div>
</esa-tab-layout>
<script>
document.getElementById('tabs').tabs = [
{ label: 'Overview' }, { label: 'Activity' }, { label: 'Settings' },
];
</script> Appearance
Two appearances, aligned to Beacon's UiTabsAppearance. underline (default) shows an underlined indicator on the active tab; segmented renders a segmented control — a sunken, bordered track with a filled selected segment and no indicator. (variant="pill" is the legacy alias for segmented.)
<esa-tab-layout appearance="underline">…</esa-tab-layout>
<esa-tab-layout appearance="segmented">…</esa-tab-layout> Sizes
<esa-tab-layout size="xs">…</esa-tab-layout>
<esa-tab-layout size="sm">…</esa-tab-layout>
<esa-tab-layout size="md">…</esa-tab-layout>
<esa-tab-layout size="lg">…</esa-tab-layout> States
Tabs accept badges, and any tab can be disabled — keyboard navigation skips disabled tabs.
<script>
el.tabs = [
{ label: 'Inbox', badge: 4 },
{ label: 'Drafts', disabled: true },
{ label: 'Archive', badge: 128 },
];
</script> API
| Prop | Type | Default | Description |
|---|---|---|---|
tabs | EsaTab[] | [] | Tab definitions (property, set via JS). Each is { label; icon?; badge?; disabled? }. |
active-index | number | 0 | Index of the selected tab. |
size | 'xs' | 'sm' | 'md' | 'lg' | 'md' | Tab height and type scale. Reflected as an attribute. |
appearance | 'underline' | 'segmented' | 'underline' | Tab style (aligned to Beacon): underlined indicator or a segmented control on a sunken track. Reflected as an attribute. |
variant | 'underline' | 'pill' | 'underline' | Legacy alias for appearance — pill maps to segmented. Prefer appearance. |
Events
| Event | Type | Default | Description |
|---|---|---|---|
tabchange | CustomEvent | — | Fired when the active tab changes. detail: { index: number }. Bubbles and composed. |
tabs is an array property — set it in JS, not as an attribute. Each EsaTab.icon is a raw inline SVG string.
Slots
| Prop | Type | Default | Description |
|---|---|---|---|
panel-{index} | slot | — | Content for the matching tab; the active panel is shown. Falls back to the default slot. |
The routed-tabs (RouterOutlet) branch of the Angular component is replaced here by content panels via the named slots above.
Accessibility
- Follows the ARIA tablist pattern:
role="tablist"/role="tab"/role="tabpanel"witharia-selected. - Roving
tabindex— only the active tab is in the tab order; ArrowLeft / ArrowRight / Home / End move focus and selection, skipping disabled tabs. - Disabled tabs are non-interactive and excluded from keyboard traversal.