HyTab
Usage examples on this page are written for Lit / plain HTML (
<hy-tab>). The same component ships asHyTabin@whitespaceux/harmony-react(native React 19), with typed wrappers for Angular, Solid, Svelte, and Vue. The API reference below applies to all frameworks.
A tab component designed to work within a tab group to control associated tab panels. Provides keyboard navigation, accessibility features, and customizable styling. Supports both horizontal and vertical orientations and can include icons alongside text labels.
Hybrid SSR/CSR Icon Visibility System
The tab works seamlessly in both SSR and CSR-only (SPAs, Storybook) environments using a hybrid visibility approach:
In SSR environments:
- The host framework detects icon slot presence at render time via its own slot API
- Passes boolean attribute (
has-icon) to the Lit component - CSS uses this attribute for immediate icon wrapper visibility during initial render
- After hydration, HasSlotController confirms slot content and adds
.has-contentclass
In CSR-only environments (SPAs, Storybook):
- Icon wrapper renders to DOM (no
has-iconattribute) - Wrapper is hidden by default via CSS (
display: none) firstUpdated()runs andhideIconWrapperWithoutContent()detects actual content- Adds
.has-contentclass which triggers CSS visibility rules
This approach ensures consistent behavior across all rendering environments while optimizing for SSR performance and preventing layout shifts from flex gap.
Usage Guidelines
Tabs should be used within an hy-tab-group component for proper state management and keyboard navigation. Each tab controls a corresponding hy-tab-panel via the panel attribute.
Orientations
- horizontal (default): Tabs displayed in a row with bottom indicator
- vertical-left: Tabs displayed in a column with left-side indicator
- vertical-right: Tabs displayed in a column with right-side indicator
States
- default: Inactive tab state
- active: Selected tab that controls the visible panel
- disabled: Non-interactive tab state
Keyboard Navigation
When used within hy-tab-group:
- Arrow Right/Down: Move to next tab
- Arrow Left/Up: Move to previous tab
- Home: Move to first tab
- End: Move to last tab
- Enter/Space: Activate focused tab
Examples
Basic usage
<hy-tab-group>
<hy-tab slot="tab" label="Dashboard" panel="dashboard-panel"></hy-tab>
<hy-tab slot="tab" label="Settings" panel="settings-panel"></hy-tab>
<hy-tab-panel slot="panel" name="dashboard-panel"> Dashboard content </hy-tab-panel>
<hy-tab-panel slot="panel" name="settings-panel"> Settings content </hy-tab-panel>
</hy-tab-group>With active state
<hy-tab-group>
<hy-tab slot="tab" label="Overview" panel="overview-panel" active></hy-tab>
<hy-tab slot="tab" label="Analytics" panel="analytics-panel"></hy-tab>
<hy-tab slot="tab" label="Reports" panel="reports-panel"></hy-tab>
</hy-tab-group>With icons
<hy-tab-group>
<hy-tab slot="tab" label="Home" panel="home-panel">
<hy-icon slot="icon" name="home"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Settings" panel="settings-panel">
<hy-icon slot="icon" name="settings"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Profile" panel="profile-panel">
<hy-icon slot="icon" name="user"></hy-icon>
</hy-tab>
</hy-tab-group>Disabled tab
<hy-tab-group>
<hy-tab slot="tab" label="Available" panel="available-panel" active></hy-tab>
<hy-tab slot="tab" label="Premium" panel="premium-panel" disabled>
<hy-icon slot="icon" name="lock"></hy-icon>
</hy-tab>
</hy-tab-group>Vertical orientation with left indicator
<hy-tab-group orientation="vertical-left">
<hy-tab slot="tab" label="Profile" panel="profile-panel" orientation="vertical-left">
<hy-icon slot="icon" name="user"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Security" panel="security-panel" orientation="vertical-left">
<hy-icon slot="icon" name="shield"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Billing" panel="billing-panel" orientation="vertical-left">
<hy-icon slot="icon" name="credit-card"></hy-icon>
</hy-tab>
</hy-tab-group>Vertical orientation with right indicator
<hy-tab-group orientation="vertical-right">
<hy-tab slot="tab" label="Profile" panel="profile-panel" orientation="vertical-right">
<hy-icon slot="icon" name="user"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Security" panel="security-panel" orientation="vertical-right">
<hy-icon slot="icon" name="shield"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Billing" panel="billing-panel" orientation="vertical-right">
<hy-icon slot="icon" name="credit-card"></hy-icon>
</hy-tab>
</hy-tab-group>Complete tabbed interface
<div>
<hy-tab-group>
<hy-tab slot="tab" label="Home" panel="home-content" active>
<hy-icon slot="icon" name="home"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Products" panel="products-content">
<hy-icon slot="icon" name="package"></hy-icon>
</hy-tab>
<hy-tab slot="tab" label="Support" panel="support-content">
<hy-icon slot="icon" name="help-circle"></hy-icon>
</hy-tab>
</hy-tab-group>
<hy-tab-panel name="home-content">
<h2>Welcome Home</h2>
<p>This is the home content area.</p>
</hy-tab-panel>
<hy-tab-panel name="products-content">
<h2>Our Products</h2>
<p>Browse our product catalog.</p>
</hy-tab-panel>
<hy-tab-panel name="support-content">
<h2>Support Center</h2>
<p>Get help and support.</p>
</hy-tab-panel>
</div>Programmatic Methods
const tab = document.querySelector('hy-tab');
// Set active state
tab.active = true;
// Focus the tab
tab.focus();
// Blur the tab
tab.blur();
// Check if disabled
console.log(tab.disabled);API
Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
label | label | string | 'Tab' | The text content of the tab. |
panel | panel | string | '' | The name of the tab panel this tab controls. |
disabled | disabled | boolean | false | Disables the tab, making it non-interactive. |
active | active | boolean | false | Marks the tab as active/selected. |
orientation | orientation | 'horizontal' | 'vertical-left' | 'vertical-right' | 'horizontal' | The orientation and indicator position of the tab, typically inherited from the parent tab-group. - 'horizontal': Horizontal layout with bottom indicator (default) - 'vertical-left': Vertical layout with left indicator - 'vertical-right': Vertical layout with right indicator |
emphasis | emphasis | 'solid' | 'soft' | 'outlined' | 'plain' | 'plain' | Visual chrome weight, typically inherited from the parent tab-group. - plain (default) — transparent rest, role underline indicator - soft — role-tinted pill on the active tab - outlined — boxed/bordered tabs - solid — fully filled active tab |
size | size | 'small' | 'medium' | 'large' | 'medium' | Type + padding rung, typically inherited from the parent tab-group. - small — label-md type on row-nav-md padding - medium (default) — body-md type on row-nav-lg padding - large — body-lg type on row-nav-lg padding |
hasIcon | has-icon | boolean | false | SSR optimization hint indicating icon slot has content. |
Events
| Event | Detail | Description |
|---|---|---|
select | — | Fired when the tab is clicked or activated, includes panel ID and tab reference |
Slots
| Slot | Description |
|---|---|
icon | Icon content to display before the tab label |
CSS Parts
| Part | Description |
|---|---|
base | The tab's base button element |
label | The tab's text label element |
CSS Custom Properties
| Property | Description |
|---|---|
--hy-tab-radius | Border radius of the plain tab (default: none) |
--hy-tab-radius-form | Border radius of the soft / outlined / solid tab (default: var(--hy-radius-form-md); set var(--hy-radius-pill-md) for a full pill) |
--hy-tab-text-transform | Text transform for tab label |
--hy-tab-font-family | Font family override for tab label |
--hy-tab-hover-duration | Transition duration for hover and focus state feedback |
--hy-tab-hover-easing | Transition easing for hover and focus state feedback |
--hy-tab-expand-duration | Transition duration for the active-indicator slide animation |
--hy-tab-expand-easing | Transition easing for the active-indicator slide animation |
Methods
focus()
Sets focus on the tab.
Parameters:
options- Optional focus options
const tab = document.querySelector('hy-tab');
tab.focus();blur()
Removes focus from the tab.
const tab = document.querySelector('hy-tab');
tab.blur();