Animated Tabs

Animated

Tabs whose active pill travels between triggers instead of cutting.

View Source

#Installation

npx @dinachi/cli@latest add animated-tabs

#Usage

tsx
import {
  AnimatedTabs,
  AnimatedTabsList,
  AnimatedTabsTrigger,
  AnimatedTabsContent,
} from "@/components/ui/animated-tabs"
tsx
<AnimatedTabs defaultValue="overview">
  <AnimatedTabsList>
    <AnimatedTabsTrigger value="overview">Overview</AnimatedTabsTrigger>
    <AnimatedTabsTrigger value="activity">Activity</AnimatedTabsTrigger>
  </AnimatedTabsList>
  <AnimatedTabsContent value="overview">Overview panel</AnimatedTabsContent>
  <AnimatedTabsContent value="activity">Activity panel</AnimatedTabsContent>
</AnimatedTabs>

#Examples

#When to use it

The travel carries the eye from the old selection to the new one, so it earns its place on a small, stable strip and loses it on a long one.

Use it forNot for
A settings pane, a profile, a chart's time range.A strip long enough to scroll. The pill will travel somewhere off screen.
Three to five tabs.A dozen entries. The further the pill goes, the more it reads as a delay.

Reach for the core Tabs in the cases on the right.

#Styling

  • The pill takes its own class. indicatorClassName themes it without touching the tab, so a coloured pill does not mean restyling the label too.
  • The selected tab is data-[active], the attribute Base UI sets. Recolour the label with data-[active]:text-primary-foreground when the pill is dark.
  • Labels can be any width. The pill is measured, not calculated, so a strip of uneven labels needs no offsets.

#Behaviour

  • Pointer changes travel, keyboard changes cut. Arrow keys repeat far more often than clicks, and animating them puts the pill between the keypress and the answer.
  • No overshoot. The spring is critically damped: a click carries no momentum, so a bounce on arrival is motion the gesture never asked for.
  • Reduced motion cuts too, matching the keyboard path.

#Accessibility

  • Roving focus and aria-controls come from Base UI's Tabs, not from this component.
  • Arrow keys move focus, Enter or Space selects. Pass activateOnFocus to select on arrow instead.
  • The pill is aria-hidden. A screen reader hears the selection once, from aria-selected, and never as a stray element.

#API Reference

PropTypeDefaultDescription
valueanySelected tab, controlled
defaultValueanyInitially selected tab, uncontrolled
onValueChange(value, details) => voidFired when the selection changes
activateOnFocusbooleanfalseSelect on arrow key rather than on Enter or Space
transitionTransitionspring, 0.3sOverrides the indicator spring
indicatorClassNamestringOn AnimatedTabsTrigger. Class applied to the travelling pill