animationと@keyframesとは何かを先に整理する
animation と @keyframes は、CSSアニメーションを構成する中心要素です。ただし、厳密にはどちらもセレクタではありません。animation は要素に動作条件を与える CSSプロパティ で、@keyframes は時間経過ごとの状態を定義する アットルール です。ここを曖昧にすると、どこに何を書くべきかが分からなくなり、コピペしたコードを調整しにくくなります。
役割は明確です。要素側のセレクタ、たとえば .card や .button に animation を書き、その animation-name で参照した名前と同じ名前を @keyframes に定義します。つまり、セレクタがアニメーションを受け取り、@keyframes が動きの中身を持つ という分担です。transition は状態変化に反応して動く仕組みですが、animation は状態変化がなくても自動で開始できる点が違います。
| 要素 | 役割 | 書く場所 |
|---|---|---|
| animation | 再生時間、回数、速度、開始遅延などを指定する | 要素のセレクタ内 |
| @keyframes | 0% から 100% までの見た目の変化を定義する | CSSのトップレベル |
| .sample:hover などのセレクタ | どの要素に適用するかを決める | 通常のCSSルール |
先に判断基準だけ言うと、ホバーや開閉のように 状態が変わった瞬間だけ動けば十分 なら transition、読み込み時の登場、ループ、段階的な見せ方のように 時間軸を自分で定義したい なら animation と @keyframes を選ぶと整理しやすいです。
animationプロパティの各種オプションを理解する
animation は1つのプロパティですが、実際には animation-name、animation-duration、animation-delay、animation-timing-function、animation-iteration-count、animation-direction、animation-fill-mode、animation-play-state の8項目を理解すると運用しやすくなります。ここでは、それぞれを h3 単位で分けて、役割、実装物、コード、注意点の順で整理します。
最初に押さえるべきことは、値を増やすほど高度になるわけではないという点です。まずは duration、timing-function、fill-mode の3つを中心に組み、必要になったら delay や iteration-count を足すほうが破綻しにくいです。
animation-name
animation-name は、どの @keyframes を参照するかを決めるプロパティです。ここが一致しないとアニメーションは動きません。複数の候補があるときは、名前だけで動きの役割が分かる命名にすると保守しやすくなります。
fadeUp を参照
softPulse を参照
<div class="option-demo-card option-demo-card--name-fade">
<p class="option-demo-card__label">fadeUp を参照</p>
</div>
<div class="option-demo-card option-demo-card--name-pulse">
<p class="option-demo-card__label">softPulse を参照</p>
</div>
.option-demo-card--name-fade {
animation-name: optionFadeUp;
animation-duration: 0.6s;
animation-timing-function: ease-out;
animation-fill-mode: both;
}
.option-demo-card--name-pulse {
animation-name: optionSoftPulse;
animation-duration: 1.2s;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
}
同じ要素でも、参照する @keyframes を変えるだけで動きは変わります。まずは名前と中身を1対1で対応させると混乱しません。
animation-duration
animation-duration は、1回の再生時間を決めます。速すぎると気づきにくく、遅すぎると待たされる印象になります。登場演出なら 0.3 秒から 0.6 秒、ループ系なら 0.8 秒以上から試すと調整しやすいです。
<div class="option-duration-box option-duration-box--fast">0.35秒</div>
<div class="option-duration-box option-duration-box--slow">0.9秒</div>
.option-duration-box--fast {
animation: optionDurationMove 0.35s ease-out both;
}
.option-duration-box--slow {
animation: optionDurationMove 0.9s ease-out both;
}
同じキーフレームでも、duration だけで印象はかなり変わります。操作系は短く、説明的な演出は少し長め、という切り分けが基本です。
animation-delay
animation-delay は、再生開始までの待ち時間を決めます。単体要素で長く待たせると反応が悪く見えますが、複数要素を順番に見せるときは効果的です。実務では、0.1 秒台で軽くずらすことが多いです。
- 要件整理
- 設計確認
- 公開準備
<ul class="option-delay-list">
<li class="option-delay-list__item">要件整理</li>
<li class="option-delay-list__item">設計確認</li>
<li class="option-delay-list__item">公開準備</li>
</ul>
.option-delay-list__item {
opacity: 0;
transform: translateY(8px);
animation: optionDelayReveal 0.45s ease-out forwards;
}
.option-delay-list__item:nth-child(2) {
animation-delay: 0.12s;
}
.option-delay-list__item:nth-child(3) {
animation-delay: 0.24s;
}
delay は順序づけに向いています。開始タイミングを少しずらすだけで、段階表示や視線誘導を作れます。
animation-timing-function
animation-timing-function は、速度変化のカーブを決めます。登場演出なら ease-out、等速で回したいローディングなら linear が使いやすいです。見た目の違和感は、この指定で大きく変わります。
<div class="option-timing-box option-timing-box--ease-out">ease-out</div>
<div class="option-timing-box option-timing-box--linear">linear</div>
.option-timing-box--ease-out {
animation-timing-function: ease-out;
}
.option-timing-box--linear {
animation-timing-function: linear;
}
ease-out は着地が穏やかで、linear は一定速度です。用途に合わない timing-function を選ぶと、動きの意味がぶれます。
animation-iteration-count
animation-iteration-count は、何回再生するかを決めます。常時動かしたいなら infinite、数回だけ注目させたいなら 2 や 3 のような具体値が向いています。ループは便利ですが、多用するとノイズになります。
<span class="option-count-badge">3回だけ動きます</span>
.option-count-badge {
animation: optionCountPulse 0.6s ease-in-out 3;
}
最初の注意喚起だけで十分なら、具体的な回数を入れるほうが画面が落ち着きます。再生ボタンを付けると、3回で止まる挙動も確認しやすくなります。無条件で infinite にする必要はありません。
animation-direction
animation-direction は、通常再生、逆再生、往復再生を決めます。往復させたいときは alternate を使うと、逆向きのキーフレームを別に書かずに済みます。
<span class="option-direction-bar"></span>
.option-direction-bar {
animation: optionDirectionMove 0.8s ease-in-out infinite alternate;
}
左右の往復、上下のゆれ、軽い呼吸感のような動きは alternate と相性が良いです。方向を変えるだけでコード量を抑えられます。
animation-fill-mode
animation-fill-mode は、再生前後の状態をどこまで保持するかを決めます。違いが分かりやすいのは、見た目が大きく変わる例です。ここでは文字色が #f00 から #0f0 へ変わるアニメーションにして、再生後も緑のまま残るようにしています。
<div class="option-fill-card">文字色が #f00 から #0f0 に変わります。</div>
.option-fill-card {
background: #111827;
color: #0f0;
animation: optionFillFade 1.1s ease-out both;
}
@keyframes optionFillFade {
from {
opacity: 0;
transform: translateY(10px);
color: #f00;
}
60% {
opacity: 1;
transform: translateY(0);
color: #ff9500;
}
to {
opacity: 1;
transform: translateY(0);
color: #0f0;
}
}
この例では both を入れているので、再生後も文字色が #0f0 のまま維持されます。fill-mode を省略すると、アニメーション終了後に初期状態へ戻って見えることがあります。終了後の見た目を残したい場面では、fill-mode を明示したほうが分かりやすいです。
animation-play-state
animation-play-state は、再生中か停止中かを切り替えるプロパティです。常時動かしたくないが、ホバー時だけ動かしたい場合に役立ちます。軽い導線強調と相性が良いです。
<a class="option-play-link" href="#">
hover すると動きます
<span class="option-play-link__arrow">→</span>
</a>
.option-play-link__arrow {
animation: optionArrowSlide 0.45s ease-in-out infinite alternate;
animation-play-state: paused;
}
.option-play-link:hover .option-play-link__arrow,
.option-play-link:focus-visible .option-play-link__arrow {
animation-play-state: running;
}
通常時は止めておき、必要な操作の瞬間だけ動かすと画面全体が落ち着きます。play-state は、アニメーションの出し過ぎを防ぐための制御にも使えます。
@keyframesの指定方法と書き方のコツ
@keyframes は、アニメーションの中身を時間軸で定義する場所です。基本は from から to までの2点指定で十分ですが、途中の見せ方が重要なときは 50% や 60% の中間点を置きます。ここで重要なのは、何を変えるかを絞ること です。実務では transform と opacity を中心にすると、レイアウト崩れや想定外の再配置を避けやすくなります。
また、@keyframes はセレクタの中に書けません。CSSのトップレベルに独立して書き、要素側で animation-name から参照します。よくあるミスは、名前の不一致、duration の未指定、開始状態と終了状態の差が小さすぎて見えない、display のように補間されにくいプロパティを動かそうとする、の4点です。迷ったらまず opacity と transform で成立するかを確認するのが近道です。
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
中間点が必要な場合は、次のように割合で増やします。たとえばローディングなら 0%、50%、100% を使い、往復感を作りたいなら animation-direction: alternate; と組み合わせると整理しやすいです。
@keyframes softPulse {
0%,
100% {
transform: scale(1);
opacity: 0.5;
}
50% {
transform: scale(1.08);
opacity: 1;
}
}
- 初回表示なら from と to の2点で十分なことが多い
- ループものは 0%、50%、100% の3点にすると意図が読みやすい
- left や top より transform を優先したほうが調整しやすい
- 名前は fadeUp や dotBounce のように役割が分かる命名にする
animationと@keyframesの実装例を3点で確認する
ここでは、実務でそのまま流用しやすい実装例を3点に絞って紹介します。読み込み時の登場、ループする待機表示、順番に見せるリストという3系統を押さえると、記事、LP、管理画面、資料請求導線など幅広い場面に応用できます。どの例も HTML と CSS を分けており、animation のオプションと @keyframes の関係が追いやすい構成です。
ポイントは、見た目の派手さよりも 意味のある動きになっているか です。登場なら視線誘導、ローディングなら待機状態の通知、ステップ表示なら順序の強調という目的を持たせると、アニメーションが飾りだけで終わりません。
フェードアップで初回表示を整える
導入文の見出し
読み込み時に少し下から上がりながら表示されます。
<section class="demo-card demo-card--fade">
<h3 class="demo-card__title">導入文の見出し</h3>
<p class="demo-card__text">読み込み時に少し下から上がりながら表示されます。</p>
</section>
.demo-card {
padding: 24px;
border: 1px solid #2b2b2b;
border-radius: 16px;
background: #282828;
}
.demo-card--fade {
animation: fadeUp 0.6s ease-out both;
}
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(16px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
この例で効いているのは animation-duration: 0.6s と animation-fill-mode: both です。時間が短すぎると気づきにくく、長すぎると待たされる印象になります。both を入れているので、開始前と終了後の状態が自然につながります。よくあるミスは、fill-mode を入れずに最初の位置へ戻って見えることです。
ローディングドットを infinite と delay で作る
<div class="demo-loading" aria-label="読み込み中">
<span class="demo-loading__dot"></span>
<span class="demo-loading__dot"></span>
<span class="demo-loading__dot"></span>
</div>
.demo-loading {
display: inline-flex;
gap: 8px;
}
.demo-loading__dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #0078d4;
animation: dotBounce 0.8s linear infinite;
}
.demo-loading__dot:nth-child(2) {
animation-delay: 0.1s;
}
.demo-loading__dot:nth-child(3) {
animation-delay: 0.2s;
}
@keyframes dotBounce {
0%,
100% {
transform: translateY(0);
opacity: 0.5;
}
50% {
transform: translateY(-6px);
opacity: 1;
}
}
ローディングでは animation-iteration-count: infinite と animation-delay の組み合わせが要点です。3つの要素をずらすだけで、JavaScriptなしでも順番に動いて見えます。ここでは linear を使ってリズムを均一にしています。ease を使うと各ドットの速度変化が強くなり、読み込み表示としてはやや落ち着かない印象になります。
ステップリストを順番に見せる
- 要件整理
- デザイン調整
- 公開前チェック
<ul class="demo-steps">
<li class="demo-steps__item">要件整理</li>
<li class="demo-steps__item">デザイン調整</li>
<li class="demo-steps__item">公開前チェック</li>
</ul>
.demo-steps {
margin: 0;
padding-left: 20px;
}
.demo-steps__item {
opacity: 0;
transform: translateY(10px);
animation: stepReveal 0.45s ease-out forwards;
}
.demo-steps__item:nth-child(2) {
animation-delay: 0.12s;
}
.demo-steps__item:nth-child(3) {
animation-delay: 0.24s;
}
@keyframes stepReveal {
to {
opacity: 1;
transform: translateY(0);
}
}
この例は、animation-delay と forwards の使いどころが分かりやすい構成です。順番に表示するだけなら、複雑なキーフレームは不要です。終了状態を維持したいので forwards を使います。both でも成立しますが、ここでは再生後の保持だけあれば十分です。説明リストや導入ステップの視線誘導に向いています。
animationとtransitionをどう使い分けるか
CSSアニメーションを実務で扱うときは、「全部 animation で書く」より、用途で役割分担する ほうが保守しやすくなります。ホバー、フォーカス、開閉のように、ユーザー操作に応じて 2 状態の間をなめらかにつなぐだけなら transition のほうが短く安全です。一方で、初回表示、ループ、段階的な演出、途中の通過点を細かく決めたい場合は animation と @keyframes のほうが向いています。
この切り分けができると、コードの意図が明確になります。transition に向く場面で無理に @keyframes を使うと、必要以上に記述が増えます。逆に、ローディングや連続演出を transition で済ませようとすると、状態管理が複雑になります。つまり、どちらが上位という話ではなく、単発の状態変化は transition、時間軸を持つ演出は animation と整理するのが実務的です。
| 場面 | 向いている指定 | 理由 |
|---|---|---|
| ホバー時の色変更、ボタンの軽い浮き上がり | transition | 状態変化にだけ反応すればよく、コードが短い |
| 読み込み時の登場演出 | animation + @keyframes | 開始と終了を自動で定義しやすい |
| ローディングや繰り返し演出 | animation + @keyframes | infinite や中間フレームを扱える |
加えて、常時動くものには prefers-reduced-motion への配慮も必要です。アニメーションは便利ですが、常に正義ではありません。読ませる画面で動きを増やしすぎると、内容理解を邪魔します。必要な箇所だけに使い、止められる設計も持っておくのが現実的です。
@media (prefers-reduced-motion: reduce) {
.demo-card--fade,
.demo-loading__dot,
.demo-steps__item {
animation: none;
}
}
CSSアニメーションの良さ
CSSアニメーションの良さは、派手な演出が作れることだけではありません。HTML構造を大きく変えずに、読み込み、待機、順序、注意喚起といった UIの意味 を補強できる点にあります。JavaScriptを使わなくても、要素の状態や時間軸をかなり細かく設計できます。特に animation と @keyframes を正しく分担して書けるようになると、コードの見通しが大きく改善します。
また、CSSだけで完結することで、ちょっとした演出を導入するハードルが低くなります。読み込み時に1回だけ見せたいのか、ずっと繰り返したいのか、往復させたいのか、終了状態を残したいのか。こうした判断をプロパティ単位で分解できるため、修正も局所化しやすいです。結果として、見た目の気持ちよさと保守性を両立しやすい のが CSS アニメーションの強みです。
結論として、animation は条件設定、@keyframes は中身の定義、セレクタは適用対象の指定、という役割で捉えると迷いません。まずは短い時間、小さい変化量、少ない回数から試し、必要になったら delay、direction、fill-mode を足していく流れが安全です。その積み重ねで、過剰ではないのに気持ちよく動くUIを作りやすくなります。