【Next.js】ヒーローセクションに入場と背景のアニメーションを付ける方法を解説
公開日: 2026年9月8日 · 読了目安 12 分
Next.js のヒーローセクションに、時間差で揃う入場アニメーション、ゆっくり動く背景、スクロールに連動するパララックスを付ける方法をコード付きで解説。CSS だけの方法と Motion(旧 Framer Motion)の方法、動きを減らす設定、公開前のチェックまで。
Next.js のヒーローセクションにアニメーションを付けるには
Next.js でサイトのトップを作り、見出しと説明文とボタンを並べた。
構成は悪くないのに、開いた瞬間に何も起きないので、写真付きのバナーと同じに見えてしまう。ヒーローセクションでよくある悩みです。
解決するには、派手な素材を足すより先に「何を動かすか」を決めます。候補は次の3つです。
要素が時間差で揃う入場
ゆっくり動く背景
カーソルへの反応
このうち2つまでに絞ります。入場は CSS で、スクロールに連動する動きは Motion(旧 Framer Motion)で作るのがいちばん手堅い方法です。
この記事では、次の4つをコード付きで作ります。
CSS だけで作る、時間差の入場アニメーション
CSS だけで作る、ゆっくり動く背景の光
Motion で作る、まとめて制御する入場とスクロール連動のパララックス
動きを減らす設定(prefers-reduced-motion)への対応
動きのないヒーローセクションがバナーに見える3つの理由
コードに入る前に、ヒーローが地味に見える原因を確認しておきます。原因がわかると、どこに動きを足せばよいかが決まります。
ヒーローのすべての要素が同時に表示される
見出しや本文、ボタンや画像が同じタイミングで出ると、目線の順番が生まれません。要素が少しずつ揃うと、読む順番が自然に伝わります。
スクロールした瞬間にヒーローが終わる
画面の高さを過ぎたとたんに次のセクションへ切り替わると、ファーストビューは1枚のポスターになります。背景が本文より遅れて動くだけでも、ページ全体の導入として働きます。
ヒーローに動きを付けすぎている
すべての要素が弾み、背景が激しくループし、マウスを追いかけてくる。
こうなると、印象より疲れが先に来ます。入場・背景・ホバーのうち2つまでにすると破綻しにくくなります。
この記事で作るヒーローセクションのアニメーション
目指す動きを先に決めておきます。数値は、そのままコードに使っています。
ラベルが先に現れ、続いて見出しが下から20px上がる。本文とボタンは0.08秒ずつ遅れて揃う
背景のぼんやりした光が、18秒かけてゆっくり位置と大きさを変える
少しスクロールすると、背景の写真が本文より遅れて動き、文字は薄くなっていく
CTA ボタンは、ホバーでほんの少しだけ縮む
3D やシェーダーは扱いません。多くの LP では、入場と背景の動きだけで十分に印象が変わります。
CSS だけでヒーローの入場アニメーションを作る
入場とホバーだけなら、ライブラリは要りません。サーバーコンポーネントのまま書けるので、ブラウザに送る JavaScript も増えません。
アニメーションを1つ作り、開始のタイミングだけをずらす
全要素に同じアニメーションを付けると、同時に出るのと変わりません。
遅延の段差は、次のくらいで十分です。
ラベル:0秒(すぐ)
見出し:0.08秒
本文:0.16秒
ボタン:0.24秒
動く距離は16〜24px にします。大きく動かすと安っぽく見えます。
最後の @media は、OS で「視差効果を減らす」などを有効にしている人向けの設定です。アニメーションを止め、最初から表示された状態にします。
@keyframes hero-rise {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.hero-rise {
animation: hero-rise 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@media (prefers-reduced-motion: reduce) {
.hero-rise {
animation: none;
}
}ヒーローの入場アニメーションをページに組み込む
App Router では、ヒーローを page.tsx の最初のセクションに置きます。高さは min-h-[100svh] にすると、スマホのアドレスバーの出し入れでずれにくくなります。
ボタンのホバーは scale を少し変えるだけにし、影や色の点滅は入場とぶつかるので避けます。
入場アニメーションと同じ要素に transform のホバーを付けると、アニメーションの値に上書きされて効きません。
そこでボタンを div で包み、入場は外側に、ホバーの scale は内側のリンクに付けます。
export default function Page() {
return (
<section className="relative isolate min-h-[100svh] overflow-hidden px-6 pt-28">
<p className="hero-rise text-sm tracking-wide">2026 Launch</p>
<h1 className="hero-rise mt-4 text-4xl font-semibold" style={{ animationDelay: "80ms" }}>
動きのあるファーストビューを、今日出す
</h1>
<p className="hero-rise mt-5 max-w-xl text-zinc-600" style={{ animationDelay: "160ms" }}>
見出し、本文、ボタンの順に揃えるだけで印象は変わります。
</p>
<div className="hero-rise mt-8" style={{ animationDelay: "240ms" }}>
<a
href="#features"
className="inline-flex rounded-full bg-zinc-950 px-5 py-3 text-white transition hover:scale-[0.98]"
>
詳しく見る
</a>
</div>
</section>
);
}CSS だけでヒーローの背景をゆっくり動かす
背景に動きがあると、文字が止まっていてもページが生きて見えます。
ここでは、ぼんやりした光を18秒かけて動かします。速くすると目障りになるので、気づくか気づかないかくらいの速さにしてください。
光は radial-gradient(円形のグラデーション)で、最初からぼかした形にしておきます。
ぼかし(filter: blur)をアニメーションさせると重くなります。そのため、動かすのは位置と大きさ(transform)だけにします。
@keyframes hero-drift {
0%,
100% {
transform: translate3d(0, 0, 0) scale(1);
}
50% {
transform: translate3d(4%, -3%, 0) scale(1.08);
}
}
.hero-glow {
background: radial-gradient(circle at 50% 40%, rgb(56 189 248 / 0.35), transparent 60%);
animation: hero-drift 18s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
.hero-glow {
animation: none;
}
}背景の光をヒーローセクションの中に置く
光は、先ほどの section の最初の子として置きます。
外枠に relative と isolate を付けておくと、-z-10 の光が本文の後ろに収まります。
aria-hidden を付け、読み上げの対象から外します。
<div
aria-hidden
className="hero-glow pointer-events-none absolute inset-x-0 -top-1/2 -z-10 h-[120%]"
/>Framer Motion でヒーローの入場とパララックスを作る
スクロール量に合わせて位置や透明度を変えたいときは、Motion を使います。npm install motion で追加できます。
古いプロジェクトで framer-motion を使っている場合も、同じ書き方で動きます。
スクロール量を読み取る仕組み(useScroll)や motion を使うファイルには、先頭に "use client"(この部品はブラウザで動かす、という印)が必要です。
ページ全体をブラウザで動く部品にせず、ヒーローだけを別ファイルに切り出してください。
ヒーローの入場の時間差を Framer Motion でまとめて書く
CSS では要素ごとに delay を書きましたが、Motion では親の staggerChildren(子要素を順番に動かす指定)で一度に指定できます。
ヒーローは開いた時点で画面内にあるので、animate で表示と同時に再生します。
下のセクションに使う場合は、whileInView(画面に入ったら動かす指定)にします。
あわせて viewport={{ once: true }} を付けると、スクロールで戻るたびに繰り返されません。
"use client";
import { motion, type Variants } from "motion/react";
const container: Variants = {
hidden: {},
show: { transition: { staggerChildren: 0.08, delayChildren: 0.1 } },
};
const item: Variants = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0, transition: { duration: 0.7, ease: [0.22, 1, 0.36, 1] } },
};
export function Hero() {
return (
<motion.section
variants={container}
initial="hidden"
animate="show"
className="min-h-[100svh] px-6 pt-28"
>
<motion.p variants={item} className="text-sm tracking-wide">2026 Launch</motion.p>
<motion.h1 variants={item} className="mt-4 text-4xl font-semibold">
動きのあるファーストビューを、今日出す
</motion.h1>
<motion.p variants={item} className="mt-5 max-w-xl text-zinc-600">
見出し、本文、ボタンの順に揃えるだけで印象は変わります。
</motion.p>
<motion.a
variants={item}
href="#features"
whileHover={{ scale: 0.98 }}
className="mt-8 inline-flex rounded-full bg-zinc-950 px-5 py-3 text-white"
>
詳しく見る
</motion.a>
</motion.section>
);
}スクロールに合わせてヒーローの背景にパララックスを付ける
パララックスは、背景が手前の文字より遅く動いて奥行きが出る表現です。
まず、スクロール量を読み取る仕組み(useScroll)で、ヒーローのスクロール量を0〜1の数値で取ります。それを値を変換する仕組み(useTransform)で、背景の位置と文字の透明度に変えます。
useReducedMotion は、動きを減らす設定が有効なら true を返します。そのときは背景を動かさないようにしています。
背景は上下に10%ずつ余白を持たせ、動いても端が見えないようにします。
"use client";
import { useRef } from "react";
import { motion, useReducedMotion, useScroll, useTransform } from "motion/react";
export function HeroParallax() {
const ref = useRef<HTMLElement>(null);
const reduce = useReducedMotion();
const { scrollYProgress } = useScroll({
target: ref,
offset: ["start start", "end start"],
});
const bgY = useTransform(scrollYProgress, [0, 1], ["0%", reduce ? "0%" : "15%"]);
const textOpacity = useTransform(scrollYProgress, [0, 0.7], [1, 0]);
return (
<section ref={ref} className="relative isolate min-h-[100svh] overflow-hidden px-6 pt-28">
<motion.div
aria-hidden
style={{ y: bgY, backgroundImage: "url(/hero.jpg)" }}
className="absolute inset-x-0 -top-[10%] -z-10 h-[120%] bg-cover bg-center"
/>
<motion.div style={{ opacity: textOpacity }}>
<h1 className="text-4xl font-semibold text-white">奥行きのあるファーストビュー</h1>
</motion.div>
</section>
);
}ヒーローのアニメーションで弱い動きと強い動きの違い
弱く見えるのは、動く時間(duration)を1.4秒以上にしたり、弾むような動きを使ったりする場合です。LP のヒーローは商品の紹介であり、読み込み画面ではありません。
強く見えるのは、減速寄りのイージングで、画像だけを0.12秒ほど遅らせる場合です。
テキストが先、ビジュアルが後という順番は、雑誌の見開きに近い読み心地になります。
パララックスも背景だけを数パーセント動かす程度にとどめ、文字まで追従させないようにします。
ヒーローのアニメーションを公開する前の確認リスト
動きを付けたら、公開の前に次の5点を確認してください。どれも見落とすと、せっかくの動きが逆効果になります。
動きを減らす設定で、入場と背景のアニメーションが止まるか
スマホのファーストビューに、見出しと申し込みボタンが収まっているか
いちばん大きな画像を、アニメーションで透明にしていないか(表示速度の指標である LCP が遅れる)
ヒーロー以外のセクションに、同じ強さの動きを並べていないか
ページ全体のファイルに、ブラウザで動かす印を書いていないか
3つ目は特に見落としがちです。最初に目に入る写真は、最初から見えている状態にしてください。
フェードさせるのは文字とボタンだけで十分です。
完成したヒーローセクションをコピーして使う方法

スクロールで草木の後ろから見出しが現れる、写真が固定されたまま切り替わる。こうした凝った動きは、この記事の方法を組み合わせても半日以上かかることがあります。
その場合は、ブラウザで実際の動きを見てから、React + Tailwind CSS で作られたヒーローを自分の Next.js に載せる方法もあります。
DesignLayer のカタログでは、動きのあるセクションをライブプレビューで確かめ、ソースをコピーできます。
閲覧にカードは要らず、コピーは無料で日本時間の1日3回までです。https://design-layer.com/ja/components
載せたあとにする作業は、見出し・CTA・画像の差し替えだけです。動きの数値には触れず、文章と画像を先に変えると崩れません。
反対に、ヒーローそのものが製品の中心という場合もあります。1pxまで自分で制御したいなら、Motion で書く方が向いています。
ヒーローセクションのアニメーションでよくある質問
入場とホバー、ゆっくり動く背景なら CSS だけで作れます。
スクロール量に合わせた変化が必要なら、Motion などのライブラリを使います。どちらの場合も、ページ全体をブラウザで動く部品にする必要はありません。
まとめ:Next.js のヒーローセクションに動きを付ける手順
ヒーローは「入場・背景・ホバー」のうち2つまで動かす
入場は CSS のアニメーション1つと、0.08秒ずつの遅れで作れる
背景の光は位置と大きさだけを、18秒ほどかけてゆっくり動かす
スクロールに合わせた動きは Motion で作り、ブラウザで動かす印はヒーローのファイルだけに書く
動きを減らす設定と、最初の写真を隠さないことを公開前に確認する
まずは CSS の入場だけを付けて公開し、物足りなければ背景とパララックスを足していくのが早道です。
見出しの文字を1文字ずつ見せる動きは、次の記事で解説しています。https://design-layer.com/ja/articles/nextjs-text-reveal-animation-guide
Motion の基本から学びたい場合はこちらです。https://design-layer.com/ja/articles/nextjs-framer-motion-guide


