CSS Scroll-Driven Animations 実践ガイド|JSなしで実現するスクロール連動アニメーション【2026年版】
CSS scroll()・view() を使ったスクロール駆動アニメーションの実装方法を解説。プログレスバー、フェードイン、パララックスをJavaScriptなしで実現する具体例付き。
スクロールに連動したアニメーションは、これまで JavaScript の scroll イベントや Intersection Observer で実装するのが一般的でした。しかし CSS Scroll-Driven Animations の登場により、JavaScript を一行も書かずにスクロール連動アニメーションを実現できるようになりました。しかもメインスレッドをブロックしないため、60fps の滑らかなアニメーションが保証されます。
CSS Scroll-Driven Animations とは
CSS Scroll-Driven Animations は、従来の CSS Animations(時間ベース)を拡張し、スクロール位置をタイムラインとして使う仕組みです。2つのタイムラインが用意されています。
Scroll Progress Timeline(scroll())
スクロールコンテナのスクロール位置(0%〜100%)に連動するタイムラインです。ページ全体のスクロールに応じたアニメーションに使います。
/* ページスクロールに連動するプログレスバー */
.progress-bar {
position: fixed;
top: 0;
left: 0;
height: 3px;
background: linear-gradient(to right, #0ea5e9, #a855f7);
transform-origin: left;
animation: grow-progress linear;
animation-timeline: scroll();
}
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
たったこれだけで、ページ上部に読了プログレスバーが実装できます。JavaScript は不要です。
View Progress Timeline(view())
要素がビューポートに入ってから出るまでの進行状況(0%〜100%)に連動するタイムラインです。要素が画面に現れたタイミングでのフェードインなどに使います。
/* スクロールで画面に入ったらフェードイン */
.fade-in {
animation: fade-in-up linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
@keyframes fade-in-up {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
animation-range: entry 0% entry 100% は、要素がビューポートに入り始めてから完全に入りきるまでの範囲でアニメーションを実行します。
ブラウザ対応状況(2026年4月)
| ブラウザ | サポート状況 |
|---|---|
| Chrome 115+ | 完全サポート |
| Edge 115+ | 完全サポート |
| Safari 26+ | 完全サポート |
| Opera 101+ | 完全サポート |
| Firefox | フラグ付き(部分サポート) |
2026年4月時点で主要ブラウザの大半が対応済みです。Firefox はまだフラグの背後にありますが、layout.css.scroll-driven-animations.enabled を有効にすることで利用可能です。
プログレッシブ・エンハンスメント
未対応ブラウザへのフォールバックは @supports で簡単に実現できます。
/* 基本スタイル(すべてのブラウザ) */
.card {
opacity: 1;
transform: none;
}
/* Scroll-Driven Animations 対応ブラウザのみ */
@supports (animation-timeline: view()) {
.card {
animation: fade-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
}
実践パターン5選
パターン1: 読了プログレスバー
ブログ記事の読了進捗を表示するプログレスバーです。
<div class="progress-bar" aria-hidden="true"></div>
<article class="post-content">
<!-- 記事本文 -->
</article>
.progress-bar {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 3px;
background: linear-gradient(90deg, #0ea5e9, #a855f7, #f97316);
transform-origin: left;
transform: scaleX(0);
z-index: 100;
animation: progress linear;
animation-timeline: scroll();
}
@keyframes progress {
to { transform: scaleX(1); }
}
パフォーマンスのポイント: transform: scaleX() はコンポジターレイヤーで処理されるため、width のアニメーションよりも圧倒的に高速です。JavaScript の scroll イベントリスナーと比較して、CPU 使用率が 50% → 2% に低減した事例も報告されています。
パターン2: カードのスタッガーフェードイン
記事カードが順番にフェードインするアニメーションです。
.post-card {
animation: card-appear linear both;
animation-timeline: view();
animation-range: entry 0% entry 80%;
}
@keyframes card-appear {
from {
opacity: 0;
transform: translateY(40px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
JavaScript の Intersection Observer で同等の機能を実装すると約20行のコードが必要ですが、CSS Scroll-Driven Animations では 6行の CSS で済みます。
パターン3: パララックス背景
スクロールに応じて背景がゆっくり動くパララックス効果です。
.hero-section {
position: relative;
overflow: hidden;
}
.hero-bg {
position: absolute;
inset: -20% 0;
animation: parallax linear;
animation-timeline: scroll();
}
@keyframes parallax {
from { transform: translateY(-10%); }
to { transform: translateY(10%); }
}
inset: -20% 0 で背景を上下に20%拡張しておくことで、スクロール時に背景が切れるのを防ぎます。
パターン4: ヘッダーの縮小アニメーション
スクロールするとヘッダーがコンパクトになるアニメーションです。
.site-header {
position: sticky;
top: 0;
animation: shrink-header linear forwards;
animation-timeline: scroll();
animation-range: 0px 200px;
}
@keyframes shrink-header {
from {
padding-block: 1.5rem;
font-size: 1.25rem;
}
to {
padding-block: 0.5rem;
font-size: 1rem;
backdrop-filter: blur(12px);
background: rgba(255, 255, 255, 0.8);
}
}
animation-range: 0px 200px で、スクロール開始から200pxまでの間でアニメーションが実行されます。200px以降はアニメーション終了状態が維持されます。
パターン5: 画像のスケールイン
スクロールで画像が拡大しながら表示されるシネマティックな効果です。
.hero-image {
animation: scale-in linear both;
animation-timeline: view();
animation-range: entry 0% cover 40%;
}
@keyframes scale-in {
from {
transform: scale(1.3);
clip-path: inset(15%);
}
to {
transform: scale(1);
clip-path: inset(0%);
}
}
clip-path と transform: scale() を組み合わせることで、画像が枠から溢れずにスケールインする効果を実現しています。
animation-range の詳細
animation-range は Scroll-Driven Animations の中でも最も重要なプロパティです。View Progress Timeline で使える範囲キーワードを整理します。
| キーワード | 意味 |
|---|---|
entry | 要素がビューポートに入り始める〜完全に入る |
exit | 要素がビューポートから出始める〜完全に出る |
contain | 要素が完全にビューポート内にある期間 |
cover | 要素がビューポートと交差している全期間 |
/* 要素が画面に入り始めて30%入ったところで完了 */
animation-range: entry 0% entry 30%;
/* 要素が画面の中央付近にある時だけ */
animation-range: contain 20% contain 80%;
/* 画面に入り始めてから出終わるまで全期間 */
animation-range: cover 0% cover 100%;
JavaScript 実装との比較
同じフェードインアニメーションを JavaScript(Intersection Observer)と CSS Scroll-Driven Animations で比較します。
JavaScript(Intersection Observer)
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.1, rootMargin: '0px 0px -50px 0px' }
);
document.querySelectorAll('.fade-in').forEach((el) => {
observer.observe(el);
});
.fade-in {
opacity: 0;
transform: translateY(20px);
transition: opacity 0.6s, transform 0.6s;
}
.fade-in.visible {
opacity: 1;
transform: translateY(0);
}
合計: JS 14行 + CSS 10行 = 24行
CSS Scroll-Driven Animations
.fade-in {
animation: fade-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 100%;
}
@keyframes fade-in {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
合計: CSS 9行のみ
パフォーマンス比較
| 指標 | JS (IO) | CSS SDA |
|---|---|---|
| コード量 | 24行 | 9行 |
| メインスレッド負荷 | あり | なし |
| FPS | 状況依存 | 常に60fps |
| バンドルサイズ | +0.5KB | 0 |
CSS Scroll-Driven Animations はコンポジタースレッドで実行されるため、メインスレッドの処理(DOM 操作、API コール等)に影響しません。これが最大のメリットです。
アクセシビリティ対応
prefers-reduced-motion メディアクエリで、動きを減らしたいユーザーに配慮します。
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
scroll-behavior: auto !important;
}
}
これにより、OS の「視差効果を減らす」設定を有効にしているユーザーには、アニメーションが即座に完了状態になります。
Astro / Tailwind CSS での活用例
本ブログ(Code & Craft)で使用している Astro + Tailwind CSS v4 環境での実装例です。
/* global.css に追加 */
@supports (animation-timeline: view()) {
.scroll-fade {
animation: scroll-fade-in linear both;
animation-timeline: view();
animation-range: entry 0% entry 80%;
}
}
@keyframes scroll-fade-in {
from {
opacity: 0;
transform: translateY(24px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
<!-- Astro コンポーネントで使用 -->
<section class="scroll-fade">
<h2>最新記事</h2>
<!-- ... -->
</section>
@supports でラップしているため、未対応ブラウザでは要素が最初から表示された状態になり、レイアウトが崩れることはありません。
まとめ
CSS Scroll-Driven Animations は、2026年の Web 開発における最も実用的な新機能の一つです。
導入すべき理由:
- JavaScript コードの削減(平均60-80%のコード削減)
- メインスレッドをブロックしない60fpsアニメーション
@supportsによる安全なプログレッシブ・エンハンスメント- 読了プログレスバー、フェードイン、パララックスなど多彩な表現
注意点:
- Firefox はまだフラグの背後にある(2026年4月時点)
- 複雑な条件分岐が必要なアニメーションには向かない(JavaScript が適切)
prefers-reduced-motionへの対応は必須
まずは 読了プログレスバー(パターン1)から導入し、効果を実感してみてください。JavaScript のスクロールイベントを一掃できるインパクトは、一度体験すると戻れなくなるはずです。
参考リンク
- CSS scroll-driven animations - MDN Web Docs
- Animate elements on scroll with Scroll-driven animations - Chrome for Developers
- Scroll-driven-animations.style(インタラクティブデモ集)
- A Practical Introduction to Scroll-Driven Animations with CSS - Codrops
- Scroll-driven animations case studies - Chrome for Developers