::scroll-marker と scroll-marker-group で、スクロールスナップのカルーセルに JS なしのタブを付ける
カルーセルに scroll-marker-group を指定すると、各スライドの ::scroll-marker がまとめてタブ列として描画され、クリックでそのスライドへスクロール・表示中のタブには :target-current が当たる。
どんな時に使う?
- 横スクロールのカルーセルに「タブ」や「ドット」のナビを付けたいが、クリック処理や現在位置の追跡を JS で書きたくない時
- scroll-snap で JS なしの「カード単位で止まる」横スクロールスライダー を、ラベル付きのタブ切り替え UI に発展させたい時
コード
html
<ul class="carousel">
<li data-tab="概要">…</li>
<li data-tab="仕様">…</li>
<li data-tab="レビュー">…</li>
</ul>css
.carousel {
display: flex;
overflow-x: auto;
scroll-snap-type: x mandatory;
/* マーカーの並び(グループ)をスクロール領域の前に置く。tabs はタブリストとして扱わせる指定 */
scroll-marker-group: before tabs;
}
.carousel > li {
flex: 0 0 100%;
scroll-snap-align: center;
}
/* マーカーが並ぶ入れ物。普通の flex コンテナとして見た目を作れる */
.carousel::scroll-marker-group {
display: flex;
gap: 8px;
height: 48px;
}
/* スライドごとに1つ生成されるマーカー。ラベルは data 属性から取る */
.carousel > li::scroll-marker {
content: attr(data-tab);
padding-inline: 12px;
border-radius: 999px;
}
/* いま表示中のスライドに対応するマーカー */
.carousel > li::scroll-marker:target-current {
background: black;
color: white;
}- マーカーをクリックすると対応するスライドまでスクロールし、スクロールやスワイプで表示中のスライドが変わると
:target-currentも自動で移る。現在地の管理を書く必要がない。 - ラベルを出さずにドット表示にしたいなら、
content: ""にして幅・高さ・丸みを付ければよい。 :target-currentは目次のハイライトと同じ仕組み(:target-current で目次の「いま読んでいる項目」をCSSだけでハイライト)。目次ではリンクがマーカーになり、こちらでは疑似要素がマーカーになる。
注意点・対応状況
::scroll-marker/::scroll-marker-group/scroll-marker-groupは Chromium 系の「CSS カルーセル」機能で、Firefox・Safari は未対応(2026-10 時点、Can I use で要確認)。未対応ブラウザではタブが出ないだけで、スクロールスナップのスライダーとしては動くので、プログレッシブエンハンスメント向き。before tabsのような「位置+モード」の書き方はbefore/afterだけの書き方より新しい。モード指定を解釈できないブラウザでは宣言ごと無視されるので、scroll-marker-group: before;を先に書いてからbefore tabsで上書きしておくと安全。- ラベルを
attr()で疑似要素に入れる方式なので、翻訳ツールや一部の支援技術での読み上げはブラウザ実装に依存する。重要なラベルは実機のスクリーンリーダーで確認する。 - 元の投稿はコードの要点のみ。HTML 構造・
scroll-snap-align・ドット表示の案・フォールバックの書き方は補足として加えたもの。