アクセシブルなアニメーションのための prefers-reduced-motion 活用
ユーザーの中には、アニメーション付きインターフェースによってめまい、吐き気、方向感覚の喪失を感じる人がいます。prefers-reduced-motion という CSS メディア特性を使えば、こうしたユーザーのシステム設定を検出し、UI から視覚的な洗練さを完全に取り除くことなく、より安全なモーションで応答できます。本記事では、よくあるコンポーネントパターンに対する CSS と JavaScript の実践的な実装方法を解説します。
重要なポイント
prefers-reduced-motionは OS レベルのアクセシビリティ設定を読み取る CSS メディアクエリで、reduceとno-preferenceの 2 つの値を持ちます。- 目的はアニメーションを完全に排除することではなく、非本質的なモーションを減らすか置き換えることです。不透明度のフェードや短縮された継続時間は、より安全な代替手段となります。
- 明示的に設定はしていないものの、モーションに敏感である可能性のあるユーザーを保護するためには、プログレッシブ(オプトイン)な CSS アプローチを採用しましょう。
- JavaScript 駆動のアニメーションには、
window.matchMediaや Motion のuseReducedMotion()フックなどのリスナーを使うことで、システム変更と動作を同期できます。 - Chrome DevTools の Rendering パネルではこの設定をエミュレートできるため、OS 設定を変更せずにテストできます。
prefers-reduced-motionは WCAG 2.3.3 への対応に役立ちますが、自動再生やループするコンテンツには対応できません。これらは WCAG 2.2.2 に基づき、明示的な一時停止コントロールが必要です。
prefers-reduced-motion が実際に行うこと
prefers-reduced-motion は新しい API ではなく、成熟した広くサポートされている CSS メディアクエリです。ユーザーが OS (macOS, Windows, iOS, Android, Linux) で有効化したシステムレベルのアクセシビリティ設定を読み取ります。値は 2 つあります:
reduce— ユーザーがモーションを減らすよう要求しているno-preference— 設定が指定されていない
注目すべき点として、@media (prefers-reduced-motion) は @media (prefers-reduced-motion: reduce) の省略形と同等です。
MDN Web Docs では完全な構文とブラウザ互換性表が確認できます。また、Can I use でモダンなブラウザエンジン全般での幅広いサポートが確認できます。
正しいメンタルモデル: 削除ではなく、削減する
よくある誤りは、これをすべてのアニメーションを止めるキルスイッチのように扱うことです。それは目的ではありません。WCAG 2.3.3 (「インタラクションからのアニメーション」、レベル AAA) を含むガイドラインでは、非本質的なモーションを減らすか置き換えることが推奨されています。特に以下の点が重要です:
- 大規模な動き(パララックス、ズーム、スライドするパネル)
- 回転または旋回する要素
- ビューポート全体にコンテンツを移動させるスクロール起動のアニメーション
不透明度のフェード、色のトランジション、継続時間の短縮は、一般的により安全な代替手段です。下から飛び出すのではなくフェードインするモーダルは、前庭系の不快感を引き起こすことなく状態変化を伝えられます。
CSS 実装: 2 つのアプローチ
ディフェンシブ(オプトアウト): デフォルトでアニメーションスタイルを定義し、メディアクエリ内でオーバーライドします。
.modal {
transform: translateY(20px);
opacity: 0;
transition: transform 300ms ease, opacity 300ms ease;
}
@media (prefers-reduced-motion: reduce) {
.modal {
transform: none;
transition: opacity 200ms ease;
}
}
プログレッシブ(オプトイン): デフォルトで静的なスタイルを定義し、ユーザーがオプトアウトしていない場合にのみモーションを追加します。
/* Static by default */
.modal {
opacity: 0;
transition: opacity 200ms ease;
}
@media (prefers-reduced-motion: no-preference) {
.modal {
transform: translateY(20px);
transition: transform 300ms ease, opacity 300ms ease;
}
}
プログレッシブアプローチは、設定をしていないがモーションに敏感である可能性のあるユーザーにとってより安全です。
CSS カスタムプロパティを活用すれば、大規模なコードベース全体に対してスケーラブルに対応できます:
:root {
--duration: 300ms;
--easing: ease;
}
@media (prefers-reduced-motion: reduce) {
:root {
--duration: 0.01ms;
}
}
.drawer {
transition: transform var(--duration) var(--easing);
}
JavaScript で設定を検出する
JS 駆動のアニメーションには、window.matchMedia を使用します:
const prefersReduced = window.matchMedia('(prefers-reduced-motion: reduce)');
if (prefersReduced.matches) {
// skip or simplify animation
}
// React to live preference changes (e.g., user toggles OS setting)
prefersReduced.addEventListener('change', (e) => {
if (e.matches) {
// pause or replace animations
}
});
これはカルーセル、スクロール起動エフェクト、CSS の外部で駆動されるあらゆるアニメーションに有用です。
Discover how at OpenReplay.com.
アニメーションライブラリ: Framer Motion と Motion.dev
Motion for React (多くの人がまだ Framer Motion と呼んでいるライブラリで、ドキュメントは現在 Motion.dev に存在します) を使う場合、useReducedMotion() フックがこれをきれいに処理してくれます:
import { motion } from "motion/react";
import { useReducedMotion } from "motion/react";
function Drawer({ isOpen }) {
const reduce = useReducedMotion();
return (
<motion.div
animate={{ x: isOpen ? 0 : -300, opacity: isOpen ? 1 : 0 }}
transition={reduce ? { duration: 0 } : { duration: 0.3 }}
/>
);
}
このフックはシステム設定をリアクティブに読み取るため、ユーザーがセッション中に OS 設定を変更しても同期が保たれます。
Chrome DevTools でのテスト
テストのために OS 設定を変更する必要はありません。Chrome DevTools では以下の操作を行います:
- DevTools を開き、Rendering タブへ移動(3 点メニュー → More tools → Rendering)
- **「Emulate CSS media feature prefers-reduced-motion」**を探す
- 値を
reduceに設定
ページは即座に反応するため、システム設定を変更することなく、すべてのアニメーション付きコンポーネントを検証できます。
WCAG カバレッジに関する注意
prefers-reduced-motion は WCAG 2.3.3 への対応に役立ちますが、すべてをカバーするわけではありません。自動再生される動画、ループする GIF、継続的に動くコンテンツは、WCAG 2.2.2 (「一時停止、停止、非表示」) に基づき、明示的な一時停止/停止コントロールが依然として必要となる場合があります。このメディアクエリはインタラクションによって起動されるアニメーションには十分対応しますが、継続的な背景モーションには別のソリューションが必要です。
実践的なまとめ
アニメーション付きコンポーネント(モーダル、ドロワー、ホバーエフェクト、カルーセル、ページトランジション)を監査し、それぞれについて継続時間を短縮するか、動きを不透明度に置き換えるか、アニメーションを完全にスキップするかを判断しましょう。CSS カスタムプロパティでロジックを一元化し、JavaScript がアニメーションを制御する場合は matchMedia を使用し、OS を変更せずに Chrome DevTools で検証してください。
結論
prefers-reduced-motion を尊重することは、フロントエンド開発者にとって最も労力が少なく、最大の効果が得られるアクセシビリティ対応の 1 つです。技術は安定しており、ブラウザサポートも優れていて、CSS、JavaScript、モダンなアニメーションライブラリにわたって実装パターンも明確です。本当の課題は、メンタルモデルを変えることにあります。つまり、アニメーションをオン/オフを切り替える機能としてではなく、敏感なユーザーに対して抑える層として考えるのです。この習慣を、出荷するすべてのコンポーネントに組み込みましょう。
FAQ
0.01ms のような非常に小さな値に設定する方が、ゼロよりも好まれる場合が多いです。これにより transitionend イベントが保持され、アニメーション完了をリッスンする JavaScript ロジックが引き続き発火します。完全にゼロにすると、一部のブラウザではイベントがスキップされ、それに依存するステートマシンが壊れる可能性があります。視覚的な結果は瞬時の変化と同等です。
影響する可能性がありますが、動作はブラウザや実装によって異なります。より安全なアプローチは、CSS スムーズスクロールを @media (prefers-reduced-motion: no-preference) で囲むことです。これにより、モーション低減を希望するユーザーは常に即座のスクロールを受け取ります。JavaScript でカスタムスクロールアニメーションを実装する場合は、設定を手動でチェックし、behavior を auto に設定した window.scrollTo を使用して即座のスクロールへフォールバックする必要があります。
ありません。アニメーションを短縮またはスキップすると、ユーザーがコンテンツの定着をより速く見ることができるため、通常は体感の応答性が向上します。トランジションが視覚的フィードバックを遅らせなくなるため、Interaction to Next Paint も改善されることが多いです。唯一の注意点は、状態変化を伝えていたアニメーションを取り除くケースです。その場合は、不透明度のフェードや色の変化に置き換えることで、インターフェースが急激ではなく応答性を保ち続けるようにしましょう。
はい、可能です。共有デバイスを使用しているユーザーや OS オプションを発見していないユーザーにとっては、良い実践方法です。設定を localStorage に保存し、論理 OR を使ってメディアクエリの結果と組み合わせます。これにより、OS 設定とアプリ内トグルのどちらでもモーション低減をトリガーでき、システム設定を上書きすることなくユーザーに最大限のコントロールを提供できます。
Truly understand users experience
See every user interaction, feel every frustration and track all hesitations with OpenReplay — the open-source digital experience platform. It can be self-hosted in minutes, giving you complete control over your customer data. . Check our GitHub repo and join the thousands of developers in our community..