anchor-name でアニメーションするタブインジケーター
アクティブなタブに anchor-name を付け、CSS トランジションで単一の下線インジケーターをアクティブタブの下にスライドさせる。
詳細な説明
パターン
よくあるデザインパターン:上部にタブが並び、現在アクティブなタブの下に単一の水平アンダーラインがスライドする。アンカーポジショニング以前は、JavaScriptでタブ位置を計測するか、タブごとに別アンダーラインを用意するかの二択でした。アンカーポジショニングなら、anchor-name を持つタブに単一のアンダーラインが追従できます。
<nav class="tabs">
<button class="tab" data-active>概要</button>
<button class="tab">アクティビティ</button>
<button class="tab">設定</button>
<span class="indicator"></span>
</nav>
/* アクティブなタブだけが anchor-name を持つ */
.tab[data-active] {
anchor-name: --active-tab;
}
.indicator {
position: absolute;
position-anchor: --active-tab;
/* アンダーライン:タブの全幅、タブ下端にぴったり */
left: anchor(--active-tab left);
right: anchor(--active-tab right);
top: anchor(--active-tab bottom);
height: 2px;
background: var(--primary);
transition:
left 200ms ease,
right 200ms ease,
top 200ms ease;
}
なぜ position-area ではなく anchor()?
position-area: bottom だとインジケーターはタブの下に別ボックスとして配置されますが、ここではインジケーターを タブの左右端と正確に同じ幅 にしたいのです。anchor() 関数なら辺ごとに制御できます。left: anchor(--active-tab left) と right: anchor(--active-tab right) でインジケーターがアクティブタブの全幅に自動的に広がります。
JavaScriptでアンカー名を移動
トランジションはインジケーターの解決位置が変わったときだけアニメーションします。タブからタブへ移動するには、どのタブが data-active 属性を持つかを変更します:
function activate(tab) {
document.querySelectorAll('.tab[data-active]')
.forEach(t => t.removeAttribute('data-active'));
tab.setAttribute('data-active', '');
}
data-active が動くと anchor-name: --active-tab も一緒に動き、インジケーターの解決位置が変わり、CSSトランジションがアニメーションを処理します。
単一の真実のソース
このパターンの良いところは、タブの数に関わらずDOMにインジケーター要素が1つだけ存在することです。タブごとアプローチ(タブごとに1つのアンダーライン、アクティブなものだけ表示)と比べて、タブ数に対して線形にスケールせず、追加のopacity/visibilityロジックも不要です。
ユースケース
アプリバー・設定パネル・ダッシュボードのタブナビゲーション。長いページのセクションナビゲーション(インジケーターが現在表示中のセクションに追従)。ネストしたルート階層のサブナビゲーション。アクティブ状態を持つフィルターチップ。