はじめに#
Webサイトでボタンにマウスを乗せたときに色がふわっと変わったり、メニューがスムーズに展開したりする効果を見たことがあるでしょう。これらの滑らかな視覚変化を実現しているのがCSSトランジションです。
CSSトランジションは、プロパティの値が変化する際のアニメーション速度を制御する機能です。JavaScriptを使わずに、CSSだけでインタラクティブなエフェクトを実装できます。
本記事では、CSSトランジションの基礎から実践的なパターンまで、以下の内容を体系的に解説します。
- transitionプロパティの構文と各サブプロパティ
- アニメーション可能なプロパティの種類
- イージング関数(ease、linear、cubic-bezier)の使い分け
- 実践的なホバーエフェクトの実装
- パフォーマンス最適化とアクセシビリティ対応
CSSトランジションの基本概念#
CSSトランジションは、要素の2つの状態間で滑らかな変化を実現する仕組みです。通常、CSSプロパティの値が変わると即座に反映されますが、トランジションを設定することで、変化を一定時間かけて行うことができます。
graph LR
A[初期状態] -->|トランジション| B[中間状態]
B -->|トランジション| C[最終状態]
style A fill:#e3f2fd
style B fill:#fff3e0
style C fill:#e8f5e9トランジションが発生するタイミングは、以下のような状態変化のときです。
:hover(マウスホバー)
:focus(フォーカス)
:active(クリック中)
- JavaScriptによるクラスの追加・削除
- メディアクエリによるスタイル変更
transitionプロパティの構文#
一括指定(ショートハンド)#
transitionプロパティは、トランジションに関する設定を一括で指定できるショートハンドプロパティです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/* 構文 */
transition: <property> <duration> <timing-function> <delay>;
/* 例:背景色を0.3秒でease-outで変化させる */
transition: background-color 0.3s ease-out;
/* 例:すべてのプロパティを対象に、0.2秒後から0.5秒かけて変化 */
transition: all 0.5s ease 0.2s;
/* 複数プロパティを個別に設定 */
transition:
background-color 0.3s ease,
transform 0.5s ease-out,
opacity 0.2s linear;
|
個別プロパティ#
トランジションは4つのサブプロパティで構成されています。
| プロパティ |
説明 |
既定値 |
transition-property |
トランジション対象のプロパティ |
all |
transition-duration |
変化にかかる時間 |
0s |
transition-timing-function |
変化の速度曲線(イージング) |
ease |
transition-delay |
変化開始までの待ち時間 |
0s |
各プロパティを個別に指定することも可能です。
1
2
3
4
5
6
|
.button {
transition-property: background-color, transform;
transition-duration: 0.3s, 0.5s;
transition-timing-function: ease-out, ease-in-out;
transition-delay: 0s, 0.1s;
}
|
transition-property#
トランジションを適用するCSSプロパティを指定します。
1
2
3
4
5
6
7
8
9
10
11
|
/* 特定のプロパティのみ対象 */
transition-property: opacity;
/* 複数プロパティを対象 */
transition-property: background-color, transform, opacity;
/* すべてのアニメーション可能なプロパティを対象 */
transition-property: all;
/* トランジションなし */
transition-property: none;
|
transition-duration#
トランジションの再生時間を秒(s)またはミリ秒(ms)で指定します。
1
2
3
4
5
6
7
8
|
/* 秒で指定 */
transition-duration: 0.3s;
/* ミリ秒で指定 */
transition-duration: 300ms;
/* 複数の値(対応するプロパティ順) */
transition-duration: 0.3s, 0.5s, 0.2s;
|
transition-delay#
トランジションが開始するまでの待ち時間を指定します。
1
2
3
4
5
|
/* 0.2秒後に開始 */
transition-delay: 0.2s;
/* 負の値も可能(途中から開始したように見える) */
transition-delay: -0.1s;
|
アニメーション可能なプロパティ#
すべてのCSSプロパティがトランジションに対応しているわけではありません。アニメーション可能なプロパティは、値の中間状態を計算できるものに限られます。
主なアニメーション可能プロパティ#
| カテゴリ |
プロパティ例 |
| 色 |
color, background-color, border-color, outline-color |
| サイズ |
width, height, max-width, max-height, padding, margin |
| 位置 |
top, right, bottom, left |
| 変形 |
transform(translate, rotate, scale, skew) |
| 透明度 |
opacity |
| フォント |
font-size, font-weight, letter-spacing, line-height |
| ボックス |
border-radius, box-shadow |
| その他 |
filter, clip-path, flex-grow, flex-shrink |
アニメーション不可のプロパティ#
以下のプロパティは離散的な値を持つため、通常のトランジションでは滑らかに変化しません。
1
2
3
4
|
/* 離散的なプロパティ(通常はアニメーション不可) */
display: block; /* block ⇔ none の間に中間状態がない */
visibility: hidden; /* visible ⇔ hidden */
position: absolute; /* static ⇔ absolute */
|
ただし、CSS Level 4ではtransition-behavior: allow-discreteを使うことで、displayやvisibilityのトランジションが可能になりました。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/* displayのトランジション(モダンブラウザ対応) */
.modal {
opacity: 0;
display: none;
transition:
opacity 0.3s ease,
display 0.3s allow-discrete;
}
.modal.active {
opacity: 1;
display: block;
}
@starting-style {
.modal.active {
opacity: 0;
}
}
|
autoの値に注意#
autoを終点または始点とするトランジションは、ブラウザ間で動作が異なる場合があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
/* 避けるべき例 */
.element {
height: auto;
transition: height 0.3s;
}
/* 代替案:max-heightを使用 */
.element {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.element.expanded {
max-height: 500px; /* 十分な高さを指定 */
}
|
イージング関数の詳解#
イージング関数は、トランジションの進行速度を制御します。適切なイージングを選ぶことで、より自然で心地よいアニメーションを実現できます。
キーワードによる指定#
CSSには、よく使われるイージングカーブのキーワードが用意されています。
| キーワード |
説明 |
cubic-bezier相当値 |
linear |
一定速度で変化 |
cubic-bezier(0, 0, 1, 1) |
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) |
graph TD
subgraph イージング関数の特徴
A[linear] --> A1[等速 - 機械的な動き]
B[ease] --> B1[自然な動き - 汎用的]
C[ease-in] --> C1[加速 - 退場時に有効]
D[ease-out] --> D1[減速 - 登場時に有効]
E[ease-in-out] --> E1[S字カーブ - 往復に有効]
end各イージングの動きの違いを比較してみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
/* 各イージングの比較 */
.box-linear {
transition: transform 1s linear;
}
.box-ease {
transition: transform 1s ease;
}
.box-ease-in {
transition: transform 1s ease-in;
}
.box-ease-out {
transition: transform 1s ease-out;
}
.box-ease-in-out {
transition: transform 1s ease-in-out;
}
|
cubic-bezier()関数#
cubic-bezier()関数を使うと、カスタムのイージングカーブを作成できます。4つの値で制御点を指定します。
1
2
3
4
5
|
/* 構文 */
cubic-bezier(x1, y1, x2, y2)
/* 例:独自のイージング */
transition: transform 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
|
2つの制御点(x1, y1)と(x2, y2)によってベジェ曲線を定義します。
x1, x2: 0から1の範囲(時間軸)
y1, y2: 任意の値(進行度、1を超えるとオーバーシュート)
よく使われるカスタムイージングの例を紹介します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/* バウンス効果(終わりに跳ね返る) */
.bounce {
transition: transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
}
/* スナップ効果(素早く動いて急停止) */
.snap {
transition: transform 0.3s cubic-bezier(0.22, 0.61, 0.36, 1);
}
/* スロースタート(ゆっくり始まり、速く終わる) */
.slow-start {
transition: transform 0.5s cubic-bezier(0.7, 0, 0.84, 0);
}
|
steps()関数#
steps()関数は、トランジションを段階的に進める場合に使用します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/* 構文 */
steps(ステップ数, ジャンプタイミング)
/* 4段階で変化 */
transition: transform 1s steps(4, end);
/* スプライトアニメーションに有効 */
.sprite {
background-position: 0 0;
transition: background-position 0.5s steps(6);
}
.sprite:hover {
background-position: -600px 0;
}
|
ジャンプタイミングのオプションは以下のとおりです。
| キーワード |
説明 |
jump-start / start |
各区間の開始時にジャンプ |
jump-end / end |
各区間の終了時にジャンプ(既定値) |
jump-both |
両端でジャンプ(ステップ数+1回のジャンプ) |
jump-none |
両端はジャンプしない(ステップ数-1回のジャンプ) |
実践的なホバーエフェクト#
ボタンのホバーエフェクト#
基本的なボタンのトランジション効果を実装します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
.button {
padding: 12px 24px;
background-color: #3b82f6;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
transition:
background-color 0.2s ease,
transform 0.2s ease,
box-shadow 0.2s ease;
}
.button:hover {
background-color: #2563eb;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
}
.button:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(59, 130, 246, 0.4);
}
|
カードのホバーエフェクト#
カードコンポーネントに浮き上がる効果を付けます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
.card {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
transition:
transform 0.3s ease,
box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-8px);
box-shadow:
0 12px 24px rgba(0, 0, 0, 0.1),
0 4px 8px rgba(0, 0, 0, 0.05);
}
|
リンクのアンダーラインアニメーション#
リンクにスライドするアンダーラインを実装します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
.link {
position: relative;
color: #3b82f6;
text-decoration: none;
}
.link::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
width: 0;
height: 2px;
background-color: #3b82f6;
transition: width 0.3s ease;
}
.link:hover::after {
width: 100%;
}
|
画像のオーバーレイエフェクト#
画像ホバー時にオーバーレイを表示します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
.image-container {
position: relative;
overflow: hidden;
}
.image-container img {
display: block;
width: 100%;
transition: transform 0.5s ease;
}
.image-container::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(
to top,
rgba(0, 0, 0, 0.7) 0%,
transparent 50%
);
opacity: 0;
transition: opacity 0.3s ease;
z-index: 1;
}
.image-container:hover img {
transform: scale(1.1);
}
.image-container:hover::before {
opacity: 1;
}
|
ナビゲーションメニュー#
メニューアイテムのトランジション効果を実装します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
.nav {
display: flex;
gap: 8px;
}
.nav-item {
padding: 12px 20px;
color: #374151;
text-decoration: none;
border-radius: 8px;
background-color: transparent;
transition:
background-color 0.2s ease,
color 0.2s ease;
}
.nav-item:hover {
background-color: #f3f4f6;
color: #111827;
}
.nav-item:focus {
outline: 2px solid #3b82f6;
outline-offset: 2px;
}
|
複数プロパティのトランジション#
プロパティごとに異なる設定#
複数のプロパティに対して、それぞれ異なる時間やイージングを設定できます。
1
2
3
4
5
6
|
.element {
transition:
opacity 0.2s ease,
transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
background-color 0.3s ease-out;
}
|
allキーワードの使用#
allを使うとすべてのアニメーション可能なプロパティにトランジションが適用されます。
1
2
3
4
5
6
7
8
9
10
11
|
/* すべてのプロパティに同じトランジションを適用 */
.element {
transition: all 0.3s ease;
}
/* 特定のプロパティだけ上書き */
.element {
transition:
all 0.3s ease,
transform 0.5s ease-out;
}
|
ただし、allの使用には注意が必要です。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/* 意図しないプロパティまで変化する可能性がある */
.card {
transition: all 0.3s ease;
width: 300px;
height: 200px;
/* heightが変わる際もトランジションが発生する */
}
/* 対象プロパティを明示する方が安全 */
.card {
transition:
transform 0.3s ease,
box-shadow 0.3s ease;
}
|
パフォーマンス最適化#
GPU加速可能なプロパティを優先#
パフォーマンスに優れたトランジションを実現するには、GPUで処理可能なプロパティを使用することが重要です。
| 推奨(GPU加速) |
避けるべき(再レイアウト発生) |
transform |
width, height |
opacity |
top, left, right, bottom |
filter |
margin, padding |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
/* 避けるべき例:再レイアウトが発生 */
.box-slow {
left: 0;
transition: left 0.3s ease;
}
.box-slow:hover {
left: 100px;
}
/* 推奨:transformを使用(GPU加速) */
.box-fast {
transform: translateX(0);
transition: transform 0.3s ease;
}
.box-fast:hover {
transform: translateX(100px);
}
|
will-changeプロパティ#
事前にブラウザに変化を通知することで、パフォーマンスを改善できます。
1
2
3
4
5
6
7
8
9
10
11
|
.animated-element {
will-change: transform, opacity;
transition:
transform 0.3s ease,
opacity 0.3s ease;
}
/* ホバー時のみ適用(メモリ節約) */
.parent:hover .animated-element {
will-change: transform;
}
|
ただし、will-changeの過度な使用はメモリ消費を増加させるため、必要な要素にのみ適用してください。
不要なトランジションを避ける#
ページ読み込み時にトランジションが発生するのを防ぐテクニックです。
1
2
3
4
|
/* ページ読み込み時のトランジションを防ぐ */
.preload * {
transition: none !important;
}
|
1
2
3
4
|
// ページ読み込み完了後にクラスを削除
window.addEventListener('load', () => {
document.body.classList.remove('preload');
});
|
アクセシビリティへの配慮#
prefers-reduced-motionの対応#
一部のユーザーはアニメーションによって不快感を覚えたり、健康上の問題を引き起こす可能性があります。prefers-reduced-motionメディアクエリで対応しましょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/* 通常のトランジション */
.element {
transition: transform 0.3s ease, opacity 0.3s ease;
}
/* モーション軽減設定のユーザー向け */
@media (prefers-reduced-motion: reduce) {
.element {
transition: none;
}
/* または、最小限のトランジションに */
.element {
transition-duration: 0.01ms !important;
}
}
|
より実践的なアプローチとして、CSS変数を活用する方法があります。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
:root {
--transition-duration: 0.3s;
--transition-timing: ease;
}
@media (prefers-reduced-motion: reduce) {
:root {
--transition-duration: 0.01ms;
}
}
.element {
transition:
transform var(--transition-duration) var(--transition-timing),
opacity var(--transition-duration) var(--transition-timing);
}
|
フォーカス表示の確保#
キーボードユーザーのために、フォーカス状態を視覚的に明確にします。
1
2
3
4
5
6
7
8
9
10
11
|
.button {
transition:
background-color 0.2s ease,
box-shadow 0.2s ease;
}
.button:focus-visible {
outline: 2px solid #3b82f6;
outline-offset: 2px;
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.3);
}
|
JavaScriptとの連携#
transitionendイベント#
トランジション完了を検知してJavaScriptで処理を行えます。
1
2
3
4
5
6
|
const element = document.querySelector('.element');
element.addEventListener('transitionend', (event) => {
console.log(`${event.propertyName}のトランジションが完了しました`);
// 完了後の処理
});
|
複数プロパティのトランジション完了を待つ場合は注意が必要です。
1
2
3
4
5
6
7
8
9
10
11
|
const element = document.querySelector('.element');
let transitionCount = 0;
const expectedTransitions = 2; // transform と opacity
element.addEventListener('transitionend', (event) => {
transitionCount++;
if (transitionCount === expectedTransitions) {
console.log('すべてのトランジションが完了しました');
transitionCount = 0;
}
});
|
クラスの追加によるトランジション#
JavaScriptでクラスを操作してトランジションを発火させます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
.modal {
opacity: 0;
visibility: hidden;
transform: scale(0.9);
transition:
opacity 0.3s ease,
visibility 0.3s ease,
transform 0.3s ease;
}
.modal.active {
opacity: 1;
visibility: visible;
transform: scale(1);
}
|
1
2
3
4
5
6
7
8
9
10
11
|
const modal = document.querySelector('.modal');
const openBtn = document.querySelector('.open-modal');
const closeBtn = document.querySelector('.close-modal');
openBtn.addEventListener('click', () => {
modal.classList.add('active');
});
closeBtn.addEventListener('click', () => {
modal.classList.remove('active');
});
|
よくある問題と解決策#
トランジションが動かない#
トランジションが期待通りに動作しない場合のチェックポイントです。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/* 問題:初期値が設定されていない */
.element {
/* transform の初期値がない */
transition: transform 0.3s ease;
}
.element:hover {
transform: translateX(100px);
}
/* 解決:初期値を明示 */
.element {
transform: translateX(0);
transition: transform 0.3s ease;
}
|
displayプロパティのトランジション#
display: noneからdisplay: blockへの変化は、従来の方法ではトランジションできません。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
/* 従来の解決策:visibilityとopacityを組み合わせ */
.element {
opacity: 0;
visibility: hidden;
transition:
opacity 0.3s ease,
visibility 0.3s ease;
}
.element.visible {
opacity: 1;
visibility: visible;
}
|
モダンブラウザでは@starting-styleとallow-discreteを使用できます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/* モダンな解決策(Chrome 117+, Safari 17.4+) */
.element {
display: none;
opacity: 0;
transition:
opacity 0.3s ease,
display 0.3s allow-discrete;
}
.element.visible {
display: block;
opacity: 1;
}
@starting-style {
.element.visible {
opacity: 0;
}
}
|
高さのトランジション#
height: autoへのトランジションは直接サポートされていません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
/* 解決策1:max-heightを使用 */
.accordion-content {
max-height: 0;
overflow: hidden;
transition: max-height 0.3s ease;
}
.accordion.open .accordion-content {
max-height: 500px; /* コンテンツの最大高さより大きい値 */
}
/* 解決策2:CSS Grid を使用 */
.accordion-content {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
}
.accordion-content > div {
overflow: hidden;
}
.accordion.open .accordion-content {
grid-template-rows: 1fr;
}
|
まとめ#
本記事では、CSSトランジションについて体系的に解説しました。
CSSトランジションを効果的に活用するためのポイントは以下のとおりです。
- 基本構文の理解:
transitionプロパティで対象、時間、イージング、遅延を指定する
- イージングの選択: 動きの性質に合わせて適切なイージング関数を選ぶ(登場は
ease-out、退場はease-in)
- パフォーマンスの考慮:
transformとopacityを優先し、will-changeは必要な場合にのみ使用する
- アクセシビリティ対応:
prefers-reduced-motionでモーション軽減設定のユーザーに配慮する
次の記事では、CSSアニメーション(@keyframes)を使ったより複雑なアニメーション効果の実装について解説します。トランジションとアニメーションを組み合わせることで、よりリッチなユーザー体験を実現できます。
参考リンク#