57
43

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JSを使わずカルーセルを作る

Last updated at Posted at 2026-01-05

はじめに

カルーセルはWEBアプリケーションでよく採用されるUIコンポーネントの1つですが、一般的な実装パターンがなく、アクセシビリティを配慮しつつ実装するのも複雑で大変なので SlickSwiperといったライブラリを採用するケースが多いのではないでしょうか。

そんなカルーセルがCSSで作成できることに関心したので備忘録として残そうと思います。

※ この記事を投稿した時点では safari, firefoxでの使用ができないので注意が必要です。

カルーセルとは

カルーセルとは、回転台、回転木馬、回転コンベア、回転棚などの意味を持つ英単語のことです。
Webページにおいては、複数の画像などのコンテンツを左右に移動して切り替えられる領域を指します。
メリーゴーラウンドのように回転する動きからカルーセルと呼ばれています。

参考
https://digital-marketing.jp/creative/how-to-use-carousel-effectively/

作り方

要素の理解

参考
https://developer.mozilla.org/ja/docs/Web/CSS/Reference/Selectors/::scroll-button

上記の画像の通り、カルーセルは以下の要素から成ります。

  • スライドするリストコンテンツ
  • スクロールボタン
    • 次へ、前へ
    • カルーセルの重要な機能はページ分割なので別個のコンテンツとして移動できるようにする
  • スクロールマーカー
    • クリックされたコンテンツを表示するリンク

これらの要素をCSSの擬似要素を使って表現します。

実装方法

::scroll-button()::scroll-marker() による実装

::scroll-button()

これで生成されたスクロールボタンはスクロールコンテナ上に生成され、CSSでスタイルを設定できます。これらは<button>要素のように振る舞い、フォーカス可能で指定された方向にスクロールできなくなると自動的に無効になります。無効時はdisabled属性を持つのでスタイルの調整が容易です。

  .carousel::scroll-button(left) {
    content: "◀︎";
  }

  .carousel::scroll-button(right) {
    content: "▶︎";
  }

::scroll-marker()

::scroll-marker-groupにグルーピングされ、スクロールコンテナ内の要素のマーカーとなりCSSでスタイルを調整できます。アンカーリンクのように振る舞い、クリックで指定のコンテンツに移動できます。

    .carousel::scroll-marker {
      content: "";
      border: 1px solid #444;
      border-radius: 50%;
    }

:target-current疑似クラスを併用することでアクティブマーカーをスタイルを調整できます。

  .carousel::scroll-marker:target-current {
    background-color: #444;
  }

ぬるっと動いてピタッと止まる

scroll-snap-type: x mandatory; では横方向にスクロールさせ、いずれかの要素が表示されるように厳密な調整が行われます。
scroll-snap-align: center; により、どの位置をベースラインとしてピタッと止めるかを調整でき、ここでは中央で止まるように調整しています。

.content {
  scroll-behavior: smooth;
  scroll-snap-type: x mandatory;
}

li {
  scroll-snap-align: center;
}

参考
https://www.webcreatorbox.com/blog/scroll-snap

シンプルなカルーセルを作ってみます。
@hoangdev01
ご指摘ありがとうございます。

ここではスライダーのサンプルになります。

    <div class="carousel">
      <ul class="content">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
    </div>
.carousel {
  position: relative;
  block-size: 400px;
}

.content {
  display: grid;
  grid-auto-columns: 100%;
  grid-auto-flow: column;
  place-items: center;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  height: 100%;
  scroll-behavior: smooth;
  scroll-marker-group: after;

  &::scroll-button(right) {
    position: absolute;
    top: 45%;
    right: 20px;
    content: "▶︎";
  }

  &::scroll-button(left) {
    position: absolute;
    top: 45%;
    left: 20px;
    content: "◀︎";
  }

  &::scroll-button(*) {
    width: 44px;
    height: 44px;
    border-radius: 50%;
    color: #444;
    border: 1px solid #444;
  }

  &::scroll-button(*):hover {
    background-color: #999;
    color: #444;
    cursor: pointer;
  }

  &::scroll-button(*):disabled {
    opacity: 0.3;
    cursor: not-allowed;
    background-color: #eee;
  }

  &::scroll-marker-group {
    display: flex;
    justify-content: center;
    margin-top: 10px;
    gap: 10px;
  }
}

.content::-webkit-scrollbar {
  display: none;
}

li {
  list-style: none;
  scroll-snap-align: center;
  background-color: black;
  height: 350px;
  width: 350px;

  &::scroll-marker {
    content: "";
    border: 1px solid #444;
    width: 20px;
    height: 20px;
    border-radius: 50%;
  }

  &::scroll-marker:target-current {
    background-color: #444;
  }
}

デモ

画面収録 2026-01-05 18.27.51 (1).gif

最後に

現段階(2026年1月)では実験的ですが、CSSだけでシンプルに表現できるのは素直に便利だと思いました。他のブラウザの対応次第でとても使いやすくなるとも思うので引き出しとして頭の片隅に置いて良いと思います。

57
43
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
57
43

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?