8pt

Animated Tabs

Animated Tabs•Navigation
Overview

The indicator follows the active tab with transform translate and scale; panels fade in and rise.

Plain HTML, CSS and a little vanilla JS, with no framework dependency. Copy the prompt into Claude Code, Cursor, Trae or Codex and the model adds Animated Tabs to your project in your colors.

Where to use it

Site headers, tab switches, segmented controls, side navigation and footers.

Install

With Builder, install it with the 8pt CLI or call it directly from an editor connected to 8pt MCP.

$ npm i -g @8pt-dev/cli && 8pt add animated-tabs
mcp › Use 8pt MCP to call get_component("animated-tabs")

Code

Source for Animated Tabs. Motion uses only transform and opacity and respects the Reduce motion setting.

<div class="x-tabs">
  <div class="x-tabs__list" role="tablist">
    <button role="tab" aria-selected="true">Overview</button>
    <button role="tab">Components</button>
    <button role="tab">Skill</button>
    <button role="tab">MCP</button>
    <span class="x-tabs__ink" aria-hidden="true"></span>
  </div>
  <div class="x-tabs__panels">
    <div class="on"><b>Overview</b><p>Two shelves: UI and Skills.</p></div>
    <div><b>Components</b><p>Native components that run as-is.</p></div>
    <div><b>Skill</b><p>Reusable end-to-end workflows.</p></div>
…
.x-tabs{width:min(400px,100%);color:var(--d-fg,#fff)}
.x-tabs__list{position:relative;display:flex;gap:4px;border-bottom:1px solid var(--d-line,rgba(255,255,255,.1))}
.x-tabs__list button{padding:12px 14px;border:0;background:transparent;color:var(--d-muted,#a0a0ab);font:600 14px/1 var(--d-font,system-ui,sans-serif);cursor:pointer;transition:color .25s}
.x-tabs__list button[aria-selected=true]{color:var(--d-fg,#fff)}
.x-tabs__ink{position:absolute;left:0;bottom:-1px;width:100px;height:2px;border-radius:2px;background:var(--d-accent,#4D74FF);transform-origin:left;transition:transform .45s cubic-bezier(.2,.9,.2,1)}
.x-tabs__panels{display:grid;padding-top:18px}
.x-tabs__panels>div{grid-area:1/1;opacity:0;transform:translateY(8px);transition:opacity .3s,transform .35s cubic-bezier(.2,.9,.2,1);pointer-events:none}
.x-tabs__panels>div.on{opacity:1;transform:none;pointer-events:auto}
.x-tabs__panels b{font:600 16px/1.3 var(--d-font,system-ui,sans-serif)}
.x-tabs__panels p{margin:6px 0 0;font:14px/1.6 var(--d-font,system-ui,sans-serif);color:var(--d-muted,#a0a0ab)}
const init = (root) => {
  const list = root.querySelector('.x-tabs__list'), tabs = [...list.querySelectorAll('button')];
  const ink = list.querySelector('.x-tabs__ink'), panels = root.querySelectorAll('.x-tabs__panels > div');
  const go = (i) => {
    const t = tabs[i];
    ink.style.transform = `translateX(${t.offsetLeft}px) scaleX(${t.offsetWidth / 100})`;
    tabs.forEach((x, j) => x.setAttribute('aria-selected', String(i === j)));
    panels.forEach((p, j) => p.classList.toggle('on', i === j));
  };
  tabs.forEach((t, i) => t.addEventListener('click', () => go(i)));
  const ro = new ResizeObserver(() => go(Math.max(0, tabs.findIndex(t => t.getAttribute('aria-selected') === 'true'))));
  ro.observe(list);
…
Subscribe to Builder to see the full source.See pricing

More navigation components

View all

Segmented Control

Stepper

More components

View all

AI Prompt Composer

Film Strip Carousel

Auto Tab Showcase

Media Bento Grid

Agent Chat Window

Model Pill Marquee

Director Art Cards

Mac File Window

Keep exploring