Tab
Organizes content into separate views that users can switch between.
First release: 1.0.0 Latest update: July 1, 2025 Current version: 1.0.0
Import
import { PlusTab } from '@plusui/react';import { PlusTabComponent } from '@plusui/angular';import { PlusTab } from '@plusui/vue';import PlusTab from '@plusui/svelte';<script src="https://cdn.jsdelivr.net/npm/@plusui/core"></script>Package
@plusui/react@plusui/angular@plusui/vue@plusui/svelte@plusui/coreDocs
Changelog
Tab Changelog
Recent changes and updates for the tab component
v1.0.0
Added Tab component for navigation
- Horizontal and vertical orientations
- Prefix and suffix icons
- Dismissible tabs
- Animated indicators
- Accessibility with ARIA attributes
The Tab component system (plus-tab-group, plus-tab, plus-tab-panel) allows organizing content into selectable sections. The plus-tab-group manages the interaction between plus-tab elements (which act as controllers) and their corresponding plus-tab-panel elements (which contain the content).
Anatomy
Section titled “Anatomy”
Basic Example
Section titled “Basic Example”Structure your tabs by placing plus-tab elements in the tablist slot and plus-tab-panel elements in the panels slot of the plus-tab-group. Ensure each plus-tab has a unique value attribute matching the value attribute of its corresponding plus-tab-panel.
import { PlusTabGroup, PlusTab, PlusTabPanel } from '@plusui/react';
export default () => { const handleTabChange = (event: CustomEvent<{ value: string }>) => { console.log(`Tab changed to: ${event.detail.value}`); };
const handleTabDismiss = (event: CustomEvent<{ value: string }>) => { console.log(`Dismissed tab: ${event.detail.value}`); // In React, you'd typically manage tabs/panels in state and filter them out };
return ( <PlusTabGroup onPlusTabsChange={handleTabChange} onPlusTabDismiss={handleTabDismiss}> <PlusTab slot="tablist" value="tab-1" active>Tab 1</PlusTab> <PlusTab slot="tablist" value="tab-2">Tab 2</PlusTab> <PlusTab slot="tablist" value="tab-3">Tab 3</PlusTab>
<PlusTabPanel slot="panels" value="tab-1" active>Content for Tab 1</PlusTabPanel> <PlusTabPanel slot="panels" value="tab-2">Content for Tab 2</PlusTabPanel> <PlusTabPanel slot="panels" value="tab-3">Content for Tab 3</PlusTabPanel> </PlusTabGroup> );};import { Component } from '@angular/core';
@Component({ selector: 'app-tab-basic', template: ` <plus-tab-group (plus-tabs-change)="handleTabChange($event)" (plus-tab-dismiss)="handleTabDismiss($event)"> <plus-tab slot="tablist" value="tab-1" active>Tab 1</plus-tab> <plus-tab slot="tablist" value="tab-2">Tab 2</plus-tab> <plus-tab slot="tablist" value="tab-3">Tab 3</plus-tab>
<plus-tab-panel slot="panels" value="tab-1" active>Content for Tab 1</plus-tab-panel> <plus-tab-panel slot="panels" value="tab-2">Content for Tab 2</plus-tab-panel> <plus-tab-panel slot="panels" value="tab-3">Content for Tab 3</plus-tab-panel> </plus-tab-group> `})export class TabBasicComponent { handleTabChange(event: any) { console.log(`Tab changed to: ${event.detail.value}`); } handleTabDismiss(event: any) { console.log(`Dismissed tab: ${event.detail.value}`); // In Angular, manage tabs/panels in component state and update the template }}<template> <plus-tab-group @plus-tabs-change="handleTabChange" @plus-tab-dismiss="handleTabDismiss"> <plus-tab slot="tablist" value="tab-1" active>Tab 1</plus-tab> <plus-tab slot="tablist" value="tab-2">Tab 2</plus-tab> <plus-tab slot="tablist" value="tab-3">Tab 3</plus-tab>
<plus-tab-panel slot="panels" value="tab-1" active>Content for Tab 1</plus-tab-panel> <plus-tab-panel slot="panels" value="tab-2">Content for Tab 2</plus-tab-panel> <plus-tab-panel slot="panels" value="tab-3">Content for Tab 3</plus-tab-panel> </plus-tab-group></template>
<script setup>const handleTabChange = (event) => { console.log(`Tab changed to: ${event.detail.value}`);};const handleTabDismiss = (event) => { console.log(`Dismissed tab: ${event.detail.value}`); // In Vue, manage tabs/panels in component state (e.g., ref or reactive array) and filter them out};</script><script> function handleTabChange(event) { console.log(`Tab changed to: ${event.detail.value}`); } function handleTabDismiss(event) { console.log(`Dismissed tab: ${event.detail.value}`); // In Svelte, manage tabs/panels in component state and filter them out }</script>
<plus-tab-group on:plus-tabs-change={handleTabChange} on:plus-tab-dismiss={handleTabDismiss}> <plus-tab slot="tablist" value="tab-1" active>Tab 1</plus-tab> <plus-tab slot="tablist" value="tab-2">Tab 2</plus-tab> <plus-tab slot="tablist" value="tab-3">Tab 3</plus-tab>
<plus-tab-panel slot="panels" value="tab-1" active>Content for Tab 1</plus-tab-panel> <plus-tab-panel slot="panels" value="tab-2">Content for Tab 2</plus-tab-panel> <plus-tab-panel slot="panels" value="tab-3">Content for Tab 3</plus-tab-panel></plus-tab-group><plus-tab-group id="basic-tab-group"> <plus-tab slot="tablist" value="tab-1" active>Tab 1</plus-tab> <plus-tab slot="tablist" value="tab-2">Tab 2</plus-tab> <plus-tab slot="tablist" value="tab-3">Tab 3</plus-tab>
<plus-tab-panel slot="panels" value="tab-1" active>Content for Tab 1</plus-tab-panel> <plus-tab-panel slot="panels" value="tab-2">Content for Tab 2</plus-tab-panel> <plus-tab-panel slot="panels" value="tab-3">Content for Tab 3</plus-tab-panel></plus-tab-group>
<script> const tabGroup = document.querySelector('plus-tab-group'); tabGroup.addEventListener('plus-tabs-change', event => { console.log(`Tab changed to: ${event.detail.value}`); }); tabGroup.addEventListener('plus-tab-dismiss', event => { console.log(`Dismissed tab: ${event.detail.value}`); event.target.remove(); tabGroup.querySelector(`plus-tab-panel[value="${event.detail.value}"]`)?.remove(); });</script>Adjust the size of the tabs using the size prop on plus-tab-group.
import { PlusTabGroup, PlusTab, PlusTabPanel } from '@plusui/react';
export default () => { return ( <> <PlusTabGroup size="sm"> <PlusTab slot="tablist" value="s1" active="true"> Small 1 </PlusTab> <PlusTab slot="tablist" value="s2"> Small 2 </PlusTab> <PlusTabPanel slot="panels" value="s1" active="true"> Small Content 1 </PlusTabPanel> <PlusTabPanel slot="panels" value="s2"> Small Content 2 </PlusTabPanel> </PlusTabGroup> <PlusTabGroup size="md"> <PlusTab slot="tablist" value="m1" active="true"> Medium 1 </PlusTab> <PlusTab slot="tablist" value="m2"> Medium 2 </PlusTab> <PlusTabPanel slot="panels" value="m1" active="true"> Medium Content 1 </PlusTabPanel> <PlusTabPanel slot="panels" value="m2"> Medium Content 2 </PlusTabPanel> </PlusTabGroup> <PlusTabGroup size="lg"> <PlusTab slot="tablist" value="l1" active="true"> Large 1 </PlusTab> <PlusTab slot="tablist" value="l2"> Large 2 </PlusTab> <PlusTabPanel slot="panels" value="l1" active="true"> Large Content 1 </PlusTabPanel> <PlusTabPanel slot="panels" value="l2"> Large Content 2 </PlusTabPanel> </PlusTabGroup> </> );};import { Component } from '@angular/core';
@Component({ selector: 'app-example', template: ` <plus-tab-group size="sm"> <plus-tab slot="tablist" value="s1" active="true"> Small 1 </plus-tab> <plus-tab slot="tablist" value="s2"> Small 2 </plus-tab> <plus-tab-panel slot="panels" value="s1" active="true"> Small Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="s2"> Small Content 2 </plus-tab-panel> </plus-tab-group> <plus-tab-group size="md"> <plus-tab slot="tablist" value="m1" active="true"> Medium 1 </plus-tab> <plus-tab slot="tablist" value="m2"> Medium 2 </plus-tab> <plus-tab-panel slot="panels" value="m1" active="true"> Medium Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="m2"> Medium Content 2 </plus-tab-panel> </plus-tab-group> <plus-tab-group size="lg"> <plus-tab slot="tablist" value="l1" active="true"> Large 1 </plus-tab> <plus-tab slot="tablist" value="l2"> Large 2 </plus-tab> <plus-tab-panel slot="panels" value="l1" active="true"> Large Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="l2"> Large Content 2 </plus-tab-panel> </plus-tab-group> `})export class ExampleComponent {}<template> <plus-tab-group size="sm"> <plus-tab slot="tablist" value="s1" active="true"> Small 1 </plus-tab> <plus-tab slot="tablist" value="s2"> Small 2 </plus-tab> <plus-tab-panel slot="panels" value="s1" active="true"> Small Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="s2"> Small Content 2 </plus-tab-panel> </plus-tab-group> <plus-tab-group size="md"> <plus-tab slot="tablist" value="m1" active="true"> Medium 1 </plus-tab> <plus-tab slot="tablist" value="m2"> Medium 2 </plus-tab> <plus-tab-panel slot="panels" value="m1" active="true"> Medium Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="m2"> Medium Content 2 </plus-tab-panel> </plus-tab-group> <plus-tab-group size="lg"> <plus-tab slot="tablist" value="l1" active="true"> Large 1 </plus-tab> <plus-tab slot="tablist" value="l2"> Large 2 </plus-tab> <plus-tab-panel slot="panels" value="l1" active="true"> Large Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="l2"> Large Content 2 </plus-tab-panel> </plus-tab-group></template><plus-tab-group size="sm"> <plus-tab slot="tablist" value="s1" active="true"> Small 1 </plus-tab> <plus-tab slot="tablist" value="s2"> Small 2 </plus-tab> <plus-tab-panel slot="panels" value="s1" active="true"> Small Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="s2"> Small Content 2 </plus-tab-panel></plus-tab-group><plus-tab-group size="md"> <plus-tab slot="tablist" value="m1" active="true"> Medium 1 </plus-tab> <plus-tab slot="tablist" value="m2"> Medium 2 </plus-tab> <plus-tab-panel slot="panels" value="m1" active="true"> Medium Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="m2"> Medium Content 2 </plus-tab-panel></plus-tab-group><plus-tab-group size="lg"> <plus-tab slot="tablist" value="l1" active="true"> Large 1 </plus-tab> <plus-tab slot="tablist" value="l2"> Large 2 </plus-tab> <plus-tab-panel slot="panels" value="l1" active="true"> Large Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="l2"> Large Content 2 </plus-tab-panel></plus-tab-group><plus-tab-group size="sm"> <plus-tab slot="tablist" value="s1" active="true"> Small 1 </plus-tab> <plus-tab slot="tablist" value="s2"> Small 2 </plus-tab> <plus-tab-panel slot="panels" value="s1" active="true"> Small Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="s2"> Small Content 2 </plus-tab-panel></plus-tab-group><plus-tab-group size="md"> <plus-tab slot="tablist" value="m1" active="true"> Medium 1 </plus-tab> <plus-tab slot="tablist" value="m2"> Medium 2 </plus-tab> <plus-tab-panel slot="panels" value="m1" active="true"> Medium Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="m2"> Medium Content 2 </plus-tab-panel></plus-tab-group><plus-tab-group size="lg"> <plus-tab slot="tablist" value="l1" active="true"> Large 1 </plus-tab> <plus-tab slot="tablist" value="l2"> Large 2 </plus-tab> <plus-tab-panel slot="panels" value="l1" active="true"> Large Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="l2"> Large Content 2 </plus-tab-panel></plus-tab-group>Alignment
Section titled “Alignment”Use the alignment prop on plus-tab-group to position the tabs (start, center, end).
import { PlusTabGroup, PlusTab, PlusTabPanel } from '@plusui/react';
export default () => { return ( <> <PlusTabGroup alignment="start"> <PlusTab slot="tablist" value="a1" active="true"> Start 1 </PlusTab> <PlusTab slot="tablist" value="a2"> Start 2 </PlusTab> <PlusTabPanel slot="panels" value="a1" active="true"> Panel </PlusTabPanel> <PlusTabPanel slot="panels" value="a2"> Panel </PlusTabPanel> </PlusTabGroup> <PlusTabGroup alignment="center"> <PlusTab slot="tablist" value="b1" active="true"> Center 1 </PlusTab> <PlusTab slot="tablist" value="b2"> Center 2 </PlusTab> <PlusTabPanel slot="panels" value="b1" active="true"> Panel </PlusTabPanel> <PlusTabPanel slot="panels" value="b2"> Panel </PlusTabPanel> </PlusTabGroup> <PlusTabGroup alignment="end"> <PlusTab slot="tablist" value="c1" active="true"> End 1 </PlusTab> <PlusTab slot="tablist" value="c2"> End 2 </PlusTab> <PlusTabPanel slot="panels" value="c1" active="true"> Panel </PlusTabPanel> <PlusTabPanel slot="panels" value="c2"> Panel </PlusTabPanel> </PlusTabGroup> </> );};import { Component } from '@angular/core';
@Component({ selector: 'app-example', template: ` <plus-tab-group alignment="start"> <plus-tab slot="tablist" value="a1" active="true"> Start 1 </plus-tab> <plus-tab slot="tablist" value="a2"> Start 2 </plus-tab> <plus-tab-panel slot="panels" value="a1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="a2"> Panel </plus-tab-panel> </plus-tab-group> <plus-tab-group alignment="center"> <plus-tab slot="tablist" value="b1" active="true"> Center 1 </plus-tab> <plus-tab slot="tablist" value="b2"> Center 2 </plus-tab> <plus-tab-panel slot="panels" value="b1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="b2"> Panel </plus-tab-panel> </plus-tab-group> <plus-tab-group alignment="end"> <plus-tab slot="tablist" value="c1" active="true"> End 1 </plus-tab> <plus-tab slot="tablist" value="c2"> End 2 </plus-tab> <plus-tab-panel slot="panels" value="c1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="c2"> Panel </plus-tab-panel> </plus-tab-group> `})export class ExampleComponent {}<template> <plus-tab-group alignment="start"> <plus-tab slot="tablist" value="a1" active="true"> Start 1 </plus-tab> <plus-tab slot="tablist" value="a2"> Start 2 </plus-tab> <plus-tab-panel slot="panels" value="a1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="a2"> Panel </plus-tab-panel> </plus-tab-group> <plus-tab-group alignment="center"> <plus-tab slot="tablist" value="b1" active="true"> Center 1 </plus-tab> <plus-tab slot="tablist" value="b2"> Center 2 </plus-tab> <plus-tab-panel slot="panels" value="b1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="b2"> Panel </plus-tab-panel> </plus-tab-group> <plus-tab-group alignment="end"> <plus-tab slot="tablist" value="c1" active="true"> End 1 </plus-tab> <plus-tab slot="tablist" value="c2"> End 2 </plus-tab> <plus-tab-panel slot="panels" value="c1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="c2"> Panel </plus-tab-panel> </plus-tab-group></template><plus-tab-group alignment="start"> <plus-tab slot="tablist" value="a1" active="true"> Start 1 </plus-tab> <plus-tab slot="tablist" value="a2"> Start 2 </plus-tab> <plus-tab-panel slot="panels" value="a1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="a2"> Panel </plus-tab-panel></plus-tab-group><plus-tab-group alignment="center"> <plus-tab slot="tablist" value="b1" active="true"> Center 1 </plus-tab> <plus-tab slot="tablist" value="b2"> Center 2 </plus-tab> <plus-tab-panel slot="panels" value="b1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="b2"> Panel </plus-tab-panel></plus-tab-group><plus-tab-group alignment="end"> <plus-tab slot="tablist" value="c1" active="true"> End 1 </plus-tab> <plus-tab slot="tablist" value="c2"> End 2 </plus-tab> <plus-tab-panel slot="panels" value="c1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="c2"> Panel </plus-tab-panel></plus-tab-group><plus-tab-group alignment="start"> <plus-tab slot="tablist" value="a1" active="true"> Start 1 </plus-tab> <plus-tab slot="tablist" value="a2"> Start 2 </plus-tab> <plus-tab-panel slot="panels" value="a1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="a2"> Panel </plus-tab-panel></plus-tab-group><plus-tab-group alignment="center"> <plus-tab slot="tablist" value="b1" active="true"> Center 1 </plus-tab> <plus-tab slot="tablist" value="b2"> Center 2 </plus-tab> <plus-tab-panel slot="panels" value="b1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="b2"> Panel </plus-tab-panel></plus-tab-group><plus-tab-group alignment="end"> <plus-tab slot="tablist" value="c1" active="true"> End 1 </plus-tab> <plus-tab slot="tablist" value="c2"> End 2 </plus-tab> <plus-tab-panel slot="panels" value="c1" active="true"> Panel </plus-tab-panel> <plus-tab-panel slot="panels" value="c2"> Panel </plus-tab-panel></plus-tab-group>Vertical Orientation
Section titled “Vertical Orientation”Set the orientation prop to vertical on plus-tab-group to arrange tabs vertically.
import { PlusTabGroup, PlusTab, PlusTabPanel } from '@plusui/react';
export default () => { return ( <> <PlusTabGroup orientation="vertical"> <PlusTab slot="tablist" value="v1" active="true"> Vertical 1 </PlusTab> <PlusTab slot="tablist" value="v2"> Vertical 2 </PlusTab> <PlusTab slot="tablist" value="v3"> Vertical 3 </PlusTab> <PlusTabPanel slot="panels" value="v1" active="true"> Content 1 </PlusTabPanel> <PlusTabPanel slot="panels" value="v2"> Content 2 </PlusTabPanel> <PlusTabPanel slot="panels" value="v3"> Content 3 </PlusTabPanel> </PlusTabGroup> </> );};import { Component } from '@angular/core';
@Component({ selector: 'app-example', template: ` <plus-tab-group orientation="vertical"> <plus-tab slot="tablist" value="v1" active="true"> Vertical 1 </plus-tab> <plus-tab slot="tablist" value="v2"> Vertical 2 </plus-tab> <plus-tab slot="tablist" value="v3"> Vertical 3 </plus-tab> <plus-tab-panel slot="panels" value="v1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="v2"> Content 2 </plus-tab-panel> <plus-tab-panel slot="panels" value="v3"> Content 3 </plus-tab-panel> </plus-tab-group> `})export class ExampleComponent {}<template> <plus-tab-group orientation="vertical"> <plus-tab slot="tablist" value="v1" active="true"> Vertical 1 </plus-tab> <plus-tab slot="tablist" value="v2"> Vertical 2 </plus-tab> <plus-tab slot="tablist" value="v3"> Vertical 3 </plus-tab> <plus-tab-panel slot="panels" value="v1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="v2"> Content 2 </plus-tab-panel> <plus-tab-panel slot="panels" value="v3"> Content 3 </plus-tab-panel> </plus-tab-group></template><plus-tab-group orientation="vertical"> <plus-tab slot="tablist" value="v1" active="true"> Vertical 1 </plus-tab> <plus-tab slot="tablist" value="v2"> Vertical 2 </plus-tab> <plus-tab slot="tablist" value="v3"> Vertical 3 </plus-tab> <plus-tab-panel slot="panels" value="v1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="v2"> Content 2 </plus-tab-panel> <plus-tab-panel slot="panels" value="v3"> Content 3 </plus-tab-panel></plus-tab-group><plus-tab-group orientation="vertical"> <plus-tab slot="tablist" value="v1" active="true"> Vertical 1 </plus-tab> <plus-tab slot="tablist" value="v2"> Vertical 2 </plus-tab> <plus-tab slot="tablist" value="v3"> Vertical 3 </plus-tab> <plus-tab-panel slot="panels" value="v1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="v2"> Content 2 </plus-tab-panel> <plus-tab-panel slot="panels" value="v3"> Content 3 </plus-tab-panel></plus-tab-group>Animated Indicator
Section titled “Animated Indicator”Add the animated attribute to plus-tab-group for a sliding indicator effect. Works for both horizontal and vertical orientations.
import { PlusTabGroup, PlusTab, PlusTabPanel } from '@plusui/react';
export default () => { return ( <> <PlusTabGroup animated="true"> <PlusTab slot="tablist" value="a1" active="true"> Animated Tab 1 </PlusTab> <PlusTab slot="tablist" value="a2"> Animated Tab 2 </PlusTab> <PlusTabPanel slot="panels" value="a1" active="true"> Content 1 </PlusTabPanel> <PlusTabPanel slot="panels" value="a2"> Content 2 </PlusTabPanel> </PlusTabGroup> <PlusTabGroup orientation="vertical" animated="true"> <PlusTab slot="tablist" value="v1" active="true"> Vertical Animated 1 </PlusTab> <PlusTab slot="tablist" value="v2"> Vertical Animated 2 </PlusTab> <PlusTabPanel slot="panels" value="v1" active="true"> Content 1 </PlusTabPanel> <PlusTabPanel slot="panels" value="v2"> Content 2 </PlusTabPanel> </PlusTabGroup> </> );};import { Component } from '@angular/core';
@Component({ selector: 'app-example', template: ` <plus-tab-group animated="true"> <plus-tab slot="tablist" value="a1" active="true"> Animated Tab 1 </plus-tab> <plus-tab slot="tablist" value="a2"> Animated Tab 2 </plus-tab> <plus-tab-panel slot="panels" value="a1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="a2"> Content 2 </plus-tab-panel> </plus-tab-group> <plus-tab-group orientation="vertical" animated="true"> <plus-tab slot="tablist" value="v1" active="true"> Vertical Animated 1 </plus-tab> <plus-tab slot="tablist" value="v2"> Vertical Animated 2 </plus-tab> <plus-tab-panel slot="panels" value="v1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="v2"> Content 2 </plus-tab-panel> </plus-tab-group> `})export class ExampleComponent {}<template> <plus-tab-group animated="true"> <plus-tab slot="tablist" value="a1" active="true"> Animated Tab 1 </plus-tab> <plus-tab slot="tablist" value="a2"> Animated Tab 2 </plus-tab> <plus-tab-panel slot="panels" value="a1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="a2"> Content 2 </plus-tab-panel> </plus-tab-group> <plus-tab-group orientation="vertical" animated="true"> <plus-tab slot="tablist" value="v1" active="true"> Vertical Animated 1 </plus-tab> <plus-tab slot="tablist" value="v2"> Vertical Animated 2 </plus-tab> <plus-tab-panel slot="panels" value="v1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="v2"> Content 2 </plus-tab-panel> </plus-tab-group></template><plus-tab-group animated="true"> <plus-tab slot="tablist" value="a1" active="true"> Animated Tab 1 </plus-tab> <plus-tab slot="tablist" value="a2"> Animated Tab 2 </plus-tab> <plus-tab-panel slot="panels" value="a1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="a2"> Content 2 </plus-tab-panel></plus-tab-group><plus-tab-group orientation="vertical" animated="true"> <plus-tab slot="tablist" value="v1" active="true"> Vertical Animated 1 </plus-tab> <plus-tab slot="tablist" value="v2"> Vertical Animated 2 </plus-tab> <plus-tab-panel slot="panels" value="v1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="v2"> Content 2 </plus-tab-panel></plus-tab-group><plus-tab-group animated="true"> <plus-tab slot="tablist" value="a1" active="true"> Animated Tab 1 </plus-tab> <plus-tab slot="tablist" value="a2"> Animated Tab 2 </plus-tab> <plus-tab-panel slot="panels" value="a1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="a2"> Content 2 </plus-tab-panel></plus-tab-group><plus-tab-group orientation="vertical" animated="true"> <plus-tab slot="tablist" value="v1" active="true"> Vertical Animated 1 </plus-tab> <plus-tab slot="tablist" value="v2"> Vertical Animated 2 </plus-tab> <plus-tab-panel slot="panels" value="v1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="v2"> Content 2 </plus-tab-panel></plus-tab-group>Dismissible Tabs
Section titled “Dismissible Tabs”Add the dismissible attribute to plus-tab-group to allow closing tabs. You need to handle the plus-tab-dismiss event to remove the tab and its corresponding panel.
import { PlusTabGroup, PlusTab, PlusTabPanel } from '@plusui/react';import { useState } from 'react';
export default () => { const initialTabs = [ { value: 'd1', label: 'Dismissible 1', content: 'Content 1' }, { value: 'd2', label: 'Dismissible 2', content: 'Content 2' }, { value: 'd3', label: 'Dismissible 3', content: 'Content 3' }, ]; const [tabs, setTabs] = useState(initialTabs); const [activeTab, setActiveTab] = useState('d1');
const handleDismiss = (event: CustomEvent<{ value: string }>) => { const valueToRemove = event.detail.value; setTabs(currentTabs => currentTabs.filter(tab => tab.value !== valueToRemove)); console.log(`Dismissed tab: ${valueToRemove}`); if (activeTab === valueToRemove) { const remainingTabs = tabs.filter(tab => tab.value !== valueToRemove); if (remainingTabs.length > 0) { setActiveTab(remainingTabs[0].value); } else { setActiveTab(null); } } };
const handleTabChange = (event: CustomEvent<{ value: string }>) => { setActiveTab(event.detail.value); };
return ( <PlusTabGroup dismissible onPlusTabDismiss={handleDismiss} onPlusTabsChange={handleTabChange}> {tabs.map(tab => ( <PlusTab key={tab.value} slot="tablist" value={tab.value} active={activeTab === tab.value}> {tab.label} </PlusTab> ))} {tabs.map(tab => ( <PlusTabPanel key={tab.value} slot="panels" value={tab.value} active={activeTab === tab.value}> {tab.content} </PlusTabPanel> ))} </PlusTabGroup> );};import { Component } from '@angular/core';
@Component({ selector: 'app-dismissible-tabs', template: ` <plus-tab-group dismissible (plus-tab-dismiss)="handleDismiss($event)" (plus-tabs-change)="handleTabChange($event)"> <plus-tab *ngFor="let tab of tabs" slot="tablist" [value]="tab.value" [active]="activeTab === tab.value"> {{ tab.label }} </plus-tab> <plus-tab-panel *ngFor="let tab of tabs" slot="panels" [value]="tab.value" [active]="activeTab === tab.value"> {{ tab.content }} </plus-tab-panel> </plus-tab-group> `})export class DismissibleTabsComponent { tabs = [ { value: 'd1', label: 'Dismissible 1', content: 'Content 1' }, { value: 'd2', label: 'Dismissible 2', content: 'Content 2' }, { value: 'd3', label: 'Dismissible 3', content: 'Content 3' }, ]; activeTab = 'd1';
handleDismiss(event: any) { const valueToRemove = event.detail.value; this.tabs = this.tabs.filter(tab => tab.value !== valueToRemove); console.log(`Dismissed tab: ${valueToRemove}`); if (this.activeTab === valueToRemove && this.tabs.length > 0) { this.activeTab = this.tabs[0].value; } }
handleTabChange(event: any) { this.activeTab = event.detail.value; }}<template> <plus-tab-group dismissible @plus-tab-dismiss="handleDismiss" @plus-tabs-change="handleTabChange"> <plus-tab v-for="tab in tabs" :key="tab.value" slot="tablist" :value="tab.value" :active="activeTab === tab.value"> {{ tab.label }} </plus-tab> <plus-tab-panel v-for="tab in tabs" :key="tab.value" slot="panels" :value="tab.value" :active="activeTab === tab.value"> {{ tab.content }} </plus-tab-panel> </plus-tab-group></template>
<script setup>import { ref } from 'vue';
const initialTabs = [ { value: 'd1', label: 'Dismissible 1', content: 'Content 1' }, { value: 'd2', label: 'Dismissible 2', content: 'Content 2' }, { value: 'd3', label: 'Dismissible 3', content: 'Content 3' },];const tabs = ref(initialTabs);const activeTab = ref('d1');
const handleDismiss = (event) => { const valueToRemove = event.detail.value; tabs.value = tabs.value.filter(tab => tab.value !== valueToRemove); console.log(`Dismissed tab: ${valueToRemove}`); if (activeTab.value === valueToRemove) { if (tabs.value.length > 0) { activeTab.value = tabs.value[0].value; } else { activeTab.value = null; } }};
const handleTabChange = (event) => { activeTab.value = event.detail.value;};</script><script> let tabs = [ { value: 'd1', label: 'Dismissible 1', content: 'Content 1' }, { value: 'd2', label: 'Dismissible 2', content: 'Content 2' }, { value: 'd3', label: 'Dismissible 3', content: 'Content 3' }, ]; let activeTab = 'd1';
function handleDismiss(event) { const valueToRemove = event.detail.value; tabs = tabs.filter(tab => tab.value !== valueToRemove); console.log(`Dismissed tab: ${valueToRemove}`); if (activeTab === valueToRemove) { if (tabs.length > 0) { activeTab = tabs[0].value; } else { activeTab = null; } } }
function handleTabChange(event) { activeTab = event.detail.value; }</script>
<plus-tab-group dismissible on:plus-tab-dismiss={handleDismiss} on:plus-tabs-change={handleTabChange}> {#each tabs as tab (tab.value)} <plus-tab slot="tablist" value={tab.value} active={activeTab === tab.value}> {tab.label} </plus-tab> {/each} {#each tabs as tab (tab.value)} <plus-tab-panel slot="panels" value={tab.value} active={activeTab === tab.value}> {tab.content} </plus-tab-panel> {/each}</plus-tab-group><plus-tab-group dismissible id="dismissible-tabs"> <plus-tab slot="tablist" value="d1" active>Dismissible 1</plus-tab> <plus-tab slot="tablist" value="d2">Dismissible 2</plus-tab> <plus-tab slot="tablist" value="d3">Dismissible 3</plus-tab>
<plus-tab-panel slot="panels" value="d1" active>Content 1</plus-tab-panel> <plus-tab-panel slot="panels" value="d2">Content 2</plus-tab-panel> <plus-tab-panel slot="panels" value="d3">Content 3</plus-tab-panel></plus-tab-group>
<script> const dismissibleGroup = document.getElementById('dismissible-tabs'); dismissibleGroup.addEventListener('plus-tab-dismiss', event => { const tabValue = event.detail.value; const tabToRemove = dismissibleGroup.querySelector(`plus-tab[value="${tabValue}"]`); const panelToRemove = dismissibleGroup.querySelector(`plus-tab-panel[value="${tabValue}"]`);
if (tabToRemove) tabToRemove.remove(); if (panelToRemove) panelToRemove.remove();
console.log(`Dismissed and removed tab: ${tabValue}`); });</script>Disabled Tabs
Section titled “Disabled Tabs”Add the disabled attribute to a plus-tab to prevent its selection.
import { PlusTabGroup, PlusTab, PlusTabPanel } from '@plusui/react';
export default () => { return ( <> <PlusTabGroup> <PlusTab slot="tablist" value="d1" active="true"> Enabled </PlusTab> <PlusTab slot="tablist" value="d2" disabled> Disabled </PlusTab> <PlusTab slot="tablist" value="d3"> Another Enabled </PlusTab> <PlusTabPanel slot="panels" value="d1" active="true"> Content 1 </PlusTabPanel> <PlusTabPanel slot="panels" value="d2"> Content 2 (won’t be shown via click) </PlusTabPanel> <PlusTabPanel slot="panels" value="d3"> Content 3 </PlusTabPanel> </PlusTabGroup> </> );};import { Component } from '@angular/core';
@Component({ selector: 'app-example', template: ` <plus-tab-group> <plus-tab slot="tablist" value="d1" active="true"> Enabled </plus-tab> <plus-tab slot="tablist" value="d2" disabled> Disabled </plus-tab> <plus-tab slot="tablist" value="d3"> Another Enabled </plus-tab> <plus-tab-panel slot="panels" value="d1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="d2"> Content 2 (won’t be shown via click) </plus-tab-panel> <plus-tab-panel slot="panels" value="d3"> Content 3 </plus-tab-panel> </plus-tab-group> `})export class ExampleComponent {}<template> <plus-tab-group> <plus-tab slot="tablist" value="d1" active="true"> Enabled </plus-tab> <plus-tab slot="tablist" value="d2" disabled> Disabled </plus-tab> <plus-tab slot="tablist" value="d3"> Another Enabled </plus-tab> <plus-tab-panel slot="panels" value="d1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="d2"> Content 2 (won’t be shown via click) </plus-tab-panel> <plus-tab-panel slot="panels" value="d3"> Content 3 </plus-tab-panel> </plus-tab-group></template><plus-tab-group> <plus-tab slot="tablist" value="d1" active="true"> Enabled </plus-tab> <plus-tab slot="tablist" value="d2" disabled> Disabled </plus-tab> <plus-tab slot="tablist" value="d3"> Another Enabled </plus-tab> <plus-tab-panel slot="panels" value="d1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="d2"> Content 2 (won’t be shown via click) </plus-tab-panel> <plus-tab-panel slot="panels" value="d3"> Content 3 </plus-tab-panel></plus-tab-group><plus-tab-group> <plus-tab slot="tablist" value="d1" active="true"> Enabled </plus-tab> <plus-tab slot="tablist" value="d2" disabled> Disabled </plus-tab> <plus-tab slot="tablist" value="d3"> Another Enabled </plus-tab> <plus-tab-panel slot="panels" value="d1" active="true"> Content 1 </plus-tab-panel> <plus-tab-panel slot="panels" value="d2"> Content 2 (won’t be shown via click) </plus-tab-panel> <plus-tab-panel slot="panels" value="d3"> Content 3 </plus-tab-panel></plus-tab-group>Tabs with Icons
Section titled “Tabs with Icons”Use the prefix-icon or suffix-icon attributes on plus-tab, or place icons directly in the default slot.
Contact (Slot)
import { PlusTabGroup, PlusTab, PlusTabPanel } from '@plusui/react';
export default () => { return ( <> <PlusTabGroup> <PlusTab slot="tablist" value="home" prefixIcon="fas fa-home" active="true"> Home </PlusTab> <PlusTab slot="tablist" value="cart" prefixIcon="fas fa-shopping-cart"> Cart </PlusTab> <PlusTab slot="tablist" value="info" suffixIcon="fas fa-info-circle"> Info </PlusTab> <PlusTab slot="tablist" value="contact"> <p> <i className="fa-solid fa-envelope"> </i> Contact (Slot) </p> </PlusTab> <PlusTabPanel slot="panels" value="home" active="true"> Home Content </PlusTabPanel> <PlusTabPanel slot="panels" value="cart"> Cart Content </PlusTabPanel> <PlusTabPanel slot="panels" value="info"> Info Content </PlusTabPanel> <PlusTabPanel slot="panels" value="contact"> Contact Content </PlusTabPanel> </PlusTabGroup> </> );};import { Component } from '@angular/core';
@Component({ selector: 'app-example', template: ` <plus-tab-group> <plus-tab slot="tablist" value="home" prefix-icon="fas fa-home" active="true"> Home </plus-tab> <plus-tab slot="tablist" value="cart" prefix-icon="fas fa-shopping-cart"> Cart </plus-tab> <plus-tab slot="tablist" value="info" suffix-icon="fas fa-info-circle"> Info </plus-tab> <plus-tab slot="tablist" value="contact"> <p> <i class="fa-solid fa-envelope"> </i> Contact (Slot) </p> </plus-tab> <plus-tab-panel slot="panels" value="home" active="true"> Home Content </plus-tab-panel> <plus-tab-panel slot="panels" value="cart"> Cart Content </plus-tab-panel> <plus-tab-panel slot="panels" value="info"> Info Content </plus-tab-panel> <plus-tab-panel slot="panels" value="contact"> Contact Content </plus-tab-panel> </plus-tab-group> `})export class ExampleComponent {}<template> <plus-tab-group> <plus-tab slot="tablist" value="home" prefix-icon="fas fa-home" active="true"> Home </plus-tab> <plus-tab slot="tablist" value="cart" prefix-icon="fas fa-shopping-cart"> Cart </plus-tab> <plus-tab slot="tablist" value="info" suffix-icon="fas fa-info-circle"> Info </plus-tab> <plus-tab slot="tablist" value="contact"> <p> <i class="fa-solid fa-envelope"> </i> Contact (Slot) </p> </plus-tab> <plus-tab-panel slot="panels" value="home" active="true"> Home Content </plus-tab-panel> <plus-tab-panel slot="panels" value="cart"> Cart Content </plus-tab-panel> <plus-tab-panel slot="panels" value="info"> Info Content </plus-tab-panel> <plus-tab-panel slot="panels" value="contact"> Contact Content </plus-tab-panel> </plus-tab-group></template><plus-tab-group> <plus-tab slot="tablist" value="home" prefix-icon="fas fa-home" active="true"> Home </plus-tab> <plus-tab slot="tablist" value="cart" prefix-icon="fas fa-shopping-cart"> Cart </plus-tab> <plus-tab slot="tablist" value="info" suffix-icon="fas fa-info-circle"> Info </plus-tab> <plus-tab slot="tablist" value="contact"> <p> <i class="fa-solid fa-envelope"> </i> Contact (Slot) </p> </plus-tab> <plus-tab-panel slot="panels" value="home" active="true"> Home Content </plus-tab-panel> <plus-tab-panel slot="panels" value="cart"> Cart Content </plus-tab-panel> <plus-tab-panel slot="panels" value="info"> Info Content </plus-tab-panel> <plus-tab-panel slot="panels" value="contact"> Contact Content </plus-tab-panel></plus-tab-group><plus-tab-group> <plus-tab slot="tablist" value="home" prefix-icon="fas fa-home" active="true"> Home </plus-tab> <plus-tab slot="tablist" value="cart" prefix-icon="fas fa-shopping-cart"> Cart </plus-tab> <plus-tab slot="tablist" value="info" suffix-icon="fas fa-info-circle"> Info </plus-tab> <plus-tab slot="tablist" value="contact"> <p> <i class="fa-solid fa-envelope"> </i> Contact (Slot) </p> </plus-tab> <plus-tab-panel slot="panels" value="home" active="true"> Home Content </plus-tab-panel> <plus-tab-panel slot="panels" value="cart"> Cart Content </plus-tab-panel> <plus-tab-panel slot="panels" value="info"> Info Content </plus-tab-panel> <plus-tab-panel slot="panels" value="contact"> Contact Content </plus-tab-panel></plus-tab-group>Visual Sections
Section titled “Visual Sections”Layout & Spacing
Section titled “Layout & Spacing”
Light & Dark Mode
Section titled “Light & Dark Mode”
Accessibility (a11y)
Section titled “Accessibility (a11y)”- Keyboard Behavior:
- When focus is on a
plus-tab:ArrowLeft/ArrowUp: Moves focus to the previous tab. Wraps around.ArrowRight/ArrowDown: Moves focus to the next tab. Wraps around.Home: Moves focus to the first tab.End: Moves focus to the last tab.EnterorSpace: Activates the focused tab, displaying its associated panel.
- When focus is on a
- Screen Reader:
plus-tab-groupusesrole="tablist".- Each
plus-tabusesrole="tab", witharia-selectedindicating the active state andaria-controlspointing to the ID of its associatedplus-tab-panel. - Disabled tabs have
aria-disabled="true". - Each
plus-tab-panelusesrole="tabpanel", witharia-labelledbypointing to the ID of its controllingplus-tab.
- Required Developer Actions:
- Ensure
plus-tabelements have meaningful text content or accessible labels (aria-label). - Link tabs and panels correctly using the
valueattribute on bothplus-tabandplus-tab-panel. - Assign unique IDs to
plus-tabandplus-tab-panelif default ID generation needs overriding foraria-controlsandaria-labelledby.
- Ensure
API Reference
Section titled “API Reference”plus-tab-group
Section titled “plus-tab-group”Properties
Section titled “Properties”| Name | Type | Default | Description | Required |
|---|---|---|---|---|
size | 'sm' | 'md' | 'lg' | 'md' | Size of the tabs within the group. | No |
orientation | 'horizontal' | 'vertical' | 'horizontal' | Displays tabs vertically or horizontally. | No |
alignment | 'start' | 'center' | 'end' | 'start' | Horizontal alignment of the tabs within the tab list. | No |
noScroll | boolean | false | Disables the scroll buttons when tabs overflow the container width. | No |
animated | boolean | false | Enables the animated (sliding) indicator. | No |
dismissible | boolean | false | Allows tabs to be closed via a dismiss button. Requires event handling. | No |
Events
Section titled “Events”| Name | Payload Type | Description |
|---|---|---|
plus-tabs-change | CustomEvent<{ value: string }> | Fired when a tab panel is activated. event.detail.value is the panel’s value. |
plus-tab-dismiss | CustomEvent<{ value: string }> | Fired when a dismissible tab is closed. event.detail.value is the tab’s value. |
| Name | Description |
|---|---|
tablist | Contains the plus-tab elements for navigation. |
panels | Contains the plus-tab-panel elements for content. |
Methods
Section titled “Methods”| Name | Parameters | Returns | Description |
|---|---|---|---|
show(panel) | panel: string | Promise<void> | Programmatically shows the specified tab panel. |
CSS Shadow Parts
Section titled “CSS Shadow Parts”| Name | Description |
|---|---|
base | The main container element. |
nav | The container for the navigation tabs (role="tablist"). |
tabs | The scrollable container for the tabs. |
active-tab-indicator | The indicator showing the active tab. |
body | The container for the tab panels. |
scroll-button | The scroll buttons (visible when tabs overflow). |
scroll-button--start | The scroll button at the start. |
scroll-button--end | The scroll button at the end. |
scroll-button__base | The base element of a scroll button. |
CSS Custom Properties
Section titled “CSS Custom Properties”| Name | Description |
|---|---|
--plus-tab-indicator-color | Color of the active tab indicator. Default is primary. |
--plus-tab-indicator-size | Thickness of the active tab indicator. Default is 2px. |
--plus-tab-track-color | Color of the track beneath the tabs. Default is neutral-base. |
--plus-tab-track-size | Thickness of the track beneath the tabs. Default is 2px. |
--plus-tab-track-gap | Space between the track and the tabs. Default is 0px. |
--plus-tab-transition-speed | Speed of the indicator transition. Default is 200ms. |
plus-tab
Section titled “plus-tab”Properties
Section titled “Properties”| Name | Type | Default | Description | Required |
|---|---|---|---|---|
value | string | '' | Unique value identifying the tab and linking it to a plus-tab-panel. | Yes |
active | boolean | false | Indicates if the tab is currently active. Managed by the parent plus-tab-group. | No |
disabled | boolean | false | Disables the tab, preventing selection. | No |
prefix-icon | string | undefined | Class name(s) for an icon to display before the tab text. | No |
suffix-icon | string | undefined | Class name(s) for an icon to display after the tab text. | No |
Events
Section titled “Events”| Name | Payload Type | Description |
|---|---|---|
plus-tab-dismiss | CustomEvent<{ value: string }> | Fired from parent when this tab is dismissed (if dismissible). |
| Name | Description |
|---|---|
(default) | The content of the tab (text, icons, etc.). |
CSS Shadow Parts
Section titled “CSS Shadow Parts”| Name | Description |
|---|---|
base | The main tab button element. |
dismiss-button | The dismiss button (if group is dismissible). |
CSS Custom Properties
Section titled “CSS Custom Properties”| Name | Description |
|---|---|
--plus-tab-color-default | Default text color. |
--plus-tab-color-default-hover | Text color on hover. |
--plus-tab-color-default-active | Text color when active. |
--plus-tab-color-disabled | Text color when disabled. |
--plus-tab-bg-default-hover | Background color on hover. |
--plus-tab-bg-default-active | Background color when active. |
--plus-tab-focus-ring-color | Color of the focus ring. |
--plus-tab-focus-ring-width | Width of the focus ring. |
--plus-tab-focus-ring-offset | Offset of the focus ring. |
plus-tab-panel
Section titled “plus-tab-panel”Properties
Section titled “Properties”| Name | Type | Default | Description | Required |
|---|---|---|---|---|
value | string | '' | Unique value matching the value attribute of its controlling plus-tab. | Yes |
active | boolean | false | Indicates if the panel is currently active. Managed by the parent plus-tab-group. | No |
| Name | Description |
|---|---|
(default) | The content of the tab panel. |
CSS Shadow Parts
Section titled “CSS Shadow Parts”| Name | Description |
|---|---|
base | The main panel container element. |
Join the Community
Plus UI is built by the community. Join us on our platforms to contribute, get help, and stay up to date.