コンテンツにスキップ

タブ

hc-tabs は、同じクラス名と見た目のもとに 2 つのマークアップ パターンを提供します:

パターン 使いどころ マークアップ JS 必要?
アプリ状態 単一ページが URL を変えずにパネルを切り替える。 <div role="tablist"> + <button role="tab"> + <div role="tabpanel"> はい — installTabs() がロービングタブインデックス、矢印キー、パネル切り替えを配線。
URL ルーティング 各タブが独自の URL またはクエリパラメータ。 <nav> + aria-current="page" つきの <a href> いいえ — ブラウザネイティブのリンクセマンティクスがキーボードとフォーカスをすでに処理。

アプリ状態パターンは WAI-ARIA APG のタブパターンに 従い、デフォルトで手動アクティベーションを使います — パネルが htmx で遅延読み込みされるとき、矢印キーのたびにリクエストが飛ばない ようにするためです。

別名: タブ切り替え、タブバー。

General account settings.

import { installTabs } from '@hypermedia-components/core';
installTabs();

installTabs() は冪等で、アンインストーラを返します。ゼロ設定の @hypermedia-components/core/behaviors エントリが自動インストールし、 htmx でスワップされた内容も自動で拾います。

パネルがアクティブになると、ビヘイビアはそのパネル上でバブリングする hc:tabactivated イベントを発行します(detail ペイロードは ありません — イベントターゲット自体がパネルです)。タブ切り替えへの 反応に使えます。例えばパネル内容の遅延読み込みなど (htmx での利用を参照)。

デフォルトでビヘイビアは手動アクティベーションを使います — 矢印キーはフォーカスを動かし、ユーザーは Enter または Space で 確定します。パネルが遅延読み込みされる(htmx、 IntersectionObserver)場合、フォーカスだけではリクエストが発火しない これが正しいデフォルトです。

タブがフォーカスを受けた瞬間にパネルをアクティブにするには、 data-activation="automatic" でオプトインします:

<div class="hc-tabs" data-activation="automatic">…</div>
Day view.
data-variant スタイル
default アクティブタブに下線のインジケーター。
pill アクティブタブに塗りつぶし背景。リストはベースラインを落とします。

data-size は sm、md(デフォルト)、lg を受け付けます。 デフォルトサイズは data-density にも従うため、密なレイアウトは タブごとの作業なしで縮みます。

<div class="hc-tabs" data-size="sm">…</div>

オーバーフロー(スクロール可能)

Section titled “オーバーフロー(スクロール可能)”

デフォルトでは、タブリストは余地がなくなると 2 行目へ折り返し ます。代わりに横スクロール可能な 1 行に保つには data-overflow="scroll" を足します:

<div class="hc-tabs" data-overflow="scroll">
<div class="hc-tabs__list" role="tablist" aria-label="Sections">
<button type="button" class="hc-tabs__tab" role="tab" …>Overview</button>
<!-- …many tabs… -->
</div>
<!-- panels -->
</div>

残りは installTabs() がやります:

  • その方向にまだスクロールできるときだけ現れる端のスクロール ボタン(マウス向けの操作部)を注入します。タブ順の外にあります — キーボードユーザーは矢印キーに頼り、それがすでにスクロールします。
  • アクティブなタブとフォーカス中のタブは見える位置に保たれます: 矢印キーのナビゲーション、アクティベーション、初期選択タブのいずれも 見える行の中へスクロールします。
  • タッチ / トラックパッドのスクロールはネイティブに動きます。 スクロールバーは隠れます。
  • 方向対応です — RTL ではボタンが反対の端へ移り、シェブロンが ミラーされます。

スクロールボタンは装飾(aria-hidden)なので、アクセシブルなタブ パターンへの純粋な追加です。

ルートに data-orientation="vertical" を足すと、タブリストがパネルの 横に列として立ちます。installTabs() はこれを tablist の aria-orientation="vertical" に反映し、矢印キーの軸が切り替わり ます: ↑ / ↓ がタブ間を移動し (← / → の代わり)、Home / End とアクティベーションは変わりません。アクティブ インジケーターは下線から inline-start のバーに移ります(論理なので RTL で反転します)。

Overview panel.

installTabs() は aria-orientation を設定してくれますが、 JavaScript の実行前から向きが正しいよう、マークアップにも含めて ください。縦方向とスクロール可能なオーバーフロー行は別のレイアウト です — 組み合わせないでください。

各タブが独自のルートである場合は、ARIA のタブ role を落とし、 ブラウザにマークアップを普通のナビゲーションとして扱わせます。 aria-current="page" がアクティブなタブに印を付けます。 installTabs() はこの変種を無視します。

パネルが初めてアクティブになったときにコンテンツを取得するには、 hc:tabactivated — パネルが表示されたときにビヘイビアが発火する イベント — を待ち受けます。

<div class="hc-tabs__panel" role="tabpanel"
id="panel-billing" aria-labelledby="tab-billing"
tabindex="0" hidden="until-found"
data-hx-get="/account/billing"
data-hx-trigger="hc:tabactivated once"
data-hx-target="this"
data-hx-swap="innerHTML">
<span class="hc-spinner" aria-hidden="true"></span>
</div>

非アクティブなパネルは hidden="until-found" を運ぶため、ブラウザの Ctrl+F がその中を検索できます。ユーザーのクエリが隠れたパネル内の テキストに一致するとブラウザが beforematch を発火し、ビヘイビアが それを捕まえて所有タブへ自動で切り替え、一致箇所を見えるようにし ます。hidden="until-found"の ないブラウザは、この属性を素の hidden として扱います。

  • アプリ状態パターンは WAI-ARIA APG に従います。role: tablist、 tab、tabpanel。
  • 各タブは自身のパネルを指す aria-controls を、各パネルは自身の タブを指し返す aria-labelledby を必要とします。
  • アクティブなタブは aria-selected="true" と tabindex="0" を、 非アクティブなタブは aria-selected="false" と tabindex="-1" を 運びます(ロービングタブインデックスパターン)。無効なタブは aria-disabled="true" を使い、矢印ナビゲーションからスキップされ ます。
  • tablist にはアクセシブルな名前が必須です。[role="tablist"] 要素に aria-label または aria-labelledby を使ってください。
  • 縦の tablist は aria-orientation="vertical" を運びます (installTabs() が data-orientation から設定)。矢印キーの軸は それに従います — 縦なら ↑ / ↓、横なら ← / →。
  • パネルはプログラム的にフォーカス可能(tabindex="0")なので、 キーボードユーザーは Tab でコンテンツへ直接入れます。
  • URL ルーティング変種は、ナビゲーションのアクセシビリティをすべて <nav> + <a> から継承します。そこに ARIA のタブ role を足さないで ください — ナビゲーションパターンと衝突します。

component トークン(component.tokens.json):

トークンパス 用途
tabs.list.gap / list.border タブ間のギャップと下枠線。
tabs.tab.height / padding-x --hc-control-*(密度)を継承。
tabs.tab.fg / hover-fg / hover-bg / active-fg タブのテキストとホバー面。
tabs.tab.indicator / indicator-size アクティブ状態の下線。
tabs.tab.disabled-fg aria-disabled="true" 時のタブテキスト。
tabs.pill.active-bg / active-fg data-variant="pill" の塗り背景。
tabs.panel.padding-y / focus-ring パネルの余白とフォーカスアウトライン。
tabs.sm.* / tabs.lg.* 専用の sm / lg 上書き。
生成される CSS 変数を表示
--hc-tabs-list-gap | -list-border
--hc-tabs-tab-height | -padding-x | -font-size | -font-weight | -radius
--hc-tabs-tab-fg | -hover-fg | -hover-bg | -active-fg
--hc-tabs-tab-indicator | -indicator-size
--hc-tabs-tab-disabled-fg
--hc-tabs-pill-active-bg | -pill-active-fg
--hc-tabs-panel-padding-y | -panel-focus-ring
--hc-tabs-scroll-size /* width of the overflow scroll buttons; default 2rem */
--hc-tabs-sm-tab-height | -sm-tab-padding-x | -sm-tab-font-size
--hc-tabs-lg-tab-height | -lg-tab-padding-x | -lg-tab-font-size
--hc-control-height (inherited from data-density)
--hc-color-action-primary-bg | -primary-fg (inherited from data-color)
--hc-color-focus-ring
  • ダイアログ — 表示され続けるべきでないコンテンツのグルーピングのためのモーダルな代替。
  • ポップオーバー — 一時的な兄弟。
  • ツールバー — 水平のアクションクラスター(タブとよく組み合う)。

レシピでの利用: レイジーパネル