From a CSS-only segmented control to sliding content panels with animated indicators.
Tabs use a visible indicator that moves between options while content changes underneath. This walkthrough builds that pattern from plain CSS to full sliding content panels.
The simplest approach uses <input type="radio"> elements hidden with sr-only, wrapped in <label> tags. The selection pill is a regular div that slides via translateX with a CSS transition-transform. No JavaScript animation library needed.
Real radio inputs give you keyboard navigation, focus management, and screen-reader announcements for free. With buttons, you'd have to manually implement role="radiogroup", aria-checked, and arrow key handlers.
Loading...
Replace the CSS pill with a motion.div that has layoutId="pill". When the active tab changes, the old pill unmounts and a new one mounts, but because they share the same layoutId, framer-motion animates the pill between the two positions. No manual coordinate math, no calc() widths.
The pill is absolutely positioned behind the label text, so it slides underneath the content. The text color switches between white (active) and foreground (inactive).
Loading...
Add content panels that slide horizontally alongside the tab indicator. The panels live in a grid with auto-cols-[100%] and grid-flow-col, which lays them out in a row. Switching tabs translates the grid by -${activeTab * 100}%.
Inactive panels get desaturated and slightly blurred, reinforcing which panel is active. The MotionConfig wrapper sets a consistent spring transition for all motion in the component.
Browse your feed and updates
Selected projects and case studies
Interactive UI experiments
Loading...
Tune the tab count and spring physics.
Browse your feed and updates
Selected projects and case studies
Interactive UI experiments
Loading...