Media Queries and the Rise of Smarter CSS

For a long stretch of CSS history, media queries were the only practical route to responsive layouts. Before Flexbox, Grid, responsive units, and container queries existed, resizing a layout meant watching the viewport and shipping breakpoint after breakpoint. That made media queries the obvious tool, and they remain deeply familiar even now that CSS offers much more sophisticated options.

But the role of media queries has shifted. Their modern extensions increasingly target accessibility and user preferences rather than pure layout resizing. Meanwhile, other CSS features have matured to take over much of the responsive work. That doesn't make media queries obsolete—they still have a clear place—but it does mean the old habit of reaching for a media query first deserves re-examination.

It is undeniable that media queries have evolved toward accessibility solutions, making space for other CSS features to take responsibility for responsiveness.

Where Media Queries Fall Short

Media queries were a good answer to the responsive problems of their era. As layouts have grown larger and more component-driven, their limitations have become harder to ignore.

1. They Can Only See the Viewport

A media query can test viewport properties such as width or orientation, but nothing else about the page context. That's fine when a tweak depends only on screen size, but components rarely live in isolation. They share space with siblings, sit inside containers of varying width, and carry content of unpredictable length.

Consider a trivial case where elements are styled to grow and collapse below a 600px viewport:

See the Pen [Old Media Queries [forked]](https://codepen.io/smashingmag/pen/xxNwbob) by Monknow.

See the Pen Old Media Queries [forked] by Monknow.

The rule reads: When the page is narrower than 600px, make these elements expand and shrink. That statement knows nothing about how many siblings exist, what content they hold, or whether the element is actually inside a narrow container. The viewport is a proxy for context, not context itself, and every breakpoint becomes a compromise across all the components that share that viewport.

2. Breakpoints Are Hard to Maintain

Component-based development makes the viewport-only limitation worse. A component can be dropped into any layout, next to any neighboring component, with content that changes over time. A media query breakpoint is blind to all of that, so the developer must find the magic number where each component happens to break—and update it whenever the component's content or surrounding context changes.

Those breakpoints are effectively hardcoded values discovered by dragging the browser window. Managing them adds friction of its own: whether they live at the bottom of a stylesheet, in partial files compiled by a preprocessor, or nested inside component rules using modern CSS Nesting, each instance is one more place to track when editing styles.

3. They Provide Adaptation, Not Fluid Responsiveness

A layout that only changes at discrete breakpoints is adaptive, not truly fluid. When a component breaks between 960px and 970px, adding another media query to fix that specific range means maintaining yet another hardcoded rule for a narrow slice of viewports. That approach scales poorly and offers little fluidity in the actual resizing behavior.

Media queries still matter for what they do well: responding to the viewport and, increasingly, to user preferences and accessibility needs. But for component-level responsiveness, modern CSS has better-equipped tools, and the most effective responsive designs combine both rather than defaulting to one.

Retiring Width Breakpoints

For pure resizing behavior, check the viewport isn’t the whole story — it never really was. Flexbox, Grid, responsive units and CSS math functions handle size shifts more directly, without the magic numbers that litter width-based breakpoints. Container queries remain in the early adoption phase, but the other tools are fully mainstream.

The recurring pattern below pairs Flexbox with media queries to flip a row of cards into vertical stacking at small viewports:

See the Pen [Using Media Queries with Flex Items [forked]](https://codepen.io/smashingmag/pen/MWdaYNx) by Monknow.

See the Pen Using Media Queries with Flex Items [forked] by Monknow.

That pairing exposes three familiar weaknesses:

  1. It’s viewport-focused. The breakpoint only describes the browser, which lands wherever testing lands — say, 700px — and says nothing about the element’s own context.
  2. It’s hard to reuse. The same component can’t be dropped into a container narrower than that threshold without looking broken or misaligned.
  3. It’s not truly fluid. Content squishes between scales, with the “optimal” experience confined to one narrow window around the breakpoint.

Add more breakpoints and problem two compounds. Drop them entirely and reach for the flex shorthand instead, letting each <article> grow and shrink toward a target of 400px:

main {
  display: flex;
  flex-flow: row wrap;
}

main article {
  flex: 1 1 400px;
}

That changes the intent from “wrap when the viewport is below 700px, without knowing why” to “aim for 400px and adapt to whatever space is available, wherever this element appears.”The result is fewer rules, no magic numbers and a layout that stays graceful across the whole range of sizes.

See the Pen [Flex Items without Media Queries [forked]](https://codepen.io/smashingmag/pen/jOobPNe) by Monknow.

See the Pen Flex Items without Media Queries [forked] by Monknow.

Grid for Equal Columns

Flexbox leaves one wrinkle: the last item in a wrapped row stretches to fill leftover space, breaking visual rhythm. Reaching for an explicit width on flexible items is usually a signal to switch to Grid, which can define fixed column tracks. Two lines do the work:

main {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
}
  • auto-fit packs in as many columns as possible and expands them over any leftover space.
  • minmax sets a floor for each column, here 500px.

See the Pen [Grid without Media Queries [forked]](https://codepen.io/smashingmag/pen/RwmWPwV) by Monknow.

See the Pen Grid without Media Queries [forked] by Monknow.
Note: Sara Soueidan’s write-up of auto-fill versus auto-fit is a useful deep dive on this snippet’s nuances.

Limits from Math Functions

Math functions set responsive boundaries without breakpoint bookkeeping. All are supported in current browsers and in wide use.

Use min() to cap growth: the element fills its parent’s width but never exceeds 300px.

.min {
  height: 400px;
  width: min(100%, 300px);
}

Combine it with aspect-ratio and the height scales in sync with width:

.min-and-aspect-ratio {
  aspect-ratio: 1/1; /* or 1 */
  width: min(100%, 300px);
}

Use max() to enforce a floor: the element grows to half its parent’s width but won’t shrink under 300px.

.max {
  height: 400px;
  width: max(50%, 300px);
}

The direction takes a moment to register — min() enforces the ceiling, max() the floor. clamp() bundles both into one declaration, adding a preferred middle value between the two extremes. This example targets full parent width, clamped to a 200px300px band:

See the Pen [math functions [forked]](https://codepen.io/smashingmag/pen/wvbKaBj) by Monknow.

See the Pen math functions [forked] by Monknow.

Don’t Use Any One Tool Alone

None of these features is a complete answer. Responsive design is built from their combination — the equivalent of the bundled standards behind the Performance API in JavaScript, each spec doing a focused job while the rest cover the gaps. These tools aren’t so much replacing media queries as joining them where they make more sense.

Fluid type shows the layering:

  • Media queries adjust font-size in fixed steps at chosen widths — valid, but you’ll write several steps to avoid gaps.

See the Pen [Resizing text using media queries [forked]](https://codepen.io/smashingmag/pen/oNRjXXz) by Monknow.

See the Pen Resizing text using media queries [forked] by Monknow.
  • Viewport units scale text continuously: each vw equals 1% of the browser width.
  • clamp() bounded by viewport units scales the size while preventing extremes — too small on a phone, too large on a monitor.

See the Pen [Resizing text individually using clamp() and vw [forked]](https://codepen.io/smashingmag/pen/yLWYNNw) by Monknow.

See the Pen Resizing text individually using clamp() and vw [forked] by Monknow.

Put the rule on the root element’s font-size and every relative-size descendant inherits the scaling factor. Use rem units for page copy (root-relative, distinct from the parent-relative em) to keep proportions aligned across the tree:

See the Pen [Resizing text by the same factor [forked]](https://codepen.io/smashingmag/pen/YzbyXyZ) by Monknow.

See the Pen Resizing text by the same factor [forked] by Monknow.

Let no feature do everything. Pick the right wrench for each joint — media queries for device conditions, layout containers for element constraints — and the breakpoints you keep will finally say something the component never could on its own.

Container Queries Fill the Gap

Media queries make layout decisions based on the viewport, which works fine when elements are free to take the full page width. A shopping cart, for example, can display products in a wide <table> when the viewport is large enough.

Cart UI on desktop
Cart UI on desktop. (Large preview)

On a narrow screen, that table layout fails. Tables bring their own responsive challenges, and common workarounds require significant engineering effort. The layout shift is not just about resizing elements—border colors, visibility, and flex directions may all need to change. Media queries can handle that, but only when the viewport itself dictates the design.

Cart UI on mobile
Cart UI on mobile. (Large preview)

The real limitation of media queries is that they ignore the element's surrounding context. If a set of components always occupies the full viewport width, the viewport size is a reliable proxy for the space available. But components often live inside a sidebar, a grid track, or a column next to a <main> element. In normal document flow, the size of one element directly influences its neighbors.

See the Pen [Responsive Cards Using Media Queries [forked]](https://codepen.io/smashingmag/pen/ExzVjPj) by Monknow.

See the Pen Responsive Cards Using Media Queries [forked] by Monknow.

Consider a list of cards placed inside an <aside>. The cards are squished because the column is narrow. What we really want is to change each card's layout when the containing list reaches a certain width—not when the viewport crosses a breakpoint.

See the Pen [Responsive Cards Using Media Queries Inside Container [forked]](https://codepen.io/smashingmag/pen/gOJapPo) by Monknow.

See the Pen Responsive Cards Using Media Queries Inside Container [forked] by Monknow.

Container queries provide exactly that: conditional styles based on an element's own size. We register the parent as a container and grant it influence over its children's layout.

.cards {
  container-name: cards;
}

The container-type property tells the browser how to measure the container. Use size to track both block and inline dimensions, or inline-size to track only the inline direction. The normal value disables size containment for style-only queries.

.cards {
  container-name: cards;
  container-type: inline-size;
}

The container shorthand property simplifies the setup.

.cards {
  container: cards / inline-size;
}

Querying follows the same syntax as media queries, but uses @container instead of @media.

.cards {
  container: cards / inline-size;
}

@container cards (width < 700px) {
  .cards li {
    flex-flow: column;
  }
}

In the example, each .card switches from a column flex direction to row once the .cards container exceeds 700px in inline size.

See the Pen [Responsive Cards Using Container Queries [forked]](https://codepen.io/smashingmag/pen/VwOvLap) by Monknow.

See the Pen Responsive Cards Using Container Queries [forked] by Monknow.

A related feature, style queries, lets you query a container's computed styles—for instance, setting a child's color to white when the container has a dark background-color. This area of CSS is still maturing, and browser support is evolving.

Container queries introduce a genuinely new capability: responsive layouts that are aware of their local context, not just the viewport.

Media Queries Still Matter

Media queries are not obsolete. Their limitation—viewport-only awareness—is precisely what container queries solve, but they remain useful for page-level layout decisions. They also cover critical accessibility requirements, since they can respond to user preferences for motion and contrast at the operating system level.

Keep using media queries where they fit. Just recognize that CSS now offers finer-grained tools for responsive design, and the viewport is no longer the only signal worth listening to.

Smashing Editorial