Tabs
Standalone directives over Angular Aria with controlled selection, roving tabindex, ARIA relationships, and keyboard navigation.
Design
Functional preview
Running with the native Angular component and @angular/aria/tabs.
Anatomy
Panel content goes here.
4.ds-tabs) — container with role="tablist".2 Tab (
.ds-tab) — individual tab button with role="tab".3 Active indicator — bottom border on the active tab.
4 Tab panel — content area with
role="tabpanel".
Default tabs
One tab is active, the others are in their default state. Each tab controls a corresponding panel.
This is the Overview panel content. It provides a summary of your account status and recent activity.
Settings panel for profile, notifications preferences, and account security options.
Activity panel listing recent logins, changes, and audit events.
With disabled tab
A disabled tab cannot be interacted with and is skipped during keyboard navigation.
General settings for your account: display name, language, and timezone preferences.
Multiple tabs
Tabs scroll horizontally when they overflow the container.
Dashboard panel showing project metrics, recent commits, and team activity overview.
Token mapping
| Property | Token | CSS variable |
|---|---|---|
| text (inactive) | component.tabs.label.color.default | --ds-tabs-label-color-default |
| text (hover) | component.tabs.label.color.hover | --ds-tabs-label-color-hover |
| text (active) | component.tabs.label.color.active | --ds-tabs-label-color-active |
| text (disabled) | component.tabs.label.color.disabled | --ds-tabs-label-color-disabled |
| active indicator | component.tabs.indicator.fill.active | --ds-tabs-indicator-fill-active |
| divider | semantic.overlay.default | --ds-overlay-default |
| list border | component.tabs.list.border-width.default | --ds-tabs-list-border-width-default |
| item padding | component.tabs.item.{padding-x,padding-top}.default | --ds-tabs-item-padding-*-default |
| gap to indicator | component.tabs.item.indicator-gap.default | --ds-tabs-item-indicator-gap-default |
| indicator height | component.tabs.indicator.height.default | --ds-tabs-indicator-height-default |
| label typography | component.tabs.label.{font-size,line-height,font-weight,letter-spacing}.default | --ds-tabs-label-*-default |
| panel padding | component.tabs.panel.padding-y.default | --ds-tabs-panel-padding-y-default |
| focus ring | component.focus-ring.{color.default,width} | --ds-focus-ring-color-default, --ds-focus-ring-width |
| focus ring radius | component.tabs.focus-ring.radius.default | --ds-tabs-focus-ring-radius-default |
Usage
When to use
Best practices
Content guidelines
| Rule | Example |
|---|---|
| Use nouns as labels | "Overview", "Settings", "Activity" — not "View overview" or "Go to settings" |
| Same hierarchy level | All tabs should represent parallel categories of the same content |
| No icons without text | Always include a text label — icons alone are ambiguous in tab context |
| Keep labels short (1-2 words) | "Pull Requests" — not "View all pull requests in this project" |
Related
When to choose this output
Choose this native Angular output when the application already uses Angular. It consumes the same DS CSS and has no dependency on the Web runtime or React adapters.
Implementation
- Status
- Beta
- Distribution
- Validated Angular tarball
Installation
npm install ./dist/tis-angular-0.0.0-beta.0.tgz ds-tispnpm add ./dist/tis-angular-0.0.0-beta.0.tgz ds-tisyarn add ./dist/tis-angular-0.0.0-beta.0.tgz ds-tisbun add ./dist/tis-angular-0.0.0-beta.0.tgz ds-tisDS styles
@import "ds-tis/css";Angular imports
import {
TisTab,
TisTabList,
TisTabPanel,
TisTabs,
} from '@tis/angular/tabs'Template
<div tisTabs>
<div
tisTabList
aria-label="Seções do projeto"
[(selectedTab)]="selectedTab"
selectionMode="follow"
[softDisabled]="false"
>
<button tisTab value="overview">Visão geral</button>
<button tisTab value="team">Equipe</button>
<button tisTab value="billing" [disabled]="true">Cobrança</button>
</div>
<div tisTabPanel value="overview">Resumo do projeto.</div>
<div tisTabPanel value="team">Equipe do projeto.</div>
<div tisTabPanel value="billing">Plano e faturamento.</div>
</div>Public contract
- Package
@tis/angular- Entrypoint
@tis/angular/tabs- Primitive
@angular/aria/tabs- Status
- Beta
Accessibility
Keyboard interaction
| Key | Action |
|---|---|
Tab | Moves focus into the tab list, landing on the active tab |
Arrow Left / Arrow Right | Moves to previous / next tab (auto-activates) |
Home | Moves to the first tab |
End | Moves to the last tab |
Tab (from list) | Moves focus into the active panel content |
Disabled tabs are skipped during arrow key navigation. Only the active tab is in the Tab sequence (tabindex="0"); all others have tabindex="-1".
Accessibility
| WCAG criterion | Requirement | Status |
|---|---|---|
| 4.1.2 Name, Role, Value (A) | role="tablist", role="tab", role="tabpanel". aria-selected on tabs. aria-controls / aria-labelledby linking tabs to panels. | ✓ |
| 2.1.1 Keyboard (A) | Arrow keys navigate between tabs. Tab enters and exits the tab list. | ✓ |
| 2.4.11 Focus Appearance (AA) | Focus ring visible on the active tab when focused. | ✓ |
role="tablist" — on the tab list container.role="tab" — on each tab button.role="tabpanel" — on each content panel.aria-selected="true|false" — indicates the active tab.aria-controls — on each tab, pointing to the corresponding panel ID.aria-labelledby — on each panel, pointing to the corresponding tab ID.aria-disabled="true" — on disabled tabs.
Output responsibility
Preserve native button semantics, submit behavior, accessible names, disabled state, and visible focus.
Validation evidence
Covered by its independent Storybook output plus browser, responsive, keyboard, Axe, and bundle checks.