Why Media Queries Are Not Enough for Responsive Design

Media queries are the first tool most of us reach for when a layout breaks. They are also the reason so many responsive builds feel like a stack of exceptions rather than a system. In this article I want to walk through what media queries actually solve, where they stop helping, and what a more resilient responsive strategy looks like when you are building custom players, streaming interfaces, and accessible time-based media. This is not a theoretical argument. It is the difference between a player that survives a real device lab and one that only works in the browser window where it was designed.

We will look at container queries, intrinsic sizing, fluid typography, and the specific failure modes that appear when video, audio, captions, and transport controls have to adapt across phones, TVs, in-car screens, and desktop browsers. Along the way I will share the tests I run before shipping a responsive media component, and the questions I ask when a client says “just make it work on mobile.”

Person testing a responsive web layout on a laptop and smartphone side by side

What Media Queries Actually Do

A media query is a conditional rule. It asks the browser a question about the viewport, the device, or the user’s display preferences, and applies a block of CSS only when the answer is true. The classic example is @media (max-width: 600px), which targets viewports narrower than 600 CSS pixels. Media queries can also test orientation, resolution, pointer type, hover capability, color scheme, and reduced motion preferences.

That is a useful capability. It lets us change a multi-column layout into a single column, reduce padding, or enlarge tap targets on small screens. For a long time, media queries were the only practical way to make a page respond to its environment. The problem is not that media queries are bad. The problem is that they are indirect. They describe the viewport, not the component. And the viewport is a poor proxy for the space a component actually has.

The Viewport Is Not the Component

Imagine a video player that lives inside a sidebar. On a desktop monitor, the sidebar might be 320 pixels wide. On a phone held in landscape, the viewport might be 740 pixels wide. A media query that says “below 600 pixels, stack the controls” will not fire on the phone, because the viewport is 740 pixels. But the player itself is still only 320 pixels wide. The controls will be cramped, the timeline will be unreadable, and the captions will overlap the play button.

This is the core limitation: media queries respond to the page, not to the part. When a component is reused in different layout contexts, the same component can have very different amounts of space. A media query cannot see that. It only sees the viewport.

For a blog that publishes articles, this might be an annoyance. For a custom media player, it is a functional defect. A player that is 320 pixels wide needs a compact control bar, a smaller timeline hit area, and a caption font size that remains legible. A player that is 900 pixels wide can show a full control set, a scrubbing preview, and a two-line caption block. The viewport does not tell you which one you are building.

Where Media Queries Fail in Practice

Let me be concrete. I have spent a lot of time testing custom players across devices, and the same failures keep appearing. They are not exotic. They are the everyday result of relying on viewport-based breakpoints for component-level decisions.

1. Sidebar and Grid Components

A player embedded in a grid cell can be 280 pixels wide on a desktop, 600 pixels wide on a tablet, and 340 pixels wide on a phone. The viewport changes, but the component width does not follow a simple pattern. If you write breakpoints for the viewport, you end up with a player that is wrong in at least one of those contexts. The only reliable fix is to measure the component’s own container.

2. Embedded Third-Party Contexts

When a player is embedded in an iframe, the iframe’s width is the component’s world. The parent page’s viewport is irrelevant. A media query inside the iframe will respond to the iframe’s own viewport, which is the iframe element’s size. That can work, but it means the player’s CSS is coupled to the iframe dimensions, not to the layout slot it occupies. If the iframe is resized dynamically, the media query may or may not update in the way you expect.

3. Dynamic Layouts and User Resizing

Many modern interfaces let users resize panels, drag dividers, or collapse sidebars. A media query does not know that the user just dragged a panel from 400 pixels to 700 pixels. The viewport did not change, so no breakpoint fires. The component stays in its old state until the user resizes the whole browser window. That is a broken experience, and it is common in dashboards, editing tools, and any interface with resizable regions.

4. TV and Large-Screen Interfaces

On a TV, the viewport is often 1920×1080 or 3840×2160. A media query that targets “mobile” will never fire. But a TV is not a desktop. The user is sitting three meters away, using a remote control, not a mouse. The player needs larger text, larger focus indicators, and a different navigation model. Media queries can target pointer: coarse or hover: none, which helps, but they still cannot tell you how far away the user is or how the player is positioned on the screen.

Wide-angle view of a living room TV displaying a video player interface

Container Queries: The Missing Piece

Container queries solve the core problem by letting CSS respond to the size of a container, not the viewport. A container query looks like this:

.player-container {
  container-type: inline-size;
}

@container (max-width: 400px) {
  .player-controls {
    flex-direction: column;
  }
}

Now the player’s controls change when the container is narrower than 400 pixels, regardless of the viewport. The same component can be placed in a sidebar, a grid cell, or a full-width section, and it will adapt to the space it actually has.

Container queries are supported in all current major browsers. They are not a future feature. They are a tool you can use today, and they are the single most important upgrade to responsive design since media queries themselves.

Container Query Units

Container queries also introduce container-relative units: cqw, cqh, cqi, cqb, cqmin, and cqmax. These work like viewport units, but they are relative to the container. For a media player, this means you can set the timeline height to 2cqi and the caption font size to 3cqi, and the player will scale proportionally to its own width. That is a much more natural fit for a component that is embedded in different contexts.

Intrinsic Sizing and Fluid Layouts

Media queries are also a poor substitute for intrinsic sizing. Intrinsic sizing means letting the content and the container determine the layout, rather than forcing the layout with fixed breakpoints. CSS Grid and Flexbox are the main tools here.

For example, a control bar can be built with display: flex; flex-wrap: wrap; and gap: 0.5rem;. The controls will wrap naturally when there is not enough space. No media query is needed. The layout is intrinsically responsive because it responds to the available space, not to a predefined viewport width.

The same principle applies to text. Instead of setting a fixed font size and then overriding it at several breakpoints, you can use clamp() to create a fluid scale:

.player-title {
  font-size: clamp(1rem, 2cqi + 0.5rem, 1.75rem);
}

This sets a minimum of 1rem, a maximum of 1.75rem, and a fluid middle value that scales with the container. The text never needs a breakpoint. It just adapts.

Media Queries Still Have a Job

I am not saying you should delete all your media queries. They are still the right tool for some jobs. The key is to use them for what they are good at, and to stop using them for what they are bad at.

Media queries are good for:

  • User preferences: prefers-reduced-motion, prefers-color-scheme, prefers-contrast, forced-colors.
  • Device capabilities: pointer: coarse, hover: none, any-pointer.
  • Orientation: orientation: portrait or landscape when the aspect ratio itself matters.
  • Print and other media types: @media print, @media speech.
  • Global layout changes: when the entire page structure needs to change, such as moving a sidebar below the main content on small screens.

Media queries are bad for:

  • Component-level sizing: use container queries instead.
  • Fluid typography: use clamp() and container-relative units.
  • Spacing and alignment inside a component: use Flexbox, Grid, and logical properties.
  • State changes that are not viewport-related: use JavaScript or CSS state selectors like :has(), :focus-visible, and aria-expanded.

Testing Responsive Media Components

When I test a custom player, I do not just resize the browser window. I test the component in the contexts where it will actually live. That means:

  • Embed the player in a narrow sidebar and a wide article body on the same page.
  • Place the player inside a grid cell that changes width when the user resizes a panel.
  • Load the player in an iframe with a fixed width and then resize the iframe dynamically.
  • Test with a keyboard, a touch screen, and a remote control if the target includes TV.
  • Test with captions enabled, because captions change the vertical space the player needs.
  • Test with prefers-reduced-motion: reduce enabled, because animations and transitions must degrade gracefully.

One practical test I use is the “container stress test.” I create a page with three copies of the same player: one in a 280-pixel sidebar, one in a 600-pixel main column, and one in a full-width section. Then I resize the browser from 320 pixels to 1920 pixels and watch all three players. If any of them breaks, the component is not truly responsive. It is just responsive to the viewport.

Accessible Time-Based Media and Responsive Design

For accessible time-based media, responsive design is not just about layout. It is about legibility, operability, and comprehension. Captions must remain readable at small sizes. Transport controls must be large enough to tap without error. Focus indicators must be visible on every background. The timeline must be scrubbable with a keyboard, a mouse, and a touch screen.

Media queries can help with some of this. For example, you can increase the caption font size on small viewports. But a caption that is readable on a 320-pixel phone may be unreadable on a 320-pixel sidebar on a desktop, because the user is sitting closer to the phone. The viewport is the same width, but the viewing distance is different. A container query cannot solve that either. This is where user preferences and good defaults matter more than breakpoints.

The Web Content Accessibility Guidelines (WCAG) provide specific guidance for captions, audio description, and keyboard operability. The W3C Web Accessibility Initiative (WAI) publishes the official WCAG 2.2 specification, which is the reference I use when testing media players. The European Accessibility Act (EAA) also sets legal requirements for accessible media in the EU, and it applies to many websites and apps from June 2025. If you are building for a European audience, responsive design is not just a UX concern. It is a compliance concern.

For example, WCAG Success Criterion 1.4.4 requires that text can be resized up to 200% without loss of content or functionality. A media player that uses fixed pixel sizes for captions and controls will fail this test. A player that uses relative units and container queries will pass. The same principle applies to reflow (1.4.10), which requires content to be presented without loss of information or functionality at 320 CSS pixels wide. A player that relies on viewport media queries may pass reflow on a phone but fail it in a narrow desktop sidebar. Container queries are the more reliable path.

A Practical Example: Building a Responsive Control Bar

Let me walk through a real example. Suppose you are building a custom HTML5 video player with a control bar that contains a play button, a timeline, a volume control, a caption toggle, and a fullscreen button. On a wide player, all five controls sit in a single row. On a narrow player, the volume control and caption toggle should move to a second row, and the timeline should become taller for easier touch scrubbing.

With media queries, you would write something like:

@media (max-width: 600px) {
  .control-bar {
    flex-wrap: wrap;
  }
  .timeline {
    height: 2rem;
  }
}

This works when the player is full-width on a phone. It fails when the player is in a 320-pixel sidebar on a desktop. The viewport is 1440 pixels, so the media query never fires. The controls stay in a single row and become unreadable.

With container queries, you write:

.player {
  container-type: inline-size;
}

@container (max-width: 400px) {
  .control-bar {
    flex-wrap: wrap;
  }
  .timeline {
    height: 2rem;
  }
}

Now the player adapts to its own width, wherever it is placed. The same component works in a sidebar, a grid cell, and a full-width article body. No viewport breakpoints needed.

You can go further with container-relative units. Instead of setting the timeline height to a fixed 2rem, you can set it to max(1.5rem, 3cqi). On a 300-pixel player, the timeline is about 9 pixels tall, which is too small for touch. The max() function keeps it at least 1.5rem. On a 900-pixel player, the timeline grows to 27 pixels, which is comfortable for mouse and touch. The component scales with its own size, not with the viewport.

Fluid Typography for Captions and Controls

Captions are the most sensitive part of a media player. They must be readable at a glance, they must not obscure important visual content, and they must remain legible when the player is small. A common approach is to set a fixed caption font size and then override it at a few breakpoints. That is brittle. A better approach is to use clamp() with container-relative units:

.captions {
  font-size: clamp(0.875rem, 2.5cqi + 0.5rem, 1.5rem);
  line-height: 1.4;
}

This sets a minimum of 0.875rem, a maximum of 1.5rem, and a fluid middle value that scales with the player width. On a 320-pixel player, the captions are about 0.9rem. On a 900-pixel player, they are about 1.4rem. The text never needs a breakpoint. It just adapts.

The same technique works for control labels, time displays, and any text inside the player. The key is to use container-relative units, not viewport units. Viewport units are still tied to the page, not the component.

When JavaScript Is the Right Tool

Some responsive behaviors cannot be expressed in CSS alone. For example, if you need to change the order of controls based on available space, or if you need to measure the actual rendered size of a caption and adjust the player height accordingly, JavaScript is the right tool. The ResizeObserver API lets you watch a container and run code when its size changes. This is useful for:

  • Switching between a compact and a full control layout based on measured width.
  • Calculating the number of visible caption lines and adjusting the player’s reserved caption area.
  • Updating a timeline’s tick marks or time labels when the player width changes.
  • Triggering a re-render of a canvas-based visualization when the player is resized.

The important principle is to use CSS for what CSS can do, and JavaScript for what it cannot. Container queries and intrinsic sizing should handle most layout changes. JavaScript should handle measurements and state changes that CSS cannot express.

The European Regulatory Lens

If you publish media in the European Union, responsive design is not optional. The European Accessibility Act (EAA) requires that websites and mobile applications, including media players, meet accessibility standards. The EAA references EN 301 549, which in turn references WCAG 2.1 AA. That means the same WCAG criteria I mentioned earlier are legally binding for many organizations.

One of the most common failures I see in media players is a control bar that becomes unusable at narrow widths. The buttons are too small, the timeline is too short, and the captions overlap the controls. This is a direct violation of WCAG 1.4.10 (Reflow) and 2.5.8 (Target Size Minimum). A player that uses container queries and fluid sizing is much more likely to pass these checks.

There is also a practical business reason to care. European users are diverse. They use a wide range of devices, screen sizes, and assistive technologies. A player that only works on a desktop browser at 1200 pixels wide is not a European player. It is a desktop player that happens to be accessible from Europe.

Close-up of a laptop screen showing a video player with captions and accessible controls

Common Questions About Media Queries and Responsive Design

Are media queries obsolete?

No. Media queries are still the right tool for user preferences, device capabilities, orientation, and global layout changes. They are obsolete only for component-level sizing, where container queries are a better fit. The two tools work together. A typical responsive player uses container queries for its internal layout and media queries for user preferences like prefers-reduced-motion.

Do container queries work in all browsers?

Container queries are supported in all current major browsers, including Chrome, Edge, Firefox, and Safari. They have been available since early 2023. If you need to support older browsers, you can use a progressive enhancement approach: build the component with container queries, and provide a fallback using media queries or a JavaScript polyfill. The fallback will be less precise, but the component will still work.

What is the difference between container queries and element queries?

Element queries were a proposed technique that never became a native CSS feature. They relied on JavaScript to measure an element and apply styles. Container queries are the native CSS implementation of the same idea. They are more performant, more reliable, and easier to maintain. If you used an element query polyfill in the past, container queries are the upgrade path.

How do I test a responsive media player?

Test the player in multiple containers on the same page: a narrow sidebar, a medium grid cell, and a full-width section. Resize the browser and watch all three players. Test with captions enabled, with a keyboard, with a touch screen, and with prefers-reduced-motion: reduce. Use the browser’s device emulation mode for phone and tablet sizes, but do not rely on it alone. Real devices reveal issues that emulation misses.

What Comes Next

This article is the first in a series on responsive media components. The next article will cover a complete container-query pattern for a custom HTML5 video player, including the control bar, captions, and timeline. After that, I will look at how to test responsive players against WCAG 2.2 and EN 301 549, with a practical checklist you can use before shipping.

If you have a player that breaks in a sidebar or a grid cell, the fix is usually not another media query. It is a container query, a fluid unit, or a more intrinsic layout. The viewport is not the component. Once you start designing for the space a component actually has, responsive design becomes less about breakpoints and more about systems.