BLOG

【JS不要!】::scroll-button と ::scroll-markerを使った、CSSのみのカルーセル

Written by uchida

INDEX

※2025年12月時点で対応していないブラウザがございます。ご注意ください。

WEBデザインに頻出するカルーセルUIですが、ハンバーガーメニューやアコーディオンと比べて実装難易度が高く工数もかかるため、実装時には、SplideやSwiperなどのJavaScriptプラグインが使用されるケースが多いかと思われます。

プラグインを使用することによって、簡単にUIを再現できるメリットがありますが、アクセシビリティ対応の工数の確保、HTML構造の制限、プラグイン専用のオリジナルclass名の付与、専用スクリプト読み込みによるパフォーマンス低下などのデメリットも存在します。

しかし、Chrome 135から登場したCSSの擬似要素、::scroll-button()::scroll-marker()を使用することで、JavaScript無し、CSSのみでカルーセルUIを実現することができるようになります。

完成版

実装手順

手順1(HTML)

<div class="wrapper">
 <div class="carousel">
  <div class="carousel__slider" role="group" aria-label="CSSのみで実装したカルーセル">
   <div class="carousel__slide">1</div>
   <div class="carousel__slide">2</div>
   <div class="carousel__slide">3</div>
   <div class="carousel__slide">4</div>
   <div class="carousel__slide">5</div>
   <div class="carousel__slide">6</div>
   <div class="carousel__slide">7</div>
   <div class="carousel__slide">8</div>
  </div>
 </div>
</div>

子要素を横並びにする構造であればどんな構造でも大丈夫です!

手順2(CSS / カルーセルの基本スタイル実装)

.wrapper {
  display: flex;
  justify-content: center;
  align-items: center;
  position: relative;
  height: 100vh;
}

.carousel {
  position: relative;
  width: fit-content;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: auto;
}

.carousel__slider {
  display: flex;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none;
  scroll-snap-stop: always;
  gap: 1vw;
  margin-block: auto;
  max-width: 400px;
  margin-inline: auto;
  position: relative;
}

.carousel__slide {
  width: 400px;
  height: 200px;
  background-color: #4ade80;
  scroll-snap-align: center;
  flex: 0 0 100%;
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
}

JavaScriptプラグインを使う場合では、オプション機能でカルーセルの挙動を調整していますが、今回はCSSで挙動を調整していきます。
お好みで、scroll-snap-typeを適用し、スクロール時の停止位置と挙動を調整します。

横並びのスタイリングは、flexでもgridでもなんでも大丈夫です。overflow-x: auto;を使用して、 はみ出した要素は横方向にスクロールさせます。

手順3(CSS / 擬似要素を使ってボタンとマーカーを追加)

.wrapper {
  /* 手順2と同様 */
}

.carousel__slider {
  /* 手順2と同様 */
  /* scroll-marker-groupを設定してマーカーグループを有効化 */
  scroll-marker-group: after;

  &::scroll-button(*) {
    position: absolute;
    top: calc(var(--img-height) / 2 - 10px);
    width: 50px;
    height: 50px;
    border-radius: 50%;
    border: none;
    background-color: #eee;
    cursor: pointer;

    &:hover {
      background-color: #eee;
    }
  }

  &::scroll-button(right) {
    right: -30px;
    content: ">";
  }

  &::scroll-button(left) {
    left: -30px;
    content: "<";
  }

  &::scroll-marker-group {
    position: absolute;
    bottom: -1.2em;
    display: flex;
    gap: 1.5em;
    justify-content: center;
  }
}

.carousel__slide {
/* 手順2と同様 */

  &::scroll-marker {
    content: "";
    width: 20px;
    height: 8px;
    background-color: #adadad;
  }

  /* アクティブなマーカーのスタイル */
  &::scroll-marker:target-current {
    background-color: #666;
  }
}

::scroll-button()

::scroll-button()は、カルーセルを囲う親要素.carousel__sliderに指定します。今回の例ではposition: absoluteで浮かせて絶対位置で指定しました。()内の引数について、文字通り、(right)(left)で左右それぞれのスタイルを分けることができ、(*)では全ての::scroll-buttonに対するスタイルを適用できます。

::scroll-marker-group()

::scroll-marker-group()::scroll-marker()使って、ページネーション(現在見ているスライドが、全体の何番目なのかを可視化する目次のようなもの)を実装することが出来ます。
まず、.carousel__sliderに対してscroll-marker-groupプロパティを指定することにより、マーカーグループを有効化させます。

::scroll-marker-group()では、ページネーションボタンの親要素を管理します。サンプルでは、ページネーションの表示位置や間隔を調整しています。

::scroll-marker()

::scroll-marker()には、ページネーションのマーカー1個1個に対するスタイル指定が出来ます。カルーセルの子要素、.carousel__slideに対して指定していきます。マーカーの背景色、大きさを調整しました。また、:target-currentが付与されているものが、アクティブになっているスライドのマーカーのため、非アクティブ状態のものと差別化するために別の背景色を指定して完成です。

まとめ

::scroll-marker()::scroll-button()で実装したカルーセルでは、矢印キーによるキーボード操作や、スクリーンリーダーの読み上げにも対応しているなど、アクセシビリティ面の恩恵が高く、JavaScriptで実装した際のアクセシビリティ対応の工数が削減できます!

アクセシビリティに関するメリットは、こちらの記事の解説が大変参考になります。

また、CSSのみでカルーセルを実装することは、HTML構造がプラグインに制限されないことや、実装の難易度がグッと下がることもメリットとして挙げられますが、個人的にはブラウザでデベロッパーツールを使って検証をしているときに、余分なクラス名や自動生成されたタグがなく、可視性が高くなるところが好きです!

参考にしていただけると幸いです!

参考文献