How to create a sliding underline tabs スライド下線タブの作り方
A minimal tab navigation where the underline indicator glides to the active item. アクティブタブに合わせて下線インジケーターが滑らかに移動するシンプルなタブナビゲーション。
ライブデモ Live Demo
概要・用途・特徴Overview, Usage & Features
何ができるかWhat it does
A minimal tab navigation where the underline indicator glides to the active item.
アクティブタブに合わせて下線インジケーターが滑らかに移動するシンプルなタブナビゲーション。
どこで使うかWhere to use
website header, dashboard sidebar, mobile menu, admin panel
コンテンツタブ、ダッシュボードビュー切り替え、フィルターバー、設定セクション
特徴Key features
Animated sliding underline indicator that moves between tab buttons using CSS transform: translateX. No layout reflow on tab switch. Supports keyboard arrow navigation. Active state tracked via JavaScript.
CSS transform:translateXでタブボタン間を移動するアニメーションするスライディングアンダーラインインジケーター。タブ切り替え時のレイアウトリフローなし。キーボード矢印ナビゲーションをサポート。JavaScriptでアクティブ状態を追跡。
調整可能パラメータ Adjustable Parameters
| Parameter | Default | Description |
|---|
実装コード Implementation Code
// 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;
}
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;
if (!container) return;
const indicator = container.querySelector(".tabs__indicator");
const buttons = container.querySelectorAll("button");
const target = buttons[activeIndex];
if (!indicator || !target) return;
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>
);
}
.tabs {
position: relative;
display: inline-flex;
gap: 18px;
}
.tabs button {
border: none;
background: transparent;
font-weight: 600;
padding: 8px 0;
color: #4b5178;
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;
}
仕組みとカスタマイズHow It Works & Customization
仕組みHow it works
A single absolutely-positioned indicator element slides beneath the active tab. On click, JavaScript reads the active button's offsetLeft and offsetWidth, then sets CSS custom properties (--indicator-left, --indicator-width) that drive the indicator's transform and width via transition.
絶対配置された単一インジケーター要素がアクティブタブの下をスライド。クリック時にJavaScriptがアクティブボタンのoffsetLeftとoffsetWidthを読み取り、CSSカスタムプロパティ(--indicator-left、--indicator-width)を設定してインジケーターのtransformとwidthをtransitionで制御。
カスタマイズ方法Customization
Change indicator height and border-radius for a pill vs line style. Add a background highlight on the active tab button alongside the underline. Use ResizeObserver to recalculate indicator position when the container width changes.
インジケーターの高さとborder-radiusでピル vs ライン スタイルに変更。アンダーラインと一緒にアクティブタブボタンに背景ハイライトを追加。コンテナ幅変更時にResizeObserverでインジケーター位置を再計算。
注意点Caveats
Use role="tablist", role="tab", aria-selected, and aria-controls to make tab navigation accessible. Associate each tab with its panel via aria-controls and manage focus so arrow keys move between tabs without activating them until Enter/Space.
タブナビゲーションをアクセシブルにするためrole="tablist"、role="tab"、aria-selected、aria-controlsを使用してください。aria-controlsで各タブをパネルに関連付け、矢印キーがEnter/Spaceまでタブを選択せずに移動できるようフォーカスを管理してください。
よくある質問 Frequently Asked Questions
How to customize the sliding underline tabs? Sliding Underline Tabsをカスタマイズするには?
Modify the CSS custom properties and class styles defined in the code section. Key adjustable values include colors, sizes, durations, and spacing. See the Adjustable Parameters section for specific variables.
コードセクションで定義されているCSSカスタムプロパティとクラススタイルを変更してください。色、サイズ、時間、間隔が主な調整可能値です。具体的な変数は調整可能パラメータセクションを参照してください。
How to use the sliding underline tabs in React? ReactでSliding Underline Tabsを使うには?
Import the provided React component and its CSS file. The component accepts props for customization. Check the React code section for the full implementation and available props.
提供されているReactコンポーネントとCSSファイルをインポートしてください。コンポーネントのpropsでカスタマイズできます。完全な実装と利用可能なpropsはReactコードセクションを参照してください。
What are the performance implications of sliding underline tabs? Sliding Underline Tabsのパフォーマンスへの影響は?
This implementation uses CSS transforms and opacity for animations, which are GPU-accelerated. It's lightweight and doesn't cause layout thrashing. Consider using prefers-reduced-motion for accessibility.
この実装はCSSのtransformとopacityを使用しており、GPUアクセラレーションされます。軽量でレイアウトスラッシングを引き起こしません。アクセシビリティのためにprefers-reduced-motionの使用を検討してください。
AIへの指示テンプレート AI Prompt Template
以下をAIに貼り付けると実装を依頼できます。 Paste the following into your AI assistant to request implementation.