OSのアニメーション設定に合わせたCSS制御

## はじめに

この記事は[NTTテクノクロスアドベントカレンダー 2021](https://qiita.com/advent-calendar/2021/ntt-tx) 6日目の記事です。

こんにちは。NTTテクノクロスの鈴木雅貴です。当社製品やソリューション、受注案件を中心とした成果物をはじめとして、イベントやらコミュニケーションといった社内のあれこれを、デザインの力でよくしていくというチームのリーダをしています。個人としてはWeb標準技術まわりのスキルで生きており、[HTML5プロフェッショナル認定試験 レベル1 対策テキスト＆問題集　Ver2.0対応版](https://amzn.to/3FZTgdO)のいち著者だったりします。と見せかけて[プロジェクト管理をテーマにしたボードゲーム『プロジェクト テーマパーク』を遊んでみた | NTTテクノクロスブログ](https://www.ntt-tx.co.jp/column/dojo_review_blog/20190819/)のように、隙あらば遊ぼうとしています。

今回は、OSのアニメーションに関する設定を使用して、CSSを制御する機能の紹介です。

## きっかけ

私のチームでは年に一度、社員が作ってみたもの・やってみたことを募集して展示会を開催しています。昨年度からコロナ禍のためWeb上での記事掲載としていたのですが、記事を見て終わりではつまらないよねということで、今年度は「閲覧」「いいね」「コメント」「シェア」といった社員同士のつながり促進に結びつく行動を計測し、その数値が増えると木に花を咲かせることで可視化する仕組みを作りました。社員の行動で木に花を咲かせているイメージです。

日々育つ木の様子をGIFアニメーションにしてみました。

![tree1108-1112.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16939/8cbd5315-dcdb-0d27-64c4-f66177ae79fd.gif)



で、その木ですが、SVGで生成してまして、表示時にはCSSTransitionを使い花を拡大させつつクルンと回すアニメーションを入れています。シンプルながら結構かわいくて、完成時にはもしかして私天才？とか思ったものです。

が、環境によってそのアニメーションが再生されないことが判明。ただ、よくある特定ブラウザに依存する問題ではなさそう。もしかしてアニメーションが重くて変になってる？とか思いながら色々調べていたところ、SVGを配置したCMSのCSSに、以下の記述を発見しました。

```css
@media (prefers-reduced-motion: reduce)
* {
     animation-duration: 0s !important;
     transition-duration: 0s !important;
}
```

animationもtransitionもdurationが0に…つまり一瞬でアニメーションが終わる…ということは、お前かッ！

で、`prefers-reduced-motion`って何？

前置きが長くなりましたが、本日はMedia Queriesのメディア特性の1つである`prefers-reduced-motion`について紹介します。

## prefers-reduced-motion とは

prefers-reduced-motionとは、CSSの[Media Queries Level 5]
(https://www.w3.org/TR/mediaqueries-5/)([参考の日本語訳](
https://triple-underscore.github.io/mediaqueries5-ja.html))で登場したメディア属性です。利用者が、画面で余計な動きをしてほしくない設定をしているかどうかを検出するために使います。

[prefers-reduced-motion - CSS: カスケーディングスタイルシート | MDN](https://developer.mozilla.org/ja/docs/Web/CSS/@media/prefers-reduced-motion)

上記のMDNによると、内耳の一部である前庭に障害を持つ方が画面の動きにより気持ち悪くなる現象、いわゆるアニメーション酔いなどを想定しているようです。そういう方向けにパララックス等の本質的ではないエフェクトがなくせるのであればよいですね。

`prefers-reduced-motion`は以下のように使います。

```css
@media (prefers-reduced-motion: reduce)
* {
     animation-duration: 0s !important;
}
```

`prefers-reduced-motion`の取りうる値は2つありまして、

- `reduce`: システム上でアニメーションを避けるような設定がされている
- `no-preference`: システム上でアニメーションを避けるような設定がされていない


ここでいうシステムはOSと考えるとわかりやすいかと。前述のCSSは値が`reduce`なので、「OS上でアニメーションを避けるような設定がされている場合、全要素の`animation-duration`を0sにする」、つまりアニメーション再生を0秒で終わらせるという指定となります。アニメーションしてないのとほぼ同等の見た目になりますね。

## 「アニメーションを避けるような設定」とは

具体的には、OSでの特定の設定を見ています。[MDNの「ユーザー設定」](https://developer.mozilla.org/ja/docs/Web/CSS/@media/prefers-reduced-motion#user_preferences)に記述がありました。以下はそこより引用しています。
 > GTK/GNOME では、 GNOME Tweaks > General タブ (バージョンによっては Appearance タブ) > Animations がオフになっている場合。
 > 他にも、 gtk-enable-animations = false を GTK 3 configuration file の [Settings] に追加する方法もあります。
 > Windows 10: 設定 > 簡単操作 > ディスプレイ > アニメーションを表示する
 > Windows 7: コントロールパネル > コンピューターの簡単操作センター > コンピューターでの作業に集中しやすくします > 必要のないアニメーションは無効にします (可能な場合)
 > macOS: システム設定 > アクセシビリティ > 表示 > 動きの抑制
 > iOS: 設定 > 一般 > アクセシビリティ > 視覚効果を減らす
 > Android 9 以上: 設定 > ユーザー補助 > アニメーションの削除

Firefoxのものと記述がありますが、Google Chromeでも同様のようでした。

## 仕様策定状況と動作するWebブラウザ

[Media Queries Level 5](https://www.w3.org/TR/mediaqueries-5/)は、最初のWorking Draftが2020.3.3発行、5回目のWorking Draftである2020.7.31のものが最新です。[Editors Draftは2021.11.3の比較的新しいもの]
(https://drafts.csswg.org/mediaqueries-5/)がありますが、ISSUEも複数見られたので、仕様が安定するまではまだ時間がかかりそうです。

しかし、[MDNのブラウザーの互換性](https://developer.mozilla.org/ja/docs/Web/CSS/@media/prefers-reduced-motion#browser_compatibility)を見る限り、IEを除く主要ブラウザ側の実装は完了していて、わりと安心して使える状態といえます。


## 注意点

`prefers-reduced-motion`は使うだけならとても簡単なのですが、使用する上で気をつけたいポイントがいくつかありました。

### (1) 意図せずアニメーションが無効になってしまう

アニメーションを避けるような設定が、Webブラウザ内のアニメーションに影響を及ぼすと知らない場合は、意図せずアニメーションをキャンセルされてしまうことが起こりえます。私の場合はもろにこれでした。

無くても困らないアニメーションが減るのであればよいですが、ローディングアイコンやインジケーターなどの有用なものまで無くなってしまっては、本末転倒です。

そのようなことを避けるため、前述のような`*`指定はなるべく避けたほうがよいでしょう。無くなっても困らないアニメーションをする要素に特定のクラスをつけてあげれば、そのクラスのみをキャンセル対象とすることができます。

### (2) アニメーション終了イベントが発火しなくなる

アニメーションをなくしてしまうだけなら、以下のような書き方ができます(避けたほうがいいと書いた`*`指定なのは気にしない方向で)。

```css
@media (prefers-reduced-motion: reduce)
* {
     transition: none !important;
     animation: none !important;
}
```

しかし、この書き方でアニメーションそのものを無くしてしまうと、アニメーション終了時に発火するはずの`transitionend`イベントや`animationend`イベントが発火しません。もしJavaScriptでこれらを使ってなにかやっていた場合は、めんどうですね。

それを避けたい場合は、以下のように書くとよいです。

```css
@media (prefers-reduced-motion: reduce)
* {
     transition-delay: 0s !important;
     transition-duration: 1ms !important;
     animation-delay: 0s !important;
     animation-duration: 1ms !important;
}
```

`transition-duration`と`animation-duration`を1ミリ秒にすることで、アニ
メーションを一瞬で終わらせます。
`transition-delay`と`animation-delay`にマイナスの値を指定すると、アニメーションが即座に終了した扱いになるテクニックもあるのですが、ブラウザによってはアニメーション終了イベントが発火しなくなるので、0sを指定しています。


### (3) セキュリティ・プライバシー面での課題が残っている

[Media Queries Level 5でもISSUEとしてあげられているのですが](https://www.w3.org/TR/mediaqueries-5/#mq-prefers-security)、このメディア特性は、利用者についての情報を取得し利用しているため、フィンガープリント的なものとしても使われるのではという議論がされています。今後の展開によっては、仕様変更・削除となるかもしれません。

そういったものであることを自覚した上で、この機能を使うかどうかを判断するのがよいでしょう。

## おわりに

今回は、OSのアニメーション抑制設定を使ってCSSを制御できるメディア特性`prefers-reduced-motion`を紹介し、 あわせて気をつけたほうがよいポイントを書きました。私は知らずに設定されていたこいつのせいでハマったというあまり喜ばしくない出会い方だったのですが、きちんと考慮して使えばなかなかいいものではないかと思います。

7日目となる明日は @ttttfx702p さんです。お楽しみに！