



WithCodeMedia-1-pc
WithCodeMedia-2-pc
WithCodeMedia-3-pc
WithCodeMedia-4-pc




WithCodeMedia-1-sp
WithCodeMedia-2-sp
WithCodeMedia-3-sp
WithCodeMedia-4-sp









Webページにアニメーションを加えたい——そう思って調べると必ず登場するのが @keyframes と animation プロパティです。しかし「書き方がよくわからない」「何個もプロパティがあって覚えられない」という声は初心者だけでなく、中級者にも多く聞かれます。
CSSアニメーションは、JavaScriptを一切書かずにフェードイン・回転・スライドといった動きを実現できる強力な仕組みです。正しい構文と各プロパティの役割を理解すれば、コピペだけに頼らず自分で思い通りのアニメーションを組めるようになります。
この記事では @keyframes の基本構文から animation プロパティの全8値、コピペで使える実用パターン、ループ・往復の制御、アクセシビリティ対応まで、体系的に解説します。読み終えたときには「自分でゼロから書ける」状態を目指してください。
まず最初に、この記事で学べることをまとめておきます。
@keyframes と animation プロパティの役割と関係性を理解できるanimation の8つのサブプロパティ(name / duration / timing-function / delay / iteration-count / direction / fill-mode / play-state)を正確に使いこなせるprefers-reduced-motion の書き方を習得できる@keyframes は、アニメーションの「コマ割り」を定義するCSSのアット規則(at-rule)です。映画のフィルムに例えるなら、各コマにどんなスタイルを適用するかを記述するものだと考えると分かりやすいでしょう。@keyframes 単体では何も動かず、あくまで「動き方の設計図」を定義するだけです。
その設計図を実際に要素に適用するのが animation プロパティです。animation-name で使いたい @keyframes の名前を指定し、animation-duration で再生時間を設定することで、初めてアニメーションが動き出します。つまり @keyframes と animation はセットで使うものと覚えておいてください。
CSSでアニメーションを実現する方法には transition と animation(+ @keyframes)の2種類があります。混同しやすいので、違いを整理しておきます。
transition は「状態が変化したとき(ホバー・クリックなど)に、変化前から変化後へ滑らかに移行する」ための仕組みです。トリガーとなる状態変化が必要で、中間のコマを自分で定義することはできません。
一方 animation は、ページ読み込み直後から自動的に動かすことも、複数の中間ステップを自由に定義することも、ループ再生することも可能です。より複雑で自律的な動きを作るなら @keyframes + animation を使います。
| 比較項目 | transition | animation + @keyframes |
|---|---|---|
| トリガー | 状態変化(hover等)が必要 | 不要(自動再生可能) |
| 中間ステップ | 定義不可(2点間のみ) | 自由に複数定義可能 |
| ループ | 不可 | 可能(infinite) |
| 逆再生・往復 | 不可 | 可能(alternate等) |
| 用途 | ホバーエフェクト等シンプルな変化 | ローディング・演出・複雑な動き |
シンプルなホバーエフェクトなら transition、ページロード時に動くアニメーションや繰り返しのある表現には @keyframes + animation を選ぶのが基本方針です。
@keyframes の構文は非常にシンプルです。@keyframes キーワードの後にアニメーション名(任意の識別子)を書き、波括弧の中に「どの時点でどんなスタイルを適用するか」を記述します。
最も基本的なのは from(開始)と to(終了)を使う書き方です。2点間の変化を定義するだけで済む場合に使います。from は 0%、to は 100% の別名です。
@keyframes fade-in {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
.box {
animation-name: fade-in;
animation-duration: 1s;
}上記の例では、fade-in という名前のキーフレームを定義し、透明(opacity: 0)から不透明(opacity: 1)に変化する1秒間のアニメーションを .box に適用しています。
3点以上の中間ステップを定義したい場合は、パーセント値を使います。0%が開始、100%が終了を表し、その間に任意のパーセント値でステップを追加できます。
@keyframes bounce-color {
0% {
transform: translateY(0);
background-color: #d16176;
}
50% {
transform: translateY(-40px);
background-color: #EFBDB7;
}
100% {
transform: translateY(0);
background-color: #d16176;
}
}
.ball {
animation-name: bounce-color;
animation-duration: 0.8s;
animation-iteration-count: infinite;
}パーセント値は昇順で書くのが慣例ですが、仕様上は順不同でも動作します。ただし可読性のために昇順で書くことを強く推奨します。
より精密なアニメーションを作る場合は、細かくステップを刻みます。以下はスライドインしながらわずかに行き過ぎて戻るような「バネ感」のある動きの例です。
@keyframes slide-scale {
0% { transform: translateX(-60px) scale(0.8); opacity: 0; }
40% { transform: translateX(10px) scale(1.05); opacity: 1; }
70% { transform: translateX(-5px) scale(0.98); }
100% { transform: translateX(0) scale(1); }
}また、複数のパーセント値に同じスタイルを適用したい場合はカンマで区切って書けます(例:0%, 100% { opacity: 0; })。これを使うとコードをすっきりまとめられます。
アニメーション名は英数字・ハイフン・アンダースコアが使えます。数字始まりはNG。既存のCSSキーワード(none、initial、inherit 等)と同じ名前は使えません。また、名前は大文字・小文字を区別します(FadeIn と fadein は別物)。チームで作業する場合はケバブケース(fade-in)に統一するのが一般的です。
animation プロパティはショートハンドで、8つのサブプロパティを一括で指定できます。ショートハンドで書くときの順序は「name → duration → timing-function → delay → iteration-count → direction → fill-mode → play-state」が推奨されます(duration と delay は順序を守らないとブラウザが正しく解釈しない場合があるため特に注意)。
/* ショートハンド(推奨) */
.element {
animation: slide-in 0.6s ease-out 0.2s 1 normal forwards running;
/* name dur timing delay count direction fill-mode play-state */
}
/* 個別指定(プロパティを明示したい場合) */
.element {
animation-name: slide-in;
animation-duration: 0.6s;
animation-timing-function: ease-out;
animation-delay: 0.2s;
animation-iteration-count: 1;
animation-direction: normal;
animation-fill-mode: forwards;
animation-play-state: running;
}8つのプロパティそれぞれの役割・使える値・初期値を以下の表で確認してください。これを手元に置いておくだけで、迷ったときに素早く引けます。
| プロパティ | 役割 | 主な値の例 | 初期値 |
|---|---|---|---|
animation-name | 使用する@keyframesの名前を指定 | 任意の識別子 / none | none |
animation-duration | アニメーション1回分の再生時間 | 0.5s / 300ms | 0s |
animation-timing-function | 速度変化(イージング)の曲線 | ease / linear / cubic-bezier() / steps() | ease |
animation-delay | アニメーション開始までの待機時間(負値も可) | 0.3s / -0.5s | 0s |
animation-iteration-count | 再生回数 | 整数 / infinite | 1 |
animation-direction | 再生方向 | normal / reverse / alternate / alternate-reverse | normal |
animation-fill-mode | 再生前後のスタイル保持 | none / forwards / backwards / both | none |
animation-play-state | 再生・一時停止の切り替え | running / paused | running |
ショートハンドで時間値が2つ登場した場合、ブラウザは「最初の時間値 = duration、2番目の時間値 = delay」と解釈します。animation: fade 0.5s 1s と書いたら「0.5秒で再生、1秒後に開始」です。意図と逆にならないよう注意してください。
animation-delay に負の値を指定すると、アニメーションがすでに指定した時間分だけ経過した状態からスタートします。たとえば animation-delay: -0.5s とすると、0.5秒経過した時点のコマから再生が始まります。複数要素を少しずつずらしてアニメーションさせるときに役立ちます。
ここからは実際の開発で頻繁に使われるアニメーションパターンを5つ紹介します。そのままコピペして使えますが、各プロパティの意味も確認しながら読み進めてください。自分でカスタマイズできるようになることが目標です。
最もよく使われるパターンです。要素が透明な状態から不透明にフェードしながら表れます。animation-fill-mode: forwards を指定することで、アニメーション終了後も to(不透明)の状態を維持します。これを忘れると再生後に元の状態に戻ってしまうので注意してください。
@keyframes fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
.fade-in-element {
animation: fade-in 0.8s ease-out forwards;
}注意を引きたいUI要素(バッジ・通知アイコン・カーソル)に使えます。0% と 100% に同じスタイルを設定し、50% で消えることで自然な点滅を作ります。linear を使うことでイージングなしのシャープな点滅になります。アクセシビリティ面では過度な点滅は避け、後述の prefers-reduced-motion と組み合わせてください。
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.blink-text {
animation: blink 1.2s linear infinite;
}ローディング中の待機表示として最もよく見かけるパターンです。border-top-color だけ色を変えた円を rotate(360deg) で回転させます。linear を使うことで一定速度の滑らかな回転になります。
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #EFBDB7;
border-top-color: #d16176;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}ボールが地面に弾むような動きです。ポイントは animation-timing-function を各キーフレームの内側に書いている点です。0%→50%の上昇は ease-in(加速しながら)、50%→100%の落下は ease-out(減速しながら)にすることで自然な重力感が生まれます。
@keyframes bounce {
0%, 100% {
transform: translateY(0);
animation-timing-function: ease-in;
}
50% {
transform: translateY(-30px);
animation-timing-function: ease-out;
}
}
.bounce-ball {
animation: bounce 0.6s infinite;
}カード・リスト・ナビゲーションのアニメーション表示に使えます。translateX で左からスライドしつつ opacity でフェードインを重ねることで洗練された動きになります。nth-child と animation-delay を組み合わせることで複数要素を順番に登場させるスタッガーアニメーションにも応用できます。
@keyframes slide-in-left {
from {
transform: translateX(-60px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.slide-in {
animation: slide-in-left 0.5s ease-out forwards;
}
/* 複数要素を順番にスライドインさせる場合 */
.slide-in:nth-child(1) { animation-delay: 0s; }
.slide-in:nth-child(2) { animation-delay: 0.1s; }
.slide-in:nth-child(3) { animation-delay: 0.2s; }アニメーションを何度も繰り返したい場合や、行き来させたい場合には animation-iteration-count と animation-direction を使います。さらに再生終了後のスタイルを保持するには animation-fill-mode が欠かせません。この3つをセットで理解しましょう。
animation-iteration-count: infinite は無限ループです。整数を指定すると再生回数を制限できます。animation-direction: alternate は「正方向 → 逆方向 → 正方向 → …」と往復させるキーワードです。alternate-reverse にすると逆方向から始まります。
/* 無限ループ */
.spinner {
animation: spin 1s linear infinite;
}
/* 3回だけ再生 */
.pulse {
animation: pulse 0.5s ease 3;
}
/* 往復アニメーション(行って戻る)×無限ループ */
@keyframes slide-lr {
from { transform: translateX(0); }
to { transform: translateX(100px); }
}
.slide-back-forth {
animation: slide-lr 0.8s ease-in-out infinite alternate;
}alternate を使うと往復がシームレスにつながるため、ローディングバーやプログレス表示のような「行って戻る」アニメーションを自然に作れます。
animation-fill-mode は「アニメーション再生前後に、キーフレームで定義したスタイルを適用するかどうか」を制御します。初期値 none だと終了後に元のスタイルに戻ってしまいます。よく使う値を整理します。
to または 100%)のスタイルを維持する。フェードイン後に消えてほしくない場合に必須。animation-delay 待機中、最初のキーフレーム(from または 0%)のスタイルを適用する。遅延中に要素が「跳び出て見える」問題を防ぐ。forwards と backwards の両方を適用。遅延あり・終了後維持が必要な場合はこれが最も便利。@keyframes drop-in {
from {
transform: translateY(-40px);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
/* forwards: アニメーション終了後にtoのスタイルを保持 */
.modal {
animation: drop-in 0.4s ease-out forwards;
}
/* backwards: 開始前(delay中)にfromのスタイルを適用 */
.delayed-item {
animation: drop-in 0.4s ease-out 0.5s backwards;
}
/* both: forwardsとbackwardsを両方適用 */
.delayed-modal {
animation: drop-in 0.4s ease-out 0.5s both;
}forwards を忘れてフェードインしたのに消えてしまうというミスは初心者に非常に多いです。「終了後に状態を維持したい」と思ったら forwards か both を指定する習慣をつけましょう。
animation-timing-function(イージング)はアニメーションの「速度変化の曲線」を定義します。同じ時間・同じ距離でも、イージングが変わるだけで動きの印象が大きく変わります。
CSSが用意しているキーワードは5種類です。それぞれ内部的には cubic-bezier() の特定値に対応しています。
.box-ease { animation-timing-function: ease; } /* 初期値。開始・終了がゆるやか */
.box-ease-in { animation-timing-function: ease-in; } /* 開始がゆっくり、終了が速い */
.box-ease-out { animation-timing-function: ease-out; } /* 開始が速い、終了がゆるやか */
.box-ease-in-out { animation-timing-function: ease-in-out; } /* 両端がゆるやか */
.box-linear { animation-timing-function: linear; } /* 一定速度(変化なし)*/| キーワード | 対応するcubic-bezier値 | 印象・用途 |
|---|---|---|
ease | cubic-bezier(0.25, 0.1, 0.25, 1) | 自然な動き。汎用 |
ease-in | cubic-bezier(0.42, 0, 1, 1) | 重い感じ。退場アニメ向き |
ease-out | cubic-bezier(0, 0, 0.58, 1) | 軽快な感じ。登場アニメ向き |
ease-in-out | cubic-bezier(0.42, 0, 0.58, 1) | 両端が滑らか。往復向き |
linear | cubic-bezier(0, 0, 1, 1) | 一定速度。回転・進行バー向き |
cubic-bezier(x1, y1, x2, y2) を使うと、既存のキーワードでは表現できない独自のイージングカーブを作れます。y値が0〜1の範囲外になるとオーバーシュート(目標を超えてから戻る)するバネ効果を作れます。
/* cubic-bezier(x1, y1, x2, y2) で独自カーブを定義 */
.custom-ease {
animation-timing-function: cubic-bezier(0.34, 1.56, 0.64, 1);
/* バネのようにオーバーシュートするイージング */
}ブラウザのDevToolsでも cubic-bezier エディタが使えます。ChromeのDevTools → Stylesパネルでイージング値の左にある波形アイコンをクリックすると、ビジュアルで調整できます。
steps(コマ数, 方向) は滑らかな補間をせず、指定したコマ数で瞬間的に切り替えるイージングです。タイピングエフェクトやゲームキャラクターのスプライトアニメーションに使われます。
方向には end(デフォルト)と start があります。end は各ステップの終わりにスタイルが変わり、start は各ステップの始まりに変わります。タイピングアニメーションには end が自然です。
/* 文字を1文字ずつ打ち込むタイピングアニメーション */
@keyframes typing {
from { width: 0; }
to { width: 15ch; }
}
@keyframes blink-cursor {
50% { border-color: transparent; }
}
.typewriter {
font-family: monospace;
white-space: nowrap;
overflow: hidden;
border-right: 2px solid #d16176;
width: 15ch;
animation:
typing 3s steps(15, end) forwards,
blink-cursor 0.7s step-end infinite;
}スプライトシートのコマ送りにも steps() が活躍します。background-position をアニメーションさせ、コマ数を steps() に指定します。
/* スプライトシートを使ったコマ送りアニメーション */
@keyframes walk {
from { background-position: 0 0; }
to { background-position: -640px 0; } /* 8コマ × 80px */
}
.character {
width: 80px;
height: 100px;
background-image: url('sprite.png');
animation: walk 0.8s steps(8, end) infinite;
}前庭障害(めまい・乗り物酔い)のあるユーザーや、発作のリスクがあるユーザーは、画面上の過度な動きによって体調不良を引き起こすことがあります。OSレベルで「視差効果を減らす」設定をしているユーザーに対して、アニメーションを抑制する配慮が必要です。
CSS では prefers-reduced-motion メディアクエリを使ってこれに対応できます。macOSなら「システム設定 → アクセシビリティ → 視差効果を減らす」、Windowsなら「設定 → アクセシビリティ → 視覚効果 → アニメーションを表示しない」をオンにするとこのメディアクエリが reduce になります。
特定の要素に対してアニメーションを無効にし、代わりに最終状態のスタイルを直接適用します。これが最も丁寧な実装方法です。
/* 通常のアニメーション定義 */
@keyframes fade-in {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.hero-title {
animation: fade-in 0.8s ease-out forwards;
}
/* prefers-reduced-motion: reduce が有効な環境では動きを抑制 */
@media (prefers-reduced-motion: reduce) {
.hero-title {
animation: none; /* アニメーションをオフ */
opacity: 1; /* 最終状態を直接適用 */
transform: none;
}
}手軽に全アニメーションを無効化したい場合は、ユニバーサルセレクタで animation-duration を極めて短くする方法もあります。完全に none にするとJavaScriptがアニメーション終了イベント(animationend)を検知できなくなる場合があるため、0.01ms にするのがよい実践です。
/* 必要なアニメーションは残しつつ、過度な動きだけ抑制する手法 */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}WCAG 2.1の達成基準 2.3.3「アニメーション」では、インタラクションによって引き起こされるアニメーションを無効化できるようにすることが要求されています(AAA)。必須ではありませんが、品質の高いWebサイトを作る上で対応を強く推奨します。
せっかくCSSを書いたのにアニメーションが動かない——そんなときに真っ先に確認すべきポイントをまとめました。ほとんどのトラブルはここに集約されます。
animation-duration の初期値は 0s です。つまり何も指定しないとアニメーションは一瞬で終わり、何も動いていないように見えます。animation-name だけ書いて duration を忘れるのは最もよくあるミスです。
/* animation-duration が未指定(0s)のままだと何も動かない */
.broken {
animation-name: fade-in; /* duration がないのでアニメーションしない */
}
/* 修正:duration を必ず指定する */
.fixed {
animation: fade-in 0.8s ease-out forwards;
}animation-name に指定した名前と @keyframes の名前が一文字でも違うと動きません。特にスペルミス・大文字小文字の違い・ハイフンとアンダースコアの混在に注意してください。ブラウザのDevTools(Styles / Computed タブ)で animation-name が正しく認識されているか確認します。
ベーススタイルで transform を使っている要素に、transform をアニメーションさせるキーフレームを適用すると、上書き競合が起きます。要素自体の位置調整と回転・拡縮アニメーションは要素を分けて管理するのが安全です。
/* transformを使うアニメーション中にtransformを上書きするとコンフリクト */
.element {
transform: translateX(50px); /* ← ベースのtransformが既にある */
animation: spin 1s linear infinite; /* spinでtransform: rotateを使っている */
}
/* 修正:ラッパー要素で役割を分離する */
.wrapper {
transform: translateX(50px); /* 位置調整はラッパーに */
}
.wrapper .inner {
animation: spin 1s linear infinite; /* 回転は子要素に */
}要素に初期スタイル(opacity: 0 など)を設定してフェードインさせても、animation-fill-mode: forwards を指定しないと再生後に初期状態に戻ります。
/* animation-fill-mode忘れ:アニメーション後に元に戻る */
.fade-problem {
opacity: 0; /* 初期状態が透明 */
animation: fade-in 0.8s ease-out; /* forwardsがない! */
}
/* → 0.8秒後にopacity:0に戻ってしまう */
/* 修正 */
.fade-fixed {
opacity: 0;
animation: fade-in 0.8s ease-out forwards;
}すべてのCSSプロパティがアニメーション可能なわけではありません。display(block/none)や visibility の切り替えは中間値を補間できないため、思ったように動きません。透明度で見え隠りを表現するか、visibility(アニメーション可能)と opacity を組み合わせます。
/* displayをアニメーションさせようとしてもブラウザは補間できない */
@keyframes show-hide {
from { display: none; } /* NG:display は補間不可 */
to { display: block; }
}
/* 修正:opacityとvisibilityを使う */
@keyframes show-element {
from {
opacity: 0;
visibility: hidden;
}
to {
opacity: 1;
visibility: visible;
}
}他のスタイルシートやフレームワークが !important でプロパティを上書きしていると、アニメーションの変化が見た目に反映されません。DevToolsの Computed タブで実際に適用されているスタイルを確認し、競合を解消してください。
animation プロパティが取り消し線になっていれば、より強いセレクタに上書きされている証拠getComputedStyle(element).animationName を打ち込んで名前が正しく取れているか確認する@keyframesとanimationプロパティは必ずセットで使う必要があります。@keyframesはアニメーションの「設計図(コマ割り)」を定義するだけで、単体では何も動きません。animation-nameプロパティで@keyframesの名前を参照し、animation-durationで再生時間を指定して初めてアニメーションが動き出します。
animation-fill-modeが指定されていない(初期値のnone)ためです。アニメーション終了後に最後のキーフレームのスタイルを維持したい場合は「animation-fill-mode: forwards」を指定してください。遅延(animation-delay)がある場合に開始前も最初のキーフレームのスタイルを適用したいなら「backwards」を、両方の効果が必要なら「both」を使います。
animation-iteration-countプロパティに「infinite」を指定します。たとえば「animation: spin 1s linear infinite;」と書けば永遠に回転し続けます。往復ループにしたい場合はさらに「animation-direction: alternate」を加えると、正方向と逆方向を交互に繰り返します。回数を制限したい場合はinfiniteの代わりに整数(例:3)を指定してください。
用途に応じて使い分けます。要素が「登場する」アニメーション(フェードイン・スライドイン)にはease-outが自然で軽快な印象になります。要素が「退場する」アニメーションにはease-inが適しています。回転やプログレスバーなど一定速度が求められる場合はlinearを選びます。汎用的にはデフォルトのeaseで問題ないことが多いです。また、cubic-bezier()で独自カーブを作ったり、steps()でコマ送りを表現することも可能です。
前庭障害や光感受性発作のリスクがあるユーザーは、画面上の激しい動きによって体調不良(めまい・吐き気・発作)を引き起こす場合があります。OSの「視差効果を減らす」設定をオンにしているユーザーに対しては、prefers-reduced-motionメディアクエリを使ってアニメーションを無効化または簡略化することが推奨されます。これはWCAG 2.1のアクセシビリティガイドラインでも言及されており、より多くのユーザーが快適に使えるWebサイトを作るための重要な配慮です。
CSSの実装テクニックを「レイアウト・装飾・アニメーション・基礎・トラブル解決」の目的別に探せる総まとめページを用意しています。実装で迷ったときの索引としてどうぞ。

副業・フリーランスが主流になっている今こそ、自らのスキルで稼げる人材を目指してみませんか?
未経験でも心配することはありません。初級コースを受講される方の大多数はプログラミング未経験です。まずは無料カウンセリングで、悩みや不安をお聞かせください!
WithCodeでWeb制作を習得後、フリーランスエンジニアとして活動。HTML/CSS・JavaScript・WordPress案件を中心に年間20件以上の制作実績を持つ。「難しい技術をわかりやすく」をモットーに、初心者〜中級者向けの技術記事を執筆。副業・フリーランス独立を目指す方に向けた情報発信に注力している。
公式サイト より
今すぐ
無料カウンセリング
を予約!