メディアクエリとは、CSSの @media 規則で条件を書き、その条件が真のときだけ中のスタイルを適用する仕組みです。最小の形は @media (min-width: 768px) { … } で、メディアタイプ(screen など)は必須ではありません。
この記事は @media という文法そのものの入門リファレンスです。メディアクエリを構成する3つのパーツ(メディアタイプ・メディア特性・論理演算子)を定義から確認し、そのまま動く最小の1本を書き、書いたのに効かないときにDevToolsのどこを見るかまでを扱います。ブレークポイントを何pxに置くかという設計判断や、比較演算子で書くrange構文は別記事の担当なので、該当箇所でリンクします。掲載しているコードと数値はすべて Chrome 150(macOS・Playwright で実ブラウザを操作)で実行して確認しました(測定日: 2026年8月2日)。
メディアクエリとは何か(@media が何をする規則か)
@media は、スタイルに適用条件を付けるための入れ物です。ブラウザは @media のカッコの中を評価し、真ならブロック内の宣言を通常のCSSとして扱い、偽なら無かったことにします。判定に使えるのはビューポートの幅や高さ、向き、画素密度、入力機器の種類、OSのダークモード設定などです(MDN: メディアクエリの使い方/2026年8月2日閲覧)。
重要なのは、@media は「どの端末か」を判定していないという点です。判定しているのは、いまブラウザが描画に使っている領域(ビューポート)の性質です。PCでウィンドウを狭めれば、スマートフォン向けに書いたつもりのブロックがそのまま当たります。
最初の1本:ビューポート幅で見た目を切り替える
まず動くものを1本書きます。ベースのスタイルを先に書き、条件が真のときだけ上書きする、という2段構えが基本形です。
.panel {
background-color: #eeeeee;
padding: 24px;
}
/* ビューポート幅が768px以上のときだけ上書きする */
@media (min-width: 768px) {
.panel {
background-color: lightblue;
}
}幅を変えながら .panel の背景色を読んだ結果です。min-width: 768px は768pxちょうどを含みます(「以上」であって「より大きい」ではない)。
| ビューポート幅 | 背景色の実測値 | 判定 |
|---|---|---|
| 375px | rgb(238, 238, 238) | ベースのまま |
| 767px | rgb(238, 238, 238) | ベースのまま |
| 768px | rgb(173, 216, 230) | 上書きが当たる(境界を含む) |
| 769px | rgb(173, 216, 230) | 上書きが当たる |
| 1200px | rgb(173, 216, 230) | 上書きが当たる |
このコードには screen が付いていません。付けなくても動きます。むしろ付けないほうが素直な理由は次の章で扱います。
効かないときに真っ先に見るのは viewport メタタグ
「PCのブラウザを狭めると効くのに、スマートフォンの実機だけ効かない」という症状の原因は、ほぼ確実に <head> の1行が抜けていることです。
<meta name="viewport" content="width=device-width, initial-scale=1">この行が無いと、モバイルブラウザはビューポート幅を実機の幅ではなく既定値として扱います。同じCSSを、この1行の有無だけ変えてiPhone相当の環境(390×844・デバイスピクセル比3)で測った結果です。
| viewport メタタグ | documentElement.clientWidth | matchMedia('(max-width: 768px)') | スタイルの適用 |
|---|---|---|---|
| 無し | 980 | false | 当たらない |
| 有り | 390 | true | 当たる |
実機で幅390pxのはずの端末が、CSSからは980px幅として見えています。この状態では max-width: 768px は永久にマッチしません。Googleもモバイル ファースト インデックスの前提として、ページがモバイル端末で正しくレンダリングされることを求めています(Google 検索セントラル: Mobile-first indexing best practices/2026年8月2日閲覧)。属性値の意味や user-scalable を指定してよいのかといった設計判断はレスポンシブデザイン入門で扱っています(MDN: viewport メタタグ/2026年8月2日閲覧)。
@media は3つのパーツでできている
メディアクエリの文法は、W3C の Media Queries Level 4 が次のように定義しています(CSS Media Queries Level 4: Syntax/2026年8月2日閲覧)。
<media-query> = <media-condition>
| [ not | only ]? <media-type> [ and <media-condition-without-or> ]?読み方は「条件だけを書く形が第一の選択肢で、メディアタイプを書く形は第二の選択肢」です。多くの入門記事が @media メディアタイプ and (条件) を基本構文として示していますが、仕様上はそちらのほうが派生形にあたります。ここから、メディアクエリを構成する3つのパーツを順に見ていきます。
パーツ1:メディアタイプ(省略できる。screen は印刷を切る)
メディアタイプは「出力先の大分類」です。いま有効なのは3つだけです(W3C: Media Queries Level 4 2.3 Media Types/2026年8月2日閲覧)。
| メディアタイプ | 意味 |
|---|---|
all | すべての出力先にマッチする |
print | プリンタ、および印刷プレビューやPDF出力のように印刷結果を再現する表示 |
screen | print にマッチしないすべての出力先 |
古い解説に出てくる handheld / tv / projection / tty / braille / embossed / aural / speech は廃止済みです。仕様は「ユーザーエージェントはこれらを有効な値として認識しなければならないが、何にもマッチさせてはならない」と定めています。実測でも @media handheld and (min-width: 768px) は、幅1000pxでも幅500pxでも印刷でも一度も適用されませんでした。エラーは出ないので、スマートフォン向けのつもりで handheld と書くと、その中のCSSは丸ごと死にます。
そしてメディアタイプは省略できます。省略した場合は all を書いたのと同じ扱いになり、画面でも印刷でも評価されます。逆に screen を付けると、印刷プレビューとPDF出力ではそのブロックが効かなくなります。次のコードで違いを確かめられます。
/* 画面でも印刷でも効く */
@media (min-width: 768px) {
.a { outline: 2px solid green; }
}
/* 画面だけ。印刷プレビューやPDF出力では効かない */
@media screen and (min-width: 768px) {
.b { outline: 2px solid green; }
}幅1000pxで、画面表示と印刷エミュレートを切り替えて測った結果です。
| 要素 | 画面表示(幅1000px) | 印刷エミュレート(幅1000px) |
|---|---|---|
.a(タイプ省略) | outline-style: solid | outline-style: solid |
.b(screen あり) | outline-style: solid | outline-style: none |
レイアウト調整のメディアクエリを全部 @media screen and … で書いていると、そのページを印刷したときにレイアウト調整が丸ごと外れます。「画面専用にしたい」という明確な意図が無いなら、screen は書かないのが安全です。逆に、印刷時だけ広告や固定ヘッダーを消したいときは @media print { … } を使います。
パーツ2:メディア特性(何を条件にできるか)
メディア特性は、カッコの中に書く条件そのものです。必ずカッコで囲みます。 特性には「range 型」と「discrete 型」の2種類があり、range 型だけが min- / max- の接頭辞を受け付けます。orientation のような discrete 型に min- を付けると存在しない特性名になり、その条件は永久に偽になります。
実務で使う主なメディア特性を一覧にします(MDN: @media/2026年8月2日閲覧)。
| メディア特性 | 何を見るか | 型 | 書き方の例 |
|---|---|---|---|
width / height | ビューポートの幅・高さ(幅はスクロールバーを含む) | range | (min-width: 768px) |
aspect-ratio | ビューポートの縦横比 | range | (min-aspect-ratio: 16/9) |
orientation | height と width の大小関係 | discrete | (orientation: portrait) |
resolution | 出力デバイスの画素密度 | range | (min-resolution: 2dppx) |
hover | 主たる入力機器でホバーできるか | discrete | (hover: hover) |
pointer | 主たる入力機器の精度 | discrete | (pointer: coarse) |
any-hover / any-pointer | 利用可能なすべての入力機器(主たるものに限らない) | discrete | (any-hover: hover) |
color / monochrome | 色深度・モノクロ階調のビット数 | range | (min-color: 1) |
update | 表示を更新できる頻度(電子ペーパー等の判別) | discrete | (update: slow) |
overflow-block | ブロック方向にあふれた内容の扱い(ページ送りか連続スクロールか) | discrete | (overflow-block: paged) |
prefers-color-scheme | OSのライト/ダーク設定(Level 5) | discrete | (prefers-color-scheme: dark) |
prefers-reduced-motion | アニメーション低減の設定(Level 5) | discrete | (prefers-reduced-motion: reduce) |
prefers-contrast | コントラスト強調の設定(Level 5) | discrete | (prefers-contrast: more) |
scripting | JavaScriptが有効かどうか(Level 5) | discrete | (scripting: enabled) |
display-mode | PWAの表示モード(Level 5) | discrete | (display-mode: standalone) |
逆に、古い解説に出てくる device-width / device-height / device-aspect-ratio は Media Queries Level 4 で非推奨になりました。これらは端末画面そのものの大きさを見るため、ブラウザのウィンドウサイズと連動しません。幅で切り替えたいときは必ず width(=ビューポート幅)を使います。 prefers-color-scheme や prefers-reduced-motion をどう実装に落とすかは、レスポンシブ設計側の話としてレスポンシブデザイン入門にまとめてあります。
なお、画像そのものを画面サイズごとに差し替えたい場合は、CSSのメディアクエリではなくHTML側の srcset / picture の領分です。使い分けはpictureタグで画像を切り替える方法で解説しています。
パーツ3:論理演算子(and / カンマ / or / not / only)
複数の条件をつなぐのが論理演算子です。同じスタイルシートに次の8本を並べ、幅と出力先を変えて適用状況を測りました。
| 書いたメディアクエリ | 画面 1000×700 | 画面 500×900 | 印刷 1000×700 | 意味 |
|---|---|---|---|---|
(min-width: 768px) | 適用 | — | 適用 | タイプ省略。画面・印刷の両方で評価される |
screen and (min-width: 768px) | 適用 | — | — | and は「両方とも真」 |
not screen and (min-width: 768px) | — | 適用 | 適用 | not はクエリ全体を否定する |
only screen and (min-width: 768px) | 適用 | — | — | screen 単独とまったく同じ結果 |
screen and (min-width: 768px), print | 適用 | — | 適用 | カンマは「どちらか一方でよい」 |
(min-width: 768px) and (orientation: landscape) | 適用 | — | 適用 | 特性どうしを and でつなぐ |
(min-width: 768px) or (orientation: portrait) | 適用 | 適用 | 適用 | or キーワードも使える |
screen and (min-width: 768px) or (orientation: portrait) | — | — | — | 文法違反。どこでも当たらない |
この表から、入門者がつまずきやすい点が3つ読み取れます。
notは「メディアタイプの否定」ではありません。 仕様は「否定されるのはメディアクエリ全体であって、メディアタイプだけではない」と明記しています。実測でもnot screen and (min-width: 768px)は、画面の幅500pxで適用されました。「screen以外」という意味だと思って書くと、意図と逆の結果になりますonlyはいま書く意味がありません。 仕様上は、メディアタイプしか理解できない旧来のユーザーエージェントからクエリを隠すための修飾子(2.2.2 Hiding a Media Query From Legacy user agents)です。実測でもscreen単独と結果が1つも変わりませんでした- メディアタイプを書いた瞬間、
orは使えなくなります。 文法定義の第二の選択肢がand <media-condition-without-or>、つまり「or を含まない条件」に限定されているためです
3つ目は目で見て確かめられます。文法に合わないメディアクエリは、仕様の 3.2 Error Handling に従ってパース時に not all へ置き換えられます。実際、screen and (min-width: 768px) or (orientation: portrait) と書いたルールを CSSOM から読み出すと、条件文字列が次のように書き換わっていました。
// 書いたとおりに残るもの
"(min-width: 768px) or (orientation: portrait)"
"(min-width: 768px), (orientation: portrait)"
// 書き換えられたもの(元は screen and (min-width: 768px) or (orientation: portrait))
"not all"条件文字列が not all に化けているかどうかは、Consoleから確かめられます(読み方は後述のL4の章で扱います)。or を使いたいなら、メディアタイプを書かないか、カンマで区切って別々のクエリにしてください。カンマ区切りなら @media screen and (min-width: 768px), (orientation: portrait) と書けます。実測では条件文字列がそのまま保持され、1000×700(幅の条件で真)でも500×900(向きの条件で真)でも適用されました。
幅で切り替える(min-width と max-width)
width は range 型なので min- と max- を付けられます。どちらも境界値そのものを含みます。実測では (max-width: 768px) は幅767pxと768pxで真、769pxで偽でした。(min-width: 768px) は768pxで真、767pxで偽です。
モバイルファースト:min-width だけを一方向に積む
幅で3段に切り替えるとき、ベースを最も狭い状態にして、min-width だけを広い側へ積み上げるのが定石です。上書きの向きが一方向に固定されるので、どのブレークポイントでどの宣言が生きているかを上から順に読めば追えます。
/* ベース:スマートフォン。1カラム */
.container {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
/* 768px以上:2カラム */
@media (min-width: 768px) {
.container {
grid-template-columns: repeat(2, 1fr);
}
}
/* 1024px以上:3カラム */
@media (min-width: 1024px) {
.container {
grid-template-columns: repeat(3, 1fr);
}
}幅を変えながら .container の計算後の grid-template-columns を読んだ結果です。
| ビューポート幅 | grid-template-columns の実測値 | 列数 |
|---|---|---|
| 375px | 375px | 1列 |
| 767px | 767px | 1列 |
| 768px | 376px 376px | 2列 |
| 1023px | 503.5px 503.5px | 2列 |
| 1024px | 330.656px 330.672px 330.656px | 3列 |
| 1400px | 456px 456px 456px | 3列 |
この書き方の要点は2つあります。1つは、メディアクエリの外側にベース宣言(display: grid と1カラム)を必ず置くことです。すべての宣言をメディアクエリの中に入れてしまうと、どの条件にも当たらない状況が生まれたときにレイアウト指定そのものが消えます。もう1つは、条件が累積することです。幅1400pxでは 768px以上 と 1024px以上 の両方が真になり、後に書いた3列が勝ちます。だから宣言の順序は「狭い側から広い側へ」で固定します。
max- と min- を向かい合わせにしない
やってはいけないのは、@media (max-width: 1023px) and (min-width: 768px) のように上下から挟んで段を排他的に区切る書き方です。旧記法には「より大きい」に相当する演算子が無いため、隣の段と重ならないようにするには片方を1pxずらすしかなく、ビューポート幅が小数になったときにどの段にも当たらない隙間が生まれます。min-width の単方向で積めばこの問題は最初から発生しません。1pxずらしがなぜ必要だったのか、比較演算子で書く新しい記法(range構文)でどう解消できるのかはCSSメディアクエリのrange構文と境界値に実測つきでまとめています。
また、画面全体の幅ではなく「その部品が置かれた親要素の幅」で切り替えたい場合は、メディアクエリではなくコンテナクエリの領分です。サイドバーとメインカラムで同じカードを使い回すようなケースでは、こちらのほうが素直に書けます(CSSコンテナクエリ完全ガイド)。
orientation は「端末の向き」ではない
orientation: portrait を「スマートフォンを縦に持ったとき」と説明している解説は珍しくありませんが、仕様の定義は違います。W3C Media Queries Level 4 の 4.4 は次のように定めています(MDN: @media / orientation/2026年8月2日閲覧)。
The orientation media feature is portrait when the value of the height media feature is greater than or equal to the value of the width media feature. […] Otherwise orientation is landscape.(orientation は、height メディア特性の値が width メディア特性の値以上のときに portrait となる。それ以外は landscape である)
つまり判定しているのはビューポートの高さと幅の大小関係だけで、端末の物理的な姿勢は一切見ていません。しかも「以上」なので、正方形のビューポートは portrait に入ります。次のコードで確かめました。
.hero {
display: flex;
flex-direction: row;
}
/* height >= width のときだけ縦積みにする */
@media (orientation: portrait) {
.hero {
flex-direction: column;
}
}| ビューポート | 想定している状況 | (orientation: portrait) | .hero の flex-direction |
|---|---|---|---|
| 500×900 | スマートフォンを縦に持つ | true | column |
| 900×500 | スマートフォンを横に持つ | false | row |
| 1400×700 | PCの通常のウィンドウ | false | row |
| 700×1400 | PCでウィンドウを縦長にしただけ | true | column |
| 768×768 | 正方形のビューポート | true | column |
PCでウィンドウを縦長にドラッグしただけで portrait になり、正方形でも portrait になります。「スマートフォンの縦持ちだけを狙う」つもりで orientation を使うと、PCユーザーにもモバイル用のレイアウトが出ます。端末の種類を条件にしたいのなら、そもそもメディアクエリでは判定できません。 実際に切り替えたいのが「入るコンテンツの列数」なら width を、「タップ操作かマウス操作か」なら pointer / hover を使うほうが意図に近くなります。
orientation が実際に役立つのは、動画プレイヤーやゲームのように「縦長のときは操作パネルを下に、横長のときは横に」といった、ビューポートの形そのものに合わせたい場面です。
書いたメディアクエリが効いているかを確かめる
メディアクエリのデバッグでもっとも危険なのは、条件が真であることを確認して安心してしまうことです。条件が真でも、その中の宣言が要素に届いているとは限りません。実測でも、幅1000pxで matchMedia('(min-width: 768px)') が true を返しているのに、@media の中で指定した色がまったく反映されないケースを再現できました(理由は次章)。
したがって確認は条件側ではなく結果側から行います。DevToolsのElementsパネルで、宣言が生き残っているかを目で見るのが最短です。DevToolsの開き方やパネルの役割から確認したい場合はChrome DevToolsで仮説と検証を回す手順を先に読んでください。
手順:境界の内側と外側で幅を振り、Styles の増減を見る
- DevToolsを開き、デバイスツールバー(Toggle device toolbar)で幅を数値入力する。ウィンドウをドラッグするのではなく、境界の内側と外側の値を直接入れる(768pxで切り替えるなら 500 と 1000 の2点)
- 効かせたい要素をクリックして選び、Elements > Styles を上から見る。
@media (min-width: 768px)という見出しの付いたブロックが出ているかを確認する - 境界の外側の幅(この例なら500)に変えて、そのブロックが一覧から消えることを確認する
- Computed タブに切り替えて、そのプロパティの最終値を読む
手順2と3で「出る/消える」を見るのには理由があります。Stylesパネルには、いま条件が一致している @media ブロックしか出てきません。 条件に合わないメディアクエリはグレー表示になるのではなく、一覧から丸ごと消えます。Chrome 150 で、DevToolsがStylesパネルを組み立てるときに使う CSS.getMatchedStylesForNode の応答を直接読んで確かめました。幅1000pxでは @media (min-width: 768px) のルールが返り、同じページを幅500pxで開くとそのルールは応答から消えます。同じスタイルシートに置いた @media print や @media (min-width: 5000px) は、どちらの幅でも一度も返りませんでした。
したがって「Stylesに出ている」ことは、そのまま「条件が一致している」ことの証明になります。逆に言えば、Stylesに出てこないルールが「書き間違えて永久に偽になっている」のか「そもそもセレクタが要素に当たっていない」のかは、Stylesパネルだけでは区別できません。その切り分けには次のスニペットを使います。スタイルシートに登録されている @media を、条件文字列ごと一覧します。
[...document.styleSheets]
.flatMap(s => { try { return [...s.cssRules]; } catch { return []; } })
.filter(r => r instanceof CSSMediaRule)
.map(r => r.conditionText)返ってくるのは条件文字列の配列です。実測では次の値が返りました。ここに自分が書いた条件が並んでいなければ、そのメディアクエリはスタイルシートに届いていません。並んでいるのに not all へ化けていれば文法違反、書いたとおりの文字列なのに一度もStylesに現れないなら値の書き間違い(単位落ちなど)です。
// 実測で返った値(左から書いた順)
["print", "(min-width: 5000px)", "(min-width: 768)", "not all", "(min-width: 768px)"]
// ^ 単位落ち ^ 文法違反で置換された結果の値だけを確かめたいときは、Consoleで1行読むだけでも代用できます。条件を評価するのではなく、最終的に要素へ適用された値を読むのがポイントです。
// セレクタとプロパティ名は自分のコードに置き換える
getComputedStyle(document.querySelector('.container')).gridTemplateColumns合否ライン
次の4つがすべて満たされていれば、そのメディアクエリは意図どおり効いています。1つでも外れたら、次の節の分岐表を見てください。
| 確認する場所 | 合格ライン |
|---|---|
| ① 境界の内側の幅での Styles パネル | @media … という見出し付きのブロックが一覧に表示されている |
| ② 境界の外側の幅での Styles パネル | そのブロックが一覧から消える(消えない=幅と無関係に効いている) |
| ③ ブロック内の宣言 | 取り消し線が入っていない(取り消し線=他のルールに負けている) |
| ④ Computed タブ | そのプロパティの最終値が期待した値になっている |
①〜③をすべて通してから④を見ることが重要です。①だけを見て「ルールは書けている」と判断すると、③の敗北を見落とします。③が、条件を確認するだけのデバッグでは絶対に捕まえられない領域です。
外れたときの分岐
症状ごとに原因と次の一手を整理します。ここに挙げた挙動はすべて実測で再現を確認したものです。
| 症状 | 意味 | 次の一手 |
|---|---|---|
① どの幅でも Styles に @media ブロックが出てこない。スニペットの一覧にも自分の条件が無い | そのCSS自体がブラウザに届いていない、またはセレクタが選択中の要素に一致していない | 読み込みパスと要素の選択を疑う。セレクタの書き方はCSSセレクタ一覧で確認できる |
② スニペットの一覧に not all がある | 文法違反。パース時に置換されている | メディアタイプと or を同時に使っていないかを見る。実測では screen and (…) or (…) がこの形になった |
| ③ 書いたとおりの条件文字列は一覧にあるのに、どの幅にしても Styles に出てこない | 値が不正で条件が永久に偽。単位の付け忘れが最有力 | 値に px が付いているかを見る。実測では (min-width: 768) はどの幅でもマッチしなかった |
| ④ 宣言に取り消し線が入っている | 条件は当たっているが、詳細度または記述順で負けている | @media は詳細度を上げない。上書きしたい側のセレクタを揃える(次章)。切り分けの全体像はCSSが反映されない原因と対処法にチェックリストがある |
| ⑤ PCでは効くのに実機のスマートフォンだけ効かない | viewport メタタグが無い | Consoleで document.documentElement.clientWidth を読む。980 が返ったらメタタグの欠落が確定 |
②と③はどちらも「一度もStylesに現れない」という同じ見え方になりますが、スニペットの返り値で区別できます。②は条件文字列そのものが not all に書き換わり、③は書いた文字列がそのまま残ります。①の切り分けに必要なセレクタの一覧はCSSセレクタ一覧にまとめてあります。
入門でつまずく3つのパターン
ここまでの内容を踏まえて、実際に踏みやすい3つを取り上げます。共通点はどれもエラーが出ないことです。
単位を落とすと丸ごと無効になる
@media (min-width: 768) のように px を書き忘れたコードは、警告もエラーも出さずに沈黙します。仕様は「メディア特性の値の構文に合致しない値は unknown となり、その値が unknown のメディアクエリは not all に置き換えられなければならない」と定めています(Media Queries Level 4 の 3.2 Error Handling)。
/* 単位を落としている。エラーは出ないが永久に当たらない */
@media (min-width: 768) {
.box { color: rgb(255, 0, 0); }
}実測では、このルールは幅1000pxでも幅500pxでも適用されず、.box の色は初期値のままでした。厄介なのは、ルール自体はスタイルシートに残っていることです。CSSOMから読み出すと (min-width: 768) という条件文字列がそのまま保持されていました。一方でStylesパネルには一度も現れません(条件が一致しないため)。つまり「コードには書いてあるのにStylesに出ない」という状態になり、CSSが読み込まれていないのかと勘違いしやすくなります。前章のスニペットで条件文字列を一覧すれば、書いた条件が残っていることが確認でき、原因を値の書き間違いに絞り込めます。
同じ理由で、存在しない特性名(min-orientation のように discrete 型に接頭辞を付けたものを含む)も静かに偽になります。なお値が 0 のときだけは単位を省略でき、実測でも (min-width: 0) と (min-width: 0px) はどちらも真になりました。ただし例外を覚えるより、常に単位を書くほうが事故が減ります。
@media は詳細度を上げない
入門者が「メディアクエリが効かない」と言うときの最頻パターンがこれです。@media で囲んでもセレクタの詳細度は1ミリも変わりません(MDN: 詳細度/2026年8月2日閲覧)。条件は当たっているのに、外側にあるより重いセレクタに負けている、という状態が起こります。
.box { color: rgb(0, 0, 0); }
#main .box { color: rgb(0, 0, 255); }
/* 条件は当たるのに、詳細度で負けて赤にならない */
@media (min-width: 768px) {
.box { color: rgb(255, 0, 0); }
}幅1000pxで測ると、matchMedia('(min-width: 768px)') は true を返します。ところが .box の実際の色は rgb(0, 0, 255)、つまり青のままでした。#main .box の詳細度が 1-1-0 なのに対し、@media の中の .box は 0-1-0 のままだからです。条件式を確認するだけのデバッグでは、この不具合は絶対に見つかりません。 Stylesパネルなら、赤の宣言に取り消し線が入っている形で一目で分かります。
直し方は、!important を足すことではなく上書きする側のセレクタを揃えることです。
.box { color: rgb(0, 0, 0); }
#main .box { color: rgb(0, 0, 255); }
/* 上書きしたい側のセレクタを揃える */
@media (min-width: 768px) {
#main .box { color: rgb(255, 0, 0); }
}この修正版を同じ条件で測ると、幅1000pxで rgb(255, 0, 0)、幅500pxで rgb(0, 0, 255) になりました。詳細度が同点になったので、後に書いた @media 内の宣言が勝ちます。メディアクエリは常にスタイルシートの後ろ側にまとめて置くという慣習には、この「同点なら後勝ち」を利用する意味があります。詳細度で負けているケースを含めたCSSが効かない原因の切り分けはCSSが反映されない原因と対処法にチェックリストとしてまとめています。
「デバイス」ではなくビューポート幅である
「768px以下のデバイス(タブレットやモバイル)」という言い回しをよく見かけますが、正確ではありません。width が見ているのはビューポートの幅で、そこには端末の種類という情報は含まれていません(MDN: @media / width/2026年8月2日閲覧)。
この違いは実害になります。デスクトップのChromeでウィンドウを幅500pxまで狭めると、実測で (max-width: 768px) は true になり、「モバイル用」と書いたスタイルがPCで適用されます。反対に、viewport メタタグが無ければ実機のスマートフォンでもビューポートは980pxとして扱われ、同じ条件は false になります。「幅が狭い」と「スマートフォンである」は別のことです。
そのため、コメントやクラス名を「モバイル用」「タブレット用」と端末名で書くと、後から読んだ人が条件を誤解します。「2カラムに入る幅から」のようにレイアウト側の言葉で書くほうが、条件と意図が一致します。
よくある質問
Q. メディアクエリとは何ですか?
CSSの @media 規則で、ビューポートの幅・高さ・向き・画素密度・入力機器・OSの表示設定といった条件が真のときだけ、中に書いたスタイルを適用する仕組みです。最小の形は @media (min-width: 768px) { … } で、条件はカッコで囲みます。判定しているのは端末の種類ではなく、いまブラウザが描画に使っている領域の性質です。
Q. @media に screen は必ず書く必要がありますか?
不要です。W3C Media Queries Level 4 の文法定義では、条件だけを書く形が第一の選択肢であり、メディアタイプは省略できます。実測でも @media (min-width: 768px) は幅1000pxで問題なく適用されました。むしろ screen を付けると印刷プレビューやPDF出力でそのブロックが効かなくなります(実測: 印刷エミュレートで未適用)。画面専用にしたい明確な意図が無いなら書かないほうが安全です。
Q. not screen and (min-width: 768px) は「screen以外」という意味ですか?
違います。not が否定するのはメディアタイプではなくクエリ全体です。仕様も「否定されるのはメディアクエリ全体であって、メディアタイプだけではない」と明記しています。実測では、画面表示の幅500pxで適用され、画面表示の幅1000pxでは適用されませんでした。「screenかつ768px以上」の否定、つまり「screenでない、または768px未満」が正しい読み方です。
Q. only screen の only はいまも必要ですか?
不要です。only は、メディアタイプしか理解できない旧来のユーザーエージェントにクエリを無視させるための修飾子で、仕様でも「そうしたユーザーエージェントは極めてまれになっており、only が必要になることはほとんどない」と注記されています。実測でも only screen and (min-width: 768px) は screen and (min-width: 768px) と結果が1つも変わりませんでした。
Q. orientation: portrait はスマートフォンを縦に持ったときだけ効きますか?
違います。仕様は「height メディア特性の値が width メディア特性の値以上のとき portrait」と定義しており、端末の姿勢は見ていません。実測では、PCでウィンドウを700×1400にしただけで true になり、正方形の768×768でも「以上」の条件を満たすため true になりました。スマートフォンだけを狙う条件としては使えません。
Q. スマートフォンの実機だけメディアクエリが効きません。なぜですか?
viewport メタタグが無いためです。この1行が無いと、モバイルブラウザはビューポート幅を実機の幅ではなく既定値として扱います。実測では、iPhone相当の環境(390×844)でメタタグ無しのページは documentElement.clientWidth が 980 を返し、(max-width: 768px) は false でした。メタタグを入れると 390 / true になり、スタイルが適用されました。Consoleで clientWidth が980を返したら、メタタグの欠落が確定します。
Q. メディアクエリの新しい書き方(Range構文)とは何ですか?
@media (width >= 768px) のように比較演算子で条件を書く Media Queries Level 4 の記法で、同じ数値であれば min-width / max-width と意味は同じです。旧記法に無かった「より大きい」「未満」が書けるため、1pxずらしをやめられます。比較演算子の一覧、チェーン比較、境界値と小数幅での挙動、いま使ってよいかのBaseline判断はCSSメディアクエリのrange構文と境界値に実測つきでまとめています。
まとめ
この記事で仕様と実測から確定させたことを整理します。
- 基本形は
@media (条件) { … }。メディアタイプは第二の選択肢であり、省略できる - 有効なメディアタイプは
all/print/screenの3つだけ。handheldなどは何にもマッチしない screenを付けると印刷プレビューとPDF出力でそのブロックが外れるnotはクエリ全体を否定する。onlyは現代のブラウザではscreen単独と同じ- メディアタイプを書くと
orは使えなくなり、そのクエリはnot allに置き換えられる orientationの判定は height と width の大小関係。PCの縦長ウィンドウでも正方形でも portrait になる- 幅で切り替えるときは、ベース宣言を外側に置き
min-widthだけを一方向に積む - 単位の付け忘れはエラーを出さずに沈黙する。ルールはCSSOMに残るがStylesパネルには現れない
@mediaは詳細度を上げない。条件が真でも宣言が負けることがある
共通するのは、メディアクエリは間違っていてもエラーを出さないという点です。だからこそ、書いたあとにStylesパネルで「境界の内側で表示されるか・外側で消えるか・宣言に取り消し線が入っていないか」の3点を通す習慣が効きます。ブレークポイントを変更したときや、既存のCSSに手を入れたときは、この記事の合否ラインをそのまま当ててください。
ここから先は目的別に分かれます。ブレークポイントの決め方や画像の切り替えを含むレスポンシブ設計の全体像はレスポンシブデザイン入門、比較演算子で書く新しい記法と境界値の実測はCSSメディアクエリのrange構文と境界値が入口になります。仕様そのものを当たりたい場合は W3C: Media Queries Level 4 と MDN: @media を参照してください。
