ライブデモ Live Demo

ナビゲーションはシンプルな <button> 要素で構成され、.tabs__indicator スパンがアクティブなタブの下でスライドします。インジケーターのタイミングはカスタムプロパティで簡単に調整できます。

The navigation consists of plain <button> elements while a .tabs__indicator span slides under the active tab. Indicator timing is exposed as a custom property for easy tuning.

0.45s

AI向け説明 AI Description

ボタンと絶対位置のインジケーターを同一コンテナに配置します。ホバー/クリック時に`offsetLeft`/`offsetWidth`を計算し、`transform`と`width`を更新します。`is-active`を切り替えてテキスト色を同期させ、アニメーション継続時間は設定可能に保ちます。

Place buttons and an absolutely positioned indicator under the same container. On hover/click, compute `offsetLeft` / `offsetWidth` and update `transform` plus `width`. Toggling `is-active` keeps the text color in sync while the animation duration stays configurable.

調整可能パラメータ Adjustable Parameters

実装 Implementation

HTML + CSS + JS

<nav class="tabs" data-tabs>
  <button class="is-active">Overview</button>
  <button>Docs</button>
  <button>API</button>
  <span class="tabs__indicator"></span>
</nav>

<style>
.tabs {
  position: relative;
  display: inline-flex;
  gap: 18px;
}
.tabs button {
  border: none;
  background: transparent;
  font-weight: 600;
  padding: 8px 0;
  color: #4b5178;
}
.tabs button.is-active {
  color: #181d40;
}
.tabs__indicator {
  position: absolute;
  bottom: 0;
  height: 3px;
  border-radius: 999px;
  background: linear-gradient(90deg, #5c6ac4, #8c6ff7);
  transition: transform var(--indicator-duration, 0.45s) ease,
    width var(--indicator-duration, 0.45s) ease;
}
</style>

<script>
const tabs = document.querySelector("[data-tabs]");
const indicator = tabs.querySelector(".tabs__indicator");
const buttons = [...tabs.querySelectorAll("button")];

function moveIndicator(target) {
  indicator.style.transform = `translateX(${target.offsetLeft}px)`;
  indicator.style.width = `${target.offsetWidth}px`;
}

buttons.forEach((button) => {
  button.addEventListener("click", () => {
    buttons.forEach((b) => b.classList.remove("is-active"));
    button.classList.add("is-active");
    moveIndicator(button);
  });
  button.addEventListener("mouseenter", () => moveIndicator(button));
});

moveIndicator(buttons[0]);
</script>

React (JSX + CSS)

// react/N-001.jsx
import { useRef, useState, useEffect } from "react";
import "./N-001.css";

const tabs = ["Overview", "Docs", "API", "Changelog"];

export default function SlidingUnderlineTabs() {
  const containerRef = useRef(null);
  const [activeIndex, setActiveIndex] = useState(0);

  useEffect(() => {
    const container = containerRef.current;
    const indicator = container?.querySelector(".tabs__indicator");
    const buttons = container?.querySelectorAll("button");
    if (!indicator || !buttons?.[activeIndex]) return;
    const target = buttons[activeIndex];
    indicator.style.transform = `translateX(${target.offsetLeft}px)`;
    indicator.style.width = `${target.offsetWidth}px`;
  }, [activeIndex]);

  return (
    <div className="tabs" ref={containerRef}>
      {tabs.map((label, index) => (
        <button
          key={label}
          className={index === activeIndex ? "is-active" : ""}
          onMouseEnter={() => setActiveIndex(index)}
          onClick={() => setActiveIndex(index)}
        >
          {label}
        </button>
      ))}
      <span className="tabs__indicator" />
    </div>
  );
}
/* react/N-001.css */
.tabs {
  position: relative;
  display: inline-flex;
  gap: 18px;
}
.tabs button {
  border: none;
  background: transparent;
  font-weight: 600;
  color: #4b5178;
  padding: 8px 0;
  cursor: pointer;
}
.tabs button.is-active {
  color: #181d40;
}
.tabs__indicator {
  position: absolute;
  bottom: 0;
  height: 3px;
  border-radius: 999px;
  background: linear-gradient(90deg, #5c6ac4, #8c6ff7);
  transition: transform var(--indicator-duration, 0.45s) ease,
    width var(--indicator-duration, 0.45s) ease;
}

AIへの指示テンプレート AI Prompt Template

以下のテンプレートをコピーしてAIアシスタントに貼り付けると、このパターンの実装を依頼できます。 Copy the template below and paste it into your AI assistant to request an implementation of this pattern.

ノート & バリエーション Notes & Variations

スクロール可能なナビバーやレスポンシブなドロップダウンには、同じマークアップに overflow-x: auto を追加するか、ボタンを select オプションに変換してインジケーターロジックを再利用してください。

For scrollable nav bars or responsive dropdown fallbacks, keep the same markup and add overflow-x: auto or convert buttons into select options while reusing the indicator logic.