DEMO
01
前へ・次へのボタンで1枚ずつ送れます。
02
下の丸い印を押すと、そのスライドへ移ります。
03
横にスワイプしても、ぴたりと止まります。
04
端まで来るとボタンが押せない表示になります。
このブラウザは CSS カルーセルに未対応のため、横スクロール(スワイプ)で送ってください
CODE
<div class="demo-stage crs-stage">
<ul class="crs-list" aria-label="おすすめの特集" tabindex="0">
<li class="crs-item" data-label="春の特集"><p class="crs-num">01</p><h3 class="crs-title">春の特集</h3><p class="crs-text">前へ・次へのボタンで1枚ずつ送れます。</p></li>
<li class="crs-item" data-label="夏の特集"><p class="crs-num">02</p><h3 class="crs-title">夏の特集</h3><p class="crs-text">下の丸い印を押すと、そのスライドへ移ります。</p></li>
<li class="crs-item" data-label="秋の特集"><p class="crs-num">03</p><h3 class="crs-title">秋の特集</h3><p class="crs-text">横にスワイプしても、ぴたりと止まります。</p></li>
<li class="crs-item" data-label="冬の特集"><p class="crs-num">04</p><h3 class="crs-title">冬の特集</h3><p class="crs-text">端まで来るとボタンが押せない表示になります。</p></li>
</ul>
<p class="crs-note">このブラウザは CSS カルーセルに未対応のため、横スクロール(スワイプ)で送ってください</p>
</div>
.crs-stage { position:relative; overflow:hidden; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:.8rem; padding:2rem 1rem; background:#0B0B10; min-height:320px; }
.crs-list { display:flex; gap:1rem; width:100%; max-width:420px; margin:0; padding:0; list-style:none;
overflow-x:auto; scroll-snap-type:x mandatory; scroll-behavior:smooth; border-radius:14px; }
.crs-list:focus-visible { outline:2px solid #fff; outline-offset:3px; }
.crs-item { flex:0 0 100%; box-sizing:border-box; scroll-snap-align:center; min-height:170px; padding:1.3rem 1.4rem; border-radius:14px; color:#fff;
background:linear-gradient(135deg,#7B37A8,#D1177A); }
.crs-item:nth-child(2) { background:linear-gradient(135deg,#0369A1,#1E3A8A); }
.crs-item:nth-child(3) { background:linear-gradient(135deg,#9A3412,#B45309); }
.crs-item:nth-child(4) { background:linear-gradient(135deg,#1E1B4B,#4338CA); }
.crs-num { margin:0; font-family:'Oswald',sans-serif; font-size:.8rem; letter-spacing:.3em; opacity:.85; }
.crs-title { margin:.3rem 0 .4rem; font-size:1.2rem; }
.crs-text { margin:0; font-size:.82rem; line-height:1.7; }
.crs-note { margin:0; color:#FCD34D; font-size:.76rem; text-align:center; }
/* ブラウザが前後のボタンと印を作る(Chrome / Edge 135 以降) */
@supports (scroll-marker-group: after) {
.crs-note { display:none; }
.crs-list { anchor-name: --crs; scroll-marker-group: after; scrollbar-width:none; }
.crs-item { padding-inline:3.4rem; } /* 左右に重なる送りボタンの分だけ本文を内側へ */
.crs-list::scroll-button(*) { position:absolute; position-anchor:--crs; top:anchor(center); translate:0 -50%;
width:40px; height:40px; border:0; border-radius:50%; background:rgb(0 0 0 / .55); color:#fff; font-size:1.1rem; cursor:pointer;
transition: opacity .2s, scale .2s; }
.crs-list::scroll-button(left) { content:"‹" / "前へ"; left:calc(anchor(left) + 8px); }
.crs-list::scroll-button(right) { content:"›" / "次へ"; right:calc(anchor(right) + 8px); }
.crs-list::scroll-button(*):hover { scale:1.08; }
.crs-list::scroll-button(*):focus-visible { outline:2px solid #fff; outline-offset:2px; }
.crs-list::scroll-button(*):disabled { opacity:.25; cursor:default; }
/* 印の入れ物はスクロール容器の内側(子要素の並び)に作られるので、anchor で容器の下へ出す */
.crs-list::scroll-marker-group { position:absolute; position-anchor:--crs; top:calc(anchor(bottom) + 14px); justify-self:anchor-center; display:flex; gap:10px; }
.crs-item::scroll-marker { content: attr(data-label); display:block; width:10px; height:10px; border-radius:999px; overflow:hidden; text-indent:24px; white-space:nowrap;
background:rgb(255 255 255 / .45); cursor:pointer; transition: width .3s cubic-bezier(.22,1,.36,1), background-color .3s; }
.crs-item::scroll-marker:target-current { width:26px; background:#F472B6; }
.crs-item::scroll-marker:focus-visible { outline:2px solid #fff; outline-offset:2px; }
}
@media (prefers-reduced-motion: reduce) {
.crs-list { scroll-behavior:auto; }
@supports (scroll-marker-group: after) {
.crs-item::scroll-marker, .crs-list::scroll-button(*) { transition:none; }
}
}
AI PROMPT
JavaScript を書かずに、前へ・次へのボタンと現在地の印をブラウザに作らせるカルーセルを CSS だけで作って。ul を display:flex・overflow-x:auto・scroll-snap-type:x mandatory・scroll-behavior:smooth にし、li を flex:0 0 100%・scroll-snap-align:center で1枚ずつ止まるスライドにする(ul には aria-label と tabindex="0" を付けてキーボードでもスクロールできるようにする)。@supports (scroll-marker-group: after) の中で、ul に anchor-name と scroll-marker-group: after を指定し、ul::scroll-button(left) に content:"‹" / "前へ"、ul::scroll-button(right) に content:"›" / "次へ"(/ の後ろは読み上げ用の名前)を付けて、anchor の位置指定で ul の左右の中央に丸いボタンとして重ねる(ボタンが本文に重ならないよう li の左右の padding を広げる)。端まで来て押せない時は :disabled で薄くする。li::scroll-marker は content: attr(data-label)(各 li に data-label で名前を付ける)で 10px の丸にし、文字は text-indent と overflow:hidden で隠す。今表示中のスライドの印は :target-current で幅 26px のピンクの横長にし、width の transition で伸びる。::scroll-marker-group はスクロール容器の内側(li の並びの最後)に作られるため、そのままでは横スクロールの先に隠れる。position:absolute・position-anchor で ul の下(top: calc(anchor(bottom) + 14px))へ出し、justify-self: anchor-center で中央に揃え、display:flex で印を並べる(印は display:block にしないと幅が効かない)。@supports が効かないブラウザ向けに「横スクロールで送ってください」の注記を出し、対応ブラウザでは消す。prefers-reduced-motion: reduce の時は scroll-behavior:auto にし、::scroll-marker と ::scroll-button の transition も止める(擬似要素は .demo-stage * のような一括指定では止まらない)。 Acceptance criteria: works with prefers-reduced-motion (animation disabled), zero console errors, zero layout shift
AIエージェントでの実装ガイド → Claude Code でWebアニメーションを実装する
BOOKS · PR · AMAZON
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
学びを止めない · AMAZONサブスク
※当サイトはAmazonアソシエイトプログラムの参加者です。リンクから商品をご購入いただくと、当方に紹介料が発生します。
CREATOR TOOLS · スクール(PR)
※本サイトはアフィリエイトリンクを含みます。リンク経由のご購入・お申し込みで運営者が紹介料を受け取る場合があります。
CSS カルーセルは、横にスクロールする一覧に、前後へ送るボタンと現在地を示す印をブラウザが作る仕組みです。MDN のガイドでは「ブラウザが生成し、開発者がスタイルを付けるスクロールボタンとスクロールマーカーによって、CSS だけで柔軟なカルーセルを作れる機能」と説明されています。ボタンは ::scroll-button()、印は各スライドの ::scroll-marker で、印は scroll-marker-group で指定した場所にまとめて並びます。
当サイトの Swipe Carousel は scroll-snap だけで作る JavaScript なしのスワイプカルーセル、Scroll Snap Sections はセクションの先頭でぴたりと止まる全画面スクロール、Infinite Moving Cards はカードが途切れず横に流れ続ける演出です。本ページは scroll-snap の一覧に、ボタンと現在地の印まで CSS で足す別テーマです。
これまでカルーセルのボタンと印は JavaScript で作り、スクロール位置を監視して現在地を更新する必要がありました。ブラウザに任せると、ボタンの押せる・押せない、印の現在地、キーボードでの操作がまとめて付いてきます。対応が Chromium 系だけのうちは、未対応でもスワイプで使える一覧を土台にして、その上に足す形で使うのが安全です。
どのブラウザで使えますか?
MDN の互換性データ(browser-compat-data 2026-09-24 版)では、::scroll-button()・::scroll-marker・scroll-marker-group は Chrome・Edge 135 から使え、Firefox と Safari は未対応で、仕様も実験的な扱いです。本実装はボタンと印の指定を @supports (scroll-marker-group: after) の中にまとめ、未対応のブラウザでは scroll-snap で1枚ずつ止まる横スクロールの一覧として使えるようにしています。
@supports selector(::scroll-marker) で判定しないのはなぜですか?
2026年9月26日に当サイトで Chromium 148 を使って試したところ、CSS.supports('selector(::scroll-marker)') は対応しているブラウザでも false を返しました(li::scroll-marker のように前に要素を書くと true)。対応を調べたい時は、プロパティの scroll-marker-group で判定すると、対応しているブラウザだけで true になりました。
コードは無料で使えますか?
はい。Motion LabのコードスニペットとAIプロンプトは、クレジット表記なしで自由に使えます。コピーしてそのままプロジェクトに利用できます。