純CSSスクロール出現(animation-timeline: view())
animation-timeline: view() を使い、JavaScript無しで複数カードをビューポート進入時にopacity+translateYで出現させます。@supportsでガードし非対応ブラウザでは普通に表示。
ライブデモ
使用例(お題: SaaS FlowDesk)
この技法を「SaaS FlowDesk」というテーマのダミーサイトで実際に使った例です。
HTML
<!-- FlowDesk 機能一覧。純CSS(view())で各機能カードがスクロール進入時に出現 -->
<div class="cdr-scroller">
<header class="cdr-hero">
<p class="cdr-kicker">FlowDesk / FEATURES</p>
<h1>毎日の業務を、なめらかに。</h1>
<p class="cdr-lead">チームの仕事をひとつにまとめる<br>4つのコア機能をご紹介します。</p>
<span class="cdr-arrow" aria-hidden="true">↓</span>
</header>
<section class="cdr-list">
<article class="cdr-card">
<span class="cdr-ico" aria-hidden="true">📋</span>
<h2>スマートボード</h2>
<p>案件・タスクをドラッグで整理。担当と期限がひと目でわかります。</p>
<span class="cdr-num">01</span>
</article>
<article class="cdr-card">
<span class="cdr-ico" aria-hidden="true">⚡</span>
<h2>ワークフロー自動化</h2>
<p>条件に合えば通知・割り当てを自動実行。手作業のミスを減らします。</p>
<span class="cdr-num">02</span>
</article>
<article class="cdr-card">
<span class="cdr-ico" aria-hidden="true">📊</span>
<h2>リアルタイム分析</h2>
<p>進捗と負荷をダッシュボードで可視化。意思決定が速くなります。</p>
<span class="cdr-num">03</span>
</article>
<article class="cdr-card">
<span class="cdr-ico" aria-hidden="true">🔒</span>
<h2>権限とセキュリティ</h2>
<p>役割ごとのアクセス制御と監査ログ。安心して全社展開できます。</p>
<span class="cdr-num">04</span>
</article>
</section>
</div>
CSS
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--navy: #0f1b34;
--blue: #4f7cff;
--white: #ffffff;
}
body {
font-family: "Hiragino Kaku Gothic ProN", "Segoe UI", sans-serif;
background: var(--navy);
color: var(--white);
-webkit-font-smoothing: antialiased;
}
/* 内部スクロール領域(view()の基準) */
.cdr-scroller {
width: 100%;
height: 100vh;
max-height: 100%;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
scrollbar-color: var(--blue) transparent;
scroll-behavior: smooth;
}
.cdr-hero {
height: 260px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
text-align: center;
padding: 0 22px;
background:
radial-gradient(circle at 25% 15%, rgba(79,124,255,.4), transparent 55%),
radial-gradient(circle at 80% 90%, rgba(79,124,255,.22), transparent 55%),
var(--navy);
}
.cdr-kicker { letter-spacing: .3em; font-size: .64rem; color: #9db5ff; }
.cdr-hero h1 { font-size: 1.8rem; font-weight: 800; }
.cdr-lead { font-size: .86rem; line-height: 1.7; color: #aeb9da; }
.cdr-arrow { font-size: 1.4rem; color: var(--blue); animation: cdrBob 1.6s ease-in-out infinite; }
@keyframes cdrBob {
0%, 100% { transform: translateY(0); opacity: .6; }
50% { transform: translateY(8px); opacity: 1; }
}
.cdr-list {
max-width: 540px;
margin: 0 auto;
padding: 34px 24px 90px;
display: grid;
gap: 18px;
}
.cdr-card {
position: relative;
padding: 22px 22px 22px 70px;
border-radius: 16px;
background: rgba(255,255,255,.045);
border: 1px solid rgba(79,124,255,.2);
box-shadow: 0 18px 40px -28px rgba(0,0,0,.9);
}
.cdr-ico {
position: absolute;
left: 20px; top: 22px;
font-size: 1.6rem;
line-height: 1;
}
.cdr-num {
position: absolute;
right: 18px; top: 18px;
font-weight: 800;
font-size: .8rem;
color: rgba(157,181,255,.5);
}
.cdr-card h2 { font-size: 1.1rem; font-weight: 800; margin-bottom: 6px; }
.cdr-card p { font-size: .86rem; line-height: 1.7; color: #b9c4e2; }
/* 対応ブラウザのみ:純CSSのスクロール駆動アニメ */
@supports (animation-timeline: view()) {
.cdr-card {
/* 初期は透明+下ずらし。view進入で復帰 */
opacity: 0;
transform: translateY(40px) scale(.97);
animation: cdrReveal linear both;
animation-timeline: view();
/* 枠の下端手前から中央までで出現を完了させる */
animation-range: entry 5% cover 32%;
}
@keyframes cdrReveal {
to { opacity: 1; transform: translateY(0) scale(1); }
}
}
/* 動きを減らす設定では常時表示 */
@media (prefers-reduced-motion: reduce) {
.cdr-card { opacity: 1 !important; transform: none !important; animation: none !important; }
.cdr-arrow { animation: none; }
}
JavaScript
// FlowDesk 機能一覧:出現演出そのものは純CSS(animation-timeline: view())。
// JSは「操作前でも演出が見えるように」枠内を軽く自動スクロールするだけ。
(() => {
const scroller = document.querySelector('.cdr-scroller');
if (!scroller) return; // null安全
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduce) return; // 動きを減らす設定では自動スクロールしない
let auto = true;
const stop = () => { auto = false; };
// ユーザー操作が入ったら自動送りを止める
['wheel', 'touchstart', 'pointerdown'].forEach(ev =>
scroller.addEventListener(ev, stop, { passive: true }));
setTimeout(function step() {
if (!auto) return;
const max = scroller.scrollHeight - scroller.clientHeight;
if (scroller.scrollTop >= max - 1) return; // 最下部で停止
scroller.scrollTop += 1.6;
requestAnimationFrame(step);
}, 700);
})();
コード
HTML
<!-- 純CSSのスクロール出現。JS無し・内部にスクロール領域を作る -->
<div class="cdr-scroller">
<header class="cdr-hero">
<p class="cdr-kicker">CSS SCROLL-DRIVEN</p>
<h1>JS不要で出現</h1>
<p class="cdr-lead">animation-timeline: view() で<br>ビューポート進入をCSSだけで検知</p>
<span class="cdr-arrow" aria-hidden="true">↓</span>
</header>
<section class="cdr-list">
<article class="cdr-card">
<span class="cdr-num">01</span>
<h2>view() タイムライン</h2>
<p>要素がスクロール枠に入る進捗に合わせてアニメを駆動します。</p>
</article>
<article class="cdr-card">
<span class="cdr-num">02</span>
<h2>JavaScriptゼロ</h2>
<p>IntersectionObserver無しでフェードアップを実現できます。</p>
</article>
<article class="cdr-card">
<span class="cdr-num">03</span>
<h2>@supports でガード</h2>
<p>非対応ブラウザでは初期状態のまま普通に表示されます。</p>
</article>
<article class="cdr-card">
<span class="cdr-num">04</span>
<h2>滑らかで軽量</h2>
<p>コンポジタ側で動くため、メインスレッドを塞ぎません。</p>
</article>
</section>
</div>
CSS
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg: #0d1117;
--ink: #eef1ff;
--accent: #5eead4;
--accent2: #818cf8;
}
body {
font-family: "Segoe UI", system-ui, sans-serif;
background: var(--bg);
color: var(--ink);
-webkit-font-smoothing: antialiased;
}
/* 内部スクロール領域(プレビュー枠内で完結) */
.cdr-scroller {
width: 100%;
height: 100vh;
max-height: 100%;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
scroll-behavior: smooth;
}
.cdr-hero {
height: 280px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
text-align: center;
background:
radial-gradient(circle at 25% 15%, rgba(129,140,248,.4), transparent 55%),
radial-gradient(circle at 80% 85%, rgba(94,234,212,.32), transparent 55%),
var(--bg);
}
.cdr-kicker { letter-spacing: .34em; font-size: .68rem; color: var(--accent); }
.cdr-hero h1 { font-size: 2.3rem; font-weight: 800; letter-spacing: .02em; }
.cdr-lead { font-size: .88rem; line-height: 1.6; color: #b7bce0; }
.cdr-arrow { font-size: 1.5rem; animation: cdrBob 1.6s ease-in-out infinite; }
@keyframes cdrBob {
0%, 100% { transform: translateY(0); opacity: .6; }
50% { transform: translateY(8px); opacity: 1; }
}
.cdr-list {
max-width: 560px;
margin: 0 auto;
padding: 36px 24px 90px;
display: grid;
gap: 22px;
}
.cdr-card {
position: relative;
padding: 26px 26px 26px 64px;
border-radius: 16px;
background: rgba(255,255,255,.045);
border: 1px solid rgba(255,255,255,.09);
box-shadow: 0 18px 40px -28px rgba(0,0,0,.9);
}
.cdr-num {
position: absolute;
left: 22px; top: 26px;
font-weight: 800;
font-size: 1.1rem;
background: linear-gradient(120deg, var(--accent), var(--accent2));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.cdr-card h2 { font-size: 1.12rem; margin-bottom: 6px; }
.cdr-card p { font-size: .9rem; line-height: 1.6; color: #c2c6e4; }
/* 対応ブラウザのみ:純CSSのスクロール駆動アニメ */
@supports (animation-timeline: view()) {
.cdr-card {
/* 初期は透明+下ずらし。view進入で復帰 */
opacity: 0;
transform: translateY(40px) scale(.97);
animation: cdrReveal linear both;
animation-timeline: view();
/* 枠の下端手前から中央までで出現を完了させる */
animation-range: entry 5% cover 32%;
}
@keyframes cdrReveal {
to { opacity: 1; transform: translateY(0) scale(1); }
}
}
/* 動きを減らす設定では常時表示 */
@media (prefers-reduced-motion: reduce) {
.cdr-card { opacity: 1 !important; transform: none !important; animation: none !important; }
.cdr-arrow { animation: none; }
}
JavaScript
// 出現演出そのものは純CSS(animation-timeline: view())。
// JSは「操作前でも演出が見えるように」枠内を軽く自動スクロールするだけ。
(() => {
const scroller = document.querySelector('.cdr-scroller');
if (!scroller) return; // null安全
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduce) return; // 動きを減らす設定では自動スクロールしない
let auto = true;
const stop = () => { auto = false; };
// ユーザー操作が入ったら自動送りを止める
scroller.addEventListener('wheel', stop, { passive: true });
scroller.addEventListener('touchstart', stop, { passive: true });
scroller.addEventListener('pointerdown', stop);
setTimeout(function step() {
if (!auto) return;
const max = scroller.scrollHeight - scroller.clientHeight;
if (scroller.scrollTop >= max - 1) return; // 最下部で停止
scroller.scrollTop += 1.6;
requestAnimationFrame(step);
}, 700);
})();
🤖 AIエージェント用プロンプト
このままコピーしてAIに貼り付け「追加する場所」だけ書き換えればOK
あなたは熟練のフロントエンドエンジニアです。私のWebサイトに「純CSSスクロール出現(animation-timeline: view())」の効果を追加してください。
# 追加してほしい効果
純CSSスクロール出現(animation-timeline: view())(スクロール演出)
animation-timeline: view() を使い、JavaScript無しで複数カードをビューポート進入時にopacity+translateYで出現させます。@supportsでガードし非対応ブラウザでは普通に表示。
# 追加する場所
👉【ここに対象箇所を記入:例「トップのヒーローセクション」「お問い合わせボタン」「記事カードの一覧」など】
# 参考実装(この見た目・挙動を再現してください)
【HTML】
<!-- 純CSSのスクロール出現。JS無し・内部にスクロール領域を作る -->
<div class="cdr-scroller">
<header class="cdr-hero">
<p class="cdr-kicker">CSS SCROLL-DRIVEN</p>
<h1>JS不要で出現</h1>
<p class="cdr-lead">animation-timeline: view() で<br>ビューポート進入をCSSだけで検知</p>
<span class="cdr-arrow" aria-hidden="true">↓</span>
</header>
<section class="cdr-list">
<article class="cdr-card">
<span class="cdr-num">01</span>
<h2>view() タイムライン</h2>
<p>要素がスクロール枠に入る進捗に合わせてアニメを駆動します。</p>
</article>
<article class="cdr-card">
<span class="cdr-num">02</span>
<h2>JavaScriptゼロ</h2>
<p>IntersectionObserver無しでフェードアップを実現できます。</p>
</article>
<article class="cdr-card">
<span class="cdr-num">03</span>
<h2>@supports でガード</h2>
<p>非対応ブラウザでは初期状態のまま普通に表示されます。</p>
</article>
<article class="cdr-card">
<span class="cdr-num">04</span>
<h2>滑らかで軽量</h2>
<p>コンポジタ側で動くため、メインスレッドを塞ぎません。</p>
</article>
</section>
</div>
【CSS】
* { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg: #0d1117;
--ink: #eef1ff;
--accent: #5eead4;
--accent2: #818cf8;
}
body {
font-family: "Segoe UI", system-ui, sans-serif;
background: var(--bg);
color: var(--ink);
-webkit-font-smoothing: antialiased;
}
/* 内部スクロール領域(プレビュー枠内で完結) */
.cdr-scroller {
width: 100%;
height: 100vh;
max-height: 100%;
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
scroll-behavior: smooth;
}
.cdr-hero {
height: 280px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
text-align: center;
background:
radial-gradient(circle at 25% 15%, rgba(129,140,248,.4), transparent 55%),
radial-gradient(circle at 80% 85%, rgba(94,234,212,.32), transparent 55%),
var(--bg);
}
.cdr-kicker { letter-spacing: .34em; font-size: .68rem; color: var(--accent); }
.cdr-hero h1 { font-size: 2.3rem; font-weight: 800; letter-spacing: .02em; }
.cdr-lead { font-size: .88rem; line-height: 1.6; color: #b7bce0; }
.cdr-arrow { font-size: 1.5rem; animation: cdrBob 1.6s ease-in-out infinite; }
@keyframes cdrBob {
0%, 100% { transform: translateY(0); opacity: .6; }
50% { transform: translateY(8px); opacity: 1; }
}
.cdr-list {
max-width: 560px;
margin: 0 auto;
padding: 36px 24px 90px;
display: grid;
gap: 22px;
}
.cdr-card {
position: relative;
padding: 26px 26px 26px 64px;
border-radius: 16px;
background: rgba(255,255,255,.045);
border: 1px solid rgba(255,255,255,.09);
box-shadow: 0 18px 40px -28px rgba(0,0,0,.9);
}
.cdr-num {
position: absolute;
left: 22px; top: 26px;
font-weight: 800;
font-size: 1.1rem;
background: linear-gradient(120deg, var(--accent), var(--accent2));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.cdr-card h2 { font-size: 1.12rem; margin-bottom: 6px; }
.cdr-card p { font-size: .9rem; line-height: 1.6; color: #c2c6e4; }
/* 対応ブラウザのみ:純CSSのスクロール駆動アニメ */
@supports (animation-timeline: view()) {
.cdr-card {
/* 初期は透明+下ずらし。view進入で復帰 */
opacity: 0;
transform: translateY(40px) scale(.97);
animation: cdrReveal linear both;
animation-timeline: view();
/* 枠の下端手前から中央までで出現を完了させる */
animation-range: entry 5% cover 32%;
}
@keyframes cdrReveal {
to { opacity: 1; transform: translateY(0) scale(1); }
}
}
/* 動きを減らす設定では常時表示 */
@media (prefers-reduced-motion: reduce) {
.cdr-card { opacity: 1 !important; transform: none !important; animation: none !important; }
.cdr-arrow { animation: none; }
}
【JavaScript】
// 出現演出そのものは純CSS(animation-timeline: view())。
// JSは「操作前でも演出が見えるように」枠内を軽く自動スクロールするだけ。
(() => {
const scroller = document.querySelector('.cdr-scroller');
if (!scroller) return; // null安全
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (reduce) return; // 動きを減らす設定では自動スクロールしない
let auto = true;
const stop = () => { auto = false; };
// ユーザー操作が入ったら自動送りを止める
scroller.addEventListener('wheel', stop, { passive: true });
scroller.addEventListener('touchstart', stop, { passive: true });
scroller.addEventListener('pointerdown', stop);
setTimeout(function step() {
if (!auto) return;
const max = scroller.scrollHeight - scroller.clientHeight;
if (scroller.scrollTop >= max - 1) return; // 最下部で停止
scroller.scrollTop += 1.6;
requestAnimationFrame(step);
}, 700);
})();
# 外部ライブラリ
なし(追加ライブラリ不要)
# 守ってほしいこと
- 既存のHTML構造・レイアウト・デザインを壊さないこと。必要に応じてクラス名・色・サイズを私のサイトに合わせて調整してよい。
- クラス名やidが既存と衝突しないよう、必要なら接頭辞で名前空間を分けること。
- レスポンシブ対応と prefers-reduced-motion への配慮を入れること。
- 私のサイトのフレームワーク(React / Vue / 素のHTML など)に合わせて実装すること。不明な場合は素のHTML/CSS/JSで提示し、組み込み手順も説明すること。
- 変更後の確認手順も簡潔に教えてください。