max-width:900px Loses to a Later Same-Specificity Rule
This article may contain affiliate links. Its content is not affected by advertising.
In short
With equal specificity, being inside @media doesn't matter — the rule that appears later in the source wins, even while the media condition is true.
Conclusion
When two CSS rules have equal specificity, being inside a @media block doesn’t matter — the rule that appears later in the source wins. We hit this on a pricing section built as a two-column label/body grid for desktop, collapsing to one column on mobile. The one-column override lived inside @media (max-width: 900px), but an equal-specificity base rule (the two-column grid) sat later in the same file. On mobile, the base rule kept winning, and short Japanese labels squeezed into the fixed-width label column wrapped one character per line. The fix changed no CSS values at all — it only moved the @media block to the end of the stylesheet.
Symptom
The pricing section had three places using a “label on the left, content on the right” two-column grid. On desktop, grid-template-columns: var(--label-col) 1fr fixed the left column at 206px so labels and descriptions sat side by side.
On mobile, a @media (max-width: 900px) block targeted the same selector with grid-template-columns: 1fr to stack label and description vertically instead. In production, opening the page at mobile width showed the label column still pinned to 206px. Labels five to eight characters long — short Japanese phrases — didn’t fit that width and wrapped one character per line, turning into a vertical strip of single characters. The body text on the right stayed squeezed into its own column too, and the whole section looked broken and elongated.
Nothing in the HTML structure or JavaScript was involved, and the @media override for the one-column layout did exist — it just wasn’t taking effect, which is what made the cause harder to isolate at first.
Cause
Before the fix, the mobile @media (max-width: 900px) block started at line 349, with this rule at line 358:
/* index.html (pre-fix, lines 349-365) */
@media (max-width: 900px) {
.gnav a:not(.btn) { display: none; }
/* ... */
.deliver, .included { grid-template-columns: 1fr; row-gap: 14px; padding-left: 0; }
/* ... */
}
The desktop base rule sat further down the same file, starting at line 390:
/* index.html (pre-fix, lines 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; }
Both target .deliver, .included — identical selector, identical specificity (a plain class combination). In the CSS cascade, when two rules carry the same specificity, whether one is wrapped in @media plays no role in the tie-break — the rule that appears later in the source always wins. In this file the base rule (line 393) came after the @media block (line 358), so grid-template-columns: var(--label-col) 1fr stayed in effect at every viewport width, and the mobile 1fr override was never applied, no matter how narrow the screen got.
The result was Japanese labels forced into a fixed 206px column, wrapping at the smallest unit the browser could break on — a single character — and producing the vertical, one-character-per-line look. The @media condition itself was evaluating correctly; the only thing that failed was which declaration actually took effect, which is the kind of bug that’s invisible until you open dev tools on the actual element.
The fix
No CSS values changed. The @media (max-width: 900px) and @media (max-width: 540px) blocks were moved, as whole blocks, past every base rule — to the end of the <style> tag.
/* index.html (post-fix, lines 416-428) */
/* responsive
Must stay at the end of <style>. The base rules for .deliver / .ways / .rate-incl used
to sit below this, and with equal specificity the later rule wins, so every "collapse to
one column" override here was being cancelled on mobile (the "お渡しする形" label was
wrapping one character per line). */
@media (max-width: 900px) {
.gnav a:not(.btn) { display: none; }
/* ... */
.deliver, .included { grid-template-columns: 1fr; row-gap: 14px; padding-left: 0; }
/* ... */
}
The base .deliver, .included rule stays exactly where it was, at line 358, value unchanged — only the @media block moved to the very end of the file. With equal specificity on both sides, source order now puts @media last, so the 1fr override takes effect at 900px and below, and the label column returns to full width with no wrapping. Desktop rendering (full page height matched exactly before and after) was unaffected.
The fix isn’t about raising specificity — it’s about keeping specificity equal and reversing source order. No !important, no :where() tricks; moving the block within the same file was enough.
Preventing a repeat
This class of bug is invisible to tooling because the CSS is valid either way. Neither the build nor a linter flags a @media block placed before a same-specificity base rule — both are syntactically correct, and the browser is just faithfully applying “equal specificity, later wins.” The only way to catch it is to look at the rendered result at the actual viewport width.
The fix leaves a comment directly above the relocated @media block explaining why it has to stay at the end of the file. That’s worth more as a repeat-prevention measure than the one-line CSS change itself — it’s there so the next person adding a same-specificity rule to this file doesn’t have to rediscover the ordering requirement from scratch.
Frequently asked questions
Q1Doesn't a @media query get priority just by matching the viewport?
No. Specificity never looks at whether a rule is wrapped in @media. With identical selectors and specificity, whichever rule comes later in the source wins, inside or outside a media query.
Q2Would dev tools have caught this?
Yes — the Computed panel shows the media-query rule struck through. It took a while to notice because both rules were valid CSS and no linter flagged anything.
Q3Is it safe to always put @media blocks at the end of the file?
Safe within one file. Across multiple files, the load order of the file containing the @media block matters just as much as its position inside it.
Environment verified
- Static HTML + CSS (no build tool, single <style> tag) / Vercel
- Found in production 2026-09-30, fixed the same day
What this article is based on
- HTML file lines 349-365commit 9cd8ba2
- HTML file lines 390-407commit 9cd8ba2
- HTML file lines 358-374commit 14eb416
- HTML file lines 416-428commit 14eb416
Every claim in this article comes from the records above. The repositories we operate are private so we cannot link to them, but which file, which lines, and at which commit we read them is recorded for every article. Nothing here is written from guesswork.