動きが苦手な人への配慮をCSSで(prefers-reduced-motion)|止める・置きかえる・足す・JSで合わせる、書き方の型4つ

動きが苦手な人への配慮をCSSで。ふつうの見え方と動きを減らす設定の見え方を並べた見本とコード

ボタンがふわっと浮く、カードがすべりこんで出てくる。ページの動きは、見る人の目を引くのに役立ちます。ただ、画面が動くと気分が悪くなる人もいます。そういう人は、スマホやパソコンで「動きを減らす」設定をオンにしていることがあり、CSSの prefers-reduced-motion で、その設定を受け取れます。

使いどころは「サイトに動きを1つでも入れるとき、毎回」です。動きを入れるなら、この指定もセットで書きます。動きが1つもないサイトには、書く必要はありません。

この記事の要点

  • prefers-reduced-motion で、端末の「動きを減らす」設定がオンかどうかをCSSやJavaScriptで受け取れる。オンの人には、動きを止めたり、小さな動きに置きかえたりできる
  • 使いどころは、サイトに動きを入れるとき毎回。飾りの動きは止め、大きな移動はフェードに置きかえ、ホバーの動きは設定がオフの人にだけ足す
  • 設定がオンでも、情報まで消さない。動きで伝えていたことは、色や文字で残す。JavaScriptの動きは matchMedia() で別に合わせる
トミー

連載「WEBの技術」の38回目です。
これまでの回でも、コードの最後にたびたび書いてきた prefers-reduced-motion。今回は、これだけを主役にして、書き方の型を4つにまとめます。

完成見本:「ふつう」と「動きを減らす設定」を並べて

下の4つは、ページを開いた時点から自動で動いています。それぞれ、左が設定がオフ(ふつう)の人の見え方、右が「動きを減らす」設定がオンの人の見え方です。右は、設定がオンのときのCSSを当てて再現しています。

※ 見本は読み上げソフトでは読まれないようにしています。お使いの端末で「動きを減らす」設定がオンのときは、左の見本も止まって見えます。

設定は、どこでオンにするのか

「動きを減らす」設定は、OSの設定画面にあります。iPhoneでは「設定」→「アクセシビリティ」→「動作」の「視差効果を減らす」です(Appleのサポートページで確認しました)。MDNのまとめでは、Windows 11は「Accessibility」→「Visual Effects」→「Animation Effects」、Macは「Accessibility」→「Display」(新しいmacOS Tahoeでは「Motion」)→「Reduce motion」、Android 9以降は「Accessibility」→「Remove animations」にあります(MDNは英語表記です)。

ブラウザは、この設定を読み取って、CSSの prefers-reduced-motion に伝えます。値は2つだけで、設定がオンなら reduce、オフ(特に指定なし)なら no-preference です。MDNでは、2020年1月から主要なブラウザで使える「Baseline」の機能とされています。

コピペ用のコード

HTMLは使いたい場所に、CSSはサイトのCSSファイルや、WordPressならテーマの「追加CSS」などに入れます。クラス名の頭の kd- は、サイトのほかのデザインと名前がぶつからないようにするための目印です。見本の「動き」と「動きを減らす設定のときの指定」が、1つのCSSの中にセットで入っています。

1. 飾りの動きを止める

背景で丸がふわふわ浮いている、よくある飾りです。中身を伝えていない動きなので、動きを減らす設定の人には animation: none で止めます。止めても、丸の形と色は残るので、デザインはくずれません。

HTML
<div class="kd-rm-hero">
  <div class="kd-rm-float" aria-hidden="true">
    <span></span><span></span><span></span>
  </div>
  <p class="kd-rm-hero-t">秋のキャンペーン</p>
</div>
CSS
/* 飾りの丸が、ふわふわ浮く */
.kd-rm-hero {
  position: relative;
  overflow: hidden;
  padding: 40px 16px;
  border-radius: 12px;
  background: #F0F9FF;
  text-align: center;
}
.kd-rm-hero-t {
  position: relative;
  margin: 0;
  font-size: 20px;
  font-weight: 700;
  color: #10283A;
}
.kd-rm-float span {
  position: absolute;
  border-radius: 50%;
  background: #BAE6FD;
  animation: kd-rm-float 3s ease-in-out infinite alternate;
}
.kd-rm-float span:nth-child(1) {
  width: 60px; height: 60px; top: -10px; left: 8%;
}
.kd-rm-float span:nth-child(2) {
  width: 36px; height: 36px; bottom: 6px; right: 12%;
  animation-delay: -1s;
}
.kd-rm-float span:nth-child(3) {
  width: 20px; height: 20px; top: 20px; right: 30%;
  animation-delay: -2s;
}
@keyframes kd-rm-float {
  to { transform: translate(12px, 16px) scale(1.15); }
}
/* 動きを減らす設定の人には、飾りの動きを止める */
@media (prefers-reduced-motion: reduce) {
  .kd-rm-float span {
    animation: none;
  }
}

2. 大きな移動を、フェードに置きかえる

左から60px(ピクセル)すべりこんで出てくるお知らせです。設定がオンの人には、全部止めるのではなく、横に動く部分だけをなくし、うすい状態から濃くなる「フェード」に置きかえます。animation-name(使う動きの名前)だけを差しかえています。

HTML
<div class="kd-rm-card">
  <p class="kd-rm-card-t">お知らせ</p>
  <p>新しいサービスを始めました。</p>
</div>
CSS
/* 左から大きくすべりこんで出てくる */
.kd-rm-card {
  padding: 18px 20px;
  border-left: 4px solid #0284C7;
  border-radius: 8px;
  background: #fff;
  box-shadow: 0 4px 14px rgba(16, 40, 58, .1);
  color: #33495B;
  animation: kd-rm-slide .8s ease-out both;
}
.kd-rm-card p {
  margin: 0;
}
.kd-rm-card-t {
  font-weight: 700;
  color: #0369A1;
}
@keyframes kd-rm-slide {
  from { opacity: 0; transform: translateX(-60px); }
}
@keyframes kd-rm-fade {
  from { opacity: 0; }
}
/* 動きを減らす設定の人には、動かさずに、ふわっと出すだけにする */
@media (prefers-reduced-motion: reduce) {
  .kd-rm-card {
    animation-name: kd-rm-fade;
  }
}

3. 動きは、設定がオフの人にだけ足す

マウスを乗せると、ボタンの色が濃くなり、少し浮き上がります。1と2とは逆に、no-preference(設定がオフのとき)の中に動きを書く形です。色の変化はだれにでも出し、浮き上がる動きだけを、設定がオフの人に足しています。

HTML
<a class="kd-rm-btn" href="/contact/">相談してみる</a>
CSS
/* 色の変化は、だれにでも出す */
.kd-rm-btn {
  display: inline-block;
  padding: 14px 28px;
  border-radius: 999px;
  background: #0369A1;
  color: #fff;
  font-weight: 700;
  text-decoration: none;
}
.kd-rm-btn:hover,
.kd-rm-btn:focus-visible {
  background: #075985;
}
.kd-rm-btn:focus-visible {
  outline: 3px solid #0284C7;
  outline-offset: 3px;
}
/* 動きは「設定がオフの人」にだけ足す */
@media (prefers-reduced-motion: no-preference) {
  .kd-rm-btn {
    transition: transform .25s, box-shadow .25s;
  }
  .kd-rm-btn:hover,
  .kd-rm-btn:focus-visible {
    transform: translateY(-4px);
    box-shadow: 0 10px 20px rgba(3, 105, 161, .3);
  }
}

4. JavaScriptの動きも、設定に合わせる

リンクを押すと、同じページの「料金」の見出しまで移ります。JavaScriptで動かすものは、CSSの @media では止められません。matchMedia()(CSSの条件をJavaScriptで調べる命令)で設定を読み、オンなら一瞬で、オフならなめらかに移ります。

HTML
<a class="kd-rm-jump" href="#kd-rm-price">料金を見る ↓</a>

<!-- (ここに、ほかの内容) -->

<h2 class="kd-rm-goal" id="kd-rm-price" tabindex="-1">料金</h2>
CSS
.kd-rm-jump {
  font-weight: 700;
  color: #0369A1;
}
/* 移った先の見出しに、枠を出さない(クリックで移ったとき) */
.kd-rm-goal:focus {
  outline: none;
}
JavaScript(</body> の直前などに)
// 動きを減らす設定がオンかどうか(あとで設定を変えても、その時点の値が読める)
const kdReduce = matchMedia('(prefers-reduced-motion: reduce)');

document.querySelectorAll('a.kd-rm-jump').forEach((a) => {
  a.addEventListener('click', (e) => {
    const goal = document.querySelector(a.getAttribute('href'));
    if (!goal) return;
    e.preventDefault();
    // オンなら一瞬で、オフならなめらかに移る
    goal.scrollIntoView({ behavior: kdReduce.matches ? 'auto' : 'smooth' });
    goal.focus({ preventScroll: true });
  });
});

4のJavaScriptは、記事の見本では小さな画面の中だけを動かしています。コピペ用のコードでは、ページ全体が「料金」の見出しまで移ります。移ったあとに focus() で見出しへ移しているのは、キーボードで操作している人の次のTabキーが、移った先から始まるようにするためです(そのため見出しに tabindex="-1" を付けています)。

仕組み:メディアクエリで「設定がオンのとき」を書き分ける

@media は「この条件のときだけ使うCSS」

@media (prefers-reduced-motion: reduce) { … } の中に書いたCSSは、設定がオンの人にだけ使われます。スマホの幅で書き分けるときの @media (max-width: 767px) と同じ形で、条件が「画面の幅」ではなく「動きを減らす設定」になっただけです。

1と2:「動き」を先に書き、オンの人には上書きする

1と2は、ふつうの動きを先に書き、あとから reduce の中で上書きしています。1は animation: none で動きそのものを止め、2は animation-name だけを差しかえて、同じ0.8秒で「横に動く」を「うすい→濃い」に置きかえています。すでに動きが入っているサイトに、あとから足すときに使いやすい書き方です。

3:「動き」をはじめから、オフの人にだけ足す

3は逆に、動き(transition と transform)を no-preference の中にだけ書いています。設定がオンの人には、もともと動きのCSSが届きません。新しく作るときは、この書き方のほうが、上書きし忘れが起きにくくなります。

4:JavaScriptは、matchMedia() で同じ条件を調べる

JavaScriptで動かしているものは、CSSの @media では止まりません。matchMedia('(prefers-reduced-motion: reduce)') は、CSSと同じ条件をJavaScriptで調べる命令で、.matches が true なら設定がオンです。4では、その結果で scrollIntoView()(要素が見える位置までスクロールする命令)の behavior を、一瞬で移る auto と、なめらかに移る smooth で切りかえています。

まとめて止める書き方もある

サイト全体の動きを一度にほぼ0にする、次のような書き方もよく見かけます。

CSS
/* サイト全体の動きを、まとめてほぼ0にする(応急処置) */
@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;
  }
}

animation: none ではなく、時間を0.01ms(ほぼ0)にしているのは、動きの最後の状態(表示された状態)は残すためです。ただ、この書き方は、ローディングの回転のように「動きで、待っていることを伝えている」ものまで、まとめて止めてしまいます。

数値を変えるとどうなるか

動きを減らす設定のときの、出し方

2のお知らせで、設定がオンのときの出し方を3通りに変えて並べました。3秒ごとに出し直しています。

左は、移動の距離を60pxから10pxに小さくしたものです。動きは目立たなくなりますが、横に動くことは変わりません。中央の見本(フェードだけ)は、場所は動かさず、うすい状態から濃くなるだけです。右は動きをなくし、最初から表示しています。「動きを減らす」は「動きを0にする」とは限らず、右のようにすべて止めるか、中央のように小さな変化に置きかえるかは、その動きの役割で決めます。

変えるところ指定今回の値目安
オンの人への出し方(2)animation-namekd-rm-fade(フェード)横・縦の移動、拡大は避ける
動きの時間animation の時間0.8秒フェードなら0.2〜0.8秒
浮き上がる量(3)translateY()-4pxオフの人だけに。-2〜-6px
移り方(4)behavior‘auto’ と ‘smooth’オンなら auto(一瞬)

スマホと、見る人への配慮

止めるのは「動き」で、「情報」ではない

設定がオンの人に向けて、動きと一緒に中身まで消してしまうと、かえって困ります。たとえば、ローディングの回転を display: none で消すと、待っていることが伝わりません。3のボタンでは、浮き上がる動きはオフの人だけにしていますが、色が濃くなる変化はだれにでも出しています。動きで伝えていたことは、色や文字で残します。

設定がオフの人にも、止める方法がいることがある

prefers-reduced-motion は、設定をオンにしている人にしか届きません。動きが苦手でも、この設定を知らない人もいます。WCAG(ウェブのアクセシビリティの国際的な基準)2.2の達成基準2.2.2では、自動で5秒より長く動くものには、止める・隠す方法を用意することとされています。ずっと流れるお知らせや、背景の大きな動きには、設定とは別に「止める」ボタンを置きます。

どんな動きが、とくに負担になるか

W3Cの達成基準2.3.3の解説では、前庭障害(体のバランスの感覚の障害)のある人は、画面の動きでめまいや吐き気が起きることがあるとされています。とくに気を付けたいのは、画面の大きな範囲が動くもの、スクロールに合わせて背景と前景がずれて動くもの(パララックス)、ズームや回転です。色の変化や、小さな部品の短いフェードは、比べると負担が小さい動きです。

スマホでの確かめ方

iPhoneなら「視差効果を減らす」をオンにして、自分のサイトを開き直すと、オンの人の見え方を確かめられます。パソコンのChromeでは、開発者ツールの「Rendering」で prefers-reduced-motion を切りかえて確かめることもできます。

トミー

動きをつけるときは、オンの人にはどう見えるかを先に決めておく。
この順番にすると、あとから「どう止めよう」と悩まずにすみます。

AIに頼むなら

Claudeなどの生成AIに動きのあるCSSを頼むときは、「動きを減らす設定のときにどうするか」まで一緒に伝えると、直す回数が減ります。

カードが下から20pxふわっと出てくるCSSを作って。
prefers-reduced-motion が reduce のときは、移動をなくしてフェードだけにして。
JavaScriptで動かす部分があれば、matchMedia で同じ設定を読んで合わせて。

AIが出したコードは、そのまま使う前に、次の3つを確かめます。

  • JavaScriptで動かしている部分が、設定を無視していないか。CSSの @media だけ書いて、JavaScriptのスクロールや自動のスライドがそのまま動いていることがあります
  • オンのときに、中身まで消えていないか。ふわっと出てくる要素は、最初が opacity: 0 です。動きだけ消して、最後まで見えないままになっていないか、iPhoneの設定をオンにして確かめます
  • まとめて止める書き方に、ローディングなど「止めてはいけない動き」が入っていないか。入っているなら、その部分だけ別に、ゆっくりした動きや文字で残します

KAKERUならこう使う/使わない

最後に、作り手としての判断です。

こう使う

  • 動きを1つでも入れたら、必ずセットで書く。この連載の見本のコードも、動きのあるものには毎回入れています
  • 新しく作るときは、3の「オフの人にだけ足す」書き方。上書きし忘れが起きにくいためです
  • すでに動きの多いサイトを直すときは、まず「まとめて止める」書き方で応急処置。そのあと、ローディングなど情報を伝える動きだけ、1つずつ戻します

使わない(しない)

  • オンの人のために、中身を減らすこと。動きを止めても、文章や画像、伝えたいことは同じにします
  • 「動きを減らす設定があるから」と、オフの人への大きな動きを増やすこと。設定を知らない人も見ています。大きな動きは、オンかオフかにかかわらず控えめにします
トミー

動きは、見る人を楽しませるための道具です。
KAKERUなら、その道具で気分が悪くなる人がいるなら、その人の画面では止める。そこまでを含めて「動きを作る」と考えています。

まとめ

  • prefers-reduced-motion は、端末の「動きを減らす」設定を受け取る指定。値は reduce(オン)と no-preference(オフ)の2つ
  • 飾りの動きは animation: none で止め、大きな移動はフェードに置きかえる
  • 新しく作るなら、動きを no-preference の中にだけ書くと、上書きし忘れが起きにくい
  • JavaScriptの動きは、matchMedia() で同じ条件を調べて合わせる
  • 止めるのは動きだけ。動きで伝えていたことは、色や文字で残す

まずは、自分のサイトでiPhoneの「視差効果を減らす」をオンにして、どこが動いたままか見てみてください。

参考にした資料

いずれも2026年11月1日に確認しました。ブラウザの対応状況や設定の名前は変わることがあります。最新の情報は各ページでご確認ください。

ふだんの制作の話や、作ったものの裏側は Instagram と X でも出しています。

関連記事

CSSだけで作るローディングアニメーション コピペで使える5種類と、見る人への配慮
スクロールでふわっと表示するCSSの動き。コピペで5種類と、使う・使わないの判断
ふんわり光るボタン・脈打つボタン。使う数と動く回数の決め方
線が描かれていくアニメをCSSで作る。チェックマーク・丸囲み・線画アイコンの見本とコード
動きが苦手な人への配慮をCSSで。ふつうの見え方と動きを減らす設定の見え方を並べた見本とコード

コメントを残す

メールアドレスが公開されることはありません。 ※ が付いている欄は必須項目です

CAPTCHA


ABOUTこの記事をかいた人

25歳からWEBの世界に携わり、WEBデザインから始めて、今はWEBシステムまで一貫して制作しています。問題点を洗い出し、改善を提案する中で、WEB・グラフィック・動画・システムを縦断して提案するのが得意です。