メディアクエリが効かない原因は、@media(max-width:900px)より後ろの同詳細度ルール
※本記事にはアフィリエイトリンクを含む場合があります。内容は広告の有無に影響されません。
結論
CSSは詳細度が同じ場合、@mediaの内側か外側かに関わらずソースコード上で後に書かれた指定が勝つため、@mediaブロックを同じ詳細度の基本指定より前に書くと、画面幅の条件を満たしていてもその指定は丸ごと無効化される。
結論
CSSの詳細度が同じ場合、@mediaの内側で書いたか外側で書いたかは優先順位に影響しない。ソースコード上で後に書かれた指定がそのまま勝つ。 実際に踏んだのは、料金セクションの見出しラベルを「PCは2列、スマホは1列」で組んでいたコンポーネントで、スマホ用の1列指定を@media (max-width: 900px)の中に書いていたが、同じセレクタ・同じ詳細度の基本ルール(2列指定)がファイルの後ろに置かれていたため、スマホの狭い画面でも2列のまま描画され、狭い列に押し込められた見出しの文字が1文字ずつ縦に折れて割れていた。直し方は@mediaブロックを<style>の末尾(すべての基本ルールより後)へ移動するだけで、CSSの値自体は1文字も変えていない。
症状
このLPの料金セクションには、左にラベル・右に説明文を置く「左=ラベル/右=中身」の2列グリッドが3箇所あった。PCではgrid-template-columns: var(--label-col) 1frで左列を206px固定にし、ラベルと説明文を横に並べる設計になっている。
スマホでは画面が狭いため、@media (max-width: 900px)の中で同じセレクタにgrid-template-columns: 1fr(1列)を指定し、ラベルと説明文を縦に積む想定だった。実際に本番でスマホ幅で開くと、ラベルの列が206px固定のまま残り、「お渡しする形」「すべてに含まれます」という5〜8文字のラベルが206pxの狭い列に収まらず、1文字ずつ縦に折り返されて表示されていた。説明文側も右の1fr列に押し込まれたままで、全体的に縦に細長く崩れた見た目になっていた。
HTMLの構造やJavaScriptは関係しておらず、@mediaで1列化の指定自体は存在していたにもかかわらず効いていなかった点が、原因を切り分ける上で引っかかった部分だった。
原因
修正前のファイル(index.html)では、スマホ用の@media (max-width: 900px)ブロックが349行目から始まり、その中の358行目に次の指定があった。
/* index.html(修正前・349-365行目) */
@media (max-width: 900px) {
.gnav a:not(.btn) { display: none; }
/* 中略 */
.deliver, .included { grid-template-columns: 1fr; row-gap: 14px; padding-left: 0; }
/* 中略 */
}
一方、PC用の基本ルールはそれよりファイルの後ろ、390行目から次のように置かれていた。
/* index.html(修正前・390-407行目) */
.deliver, .included {
display: grid;
grid-template-columns: var(--label-col) 1fr;
column-gap: 48px;
padding-left: 26px;
align-items: start;
margin-top: 16px;
}
.included { align-items: center; }
どちらもセレクタは.deliver, .includedで同一、詳細度も同じ(クラス単体の組み合わせ)。CSSのカスケードでは、詳細度が同じ指定が複数ある場合、@mediaで囲まれているかどうかは評価に入らず、ソースコード上で後に出現した方が常に勝つ。このファイルでは@mediaブロック(358行目)より基本ルール(393行目)が後に書かれていたため、画面幅が900px以下でもgrid-template-columns: var(--label-col) 1frが常に有効なまま残り、スマホ用の1fr指定は一度も反映されていなかった。
結果として、206px固定の狭い列に日本語ラベルが流し込まれ、折り返せる文字単位(1文字)ごとに改行される縦書き状の見た目になっていた。@mediaの条件判定自体は正しく動いており、ブラウザの開発者ツールで要素を見ない限り「指定はあるのに効いていない」ことに気づきにくい種類の不具合だった。
直し方
CSSの値は一切変更せず、@media (max-width: 900px)ブロックと@media (max-width: 540px)ブロックを、ファイル内のすべての基本ルールより後ろ(<style>の末尾)へ丸ごと移動した。
/* index.html(修正後・416-428行目) */
/* responsive
⚠ 必ず <style> の末尾に置く。以前はこの下に .deliver / .ways / .rate-incl の基本指定があり、
同じ強さのセレクタは後勝ちなので、スマホで「1列にする」指定が全部打ち消されていた
(料金欄の「お渡しする形」が1文字ずつ縦に割れていた)。 */
@media (max-width: 900px) {
.gnav a:not(.btn) { display: none; }
/* 中略 */
.deliver, .included { grid-template-columns: 1fr; row-gap: 14px; padding-left: 0; }
/* 中略 */
}
基本ルールの.deliver, .includedは358行目からそのままの内容で残り(値は無変更)、@mediaブロックだけがファイルの最後尾に移った。これで同じ詳細度の指定同士でも@media側がソース順で後になるため、900px以下では1frが有効になり、ラベル列は折り返しの要らない全幅に戻る。PC表示(全高8988pxで修正前と一致)は変化していない。
この修正の本質は「詳細度を上げる」のではなく「同じ詳細度のまま、ソース順を逆転させる」こと。!importantや:where()で詳細度をいじる必要は無く、ファイル内の並びを変えるだけで解決した。
再発防止
この種の不具合は、CSSの構文としては完全に正しいため、ビルドやリンタでは検出できない。@mediaブロックをどこに置いても文法エラーにはならず、ブラウザは指定された通りに「詳細度が同じなら後優先」を忠実に実行するだけだからだ。気づくには実際の画面幅で描画結果を見るしかない。
今回の修正では、移動した@mediaブロックの直前に「必ず<style>の末尾に置く」という理由付きのコメントを残した。1行のCSSを直すよりも、次にこのファイルへルールを追記する人が、同じ詳細度の指定をどこに置くべきか迷わないようにすることの方が、再発防止として効く。
よくある質問
Q1@media自体にはmin-widthやmax-widthを使った優先度はないのですか?
ありません。CSSの詳細度は@mediaの有無を見ず、内側のセレクタだけで決まります。同じセレクタ・同じ詳細度であれば、@mediaの中に書かれているか外に書かれているかに関係なく、ソースコード上で後に出てくる方がそのまま有効になります。
Q2ブラウザの開発者ツールで見れば気づけましたか?
気づけます。要素を選択してComputed欄を見れば、@media側の指定が打ち消し線で無効表示されます。今回気づくまで時間がかかったのは、CSSの構文としては両方とも正しく、ビルドやリンタがエラーを出さなかったためで、実機の狭い画面で見るまで疑う理由が無かったことが原因です。
Q3@mediaを常にファイルの末尾に書けば今後は安全ですか?
同じファイル内で詳細度が揃っている限りは安全です。ただしCSSを複数ファイルに分割している場合は、@mediaを含むファイルの読み込み順そのものが後ろになっているかも合わせて確認する必要があります。詳細度が同じ指定が別ファイルの後続import で上書きしてくる経路は、ファイル内の並び順だけでは防げません。
確認した環境
- 静的HTML + CSS(ビルドツールなし、単一<style>タグ)/ Vercel
- 2026-09-30 本番で発覚、同日中に修正
この記事の根拠
- HTMLファイル 349〜365行目コミット 9cd8ba2
- HTMLファイル 390〜407行目コミット 9cd8ba2
- HTMLファイル 358〜374行目コミット 14eb416
- HTMLファイル 416〜428行目コミット 14eb416
本文の主張は、上の記録に書かれていることだけです。運用しているリポジトリは非公開のため リンクは張れませんが、どのファイルの何行目を、どのコミット時点で見て書いたかは 記事ごとに残しています。推測で書いた箇所はありません。