webdevpuneet.com

Animated Tabs — Free HTML CSS JS Sliding Pill Snippet

Animated Tabs with Indicator · Navigation · Plain HTML, CSS & JS · Live preview

CategoryNavigation

Copy & paste

Source Code

<div class="demo">
  <div class="tabs" id="tabs">
    <button class="tab active" onclick="switchTab(this,0)">Overview</button>
    <button class="tab" onclick="switchTab(this,1)">Features</button>
    <button class="tab" onclick="switchTab(this,2)">Pricing</button>
    <button class="tab" onclick="switchTab(this,3)">Docs</button>
    <div class="indicator" id="indicator"></div>
  </div>
  <div class="panels">
    <div class="panel active"><h3>Product Overview</h3><p>A high-level summary of what the product does and who it's for. Start here if you're new.</p></div>
    <div class="panel"><h3>Key Features</h3><p>Deep dive into individual features — integrations, automations, custom workflows, and API access.</p></div>
    <div class="panel"><h3>Pricing Plans</h3><p>Compare Starter, Pro, and Team plans. All include a 14-day free trial with no credit card required.</p></div>
    <div class="panel"><h3>Documentation</h3><p>Full API reference, SDK guides, tutorials, and example projects to get you up and running fast.</p></div>
  </div>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

Sliding pill indicator via indicator.style.left = btn.offsetLeft + "px"
indicator.style.width = btn.offsetWidth + "px" — adapts to tab label length
CSS transition: left 0.2s, width 0.2s on .indicator — smooth pill slide
Panel .active class switch with opacity + transform fade-in transition
Index-based tab-to-panel mapping via querySelectorAll and numeric idx
Pill background on dark tab container: #e2e8f0 container, white pill
Tabs use flex: 1 for equal-width distribution
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Animated Tabs — Sliding Pill via offsetLeft/offsetWidth & Panel Active Class

Screenshot of the Animated Tabs with Indicator snippet rendered live

Animated tabs replace the basic underline indicator of the standard tab bar with a sliding pill background that smoothly glides between tabs when clicked. The pill communicates transition direction and gives the interface a premium, physically responsive quality — for stacked tabs, see the vertical tabs; for a two-option switch, the segmented control.

The sliding indicator

A .indicator div sits inside the .tabs container with position: absolute; transition: left 0.2s, width 0.2s. moveIndicator(btn) reads the clicked tab's offsetLeft and offsetWidth — the tab's pixel position and size relative to the container — and assigns them to indicator.style.left and indicator.style.width. The CSS transition animates the pill to the new position. This correctly handles tabs of different widths.

Why offsetLeft/offsetWidth instead of a class

The indicator's position is dynamic — it depends on the exact pixel dimensions of each tab. A CSS class can't encode runtime pixel values. Using offsetLeft and offsetWidth reads the actual rendered size, so tabs with longer labels get a wider pill automatically.

Panel switching

switchTab(btn, idx) removes .active from all tabs and panels, then adds .active to the clicked tab and the panel at index idx. The panel uses a opacity + transform transition on .panel.active for a fade-in effect. The idx parameter maps each tab button to its corresponding panel by DOM order.

Adding tabs

Add a new .tab button and a matching .panel div. The JS uses querySelectorAll and numeric index — the order in the HTML determines the mapping.

The offsetLeft/offsetWidth technique

The pill indicator reads the active tab button's actual pixel position using offsetLeft and offsetWidth — values that the browser computes after layout. Setting pill.style.left = btn.offsetLeft + 'px' and pill.style.width = btn.offsetWidth + 'px' positions and sizes the pill to exactly match the active tab. CSS transition: left 0.2s, width 0.2s animates between positions. This approach handles any tab label length, any font size, and any number of tabs automatically — no hardcoded positions needed.

Panel switching

Each tab button has a data-panel attribute matching a panel ID. Clicking a tab: (1) removes .active from all tab buttons, (2) adds .active to the clicked button, (3) hides all panels, (4) shows the matching panel. The panel switch is instant (no animation) while the pill indicator slides smoothly — this matches the expected tab behaviour where content switches immediately but the indicator communicates which tab was clicked.

Accessible implementation

Add role="tablist" to the tab container, role="tab" to each button, and role="tabpanel" to each panel. Add aria-selected="true/false" to tab buttons and aria-hidden="true/false" to panels. Add aria-controls on each tab pointing to its panel ID, and aria-labelledby on each panel pointing to its tab ID.

Step by step

How to Use

  1. 1
    Click each tabClick Design, Development, and Marketing tabs to see the pill indicator slide smoothly between them and the panel content switch.
  2. 2
    Update tab labels and panel contentIn the HTML panel, change the tab button text and each .panel div content.
  3. 3
    Add a fourth tabAdd a .tab button and a matching .panel div. The JS uses index order automatically.
  4. 4
    Change animation speedUpdate 0.2s on transition: left, width in the CSS .indicator rule.
  5. 5
    Change indicator colourUpdate background: #fff on .indicator to any colour.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Product detail tabbed navigation
A more polished version of the Tab Bar snippet. The sliding pill gives clear visual feedback about which tab is active and the direction of navigation.
Settings and preference panels
Organise settings into categories with animated tabs. The sliding indicator makes the active section unmistakable even in complex UIs.
Learn offsetLeft and offsetWidth
The sliding indicator reads the tab element's actual pixel position and size. Edit the indicator move function to understand how DOM geometry APIs work.
Dashboard section switcher
Use animated tabs to switch between Overview, Analytics, and Reports sections. The premium slide animation signals a polished dashboard product.
Onboarding step switcher
Use as a step indicator in an onboarding flow. The sliding pill communicates progress through sections more clearly than a simple underline.
Drop-in replacement for basic tabs
The HTML structure is identical to the Tab Bar snippet. Swap in the animated indicator by replacing the simple border-bottom with the sliding pill CSS and JS.
Related: Breadcrumb Trail with Collapsing Middle Items
See the Breadcrumb Trail with Collapsing Middle Items for a related navigation pattern worth pairing with this one.
Related: Icon Rail with Flyout Submenu
See the Icon Rail with Flyout Submenu for a related navigation pattern worth pairing with this one.
Related: Drill-Down Settings Navigation
See the Drill-Down Settings Navigation for a related navigation pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

moveIndicator(btn) reads btn.offsetLeft (the tab's left pixel position relative to .tabs) and btn.offsetWidth (the tab's width). It sets indicator.style.left and indicator.style.width to these values. CSS transition: left 0.2s, width 0.2s animates between positions.

The indicator position needs to match the exact pixel location and size of each tab — values that depend on the rendered tab label length. CSS classes cannot encode runtime pixel values. offsetLeft and offsetWidth read the actual DOM geometry.

Add a <button class="tab" onclick="switchTab(this, N)">Label</button> where N is the zero-based panel index. Add a matching <div class="panel">content</div>. The querySelectorAll picks up new tabs automatically.

Call switchTab(document.querySelectorAll(".tab")[N], N) after the JS code to open the Nth tab on page load.

Yes. Click "JSX" for a React component. In React, manage activeTab in useState. Use useRef on the indicator element and read the active tab button's offsetLeft/offsetWidth in a useEffect after state changes to update the indicator position.

The Tab Bar uses a CSS border-bottom underline indicator. Animated Tabs uses a sliding pill background. The pill is more visually prominent and communicates transition direction. The panel switching logic is similar in both.

Yes. While the Animated Tabs with Indicator snippet gives you one pattern to copy, the free Responsive Navbar Builder at webdevpuneet.com/navbar-builder/ designs a whole navigation bar: logo, links and dropdown menus, a mobile hamburger menu (dropdown panel, slide-in drawer or full screen) and a scroll effect (sticky, shrink, hide on scroll or transparent). It has six presets, a live preview that runs the real code, and exports HTML + CSS + vanilla JS, React, Tailwind, Vue 3 or Angular — or you can click Fork & Edit to keep editing it in My Code.