メディアクエリが効かない時は、詳細度より先に書く順序を疑う
@media の中に書いた指定が、条件を満たしているはずの画面で効かないことがあります。セレクタは合っていて、画面幅も条件の範囲にあるのに、見た目だけが変わらないという状態です。
効かない理由はいくつか考えられますが、今回はセレクタの詳細度ではなく、スタイルシートのどこにメディアクエリのブロックを置いたかでした。順序が原因なのかどうかを切り分けるところまでをまとめます。
症状: 画面幅の条件を満たしても、見た目が変わらない
題材は、ブラウザで動かしている推理パズルです。記号を置くタイルが並んだ盤面と、置く記号を選ぶパレットがあります。広い画面では、タイルを小さくして、パレットを 1 行(10 列)に収めたいという調整をしました。
追加したのは、次のメディアクエリです。
@media (min-width: 720px) {
.slot { width: 40px; height: 40px; }
.palette { grid-template-columns: repeat(10, 1fr); }
}
幅 800px の画面を撮ると、パレットは 5 列のままで、タイルの大きさも変わらないままでした。条件の 720px は満たしています。セレクタの綴りも、クラス名の付け方も合っています。
違っていたのは、このブロックをスタイルシートのどこに置いたかでした。.slot と .palette には、メディアクエリで囲まない指定も書いてあります。画面幅によらず当たるこちらを基本ルールと呼ぶことにすると、メディアクエリを、その基本ルールより前に書いていました。
手元で再現する
次の HTML を保存してブラウザで開き、ウィンドウを 720px より広げると、同じ状態を再現できます。タイルは 54px のままで、パレットは 5 列のままです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
@media (min-width: 720px) {
.slot { width: 40px; }
.palette { grid-template-columns: repeat(10, 1fr); }
}
.slot { width: clamp(40px, 11vw, 54px); height: 40px; background: #cfd8dc; }
.palette { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
</style>
</head>
<body>
<div class="palette">
<div class="slot"></div><div class="slot"></div><div class="slot"></div>
<div class="slot"></div><div class="slot"></div><div class="slot"></div>
<div class="slot"></div><div class="slot"></div><div class="slot"></div>
<div class="slot"></div>
</div>
</body>
</html>
@media のブロックを、.palette の行の後ろへ移して開き直すと、今度はタイルが 40px になり、パレットが 10 列になります。動かしたのはブロックの位置だけです。
原因: メディアクエリは詳細度を上げないので、後ろのルールに負ける
詳細度とは、1 つの要素に複数のルールが当たった時に、どのルールの値を使うかを決める点数のことです。ID セレクタ、クラスセレクタ、要素セレクタをそれぞれいくつ使ったかで決まります(詳細度)。
ここで効いてくるのは、@media で囲んでも、中のセレクタの詳細度は上がらないという点です。メディアクエリの中の .slot も、外の .slot も、クラスセレクタ 1 つぶんで同点です。
同点になった場合は、スタイルシートに書いた順で決まります。後ろにある宣言が勝ちます(CSS カスケード入門)。メディアクエリを前に書いていたため、後ろにある基本ルールが、条件を満たしているメディアクエリの指定を上書きしていました。

基本ルール側の値がそのまま出ていれば、順序で負けている
最小の再現を作り、並び順だけを入れ替えて、画面幅 800px でタイルの幅とパレットの列数を測りました。
54px という値が、メディアクエリが丸ごと負けていることを示しています。基本ルールは clamp(40px, 11vw, 54px) なので、幅 800px では 11vw が 88px になり、上限の 54px に張り付きます。メディアクエリが効いていれば 40px になるので、54px のままというのは、メディアクエリ側の指定が 1 つも採用されていないということです。
列数も同じで、5 列のままというのは repeat(5, 1fr) が生き残っている状態です。
負け方は、ブロック単位ではなくプロパティ単位です。基本ルールが同じプロパティを指定していれば負け、指定していないプロパティはメディアクエリ側が残ります。メディアクエリにだけ border-radius: 8px を足して、順序は変えずに測ると、次のようになります。
| メディアクエリ側の指定 | 基本ルールにも同じ指定があるか | 画面幅 800px での結果 |
|---|---|---|
width: 40px | ある | 54px(基本ルールの値) |
border-radius: 8px | ない | 8px(メディアクエリの値) |
一部だけ効いているように見える時も、順序で負けている可能性があります。効かないプロパティが、基本ルール側にも書かれていないかを見ると切り分けられます。
開発者ツールでは、負けたルールに打ち消し線が付く
要素を選んで Styles のパネルを見ると、当たっているルールが上から順に並びます。負けた宣言には打ち消し線が付くので、次の 2 つを区別できます。
| 見え方 | 意味 |
|---|---|
| メディアクエリのルールが一覧に出ていて、宣言に打ち消し線が付いている | セレクタも条件も合っていて、順序で負けている |
| メディアクエリのルールが一覧に出てこない | セレクタが当たっていないか、画面幅が条件を満たしていない |
打ち消し線が付いていた場合、セレクタを書き換えても直らないです。見るのは、どのルールが下に並んでいるかです。
対処: メディアクエリを、対応する基本ルールの後ろへ移す
ブロックを移動しただけで直りました。最終的なスタイルシートは次の形です。
.slot { width: clamp(40px, 11vw, 54px); height: clamp(40px, 11vw, 54px); font-size: 1.15rem; }
.palette { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
@media (min-width: 720px) {
.slot { width: 40px; height: 40px; font-size: 1.05rem; }
.palette { grid-template-columns: repeat(10, 1fr); gap: 6px; }
}
幅 800px・高さ 800px で画面を撮り、盤面が 2 列に収まり、パレットが 1 行に並ぶことを確認しました。
!important を付けたり、.palette.palette のようにセレクタを重ねて詳細度を上げたりしても勝たせられますが、同点で順序に負けているだけなので、ブロックを後ろへ移す方が素直です。
再発を防ぐ
メディアクエリの位置を、min-width の小さい順にそろえる
min-width の条件は、広い画面では複数が同時に成立します。幅 1000px なら、min-width: 720px も min-width: 960px も当たります。この時も詳細度は同点なので、後ろに書いた方が勝ちます。
小さい順に並べておくと、広い画面ほど後ろのブロックが効く形になり、順序と意図が一致します。
.palette { grid-template-columns: repeat(5, 1fr); }
@media (min-width: 720px) {
.palette { grid-template-columns: repeat(10, 1fr); }
}
@media (min-width: 960px) {
.palette { grid-template-columns: repeat(12, 1fr); }
}
逆向きの max-width を使う場合は、大きい順に並べると同じ関係になります。基本ルールを後から足す時に、既存のメディアクエリより後ろへ書いてしまうと、また同じ負け方をします。
画面幅ごとの値を、目視ではなく測る
見た目だけで判断すると、少し小さくなったのか、まったく効いていないのかを区別しにくいです。ブラウザを操作するツールで、計算後の値を取り出すと一目で分かります。
await page.setViewport({ width: 800, height: 800 });
const width = await page.$eval('.slot', (el) => getComputedStyle(el).width);
if (width !== '40px') {
throw new Error(`.slot の幅が ${width} です`);
}
ブレークポイントをまたぐ 2 つの幅で測っておくと、片方の画面幅でだけ崩れる変更に気づけます。
まとめ
メディアクエリが効かない時は、セレクタを強くする前に、ブロックを書いた位置を見ると早く解決できます。
- メディアクエリで囲んでも、中のセレクタの強さは変わらない。条件を満たしていることと、勝つことは別である
- 同じ強さのルールが競合した場合は、スタイルシートの後ろに書いた方が勝つ。画面幅ごとの指定は、対応する基本の指定より後ろに置く
- 開発者ツールで打ち消し線が付いているかを見ると、順序で負けているのか、そもそもルールが当たっていないのかを切り分けられる