HyTabGroup
A container component that manages a collection of tabs and their associated panels. Provides centralized state management, keyboard navigation, and accessibility features. Coordinates the relationship between tab elements and their corresponding content panels supporting both horizontal and vertical orientations.
Layout Orientations
- horizontal (default): Tabs displayed in a row above content panels
- vertical-left: Tabs displayed in a column beside content panels with left indicator
- vertical-right: Tabs displayed in a column beside content panels with right indicator
Activation Modes
- automatic (default): Tabs activate immediately when focused with keyboard
- manual: Tabs require Enter/Space key press to activate after focusing
Keyboard Navigation
- Arrow Right/Down: Move focus to next tab
- Arrow Left/Up: Move focus to previous tab
- Home: Move focus to first tab
- End: Move focus to last tab
- Enter/Space: Activate focused tab (manual mode only)
State Management
The tab group automatically:
- Manages active state across tabs and panels
- Synchronizes orientation property to children
- Updates tabindex values for proper focus management
- Skips disabled tabs during keyboard navigation
Examples
Basic usage
<hy-tab-group>
<hy-tab slot="tab" label="Tab 1" panel="panel1"></hy-tab>
<hy-tab slot="tab" label="Tab 2" panel="panel2"></hy-tab>
<hy-tab slot="tab" label="Tab 3" panel="panel3"></hy-tab>
<hy-tab-panel slot="panel" name="panel1">Content for tab 1</hy-tab-panel>
<hy-tab-panel slot="panel" name="panel2">Content for tab 2</hy-tab-panel>
<hy-tab-panel slot="panel" name="panel3">Content for tab 3</hy-tab-panel>
</hy-tab-group>Vertical orientation with icons
<hy-tab-group orientation="vertical-left" active-panel="settings">
<hy-tab slot="tab" label="Dashboard" panel="dashboard">
<hy-icon slot="icon" name="home"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Analytics" panel="analytics">
<hy-icon slot="icon" name="chart"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Settings" panel="settings">
<hy-icon slot="icon" name="settings"></hy-icon>
</hy-tab>
<hy-tab-panel slot="panel" name="dashboard">
<h2>Dashboard</h2>
<p>Overview of your data and metrics.</p>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="analytics">
<h2>Analytics</h2>
<p>Detailed analytics and reporting.</p>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="settings">
<h2>Settings</h2>
<p>Configure your application preferences.</p>
</hy-tab-panel>
</hy-tab-group>Manual activation mode
<hy-tab-group activation="manual">
<hy-tab slot="tab" label="Profile" panel="profile-panel"></hy-tab>
<hy-tab slot="tab" label="Security" panel="security-panel"></hy-tab>
<hy-tab slot="tab" label="Billing" panel="billing-panel"></hy-tab>
<hy-tab-panel slot="panel" name="profile-panel">
<h3>Profile Settings</h3>
<p>Manage your personal information.</p>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="security-panel">
<h3>Security Settings</h3>
<p>Configure password and authentication settings.</p>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="billing-panel">
<h3>Billing Information</h3>
<p>View and manage your subscription.</p>
</hy-tab-panel>
</hy-tab-group>With disabled tab
<hy-tab-group orientation="horizontal" active-panel="overview">
<hy-tab slot="tab" label="Overview" panel="overview">
<hy-icon slot="icon" name="eye"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Projects" panel="projects">
<hy-icon slot="icon" name="folder"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Team" panel="team">
<hy-icon slot="icon" name="users"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Reports" panel="reports" disabled>
<hy-icon slot="icon" name="file-text"></hy-icon>
</hy-tab>
<hy-tab-panel slot="panel" name="overview">
<h2>Project Overview</h2>
<p>View your project statistics and metrics.</p>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="projects">
<h2>Projects</h2>
<p>Manage your active projects.</p>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="team">
<h2>Team Members</h2>
<p>View and manage team members.</p>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="reports">
<h2>Reports</h2>
<p>Feature not yet available.</p>
</hy-tab-panel>
</hy-tab-group>Programmatic control
<hy-tab-group id="responsive-tabs" activation="manual">
<hy-tab slot="tab" label="Mobile" panel="mobile-view"></hy-tab>
<hy-tab slot="tab" label="Tablet" panel="tablet-view"></hy-tab>
<hy-tab slot="tab" label="Desktop" panel="desktop-view"></hy-tab>
<hy-tab-panel slot="panel" name="mobile-view">
<p>Mobile-optimized content and layout.</p>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="tablet-view">
<p>Tablet-optimized content and layout.</p>
</hy-tab-panel>
<hy-tab-panel slot="panel" name="desktop-view">
<p>Desktop-optimized content and layout.</p>
</hy-tab-panel>
</hy-tab-group>
<script>
const tabGroup = document.getElementById('responsive-tabs');
// Set active panel programmatically
tabGroup.activePanel = 'desktop-view';
// Listen for tab changes
tabGroup.addEventListener('change', (event) => {
console.log('Active panel:', event.detail.activePanel);
console.log('Previous panel:', event.detail.previousPanel);
});
</script>Programmatic Methods
const tabGroup = document.querySelector('hy-tab-group');
// Change active panel
tabGroup.activePanel = 'settings';
// Change orientation
tabGroup.orientation = 'vertical-left';
// Get current active panel
console.log(tabGroup.activePanel);Async loading
For tabs whose content is fetched over the network, combine three primitives:
before-change— cancelable event fired when the user (click or keyboard) attempts to switch tabs. Callevent.preventDefault()to block the switch while a fetch is in flight. The event does NOT fire for programmatic updates (settingactivePaneldirectly), matching the HTMLchangeevent pattern — this prevents infinite loops when the consumer's handler eventually commits the switch.loading— whentrue, overlays the panel area with anhy-spinnerand setsaria-busy="true"on the panels container. The tab rail stays interactive so the user can still switch away or cancel.--hy-tab-panel-min-height— CSS custom property that sets a minimum height on the panel container, eliminating layout shifts when switching between panels
### Async panel loading
```html
<hy-tab-group
id="async-tabs"
active-panel="overview"
style="--hy-tab-panel-min-height: 22rem"
>
<hy-tab slot="tab" label="Overview" panel="overview"></hy-tab>
<hy-tab slot="tab" label="Details" panel="details"></hy-tab>
<hy-tab-panel slot="panel" name="overview">…</hy-tab-panel>
<hy-tab-panel slot="panel" name="details" id="details-panel">…</hy-tab-panel>
</hy-tab-group>
<script>
const tabs = document.getElementById('async-tabs');
tabs.addEventListener('before-change', async (event) => {
if (event.detail.activePanel !== 'details') return;
event.preventDefault(); // block the switch while fetching
tabs.loading = true; // show spinner overlay
try {
const html = await fetchDetails();
document.getElementById('details-panel').innerHTML = html;
tabs.activePanel = 'details'; // programmatic set does NOT re-fire before-change
} finally {
tabs.loading = false;
}
});
</script>Tab-bar actions
The actions-start / actions-end slots host controls that apply to the whole tabbed view (e.g. a global search, a "New" primary). They render in the tab-bar as siblings of the role="tablist" element — never inside it — so the tablist stays pure and arrow-key roving is unaffected (the action controls are reached with Tab).
actions-start— leading edge (left in horizontal LTR, top of the rail in vertical).actions-end— trailing edge (right in horizontal LTR, bottom of the rail in vertical).- Both optional; when empty the tab-bar collapses to the prior layout (zero visual diff).
Each region auto-promotes to role="toolbar" when it holds 2 or more controls (a single control needs no toolbar role per the ARIA APG). Name the toolbar with actions-start-label / actions-end-label. Note: the component manages roving only inside the tablist — within a promoted toolbar the controls are reached with Tab,
### Tab-bar actions
```html
<hy-tab-group active-panel="files">
<hy-tab slot="tab" label="Files" panel="files"></hy-tab>
<hy-tab slot="tab" label="Activity" panel="activity"></hy-tab>
<hy-button slot="actions-end" emphasis="soft">New</hy-button>
<hy-tab-panel slot="panel" name="files">…</hy-tab-panel>
<hy-tab-panel slot="panel" name="activity">…</hy-tab-panel>
</hy-tab-group>Events
- change - Fired when the active tab changes. Detail:
{ activePanel: string, previousPanel: string } - before-change - Cancelable. Fired on user-initiated tab switch (click or keyboard) before
change. Callevent.preventDefault()to block the switch and commit it later viatabGroup.activePanel = target. Not fired for programmaticactivePanelupdates. Detail:{ activePanel: string, previousPanel: string }
Slots
- tab - Tab elements that trigger panel changes
- panel - Tab panel elements that display content
- actions-start - View-global controls rendered at the leading edge of the tab-bar, beside the tablist. Auto-promotes to
role="toolbar"with 2+ controls. - actions-end - View-global controls rendered at the trailing edge of the tab-bar. Auto-promotes to
role="toolbar"with 2+ controls.
Methods
willUpdate()
Generates unique IDs and initializes tabs/panels before first render to prevent update-after-update warnings.