Navigation
Animated Tabs
Layout-spring sliding active pill indicator
detent-animated-tabs
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 2
- Wave
- phase-2-navigation
- Group
- Navigation
6.1 KB gzip for the class module · inlines the shared spring
Variants
Recorded states
activerecorded state; no setter
Usage
Import
import '/components/animated-tabs/define.js';
// or register it yourself
import { DetentAnimatedTabs } from '/components/animated-tabs/animated-tabs.js';
Attributes
active-tabdefault-tablayout-idrender-contenttabs-container-class-namevariant
Properties
tabsactiveTabdefaultTabvariantrenderContentlayoutIdonChange
Slots
No slots are recorded for this component.
Events
detent-change— { id: string }
Example
Runnable example: site/examples/animated-tabs.ts. The snippet below is generated from the catalogued attributes.
<detent-animated-tabs
active-tab
default-tab
layout-id
render-content
tabs-container-class-name
variant
></detent-animated-tabs>
Property-only values
const element = document.querySelector('detent-animated-tabs');
if (element !== null) {
// tabs: TabItem[]
element.tabs = [];
// onChange: (id: string) => void
element.onChange = () => {};
}
Source
The pinned upstream source for this row:
src/components/ui/AnimatedTabs.tsxsha256 53fb159bd6e4…
Native files
The native module and its artifacts:
src/components/animated-tabs/animated-tabs.tssrc/components/animated-tabs/styles.csssrc/components/animated-tabs/define.tsdocs/components/animated-tabs.mdsite/examples/animated-tabs.tstests/browser/animated-tabs.spec.tsevidence/components/animated-tabs.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
active-tab | — | — | — | Recorded in the catalog as an attribute. |
default-tab | defaultTab | string | tabs[0].id | Kebab-case attribute; camelCase property. |
layout-id | — | — | — | Recorded in the catalog as an attribute. |
render-content | — | — | — | Recorded in the catalog as an attribute. |
tabs-container-class-name | — | — | — | Recorded in the catalog as an attribute. |
variant | — | — | — | Recorded in the catalog as an attribute. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
tabs | TabItem[] | [] | Property-only: assign from script; not accepted as an attribute. |
activeTab | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
defaultTab | string | tabs[0].id | Property-only: assign from script; not accepted as an attribute. |
variant | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
renderContent | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
layoutId | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
onChange | (id: string) => void | undefined | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-change | onChange | (id: string) => void | Composed, bubbles; fires only for a user action. |
Slots
No slots are recorded for this component.
CSS customisation
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- `layoutId` no longer joins a Framer Motion shared layout - observable as `data-layout-id` on `part="tab-indicator"`, motion scoped to the element's own tablist.
- `tabsContainerClassName` cannot cross the shadow boundary, so its tokens become extra CSS part names on `part="tablist"`; `className` maps to `part="root"`.
- `TabItem.icon`/`content` were React nodes and are text (no slot invented, no HTML parsing).
- Arrow-key selection added: the row records tablist/tab/tabpanel roles plus keyboard arrow navigation as obligations, upstream had plain buttons.
- `render-content` resolves true, is reflected while connected, and the property is the opt-out (PC-3.2), so `render-content="false"` is still true.
- The panel cross-fades in with springGentle and AnimatePresence exit-then-enter is dropped (a stale labelled panel would otherwise remain); the indicator travels on two axes with springMorph (220/32/1) via the shared spring helper.
What it does
A tablist with a sliding indicator and cross-faded panels; the indicator follows the selected tab with a short spring.
Upstream behaviour
- Shared layout active pill with spring easing
- Independent content animation cross-fade
- Badge count support for notifications
Differences from upstream
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- `layoutId` no longer joins a Framer Motion shared layout - observable as `data-layout-id` on `part="tab-indicator"`, motion scoped to the element's own tablist.
- `tabsContainerClassName` cannot cross the shadow boundary, so its tokens become extra CSS part names on `part="tablist"`; `className` maps to `part="root"`.
- `TabItem.icon`/`content` were React nodes and are text (no slot invented, no HTML parsing).
- Arrow-key selection added: the row records tablist/tab/tabpanel roles plus keyboard arrow navigation as obligations, upstream had plain buttons.
- `render-content` resolves true, is reflected while connected, and the property is the opt-out (PC-3.2), so `render-content="false"` is still true.
- The panel cross-fades in with springGentle and AnimatePresence exit-then-enter is dropped (a stale labelled panel would otherwise remain); the indicator travels on two axes with springMorph (220/32/1) via the shared spring helper.