はじめに

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を使うことで、displayvisibilityのトランジションが可能になりました。

 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-styleallow-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
  • パフォーマンスの考慮: transformopacityを優先し、will-changeは必要な場合にのみ使用する
  • アクセシビリティ対応: prefers-reduced-motionでモーション軽減設定のユーザーに配慮する

次の記事では、CSSアニメーション(@keyframes)を使ったより複雑なアニメーション効果の実装について解説します。トランジションとアニメーションを組み合わせることで、よりリッチなユーザー体験を実現できます。

参考リンク