OGE logoOGE

Tabs

<oge-tab-panel> renders a tab strip with content panels; <oge-tabs> is the strip alone. Both accept declarative <oge-tab> children and a data-driven items array, follow the WAI-ARIA tabs pattern (roving tabindex, arrow keys, automatic or manual activation) and speak RTL out of the box.

[(selectedIndex)] / [(selectedKey)] deferRendering closeGuard drag reorder

Declarative tabs

Projected <oge-tab> children carry their content. selectedIndex is a two-way model; a user gesture first fires the cancelable selectionChanging, then selectionChanged. Disabled tabs are skipped by clicks and arrow keys.

two-way selection cancelable selectionChanging

Project overview — selected index: 0

Data-driven items

The items array drives the strip; selectedKey selects by identity, so it survives reordering and insertions. badge renders a counter, dirty the unsaved-changes dot (announced to screen readers). A component-level ogeTabContentTemplate renders every item's panel.

items [(selectedKey)] badge dirty

Editing README.md — selectedKey: readme

Lazy rendering & keep-alive

With deferRendering (default) a lazy ogeTabContentTemplate is instantiated on first visit; keepAlive (default) then keeps it mounted while hidden — note the creation time does not change when you come back. Toggle keep-alive off and the content is recreated on every visit.

deferRendering keepAlive
created at 9:37:21 AM

Closable tabs & async close guard

Closing runs a pipeline: cancelable tabClosing → the tab's async closeGuard (the ✕ shows a pending spinner, extra clicks are ignored) → tabClosed, where the app removes the tab — focus hands off per the APG (following tab, else preceding). The guarded tab here asks for a second click within 3 seconds. Delete/Backspace on a focused tab closes it too.

closable async closeGuard Delete key

a.ts content…

Overflow: arrows & all-tabs menu

When the strip overflows, showNavButtons: 'auto' reveals scroll arrows (RTL-aware) and the selected tab is kept in view. showTabListButton adds an all-tabs menu — the active tab is checked, disabled tabs stay disabled.

showNavButtons showTabListButton

selected: Chapter 1

Drag reorder

Drag a header to reorder — a drop indicator marks the target, Escape cancels the drag, and the selection follows the moved tab. tabReordering is cancelable; tabReordered reports the committed move. Give tabs a key for stable identity.

allowTabReordering tabReordered

To do stage…

Positions & styling

tabsPosition accepts logical top / bottom / start / end — vertical strips switch the arrow keys to Up/Down and RTL flips start/end for free. stylingMode='secondary' renders soft pills, size controls density.

tabsPosition stylingMode size

General project settings…

Alignment, indicator & empty state

tabAlignment distributes the tabs while they fit — justify spreads them to the edges, stretch gives each an equal share (the reference stretchTabs / tabAlignment options). indicatorFit='content' shrinks the selected-tab underline to the label (the reference fitInkBarToContent). With no visible tabs the strip renders messages.noData.

tabAlignment indicatorFit empty state

Empty strip:

No tabs to display

Panel transitions

panelAnimation fades or slides the incoming panel — slide enters from the direction of travel and mirrors itself under RTL. dynamicHeight animates the content box between panel heights instead of letting the page jump, tracking async content with a ResizeObserver. Duration is the --oge-tab-panel-transition variable rather than an input, and both are suppressed under prefers-reduced-motion.

panelAnimation dynamicHeight reduced-motion

One line of content.