- 技術 💻
カルーセルとは|意味・使いどころ・3通りの作り方を実測で比較
結論
カルーセルとは、複数の画像や内容を同じ場所で順番に切り替えて見せる UI(画面部品)です。場所を節約できる一方で、2枚目以降はほとんど見られないという調査があり、「使うかどうか」の判断と、使うなら「操作できる・止められる・軽い」作りが要点です。
この記事でわかること
- カルーセルの意味、スライダー・スライドショーとの違い、使う/使わない判断表
- CSS scroll-snap・自作 JS・Swiper の3通りを同じ画像5枚で作り、JS/CSS の量・初期表示・CLS・キーボード・スワイプ・属性を Chromium で実測した表
- 自動再生の決まり(WCAG 2.2.2)と、画像が潰れる・ガタつく・端で戻る、の原因と直し方
カルーセルは「1枚ずつ順番に見せる」部品なので、画面の中では次の図のように動きます。切り替えのきっかけは、利用者の操作(矢印・点・スワイプ・キーボード)と、自動再生の2つです。
flowchart TD
S1[スライド1<br>表示中] -->|次へ・スワイプ<br>自動再生| S2[スライド2]
S2 --> S3[スライド3…5]
S3 -->|端まで行くと| E{ループする?}
E -->|する| S1
E -->|しない| STOP[端で止まる]
よく調べられることの答えを先にまとめます。
| 知りたいこと | 答え | 詳しい節 |
|---|---|---|
| カルーセルとは(意味) | 複数の内容を同じ場所で順番に切り替えて見せる UI。語源は回転木馬 | カルーセルとは |
| スライダーとの違い | ほぼ同じもの。呼び方の違いで、厳密な区別は無い | カルーセルとは |
| カルーセル UI のメリット | 場所の節約・大きく見せられる・動きで目を引く | メリットとデメリット |
| デメリット | 2枚目以降が見られない・重くなる・自動再生が読みづらい | 「1枚目以外は見られない」 |
| 使うべきか | 利用者が自分でめくる一覧・ギャラリーなら向く。必ず読ませたい情報には使わない | 使う・使わない判断表 |
| 作り方 | CSS scroll-snap(JS なし)・自作 JS・Swiper の3通り | 3通りの作り方 |
| どれが軽いか | scroll-snap は CSS 792 バイト、Swiper は JS と CSS で約 170KB(gzip 約 46KB) | 実測1:JS/CSS の量 |
| 自動再生してよいか | 5秒を超えて動くなら「止める手段」が WCAG のレベル A で要る | 自動再生の是非 |
| 画像が潰れる・ガタつく | object-fit と width/height(aspect-ratio)の指定で直る | よくある不具合 |
確認環境:Ubuntu 24.04.5 LTS の上で、Python 3.13.18 の http.server を 127.0.0.1:8104 に起動し、自分で作った3つのページ(CSS scroll-snap 版・自作 JS 版・Swiper 12.2.0 版)と同じ JPEG 画像5枚(1200×600 ピクセル、各 23〜24KB)を置きました。Swiper は npm で作業用のフォルダーに入れ、swiper-bundle.min.js と swiper-bundle.min.css をそのまま配信しています。表示は Chromium 153.0.8010.12(Playwright 1.63.0、Node.js 22.23.4)で開き、PC(幅 1280px)とスマホ相当(幅 390px・タッチあり)の2通り、回線は「制限なし」と、Chrome DevTools と同じ条件の「低速 4G 相当(下り 1.6Mbps・遅延 150ms・CPU 4倍遅く)」の2通りで測りました。初期表示の時間は Navigation Timing と Paint Timing、CLS と LCP は PerformanceObserver、キーボード・スワイプ・自動再生の挙動はページ内の値を読んで確かめています。WAI-ARIA の属性は W3C の APG「Carousel Pattern」、自動再生の決まりは WCAG 2.2 の原文、クリック率の数値は公開されている調査の原文で確かめました。図は mermaid 11.16.0 で描画を確かめています(確認日 2026年10月29日)。
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「カルーセルとは|意味・使いどころ・3通りの作り方を実測で比較」
カルーセルとは(意味・語源・スライダーとの違い)
この節では、カルーセルという言葉の意味と、スライダー・スライドショーとの関係が分かります。
カルーセル(carousel)とは、Web サイトやアプリで、複数の画像・カード・バナーを同じ枠の中で順番に切り替えて見せる UI のことです。英語の carousel は「回転木馬(メリーゴーラウンド)」で、ぐるぐる回って次の馬が目の前に来る様子から名付けられました。語源の話はここまでで、以降は Web の部品としてのカルーセルを扱います。
利用者から見える要素は、だいたい次の4つです。
- スライド:切り替わる1枚1枚。画像、画像+見出し、商品カードなど。
- 矢印(前へ・次へ):1枚ずつ送るボタン。
- インジケーター(点・番号):全部で何枚あり、今何枚目かを示す。点を押すとその枚へ飛べることが多い。
- 自動再生:一定秒数で勝手に次へ進む仕組み。無いものもある。
スライダー・スライドショー・バナーとの違い
| 言葉 | 使われ方 | カルーセルとの関係 |
|---|---|---|
| スライダー | 制作側がライブラリや実装を指して使うことが多い(Swiper・slick など) | ほぼ同じもの。厳密な区別は無い |
| スライドショー | 自動で流れる演出を指すことが多い(写真が次々に出る) | 自動再生中心のカルーセル |
| バナー・ヒーロー画像 | ページ上部の大きな画像1枚 | カルーセルにせず1枚で固定する選択肢 |
| カルーセル広告・カルーセル投稿 | Meta・LINE・Google の広告、Instagram・X の投稿で、複数枚を横にめくる形式 | 同じ見せ方を広告・SNS の機能として提供したもの |
この記事は Web サイトの部品としてのカルーセルが主題です。広告・SNS のカルーセルは次の節で触れるだけにします。
カルーセルが使われる場所(Web サイト・SNS・広告)
この節では、「カルーセル」という言葉がどの場面で出てくるかが分かります。
同じ「カルーセル」でも、Web サイトの部品、SNS の投稿形式、広告の形式の3つは、作る人も目的も違います。
1Web サイトの部品
トップページの最上部(ファーストビュー)、商品画像の切り替え、導入事例やお客様の声、関連記事の一覧。この記事で作り方と実測を扱うのはここ。
2SNS の投稿形式
Instagram・X・LinkedIn などで、複数枚の画像を横にめくる投稿。「1枚目で止まらせて、2枚目以降で説明する」構成が多い。作るのは画像で、コードは書かない。
3広告の形式
Meta 広告・LINE 広告・Google 広告などの「カルーセル広告」。1枚ごとに別のリンク先を付けられる。設定は各広告の管理画面で行う。
Web サイトで使う場合も、「商品を何枚も見比べたい」「写真を眺めたい」のように利用者が自分でめくる理由があるかが、後で出てくる判断の分かれ目になります。
カルーセル UI のメリットとデメリット
この節では、カルーセルを使うと何が良くなり、何が悪くなるのかが分かります。
メリットは「場所」、デメリットは「見られない・重い・読みづらい」です。検索上位の解説でも、挙げられている内容はほぼこの3対3に収まります。
メリット
限られた場所に複数の内容を置ける(特にスマホの縦長画面)
縮小せずに1枚ずつ大きく見せられる
動きがあると目に留まりやすい
デメリット
2枚目以降はほとんど見られない(次の節の数値)
ライブラリと画像で重くなり、初期表示が遅れる(実測1)
自動再生は読んでいる途中で消え、操作の邪魔になる(WCAG の決まり)
メリットは「作る側・載せる側」に効き、デメリットは「見る側」に効く、という非対称があります。社内の各部署が「自分のバナーもトップに載せたい」と言うと枚数が増えがちですが、増えた分だけ見られなくなるのがカルーセルです。
「1枚目以外はほとんど見られない」調査の数値
この節では、カルーセルの2枚目以降がどれだけ見られないのかを、公開されている調査の数値で確かめます。
よく引かれるのは、米ノートルダム大学の Web 担当だった Erik Runyon 氏が2013年1月に公開した「Carousel Interaction Stats」です。大学のトップページで、カルーセルの中身をクリックした訪問者は約1%、そのクリックのうち84%が1枚目に集中していました。同氏が併せて載せた他のサイトの数字も、次のとおりです(出典は末尾)。
13年前の1つの大学の数字なので、そのまま自分のサイトに当てはめることはできません。それでも「1枚目に重要な内容を置き、2枚目以降は見られない前提で作る」という設計の根拠としては、今もこの調査が引かれています。自分のサイトでは、GA4 のイベントやヒートマップで「何枚目まで操作されたか」を測ると、同じ形の数字が取れます。
カルーセルを使う・使わない判断表
この節では、自分のページにカルーセルを置くべきかどうかを、場面ごとに判断できます。
「利用者が自分でめくる理由があるか」と「全員に読ませたいか」の2つで決まります。
| 場面 | 判断 | 理由・代わり |
|---|---|---|
| 商品ページの写真(正面・側面・使用例) | 使う | 見比べたい理由がある。サムネイル付きが望ましい |
| 写真ギャラリー・導入事例の一覧 | 使う | 眺める用途。自動再生はしない |
| スマホで横に並ぶカード(関連記事・おすすめ) | 使う(scroll-snap) | 横スクロールの一覧として自然。JS は要らない |
| トップページのファーストビュー | 1枚に絞るのを先に検討 | 2枚目以降は約1%のさらに一部しか見ない。ヒーロー画像1枚+下にカードの並び |
| お知らせ・注意事項・キャンペーン条件 | 使わない | 全員に読ませたい情報は隠さない。一覧(リスト)で縦に並べる |
| ナビゲーション・メニュー | 使わない | 動くメニューは押せない。固定の一覧にする |
| PC の広い画面で複数の訴求 | 使わない | グリッドで全部並べれば、めくらなくても見える |
- 1全員に読ませたい → カルーセルにしない縦に並べる。隠れた内容は読まれない
- 2並べれば全部見える → グリッドPC では 3〜4 列に並ぶなら、めくらせない
- 3スマホで横に流したい → CSS scroll-snapJS なし。指でめくれて、PC ではスクロールバーが出る
- 4矢印・点・ループ・自動再生が要る → 自作 JS か Swiper要件が少なければ自作(約 4KB)、多ければ Swiper(約 170KB)
- 5自動再生する → 止めるボタンを付け、ホバーと操作で止めるWCAG 2.2.2(レベル A)と APG の決まり
3通りの作り方(CSS scroll-snap・自作 JS・Swiper)
この節では、同じ画像5枚で作った3通りのカルーセルの、コードの要点が分かります。
3通りとも、HTML はリスト(ul・li)に画像を入れた同じ形で、違いは「送る仕組み」だけです。画像には width・height を付け、CSS で aspect-ratio: 2/1 と object-fit: cover を指定して、3通りを同じ条件にしています(この指定が無いときに何が起きるかは「よくある不具合」の節で実測します)。
A:CSS scroll-snap(JS なし)
横スクロールの入れ物に scroll-snap-type: x mandatory を付け、各スライドに scroll-snap-align: start を付けるだけです。指でめくると1枚ごとに止まり、PC ではスクロールバーとキーボードで送れます。点はページ内リンク(href="#s2")にしました。
<section class="cs" aria-roledescription="カルーセル" aria-label="おすすめの写真">
<ul class="cs__track" tabindex="0" aria-label="横にスクロールできます">
<li class="cs__slide" id="s1"><img src="img/1.jpg" width="1200" height="600" alt="写真1"></li>
<li class="cs__slide" id="s2"><img src="img/2.jpg" width="1200" height="600" alt="写真2" loading="lazy"></li>
…(5枚まで同じ)
</ul>
<nav class="cs__dots" aria-label="スライドを選ぶ"><a href="#s1">1</a><a href="#s2">2</a>…</nav>
</section>
.cs__track{display:flex;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;list-style:none;margin:0;padding:0}
.cs__slide{flex:0 0 100%;scroll-snap-align:start;scroll-snap-stop:always}
.cs__slide img{display:block;width:100%;height:auto;aspect-ratio:2/1;object-fit:cover}
@media (prefers-reduced-motion:reduce){.cs__track{scroll-behavior:auto}}
scroll-snap の書き方の細部(余白・複数枚表示・スクロールバーの消し方)は、関連記事「CSS scroll-snapで横スクロールカルーセル|JSなしの完成版」で扱っています。この記事は入口として、3通りの比較に絞ります。
B:自作 JS(transform で送る)
スライドを横一列に並べ、transform: translateX(-100% × 枚数) で送ります。前へ・次へ・点・自動再生とその停止ボタン・矢印キー・スワイプを、約 90 行(3,225 バイト)で書きました。要点は次のとおりです。
function go(k) {
i = (k + n) % n; // 端で反対側へ回る
track.style.transform = 'translateX(' + (-100 * i) + '%)';
slides.forEach(function (s, k2) {
var on = k2 === i;
s.setAttribute('aria-hidden', on ? 'false' : 'true');
if (on) { s.removeAttribute('inert'); } else { s.setAttribute('inert', ''); }
dots.children[k2].setAttribute('aria-selected', on ? 'true' : 'false');
});
}
function start() { // 自動再生(5秒)
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { return; }
timer = setInterval(function () { go(i + 1); }, 5000);
}
root.addEventListener('mouseenter', pause); // ホバーで止める
root.addEventListener('focusin', pause); // フォーカスが入ったら止める
root.addEventListener('keydown', function (e) { // 矢印キー
if (e.key === 'ArrowRight') { go(i + 1); stop(); }
if (e.key === 'ArrowLeft') { go(i - 1); stop(); }
});
C:Swiper 12
npm の swiper(12.2.0)の bundle 版を読み込み、ループ・キーボード・自動再生・矢印・点を設定で有効にしました。アクセシビリティ用の属性は Swiper の a11y モジュールが自動で付けます。
new Swiper('.swiper', {
loop: true,
keyboard: { enabled: true },
autoplay: { delay: 5000, pauseOnMouseEnter: true },
navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
pagination: { el: '.swiper-pagination', clickable: true },
a11y: { prevSlideMessage: '前のスライド', nextSlideMessage: '次のスライド' }
});
Swiper のオプション全体・React や Vue での使い方・動かないときの原因は、関連記事「Swiperの使い方|オプション早見表・React/Vue・動かない原因を実演」で扱っています。
実測1:送られる JS/CSS の量と初期表示の時間
この節では、3通りで利用者に送られるファイルの量と、ページが表示されるまでの時間の違いが分かります。
scroll-snap は CSS 792 バイトだけ、自作 JS は約 4KB、Swiper は約 170KB(gzip で約 46KB)でした。画像は3通りとも同じで、初期表示までに読み込まれたのは5枚中2〜3枚です(loading="lazy" を2枚目以降に付けたため)。
| 作り方 | JS(バイト) | CSS(バイト) | 合計(gzip 後) |
|---|---|---|---|
| A:CSS scroll-snap | 0 | 792 | 792(451) |
| B:自作 JS | 3,225 | 758 | 3,983(1,541) |
| C:Swiper 12 bundle | 155,297 | 14,609 | 169,906(46,619) |
Swiper は、使うモジュールだけを ES Modules で取り込めば(swiper.min.js は 68,966 バイト)小さくなります。次は初期表示の時間です。回線に制限が無いと差は数十ミリ秒ですが、「低速 4G 相当」にすると、JS を読み終えるまで DOMContentLoaded が待たされる差が出ました。
| 条件 | 指標(ms) | A:scroll-snap | B:自作 JS | C:Swiper |
|---|---|---|---|---|
| 制限なし・PC | DOMContentLoaded | 26 | 58 | 93 |
| 制限なし・PC | LCP(最大の画像の表示) | 64 | 104 | 116 |
| 低速 4G・PC | DOMContentLoaded | 245 | 505 | 1,599 |
| 低速 4G・PC | FCP(最初の描画) | 444 | 444 | 656 |
| 低速 4G・PC | LCP | 520 | 544 | 712 |
| 低速 4G・スマホ幅 | DOMContentLoaded | 326 | 462 | 1,709 |
| 低速 4G・スマホ幅 | LCP | 628 | 540 | 700 |
Swiper の DOMContentLoaded が約 1.6 秒になったのは、script を body の末尾に置いた素の読み込みで、155KB の JS を下り 1.6Mbps で受け取る時間がそのまま乗ったためです。defer と gzip 配信にすれば縮みますが、「カルーセルのために 1 枚目の画像より大きいファイルを送る」構図は変わりません。
実測2:CLS(表示のガタつき)と画像の寸法
この節では、カルーセルが原因で起きる「表示のガタつき(CLS)」を、寸法の指定あり・なしで比べた結果が分かります。
画像に width・height(または aspect-ratio)を付けた3通りはいずれも CLS 0、外した scroll-snap 版は低速 4G で 0.0501 でした。画像が届くまで高さ 0 だった枠が、届いた瞬間に 480px 分ふくらみ、下の本文が押し下げられたのが原因です。
| ページ | 画像の寸法指定 | CLS(低速 4G) | 何が起きたか |
|---|---|---|---|
| A・B・C | width/height+aspect-ratio | 0 | 読み込み前から高さが確保されている |
| A から寸法を外した版 | なし | 0.0501(362ms 時点で1回) | 画像が届いた瞬間に下の本文が 480px 下がった |
| B の最初の版 | あり(点を JS で後から生成) | 0.0018 | JS が点を挿入した 70ms 時点で下の本文が 44px 下がった |
| B(点の枠に min-height:44px) | あり | 0 | 点の枠の高さを CSS で先に確保 |
B の 0.0018 は小さいものの、「JS が後から部品を挿入すると、その分だけガタつく」という、自作・ライブラリを問わず起きる形です。Swiper でも、矢印や点の要素を JS で生成する設定にすると同じことが起きるので、HTML に先に置いて高さを持たせておきます。画像の寸法と object-fit の組み合わせは、関連記事「CSS object-fit・aspect-ratio|画像トリミング5パターン」で扱っています。
実測3:キーボード操作・スワイプ・自動再生・端での挙動
この節では、3通りのカルーセルを、マウス以外の方法で操作したときに何が起きるかが分かります。
3通りとも、Tab キー1回でカルーセルに入り、→キーで次のスライドへ進み、スマホ幅の指の動き(タッチ)で1枚送れました。違いが出たのは、自動再生の止まり方と、端まで行ったときの動きです。
| 確かめたこと | A:scroll-snap | B:自作 JS | C:Swiper |
|---|---|---|---|
| Tab で最初に止まる場所 | スクロールの枠(tabindex=0) | 「前のスライド」ボタン | 「前のスライド」ボタン |
| Tab の停止数(枠の中) | 6(枠+点5) | 8(矢印2+停止1+点5) | 7(矢印2+点5) |
| →キーで次へ | 進む(枠にフォーカスがある時だけ) | 進む | 進む(画面内にある時) |
| タッチで左へ払う(幅 390px) | 1→2 | 1→2 | 1→2 |
| 5.6 秒放置(自動再生) | 動かない(機能なし) | 1→2 | 1→2 |
| マウスを乗せて 5.6 秒 | ― | 止まる(1のまま) | 止まる(1のまま) |
| 「動きを減らす」設定で 5.6 秒 | ― | 止まる(自作で判定) | 1→2(設定を見ない) |
| 最後の枚で「次へ」 | 端で止まる | 1へ戻る(巻き戻しが見える) | 1へ進む(ループ) |
「最後の枚で次へ」の違いを、もう少し見ます。B は translateX(-400%) から 0 へ 0.4 秒で戻るので、途中(150ms 時点)の値は matrix(1, 0, 0, 1, -1628.44, 0)、つまり4枚分を逆走している最中でした。これが「無限ループのはずが、端で巻き戻って見える」不具合の正体です。C は端に来ると、Swiper が DOM の先頭の li(写真1)を末尾へ動かし(並びが「2,3,4,5,1」になった)、前へ進む動きのまま1枚目に着きました。A はループしないので、端では止まります。
もう1つ、A の点をページ内リンクにした副作用があります。カルーセルの上に 600px の余白を置いた状態で「3」のリンクを押すと、横に3枚目へ進むと同時に window.scrollY が 0 から 488 に変わり、ページ全体も縦に動きました。href="#id" はその要素が見える位置までページ全体をスクロールするためで、点を使うなら JS で scrollIntoView({ inline: 'start', block: 'nearest' }) を呼ぶか、点を付けずにスクロールバーに任せます。
実測4:スクリーンリーダー向けの属性(WAI-ARIA APG)
この節では、W3C の APG「Carousel Pattern」が推奨する属性と、3通りで実際に付いた属性の対応が分かります。
APG の推奨は、入れ物に aria-roledescription="carousel" とラベル、各スライドに role="group"・aria-roledescription="slide"・「3 of 10」のような名前、自動回転中は aria-live="off"、停止中は polite、ボタンはネイティブの button、です。3通りのページから、実際に付いていた属性を読み出しました。
| 要素 | A:scroll-snap(手書き) | B:自作 JS(手書き) | C:Swiper の a11y |
|---|---|---|---|
| 入れ物 | aria-roledescription・aria-label | aria-roledescription・aria-label | aria-label(自分で付けた)のみ |
| スライド | なし | role=group・aria-roledescription=スライド・aria-label="1 / 5"・aria-hidden・inert | role=group・aria-label="1 / 5" |
| ライブリージョン | なし | ul に aria-live=polite | wrapper に aria-live=off、別に aria-live=assertive の通知用 span(操作後も空のまま) |
| 矢印 | なし | button・aria-label | span role=button tabindex=0・aria-label(設定した日本語) |
| 点 | a(1〜5の文字) | button role=tab・aria-label・aria-selected | span role=button tabindex=0・aria-label="Go to slide 1"(既定は英語)・aria-current |
Swiper の a11y モジュールは、矢印と点に role="button" と tabindex="0" を付けて span をボタン扱いにし、点のラベルは英語の既定値のままでした。日本語サイトでは a11y.paginationBulletMessage: 'スライド {{index}} へ' のように設定します。また、APG は自動再生の停止ボタンについて「ラベルを『Stop slide rotation』『Start slide rotation』のように切り替え、aria-pressed は付けない」としています。B の停止ボタンは aria-pressed を付けていたので、APG に合わせるならラベルの切り替えだけにします。
WCAG 2.2 の達成基準を一覧で確かめる手順は、関連記事「WCAG 2.2チェックリスト|A・AA全55項目と確認方法・axe検査手順」で扱っています。
自動再生の是非(WCAG 2.2.2「一時停止、停止、非表示」)
この節では、カルーセルを自動で動かしてよいのか、動かすなら何が要るのかが、WCAG の原文で分かります。
WCAG 2.2 の達成基準 2.2.2(レベル A)は、自動で始まり、5 秒を超えて続き、他の内容と並んで表示される「動く・点滅する・スクロールする情報」には、利用者が一時停止・停止・非表示にできる仕組みを求めています。原文は次のとおりです。
For any moving, blinking or scrolling information that (1) starts automatically, (2) lasts more than five seconds, and (3) is presented in parallel with other content, there is a mechanism for the user to pause, stop, or hide it unless the movement, blinking, or scrolling is part of an activity where it is essential
自動再生のカルーセルは、この3条件を全部満たします。つまり「レベル A を満たすには、止める手段が必ず要る」ということです。APG はさらに、マウスが乗っている間とキーボードフォーカスが中にある間は回転を止め、停止ボタンを Tab 順の最初に置く、としています。
自動再生の作り方
実測では、Swiper 12 は prefers-reduced-motion を見ずに動いたので、自分で判定して autoplay を止める
Swiper で「動きを減らす」設定に合わせるには、初期化の前に window.matchMedia('(prefers-reduced-motion: reduce)').matches を見て、真なら autoplay を渡さない(または swiper.autoplay.stop() を呼ぶ)形になります。
スマホでのカルーセル(スワイプ・幅・画像の大きさ)
幅 390px では、3通りとも 1200×600 の画像が 358×179 ピクセルに縮んで表示され、指で左へ払うと 1 枚送れました。見た目は同じでも、裏で送っている画像は 1200px のままなので、スマホ向けに小さい画像を用意する srcset・sizes の出番です(関連記事「srcset・sizesの使い方|picture併用とDevToolsでの検証」で扱っています)。
- scroll-snap はスワイプが「ただのスクロール」なので、慣性・引っかかり・戻りが OS のスクロールと同じ感触になります。自作 JS は
touchstart・touchendの差 40px で判定した簡易版、Swiper は指に追従して動きます。 - 縦スクロールを邪魔しないこと。自作 JS では
touch-action: pan-yを指定し、縦の指の動きはブラウザに渡しました。これが無いと、カルーセルの上で縦にスクロールできなくなることがあります。 - 矢印はスマホでは押しにくいので、点かスワイプに頼ります。点は 44×44px 以上にしました(3通りとも)。
- 画像は2枚目以降を
loading="lazy"にしました。制限なしでは 1.5 秒後に5枚中4枚、低速 4G では 3 秒後に2枚が読み込まれていました。Chromium は画面外でも近い画像を先読みするため、「見えるまで一切読まない」わけではありません。
よくある不具合と直し方(画像が潰れる・ガタつく・端で戻る)
この節では、カルーセルで起きやすい不具合を手元で再現し、原因と直し方が分かります。
不具合の多くは「画像の寸法」「初期化のタイミング」「ループの作り」の3つに集約されます。
| 症状 | 手元で再現した状態 | 原因 | 直し方 |
|---|---|---|---|
| 画像が横に潰れる・伸びる | 高さ 240px 固定で、表示 960×240(比 4.00)、元画像は比 2.00、object-fit は fill | 枠の比と画像の比が違い、既定の object-fit: fill で引き伸ばされた |
object-fit: cover(切り抜く)か contain(余白を出す)。比をそろえるなら aspect-ratio |
| 読み込み時にガタつく | 寸法なしで CLS 0.0501、本文が 480px 下がった | 画像が届くまで高さ 0 | width・height 属性か aspect-ratio。JS で作る点や矢印は枠の高さを先に確保 |
| 表示したら全スライドが縦に並ぶ・幅 0 | display:none の中で Swiper を初期化すると、初期化直後は width が null、slidesGrid が [0, NaN, NaN, …] | 隠れた要素は幅が測れない | Swiper 12 では表示後に resizeObserver が自動で再計算して直った。確実にするなら表示した直後に swiper.update() |
| 端で巻き戻って見える | 自作 JS で 5→1 のとき 4 枚分を逆走(150ms 時点で -1628px) | translateX を 0 に戻す遷移がそのまま見える |
端ではアニメーションを切る、または先頭と末尾の複製を置いて位置を差し替える(Swiper はこの方式) |
| 点を押すとページが飛ぶ | scroll-snap 版の href="#s3" で scrollY が 0→488 |
ページ内リンクはページ全体をスクロールする | JS で scrollIntoView({ inline: 'start', block: 'nearest' }) |
| 縦にスクロールできない | 自作 JS で touch-action なし |
指の動きをカルーセルが全部受け取る | touch-action: pan-y |
| キーボードで送れない | scroll-snap 版で枠にフォーカスが無いと →キーは無反応(scrollLeft 0 のまま) | スクロールの枠がフォーカスを受けない | 枠に tabindex="0" と aria-label。Swiper は keyboard モジュールを有効に |
Swiper の「ループ時にスライド数が足りない」件も試しました。5枚で slidesPerView: 3・loop: true にしても、12.2.0 ではコンソールに警告は出ず、「次へ」を5回押すと realIndex が 0 に戻ってループしました。以前の版で出ていた警告文は、版によって挙動が違うため、手元の版で確かめるのが確実です。
カルーセルの代わりになる見せ方
この節では、「カルーセルにしない」と決めたときに、何に置き換えるかが分かります。
置き換え先は、ヒーロー画像1枚、グリッド、縦の一覧、動画、タブ、アコーディオンのどれかです。「複数の訴求を1か所に」という要望に対して、カルーセル以外の答えを先に持っておくと、枚数が増え続けるのを止められます。
1ヒーロー画像1枚+下にカード
最上部は最も伝えたい1枚に絞り、残りは下にカードで並べる。全部が最初から見え、CLS もライブラリも増えない。
2グリッド(PC)+横スクロール(スマホ)
PC では 3〜4 列に並べ、スマホでは同じ HTML を scroll-snap で横に流す。CSS だけで切り替えられる。
3タブ・アコーディオン
利用者が選んで開く形。自動で切り替わらないので、読んでいる途中で消えない。見出しが一覧になる利点もある。
商品写真のように「同じものの別の面」を見せる用途は、カルーセルのままで問題ありません。置き換えを考えるのは、「別々の訴求を1つの枠に詰めている」ときです。
公開前のチェックリスト
実測で差が出た項目を、そのまま確認項目にしました。
- 11枚目に最も重要な内容がある2枚目以降は見られない前提。順番は優先度順
- 2画像に width・height(または aspect-ratio)と object-fit があるCLS 0 と、潰れない表示
- 3Tab と →キーだけで全スライドに行ける枠に tabindex、または矢印ボタン。ボタンはネイティブの button
- 4自動再生なら、止めるボタン・ホバーとフォーカスで停止・5秒以上WCAG 2.2.2(レベル A)。prefers-reduced-motion も見る
- 5入れ物とスライドに名前が付いているaria-roledescription・aria-label・role=group。ライブラリの既定の英語ラベルを日本語に
- 6低速回線で初期表示を測ったDevTools の低速 4G で、JS がファーストビューを遅らせていないか
- 7スマホで縦スクロールとスワイプの両方が動くtouch-action: pan-y。点は 44px 以上
よくある質問
Q. カルーセルとは何ですか? A. 複数の画像や内容を同じ枠の中で順番に切り替えて見せる UI です。矢印・点・スワイプ・自動再生で切り替えます。語源は回転木馬(carousel)です。
Q. カルーセルとスライダーの違いは何ですか? A. ほぼ同じものを指し、厳密な区別はありません。制作側はライブラリ名にちなんで「スライダー」と呼ぶことが多く、自動で流れる演出は「スライドショー」と呼ばれがちです。
Q. カルーセルは SEO に悪いですか? A. カルーセルそのものが順位を下げるわけではありません。影響するのは、JS と画像で初期表示(LCP)が遅れること、寸法の指定が無くてガタつく(CLS)ことです。手元の実測では、寸法を付けた3通りはいずれも CLS 0 でした。
Q. 自動再生は付けるべきですか? A. 付けるなら、WCAG 2.2.2(レベル A)に従って止める手段が要ります。5 秒以上の間隔、停止ボタン、ホバーとフォーカスでの停止が目安です。写真ギャラリーや商品画像では、自動再生を付けない方が操作しやすくなります。
Q. 2枚目以降は本当に見られないのですか? A. 2013年の Erik Runyon 氏の調査では、大学トップページでカルーセルを押した訪問者は約1%、そのうち84%が1枚目でした。サイトによって違うので、自分のサイトでは GA4 のイベントで「何枚目まで操作されたか」を測ります。
Q. JS なしでカルーセルは作れますか? A. 作れます。CSS の scroll-snap で、指でめくれて PC ではスクロールバーとキーボードで送れるものが CSS 792 バイトでできました。矢印・点・ループ・自動再生が要るときは JS が必要です。
Q. Swiper と自作、どちらにすべきですか? A. 要件が「矢印と点だけ」なら自作(約 4KB)で間に合い、ループ・複数枚表示・サムネイル連動・縦方向・エフェクトなどが要るなら Swiper(bundle で約 170KB、gzip 約 46KB)が早道です。Swiper は必要なモジュールだけを ES Modules で取り込むと小さくなります。
Q. 画像が潰れて表示されるのはなぜですか?
A. 枠の縦横比と画像の縦横比が違うのに、object-fit が既定の fill のままだからです。手元では高さ固定の枠で 1200×600 の画像が 960×240 に引き伸ばされました。object-fit: cover か contain を指定します。
Q. 何枚まで入れてよいですか? A. 決まりはありませんが、3〜5枚が目安としてよく挙げられます。実測の数値から言えるのは「枚数を増やすほど、1枚あたりが見られる確率は下がる」ことです。
Q. スクリーンリーダーには何が必要ですか?
A. 入れ物に aria-roledescription="carousel" とラベル、スライドに role="group" と「1 / 5」のような名前、ネイティブの button、自動回転中は aria-live="off"、停止中は polite、が W3C APG の推奨です。
Q. カルーセル広告とは何ですか? A. Meta・LINE・Google などの広告で、複数枚の画像やカードを横にめくる形式です。1枚ごとに別のリンク先を設定できます。Web サイトの部品としてのカルーセルとは、作る場所も設定方法も別です。
まとめ
- カルーセルとは、複数の内容を同じ枠で順番に切り替えて見せる UI。スライダー・スライドショーとの厳密な区別は無い。
- 場所を節約できる一方で、2枚目以降はほとんど見られない(約1%のうち84%が1枚目という調査)。全員に読ませたい情報には使わず、1枚目に最重要の内容を置く。
- 作り方は CSS scroll-snap(CSS 792 バイト・JS なし)、自作 JS(約 4KB)、Swiper 12(約 170KB、gzip 約 46KB)の3通り。低速 4G では Swiper の DOMContentLoaded が約 1.6 秒、他の2つは 0.25〜0.5 秒だった。
- 画像に width・height(aspect-ratio)と object-fit を付ければ3通りとも CLS 0。外すと 0.05 のガタつきと、潰れた画像になった。
- 自動再生には止める手段が要る(WCAG 2.2.2 レベル A)。Swiper 12 は prefers-reduced-motion を見ないので、自分で判定して止める。端での「巻き戻り」は translateX の遷移が見えているだけで、複製スライドで差し替える方式なら起きない。
次に読むとよい記事です。JS なしで作るなら「CSS scroll-snapで横スクロールカルーセル|JSなしの完成版」、Swiper のオプションを調べるなら「Swiperの使い方|オプション早見表・React/Vue・動かない原因を実演」、画像の切り抜きと縦横比は「CSS object-fit・aspect-ratio|画像トリミング5パターン」、アクセシビリティの確認手順は「WCAG 2.2チェックリスト|A・AA全55項目と確認方法・axe検査手順」が続きです。
参考・出典
確認日はいずれも 2026年10月29日です。
- W3C「WCAG 2.2」2.2.2 Pause, Stop, Hide:https://www.w3.org/TR/WCAG22/#pause-stop-hide
- W3C WAI「ARIA Authoring Practices Guide: Carousel Pattern」:https://www.w3.org/WAI/ARIA/apg/patterns/carousel/
- Erik Runyon「Carousel Interaction Stats」(2013年1月22日):https://erikrunyon.com/2013/01/carousel-interaction-stats/
- MDN「CSS スクロールスナップ」:https://developer.mozilla.org/ja/docs/Web/CSS/CSS_scroll_snap
- MDN「object-fit」:https://developer.mozilla.org/ja/docs/Web/CSS/object-fit
- MDN「aspect-ratio」:https://developer.mozilla.org/ja/docs/Web/CSS/aspect-ratio
- MDN「prefers-reduced-motion」:https://developer.mozilla.org/ja/docs/Web/CSS/@media/prefers-reduced-motion
- web.dev「Cumulative Layout Shift (CLS)」:https://web.dev/articles/cls
- Swiper「Swiper API」(Autoplay・Keyboard・A11y・Loop のパラメーター):https://swiperjs.com/swiper-api
- Swiper「Get Started」(npm・ES Modules での読み込み):https://swiperjs.com/get-started
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「カルーセルとは|意味・使いどころ・3通りの作り方を実測で比較」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。