CSSのメディアクエリを使ってレスポンシブに対応させる書き方
レスポンシブデザインには欠かせないメディアクエリですが、よく使う画面サイズの判定のほかにも、たくさんの機能があります。デバイスの機能やユーザーの設定によって、最適な表示を実装できたりします。意外と深いメディアクエリの書き方について紹介します。
<meta name="viewport" content="width=device-width, initial-scale=1">
前提として、<head> 要素内にレスポンシブ用の <meta> タグを指定する必要があります。
基本の構文
メディアクエリの基本的な構文は、メディアタイプとメディア特性からなります。
@media <メディアタイプ> and (<メディア特性>) { ... }
メディアタイプはデバイスの種類、メディア特性は画面サイズやデバイスの機能、環境などの特性を指定します。
@media screen and (max-width: 768px) { ... }
例えば、上記のように記述するとデバイスが画面に出力されるもので、かつ画面サイズが 768px 以下のときにスタイルを適用させられます。
@media (max-width: 768px) { ... }
また、メディアタイプは省略できるので、より簡潔に書くこともできます。
@media print { ... }
メディアタイプだけを記述することもでき、この場合はプリンターや印刷プレビューのときにスタイルを適用させられます。
メディアタイプの一覧
指定できるデバイスの種類です。以前は下記以外にも多くの種類がありましたが、現在は 4 種類だけです。
all
すべてのデバイスが対象となります。
screen
画面で見られるものが対象となります。パソコンやスマホなどディスプレイがついているものは screen になります。
プリンターや印刷プレビューが対象となります。
speech
音声合成系が対象となります。
メディア特性の一覧
指定できる特性です。
[min-/max-]width
ビューポート(画面)の横幅で判定できます。
@media (width: 400px) {
/* ビューポートの横幅が400pxのとき */
}
@media (min-width: 400px) {
/* ビューポートの横幅が400px以上のとき */
}
@media (max-width: 400px) {
/* ビューポートの横幅が400px以下のとき */
}
[min-/max-]height
ビューポート(画面)の高さで判定できます。
@media (height: 400px) {
/* ビューポートの高さが400pxのとき */
}
@media (min-height: 400px) {
/* ビューポートの高さが400px以上のとき */
}
@media (max-height: 400px) {
/* ビューポートの高さが400px以下のとき */
}
orientation
portrait 値を指定すると、デバイスが縦長の向きのときを判定できます。landscape 値を指定すると、デバイスが横長の向きのときを判定できます。
@media (orientation: portrait) { ... }
@media (orientation: landscape) { ... }
[min-/max-]resolution
デバイスのピクセル解像度を dpi, dpcm, dppx, x 単位で指定します。dpi 単位は 1 インチあたりのドット数を表します。dpcm 単位は 1cm あたりのドット数を表します。よく使うのは、dppx 単位で 1px あたりのドット数を表します。x 単位は dppx 単位の別名です。
@media (resolution: 2dppx) { ... }
@media (min-resolution: 2dppx) { ... }
@media (max-resolution: 2dppx) { ... }
例えば、iPhone 11 Pro では解像度が 2436×1125 で、メディアクエリなどで使われる CSS ピクセルが 812×375 なので 3dppx となります。デバイスの解像度によって高画質の画像を見せたいときなどに使えます。
対応ブラウザ dpi, dpcm 単位
・IE 9
・Edge 12
・Firefox 8
・Chrome 29
・Opera 16
・Android 37
対応ブラウザ dppx 単位
・Edge 12
・Firefox 16
・Chrome 29
・Opera 12.1
・Android 37
対応ブラウザ x 単位
・Edge 79
・Firefox 62
・Chrome 68
・Opera 55
・Android 68
Safari と iOS Safari は非標準の -webkit-[min-/max-]device-pixel-ratio を使うことで、Safari 3 から対応しています。値を指定するときは dppx 単位は省略して書きます。
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 2dppx) { ... }
このように書けば、Retina ディスプレイ向けの指定ができます。
prefers-color-scheme
light 値を指定すると、デバイスの設定でライトモードにしているときを判定できます。dark 値を指定すると、デバイスの設定でダークモードにしているときを判定できます。
@media (prefers-color-scheme: light) { ... }
@media (prefers-color-scheme: dark) { ... }
対応ブラウザ
・Edge 79
・Firefox 67
・Chrome 76
・Opera 62
・Safari 12.1
・iOS Safari 13
・Android 76
prefers-reduced-motion
reduce 値を指定すると、デバイスのアニメーションの動きを最小限にするように設定されているときを判定できます。no-preference 値を指定すると、ユーザーが特に設定していないときを判定できます。
@media (prefers-reduced-motion: reduce) { ... }
@media (prefers-reduced-motion: no-preference) { ... }
詳しくは以前書いた記事を見てください。
対応ブラウザ
・Edge 79
・Firefox 63
・Chrome 74
・Opera 62
・Safari 10.1
・iOS Safari 10.3
・Android 74
hover
none 値を指定すると、メインの入力システムでホバー機能が使えないデバイスを判定できます。例えば、タッチスクリーンや手書きスタイラスが使えるスクリーンなどです。
hover 値を指定すると、メインの入力システムでホバー機能が使えるデバイスを判定できます。例えば、マウスや Nintendo Wii コントローラーなどです。
@media (hover: none) { ... }
@media (hover: hover) { ... }
一例として、iPhone ではホバー機能が使えないため、none 値となります。
対応ブラウザ
・Edge 12
・Firefox 64
・Chrome 38
・Opera 28
・Safari 9
・iOS Safari 9
・Android 38
any-hover
none 値を指定すると、入力システムのうち 1 つもホバー機能のないデバイスを判定できます。hover 値を指定すると、入力システムのうち少なくとも 1 つはホバー機能が使える場合を判定できます。
@media (any-hover: none) { ... }
@media (any-hover: hover) { ... }
一例として、iPhone ではホバー機能が使えないため、none 値となります。
対応ブラウザ
・Edge 16
・Firefox 64
・Chrome 41
・Opera 28
・Safari 9
・iOS Safari 9
・Android 41
pointer
none 値を指定すると、メインの入力システムでマウスなどのポインティングデバイスが実装されていない場合を判定できます。
coarse 値を指定すると、メインの入力システムで正確性が限定されたポインティングデバイスが実装されている場合を判定できます。例えば、タッチスクリーンや Xbox の Kinect のようなモーション検出センサーです。
fine 値を指定すると、メインの入力システムで正確性が高いポインティングデバイスが実装されている場合を判定できます。例えば、マウスやタッチパッド、スタイラスペンなどです。
@media (pointer: none) { ... }
@media (pointer: coarse) { ... }
@media (pointer: fine) { ... }
coarse 値の場合は、チェックボックスやラジオボタンなどの反応するクリック範囲を広くするというような使い方ができます。
対応ブラウザ
・Edge 12
・Firefox 64
・Chrome 41
・Opera 28
・Safari 9
・iOS Safari 9
・Android 41
any-pointer
none 値を指定すると、マウスなどのポインティングデバイスが実装されていない場合を判定できます。coarse 値を指定すると、正確性が限定されたポインティングデバイスが少なくとも 1 つ実装されている場合を判定できます。fine 値を指定すると、正確性が高いポインティングデバイスが少なくとも 1 つ実装されている場合を判定できます。
@media (any-pointer: none) { ... }
@media (any-pointer: coarse) { ... }
@media (any-pointer: fine) { ... }
一例として、iPhone では主な入力システムはタッチ操作ですが、外部接続によりマウスを使うこともできるため、coarse 値となります。
対応ブラウザ
・Edge 12
・Firefox 64
・Chrome 41
・Opera 28
・Safari 9
・iOS Safari 9
・Android 41
[min-/max-]aspect-ratio
ビューポートの幅と高さのアスペクト比を <幅>/<高さ> のように指定します。16/9 なら 16:9 の画面サイズということになります。
@media (aspect-ratio: 16/9) { ... }
@media (min-aspect-ratio: 16/9) {
/* 最小アスペクト比が16:9のとき */
}
@media (max-aspect-ratio: 16/9) {
/* 最大アスペクト比が16:9のとき */
}
対応ブラウザ
・IE 9
・Edge 12
・Firefox 3.5
・Chrome 3
・Opera 10
・Safari 5
・iOS Safari 4.2
・Android 37
[min-/max-]color
デバイスの赤・緑・青の色成分あたりのビット数を表し、白黒デバイスなら 0 となります。
@media (color: 8) {
/* デバイスの色成分のビット数が8のとき */
}
@media (color) {
/* デバイスが白黒でないとき */
}
@media (min-color: 8) {
/* デバイスの色成分のビット数が8以上のとき */
}
@media (max-color: 8) {
/* デバイスの色成分のビット数が8以下のとき */
}
対応ブラウザ
・IE 9
・Edge 12
・Firefox 2
・Chrome 1
・Opera 10
・Safari 3
・iOS Safari 1
・Android 37
color-gamut
デバイスがどこまで色域に対応しているかを判定できます。srgb 値を指定すると、デバイスが sRGB 色域にほぼ対応しているか、それ以上の場合を判定できます。p3 値を指定すると、デバイスが DCI P3 色域にほぼ対応しているか、それ以上の場合を判定できます。rec2020 値を指定すると、デバイスが BT.2020 色域にほぼ対応しているか、それ以上の場合を判定できます。
これらの色域の違いについては、以下のページを参照してください。
@media (color-gamut: srgb) { ... }
@media (color-gamut: p3) { ... }
@media (color-gamut: rec2020) { ... }
対応ブラウザ
・Edge 79
・Chrome 58
・Opera 45
・Safari 10
・iOS Safari 10
・Android 58
[min=/max-]color-index
デバイスの色参照テーブルの項目数を指定し、色参照テーブルがない場合は 0 となります。
@media (color-index) {
/* 色参照テーブルがあるとき */
}
@media (color-index: 256) {
/* 色参照テーブルの項目数が256のとき */
}
@media (min-color-index: 256) {
/* 色参照テーブルの項目数が256以上のとき */
}
@media (max-color-index: 256) {
/* 色参照テーブルの項目数が256以下のとき */
}
対応ブラウザ
・Edge 79
・Chrome 29
・Opera 16
・Safari 8
・iOS Safari 8
・Android 37
[min-/max-]monochrome
モノクロフレームバッファのピクセルあたりのビット数を指定します。デバイスがモノクロでなければ 0 となります。
@media (monochrome) {
/* デバイスがモノクロのとき */
}
@media (monochrome: 0) {
/* デバイスがモノクロでないとき */
}
対応ブラウザ
・Edge 79
・Firefox 2
・Chrome 1
・Opera 10
・Safari 3
・iOS Safari 3
・Android 37
inverted-colors
none 値を指定すると、デバイスの設定で色が通常通りの設定になっているときを判定できます。inverted 値を指定すると、デバイスの設定で色が反転されるように設定されているときを判定できます。
@media (inverted-colors: none) { ... }
@media (inverted-colors: inverted) { ... }
対応ブラウザ
・Safari 9.1
・iOS Safari 10
display-mode
Web アプリケーションの表示モードを判定できます。fullscreen 値を指定すると、全画面表示のときを判定できます。standalone 値を指定すると、Web アプリケーションが個別にウィンドウを持った状態、つまりネイティブアプリのような表示のときを判定できます。minimal-ui 値を指定すると、standalone のような外観や操作感ですが、ナビゲーション制御のための最小限の UI を持った表示のときを判定できます。browser 値を指定すると、通常のブラウザの表示と同じ場合を判定できます。
@media (display-mode: fullscreen) { ... }
@media (display-mode: standalone) { ... }
@media (display-mode: minimal-ui) { ... }
@media (display-mode: browser) { ... }
対応ブラウザ
・Edge 79
・Firefox 47
・Chrome 45
・Opera 32
・Safari 13
・iOS Safari 13
・Android 45
grid
デバイスがグリッドベースの画面を使用しているかどうかを判定します。グリッドベースとは文字だけのデバイスや電話などのことで、スマホやパソコンはビットマップベースと呼ばれています。0 値(false)または 1 値(true)を指定できます。
@media (grid: 0) { ... }
@media (grid: 1) { ... }
対応ブラウザ
・IE 10
・Edge 12
・Firefox 2
・Chrome 1
・Opera 10
・Safari 3
・iOS Safari 3
・Android 37
論理演算子
メディアクエリには論理演算子があり、より複雑な条件で判定できます。
and 演算子
複数のメディア特性をつなぐことができ、「かつ」の意味をもちます。
@media (min-width: 768px) and (prefers-color-scheme: dark) and (orientation: landscape) { ... }
このように and 演算子は何回でも使うことができ、「ビューポートの横幅が 768px 以上かつ、ダークモードかつ、デバイスの向きが横向き」の場合というように条件を指定できます。
,(カンマ)
複数のクエリをつなぐことができ、「または」の意味をもちます。
@media (min-width: 768px), (prefers-color-scheme: dark) and (orientation: landscape) { ... }
このように書くと、「ビューポートの横幅が 768px 以上」または、「ダークモードかつデバイスの向きが横向き」の場合というように条件を指定できます。
ちなみに Media Queries Level 4 では、カンマではなく or 演算子が提唱されており、よりわかりやすくなっています。
not 演算子
クエリ全体を否定します。クエリとは、カンマによって区切られたメディア特性の集合体です。ただし、not 演算子を使う場合はメディアタイプも必ず指定する必要があります。
@media (min-width: 768px), (prefers-color-scheme: dark) and (orientation: landscape) { ... }
例えば、このメディアクエリの場合は (min-width: 768px) と (prefers-color-scheme: dark) and (orientation: landscape) という 2 つのクエリからなります。
@media not all and (min-width: 768px), not all and (prefers-color-scheme: dark) and (orientation: landscape) { ... }
必須のメディアタイプには all を指定しています。このように書くと、「ビューポートの横幅が 768px 以上でない」または「ダークモードでない、かつデバイスの向きが横向きでない」という条件になります。
@media not all and (min-width: 768px), (prefers-color-scheme: dark) and (orientation: landscape) { ... }
もちろん、片方のクエリだけ否定することもでき、「ビューポートの横幅が 768px 以上でない」または「ダークモードでデバイスの向きが横向き」という条件になります。
クエリ全体を否定するので、個別のメディア特性だけ否定することはできませんが、後述する方法で対応もできます。
これから先を読む前に、まずは以前 Twitter でアンケートした質問について考えてみてください。
では、解説していきます。
not 演算子を使う場合にメディアタイプだけを指定するとどうなるでしょうか。
@media not screen { ... }
これはもちろん、スクリーン以外となります。ここで、メディア特性を追加するとどうなるでしょうか。
@media not screen and (prefers-color-scheme: dark) { ... }
先ほど not 演算子はクエリ全体を否定すると書いてあったので、
@media not (screen and (prefers-color-scheme: dark)) { ... }
「デバイスがスクリーンではなく、ダークモードでない」と考えるのが当然かも知れませんが、実はメディア特性がある場合、not 演算子はメディア特性の方を否定し、メディアタイプは無視します。
@media screen and (not (prefers-color-scheme: dark)) { ... }
つまり、正解はアンケートの選択肢の中にはありませんが(このときは単にメディアタイプ以外を否定すると思っていた)、「デバイスがダークモードでない」となります。一番近い選択肢を選んでいる人が 7.9% と最も少なくなっています。
これに関しては、仕様自体が非常にわかりづらいというのがあるので、仕方がないと思います。Media Queries Level 4 ではよりわかりやすいように改善されているので、後述します。
Media Queries Level 4
これは、まだ新しい仕様で、すべてのブラウザで実装されているわけではありませんが、徐々に実装されつつあります。すでに、Media Queries Level 5 もあります。
or 演算子
複数のクエリをつなぐのにカンマを使用していましたが、or 演算子を使えるようになります。
/* BEFORE */
@media (min-width: 768px), (prefers-color-scheme: dark) and (orientation: landscape) { ... }
/* AFTER */
@media (min-width: 768px) or (prefers-color-scheme: dark) and (orientation: landscape) { ... }
not() 関数
not 演算子を使うとクエリ全体の否定となりますが、not() 関数を使えば一部のメディア特性のみを否定できます。
@media (not (monochrome)), (min-width: 768px) and (not (prefers-color-scheme: dark)) { ... }
範囲クエリ
不等号(<, >, <=, >=)を使って、より直感的にメディアクエリを書けるようになります。
/* BEFORE */
@media (min-width: 400px) and (max-width: 768px) { ... }
/* AFTER */
@media (400px <= width <= 768px) { ... }
また、未満といった条件も簡単に指定できます。
@media (width < 768px) {
/* ビューポートの横幅が768px未満のとき */
}
メディアクエリの入れ子
実は、メディアクエリを入れ子にすることもできます。これにより、複雑な条件でも実装しやすくなります。
例えば、not 演算子はクエリ全体を否定するため、一部のメディア特性のみを否定したい場合は not() 関数を使います。しかし、not() 関数はまだほとんどのブラウザで実装されていません。
そこで、メディアクエリを入れ子にすることで、同じ条件を再現できます。
@media (hover: hover) and (not (pointer: fine)) { ... }
このメディアクエリを入れ子を使って再現すると、
@media (hover: hover) {
@media not all and (pointer: fine) { ... }
}
となります。入れ子にすることで、「かつ」の条件を指定できます。
対応ブラウザ
・Edge 12
・Firefox 11
・Chrome 26
・Opera 12.1
・Safari 6.1
・iOS Safari 7
・Android 37
色々な活用
メディアクエリをCSS ファイル内だけでなく、色々な場面で使えます。
HTML 要素
<link> や <source> 要素の media 属性でメディアクエリを指定できるので、特定の条件に一致した場合のみ、対象のソースを読み込めます。
<link href="スマホ用のCSS" rel="stylesheet" media="screen and (max-width: 414px)">
CSS の @import
CSS ファイル内で使える @import の url() 関数を使うときに、メディアクエリを指定することができます。
@import url("スマホ用のCSS") screen and (max-width: 414px);
@import はあまり使いすぎると読み込み速度が遅くなるので、おすすめはしません。
JavaScrpt
JavaScript でも matchMedia() 関数を使うことで、メディアクエリによる判定ができます。
// メディアクエリ
const mediaQuery = matchMedia('(min-width: 520px)')
const handler = mq => {
// マッチしたとき
if (mq.matches) {
document.body.style.background = 'green'
} else {
document.body.style.background = 'blue'
}
}
// 初回ページ読み込み時
handler(mediaQuery)
// イベントリスナーでウィンドウをリサイズしたとき
try {
mediaQuery.addEventListener('change', () => {
handler(mediaQuery)
})
} catch {
mediaQuery.addListener(handler)
}
モダンブラウザでは addEventListener() が使えますが、まだ使えるブラウザが少ないので addListener() も後方互換性のために書いておきます。