Why Your CSS Media Query Is Not Triggering — and How to Fix It

Quick answer

💡A CSS media query that does not trigger is almost always caused by one of three issues: the viewport meta tag is missing (so mobile browsers never reach the intended breakpoint width), the feature value uses no unit or incorrect syntax (silently ignored), or a higher-specificity rule outside the query overrides it. Check each of these before anything else.

Error symptoms

  • Mobile layout never switches to the responsive version
  • Media query works in Chrome DevTools but not on a real phone
  • Breakpoint triggers at the wrong viewport width
  • Styles inside the media query are completely ignored
  • Larger-screen styles persist on small screens
  • Only some properties inside the query apply, others are overridden

Common causes

  • Missing viewport meta tag — mobile browsers default to ~980px viewport and never reach small breakpoints
  • Syntax error: @media max-width: 768px without parentheses around the feature
  • Unitless value: max-width: 768 instead of max-width: 768px
  • CSS custom property in the condition: @media (max-width: var(--bp)) — browsers reject this silently
  • Higher-specificity rule elsewhere overrides the responsive declaration
  • Stale browser cache serving the old CSS without the media query

When it happens

  • First time adding responsive styles to an existing non-responsive project
  • After copying a media query from a tutorial without checking parenthesis syntax
  • After a build tool minifies or bundles CSS and alters whitespace or syntax
  • When testing on a real device for the first time after only using DevTools
  • After refactoring CSS and introducing a more-specific selector in the base styles

Examples and fixes

Without the meta tag, mobile browsers render at ~980px and breakpoints designed for 768px never fire.

Missing viewport meta tag

❌ Wrong

<!-- Missing viewport meta — page renders at ~980px on mobile -->
<!DOCTYPE html>
<html>
<head>
  <title>My Page</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <nav class="nav">...</nav>
</body>
</html>

✅ Fixed

<!-- Viewport meta tag present — CSS width matches device width -->
<!DOCTYPE html>
<html>
<head>
  <title>My Page</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <nav class="nav">...</nav>
</body>
</html>

Mobile browsers have two viewport concepts: the layout viewport (the width CSS operates in) and the visual viewport (what the user sees). Without the meta tag, the layout viewport defaults to roughly 980px. Your @media (max-width: 768px) rule evaluates against 980px and is never true, no matter how small the physical screen. The meta tag instructs the browser to set the layout viewport equal to the device's screen width in CSS logical pixels, so a 390px iPhone reports 390px to CSS media queries.

Media features must be wrapped in parentheses — omitting them silently invalidates the entire rule.

Syntax error: missing parentheses

❌ Wrong

/* WRONG: no parentheses around the media feature */
@media screen and max-width: 768px {
  .sidebar {
    display: none;
  }
}

✅ Fixed

/* CORRECT: parentheses required around each feature */
@media screen and (max-width: 768px) {
  .sidebar {
    display: none;
  }
}

The CSS media query specification requires each media feature to be enclosed in parentheses. @media max-width: 768px is a syntax error. Browsers do not log a warning in the console — the entire at-rule is silently discarded. This is one of the hardest bugs to spot by eye because the query looks almost correct. Paste your CSS into the CSS Formatter tool and check the DevTools Sources panel for parse errors highlighted in the gutter.

An ID selector or inline style outside the media query wins over a class inside it.

Specificity override from a more-specific base rule

❌ Wrong

/* High-specificity base rule — media query cannot override it */
#main-nav .nav-link {
  font-size: 18px;
}
@media (max-width: 600px) {
  .nav-link {
    font-size: 14px; /* Loses — ID rule has higher specificity */
  }
}

✅ Fixed

/* Match or exceed the base specificity inside the media query */
#main-nav .nav-link {
  font-size: 18px;
}
@media (max-width: 600px) {
  #main-nav .nav-link {
    font-size: 14px; /* Wins — same specificity, later source order */
  }
}

CSS specificity is calculated independently of media query wrappers. A rule written inside a media query is not automatically more specific than one outside it. The specificity score for .nav-link (0,1,0) is lower than #main-nav .nav-link (1,1,0). The mobile rule loses regardless of source order. The fix is to use the same selector inside the media query so specificity is equal — then source order determines the winner, and the media query rule (which appears later) wins. Alternatively, refactor the base rule to use a class instead of an ID.

Root causes of silent media query failures

CSS media queries fail silently. Unlike JavaScript errors, a broken media query produces no console warning, no DevTools alert, and no visible indication that anything went wrong. The browser simply skips the block and moves on. This makes debugging harder than most CSS problems because you are looking for an absence of behaviour rather than an error message.

The most impactful failure is the missing viewport meta tag. Mobile browsers ship with a built-in default: if no viewport directive is present, the layout viewport is set to approximately 980px wide. This is a legacy default from the early smartphone era, designed to make desktop-only sites fit on small screens by zooming out. CSS media queries evaluate against the layout viewport, not the physical screen size. A phone at 390px physical width reports 980px to CSS if the meta tag is absent, and any breakpoint under 980px is permanently false.

Syntax errors are the second most common cause. The media feature — width, height, orientation — must be in parentheses. Forgetting them invalidates the rule. Using double colons instead of single, or writing the condition as a plain string, produces the same silent failure. CSS parsers are forgiving with some errors but strict about at-rule structure.

Specificity conflicts are harder to diagnose. Media queries do not raise or lower specificity — a rule inside @media (max-width: 768px) has exactly the same specificity score as the same rule written without a wrapper. If a base stylesheet rule uses an ID selector or a long class chain to target an element, a simple class-based responsive rule will lose even if it appears later in the file. The DevTools Computed tab is the best tool for identifying these conflicts — it shows every applicable rule with struck-through losers and the winning declaration highlighted.

Browser caching is the third common non-code cause. A stylesheet cached from a previous deploy may still be active despite a new deployment. Force a cache bypass with Ctrl+Shift+R or Cmd+Shift+R in Chrome and Firefox, or open DevTools, tick Disable cache in the Network tab, and reload. In production, versioning asset filenames ensures fresh delivery.

Diagnosing non-firing queries in DevTools

Open Chrome DevTools and go to the Elements panel. Select the element that should be responding to the breakpoint. In the Styles tab, scroll through all applied rules until you find the media query block. If the block is present but greyed out, the viewport is currently outside the query range. Drag the DevTools viewport handle to resize and watch the rule activate.

If the rule is active (not greyed out) but the property is struck through, a more specific rule elsewhere is winning. Hover over the struck-through property — DevTools will show the winning rule's selector and source file. That is the rule you need to match or refactor.

For mobile-specific bugs, enable the Device Toolbar (Ctrl+Shift+M). This simulates viewport width, touch events, and device pixel ratio. However, it does not simulate the missing meta tag bug — DevTools always applies a virtual meta tag. To reproduce the no-meta-tag bug accurately, you must test on a real device or use remote debugging.

To check for CSS parse errors, open the DevTools Sources panel, find your stylesheet, and look for red X markers in the gutter. These indicate lines the parser rejected. A broken @media syntax often marks the opening line of the rule.

For user preference media features like prefers-color-scheme or prefers-reduced-motion, use the Rendering panel. Open DevTools, press Escape to show the drawer, click the three-dot overflow menu, choose Rendering, and scroll to the Emulate CSS media features section. You can toggle dark mode and reduced motion without changing OS settings.

Fixes for each common failure mode

Fix the viewport meta first. Add meta name=viewport content=width=device-width, initial-scale=1 to the HTML head. Confirm it appears in the page source after server rendering or static site generation — some frameworks inject it via a layout component that may not always render. Check the Network tab in DevTools and inspect the raw HTML response to be certain.

For syntax errors, validate your CSS through the CSS Formatter tool. It will highlight malformed @media statements. Alternatively, open DevTools, go to Sources, select your stylesheet, and look for red error markers. Fix the parentheses, units, and operator spelling, then reload.

For unitless values — max-width: 768 — add the px unit: max-width: 768px. Remember that em-based breakpoints scale with the user's browser font size. 48em at a 16px base is 768px, but a user who has set 20px base font gets a 960px breakpoint. Pixel-based values are predictable. Em-based values respect font scaling but require careful documentation.

For CSS variable failures in media conditions — @media (max-width: var(--bp)) — the only fix is to output the literal value. Use a SCSS variable, a PostCSS custom media plugin, or JavaScript to read the computed custom property and apply a class instead.

For specificity conflicts, increase the selector specificity inside the media query to match the base rule. If the base rule uses #id .class, the media query override must use the same or a more specific selector. Better long-term: refactor the base rule to use a lower-specificity selector, which makes responsive overrides easier across the entire codebase.

Edge cases that trip up experienced developers

The min-width and max-width directions can conflict when you mix mobile-first and desktop-first rules in the same file. A desktop-first max-width: 768px rule and a mobile-first min-width: 768px rule both match at exactly 768px. The last one in source order wins, which may not be your intention. Use the range syntax or offset by 1px to create clean boundaries.

Some CSS-in-JS libraries generate media queries with string interpolation and can introduce whitespace or formatting bugs that invalidate the query. If you use styled-components, Emotion, or similar libraries, check the generated CSS in DevTools Sources rather than the source file — the bug may be in the output, not the input.

Iframes have their own viewport. A responsive component inside an iframe responds to the iframe's width, not the page's viewport width. If you embed a component in an iframe and expect page-level breakpoints to control it, they will not. Container queries are the correct solution for components that need to respond to their own container width rather than a global viewport.

Some CSS preprocessors like Sass interpolate variables inside at-rules. @media #{$bp} { } is valid Sass but only if the variable outputs valid media syntax including the parentheses. A Sass variable that contains max-width: 768px without parentheses produces broken output. Always test compiled CSS output, not just source.

Firefox historically handled em-based breakpoints slightly differently from Chrome when the user zoomed the page. Modern Firefox aligns with the specification, but if you support older Firefox and use em queries, test zoom behaviour. Logical pixel widths in CSS do not change when the user zooms — only physical rendering does — but some older browser versions had inconsistencies.

Mistakes that cause persistent query failures

Using !important inside a media query to fight a specificity battle is a sign the underlying problem is a high-specificity base rule. Adding !important may fix the symptom but creates a larger specificity problem: the only way to override an !important rule is with another !important rule of equal or higher specificity. Refactor the base selector instead.

Testing only in desktop DevTools and shipping to production without a real device check is the most common workflow mistake. DevTools responsive mode simulates viewport size and pixel ratio but does not simulate actual browser behaviour differences, font rendering, touch event handling, or hardware limitations. Keep a test device on your desk or use BrowserStack for remote testing.

Copying media queries from frameworks like Bootstrap or Tailwind into a custom stylesheet without understanding the base styles those frameworks rely on is a setup for conflicts. Bootstrap's grid, for example, depends on its own box-sizing: border-box global reset. Pasting Bootstrap media queries into a project without that reset produces unexpected behaviour.

Forgetting to test the fallback for browsers that do not support a media feature. prefers-color-scheme has broad support, but prefers-contrast and update have partial support. Always define a reasonable default outside the query. The query is progressive enhancement, not a requirement.

Using max-device-width instead of max-width. max-device-width is deprecated — it refers to the physical display resolution rather than the layout viewport. It behaves unexpectedly on high-DPI screens and returns the physical pixel count rather than CSS logical pixels. The correct feature is max-width, which targets the CSS layout viewport width correctly and works consistently across all modern browsers without caveats.

Building reliable media queries from the start

Validate every page template for the viewport meta tag before adding any responsive CSS. A linting rule, a CI check, or even a simple grep for the meta string in your HTML templates catches this early. Without it, all responsive CSS on that page is inert on mobile devices.

Keep media queries co-located with the rules they modify. Placing all responsive overrides in a single large @media block at the bottom of the file makes it harder to maintain — when you change a base rule, you must also scroll to the bottom to update the responsive version. Keeping the responsive rule immediately after the base rule reduces this friction.

Name your breakpoints semantically in a token file: $bp-tablet: 768px, $bp-desktop: 1024px. Reference these variables everywhere instead of raw pixel values. When a breakpoint needs to change, it changes in one place. Document why each breakpoint exists and what layout change it controls.

For new components, consider container queries first if the component will be used in multiple layout contexts. Container queries decouple responsive behaviour from the global viewport and make components portable. Use viewport queries for layout-level decisions — navigation patterns, sidebar visibility, full-page grid changes.

After writing media queries, paste the CSS into the CSS Formatter tool to verify formatting and catch unitless values or mismatched parentheses. A clean, formatted stylesheet is easier to audit for errors and easier for teammates to read and maintain.

To enforce consistent breakpoint ordering across the entire stylesheet, configure stylelint with the stylelint-order plugin. Its media-query-no-invalid rule flags malformed conditions, and a custom ordering rule can require that min-width queries appear in ascending order. Running this check in CI catches inconsistent or out-of-order breakpoints before they reach review, making it easier to reason about which query applies at any given viewport width.

Media query debug checklist

  • Confirm viewport meta tag is present in the rendered HTML, not just source files
  • Check media feature syntax: parentheses are required around each condition
  • Verify all breakpoint values include a unit (px, em, rem) — not bare numbers
  • Remove any CSS custom properties from media conditions — use literal values
  • Open DevTools Styles tab and check for struck-through properties indicating specificity loss
  • Hard-refresh the page (Ctrl+Shift+R) to bypass potentially stale cached CSS
  • Test on a real mobile device, not only DevTools responsive mode
  • Use DevTools Rendering panel to emulate preference media features for testing

Related guides

Frequently asked questions

Why does my media query work in DevTools but not on my real phone?

The most likely cause is a missing viewport meta tag. DevTools responsive mode applies a virtual meta tag regardless of your HTML, but a real mobile browser does not. Without meta name=viewport content=width=device-width initial-scale=1, the mobile browser renders at approximately 980px layout width, and your breakpoints designed for 768px or smaller are never true. Add the meta tag and test on device again.

What happens when you write @media max-width: 768px without parentheses?

The entire @media block is silently discarded. Parentheses around the media feature are required by the CSS specification. The browser does not log a console error or highlight the issue in DevTools. To catch this, open the DevTools Sources panel and look for red error markers in the CSS file gutter, or paste your CSS into a validator like the CSS Formatter tool which highlights malformed at-rules.

Can I use CSS custom properties inside media query conditions?

No. CSS custom properties cannot be used inside media feature conditions. @media (max-width: var(--breakpoint)) is invalid and the browser ignores the entire rule silently. Custom properties resolve at cascade time, but media queries are evaluated earlier in the process before custom properties are resolved. Use SCSS variables, PostCSS plugins with custom-media syntax, or JavaScript to apply breakpoints dynamically.

How do I fix a media query that is being overridden by a more specific base rule?

Match or increase the specificity of your selector inside the media query. Open DevTools Elements panel, select the element, and check the Styles tab for struck-through properties — that indicates a specificity loss. The winning rule's selector is shown. Replicate that selector inside your media query. Long-term, refactor base styles to use lower-specificity selectors so responsive overrides require only a class selector.

Does browser caching prevent media queries from working?

Yes, if you recently updated your CSS and the browser is serving a cached version, media queries from the old file remain active and new ones do not appear. Force a hard refresh with Ctrl+Shift+R on Windows or Cmd+Shift+R on Mac. In DevTools, tick Disable cache in the Network tab before reloading. In production, use content-hashed filenames so every deployment creates a new URL that the browser must fetch fresh.

Why does my media query not trigger at exactly the breakpoint pixel value?

max-width and min-width are both inclusive at the boundary. @media (max-width: 768px) is true when width is 768px or less. @media (min-width: 768px) is true when width is 768px or more. Both apply at exactly 768px, so both media query blocks are active simultaneously at that width. To avoid this, use the range syntax: @media (width < 768px) which is exclusive at 768px, creating a clean boundary with no overlap.

Does @media (orientation: landscape) work reliably on phones?

It works but has nuances. Landscape orientation means the viewport width is greater than the viewport height. On modern phones with software keyboards, the keyboard reduces the viewport height, which can briefly trigger a landscape query even in portrait mode. Virtual keyboards affect orientation detection on some devices. For layout decisions, prefer width-based queries over orientation queries for more predictable behaviour across devices.

Why does my media query work in Chrome but not in Firefox or Safari?

The most likely cause is a non-standard media feature or a feature with limited cross-browser support. Check the MDN compatibility table for the specific feature you are using. Features like prefers-contrast and update have partial support. For width-based queries, cross-browser support is complete in all modern browsers. If only a specific browser fails, check for a browser-specific CSS reset or user stylesheet interfering with your rules.

Can I put multiple conditions in one media query?

Yes. Use and to require both conditions: @media (min-width: 768px) and (max-width: 1024px) targets tablets only. Use a comma to express OR: @media (max-width: 480px), (orientation: landscape) applies on small screens or landscape mode. The not operator negates: @media not screen applies to all media types except screen. Modern range syntax handles width ranges more cleanly: @media (768px <= width <= 1024px).

All tools run in your browser. Your data never leaves your device. Last updated: 2026-05-06.