動きとアニメーション
派手なアニメーションは魅力的だが、前庭障害のある人は気分が悪くなり、 光感受性てんかんのある人は発作のリスクが上がる。動きは「オフにできる」のが基本。
動きが引き起こす実害
- 前庭障害(vestibular disorder): 大きなパララックス、ズーム、ぐるぐる回るアニメはめまい・吐き気を誘発する。
- 光感受性てんかん: 高速点滅で発作の可能性。
- 注意散漫: ADHD の方や認知負荷の高い状況で、動くものが集中を奪う。
- 視覚的疲労: 長時間の使用で疲労が蓄積。
prefers-reduced-motion メディアクエリ
OS 設定で「視差効果を減らす」を ON にしている人を検出できる。 派手な動きは原則これに従って弱める or 止める。
- macOS: システム設定 → アクセシビリティ → ディスプレイ → 視差効果を減らす
- Windows: 設定 → アクセシビリティ → 視覚効果 → アニメーション効果
- iOS / Android: アクセシビリティの「動きを減らす」
/* 通常時の派手なアニメ */
.hero {
animation: float 3s ease-in-out infinite;
}
/* reduce 時は止める */
@media (prefers-reduced-motion: reduce) {
.hero {
animation: none;
}
}
「全てのアニメをまとめて止める」セーフティネット
個別に書くのが面倒なときは、ベースとしてすべてのアニメと transition を最低限に:
@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;
}
}
重要なフィードバック(state 切り替え時のフェード等)は完全に消すと UX が悪くなる場合があるので、 個別に最低限残す方が親切。
JS から検出する
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)")
if (reduce.matches) {
// 派手なアニメをスキップ
}
// 変更を監視
reduce.addEventListener("change", (e) => {
if (e.matches) stopAllAnimations()
})
GSAP、Framer Motion、React Spring など主要なアニメライブラリは
prefers-reduced-motion を読む組み込みオプションを持っている。
パララックススクロール
前庭障害の人にとって最も困るのがパララックス。背景と前景が違う速度でスクロールするため、 三半規管が混乱する。
- 派手なパララックスは
prefers-reduced-motionで完全に止める。 - 軽いものでも視差は最小限に。
- 長距離をスクロールジャックしないこと。
autoplay 動画・GIF
- 5 秒以上自動で動くコンテンツは停止/一時停止のコントロールが必要(WCAG 2.2.2)。
- GIF アニメも対象。停止できないループ GIF はNG。
<video autoplay>は基本的に無音 + ループ + ユーザーが止められること。- ヒーロー動画は大きく動かない映像を選ぶ(ゆっくりとした波、雲など)。
<video autoplay muted loop playsinline controls>
<source src="hero.mp4" type="video/mp4" />
</video>
または ユーザーが「再生」ボタンを押すまで動かないのが安全。
点滅と発作リスク
WCAG 2.3.1(必須): 1秒間に3回以上の点滅は禁止。 ただし「全画面の中の小さな部分」かつ「赤の閾値以下」なら許容(細かい数式あり)。 実務上は「点滅は使わない」が一番安全。
WCAG 2.3.2(AAA): 1秒3回以上の点滅は面積に関わらず禁止。
<blink>は廃止済み- カメラのフラッシュ的な強い光のシーケンスは避ける
- 派手なネオン的なアニメは1秒以内に複数回光らせない
スクロールジャック
スクロール量を独自に解釈して画面に表示する量を変える「スクロールジャック」は、 マウスホイール / トラックパッドの感覚を奪うので避ける。
scroll-snap など標準の挙動を使う方が a11y にも親切。
ホバーのみで現れる UI
WCAG 1.4.13(Content on Hover or Focus)。ホバー/フォーカスで現れる吹き出しやドロップダウンは:
- 解除可能: Esc で消せる、または対象の外をクリックで消せる
- ホバー可能: ポップアップ自体にもマウスを乗せられる(移動中に消えない)
- 持続的: ユーザーが意図して閉じるまで表示される
ツールチップが「カーソルを動かしたら一瞬で消える」だと、視覚以外でも触覚的にも難しい。
カルーセル / スライドショー
自動回転するカルーセルは注意を奪う + 操作不能の二重苦。
- 自動回転をデフォルトで止めるか、停止/再生ボタンを必ず付ける。
- キーボードで前/次に動かせる。
- 各スライドが見出し・ランドマーク的に区切られている。
aria-liveでスライド変更を通知(自動の場合)。
近年は「そもそも自動カルーセルを使わない」ベストプラクティスが主流。
スピナー・ローディングインジケータ
無限ループするスピナー自体は問題ない(小さくゆっくり動くものなら)。気を付けるべきは:
- スクリーンリーダに 「読み込み中」と通知:
role="status"+ テキスト - 長時間(5秒超)続く場合は進捗か停止を提供
<div role="status" aria-live="polite">
<span class="sr-only">読み込み中...</span>
<svg aria-hidden="true" class="spinner">...</svg>
</div>
3D / WebGL シーン
R3F や Three.js で作る 3D シーンも例外ではない。
- 常時カメラがぐるぐる動いているシーンは停止オプションを提供。
prefers-reduced-motion時は自動回転を止める。- カメラが急激に動く演出は避ける(FOV の急変、瞬間移動など)。
// Three.js
const reduce = matchMedia("(prefers-reduced-motion: reduce)").matches
controls.autoRotate = !reduce
View Transitions と動きの配慮
View Transitions もデフォルトでクロスフェードが入る。
派手なカスタムアニメを当てる時は prefers-reduced-motion 対応を忘れずに。
@media (prefers-reduced-motion: reduce) {
::view-transition-group(*),
::view-transition-old(*),
::view-transition-new(*) {
animation: none !important;
}
}
判断ガイド
| 動き | 常時OK | reduce時に弱める | 常に避ける |
|---|---|---|---|
| 0.2 秒のフェード | ○ | — | |
| 軽いスケール変化 | ○ | — | |
| 大きなスライドイン | ○ | ||
| パララックス | ○(off) | ||
| ぐるぐる回るループ | ○(off) | ||
| 1秒3回以上の点滅 | ○ | ||
| スクロールジャック | ○(off) | ||
| 強いストロボ・カメラフラッシュ | ○ |
サービスの「動きトークン」(duration / easing / 動かす距離)を最初に定めて、reduce 時の代替も同時に決めておく。 実装時に都度判断するのではなく、デザインシステムレベルで保証する。