Portfolio
  • 01Home
  • 02Projects
  • 03About
  • 04Skills
  • 05Lab
  • 06Writing
  • 07Contact

© 2026 Hamza Syrage

LabView all
Arrow FieldCursorCursor SpotlightCursorGooey CursorCursorImage CompareMediaInteractive ChartDataMagnetic ButtonInteractionMarquee RevealTypographyMorphing ShapeAnimationsNested ViewsOverlaysOdometer CounterDataParticle ConstellationCanvasRipple ClickButtonsScroll RevealScrollShuffle GridLayoutStep WizardNavigationTabsNavigationTilt CardCards

UI Experiments

All Lab Entries
Navigation

Tabs

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.

#Step 1: CSS-Only Pill

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.

tsx
Loading...

#Step 2: layoutId Pill

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).

Day
Week
Month
tsx
Loading...

#Step 3: Sliding Content

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.

Home

Browse your feed and updates

Work

Selected projects and case studies

Lab

Interactive UI experiments

Home
Work
Lab
tsx
Loading...

#Configuration

Tune the tab count and spring physics.

Home

Browse your feed and updates

Work

Selected projects and case studies

Lab

Interactive UI experiments

Home
Work
Lab
Parameters
Tabs3
Spring physics
Stiffness150
Damping15
Mass1
tsx
Loading...