You Can Delete Most of Your Media Queries

Table of Contents
- Breakpoints Were Always a Compromise
- The Component Does Not Know the Viewport
- Container Queries Fix the Wrong Question
- Intrinsic Layout Without Breakpoints
- Fluid Typography and Spacing
- Where Media Queries Still Belong
- Logical Properties and Internationalisation
- Migrating an Existing Codebase
- Common Pitfalls
- Conclusion
- Frequently Asked Questions
Key takeaway: Media queries ask about the viewport, but components care about their own available space. Container queries and intrinsic sizing let layout respond to what actually matters, which eliminates most breakpoint code.
Breakpoints Were Always a Compromise
The standard responsive approach picks a set of viewport widths and rewrites layout at each. Mobile below 768, tablet to 1024, desktop above.
This produced working sites for a decade, and it was always a workaround rather than a solution. The evidence is in the symptoms every large codebase exhibits: layouts that break at widths between the chosen breakpoints, a component that looks correct in one context and wrong in another, breakpoint values duplicated across dozens of files, and the recurring discovery that a new device size falls awkwardly between existing rules.
Those symptoms share a cause. Breakpoints describe the viewport, and the thing that determines whether a layout works is the space available to the specific component. A card in a full-width container and the same card in a narrow sidebar have entirely different needs at the same viewport width.
Media queries cannot express that, so codebases compensate with variant classes, context-specific overrides, and increasingly specific selectors. The complexity is a consequence of asking the wrong question.
The Component Does Not Know the Viewport
The mismatch is worth stating precisely, because it explains why the workarounds are necessary.
A reusable component may appear in a main content area, a sidebar, a modal, a grid cell, or a dashboard panel. Its available width in each is different, and unrelated to viewport width. On a wide desktop screen, a component in a narrow sidebar has less space than the same component on a phone.
The consequences in a media-query-based codebase:
Variant classes proliferate. card, card--compact, card--sidebar, card--modal. Each is the same component with different assumptions about space.
Context leaks into the component. The component’s styles must know where it is used, which breaks encapsulation and means adding a new context requires editing the component.
Breakpoints get chosen for the worst case. A layout that works everywhere becomes a layout optimised for nowhere.
Testing surface multiplies. Every component must be verified in every context at every breakpoint.
The correct question is not “how wide is the browser window” but “how much space do I have.” Until recently CSS could not ask that, which is why an entire methodology grew around the approximation.
Container Queries Fix the Wrong Question
Container queries let a component respond to its container’s size rather than the viewport’s.
.card-container {
container-type: inline-size;
}
/* Applies when the container is at least 400px wide,
regardless of viewport size */
@container (min-width: 400px) {
.card {
display: grid;
grid-template-columns: 200px 1fr;
}
}
The component now works correctly in any context without knowing what that context is. The same card in a sidebar renders its narrow layout; in a main column it renders wide. No variant classes, no context-specific overrides, and no knowledge of the viewport.
Two details worth knowing. Establishing a container with container-type: inline-size creates a containment boundary, which means the element’s height no longer depends on its contents in the way it did — this occasionally surprises. And named containers let a component query a specific ancestor rather than the nearest one, which matters in nested layouts.
The practical effect on a codebase is substantial. Components become genuinely reusable, since they adapt to available space rather than assuming it. Most of the variant classes that existed to handle context can be deleted.
Intrinsic Layout Without Breakpoints
Beyond container queries, several primitives handle responsive behaviour with no conditional logic at all.
Auto-fitting grid. A grid that fits as many columns as space permits, with a minimum size, reflowing automatically.
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1.5rem;
}
Four columns on a wide screen, one on a phone, and every count between — with no breakpoints. This single declaration replaces what would otherwise be four or five media queries.
Flex wrap with a basis. Items that sit side by side until they cannot, then wrap.
.row > * {
flex: 1 1 20rem; /* grow, shrink, prefer 20rem */
}
Clamped sizing. A value that scales with available space between defined bounds.
.container {
width: clamp(20rem, 90%, 75rem);
}
Aspect ratio. Maintaining proportion without padding tricks.
Min and max functions. Choosing the smaller or larger of two values, which handles many cases that previously required a query.
The common property is that these express intent — “as many columns as fit, at least 280px each” — rather than enumerating conditions. Intent-based declarations handle sizes nobody anticipated, which is precisely where breakpoint-based layouts fail.
Fluid Typography and Spacing
Type and spacing scales are another area where breakpoints are usually unnecessary.
The conventional approach sets a font size per breakpoint, producing jumps as the viewport crosses each threshold. clamp() replaces that with smooth scaling between bounds.
h1 {
/* minimum 1.75rem, scales with viewport, maximum 3.5rem */
font-size: clamp(1.75rem, 1.25rem + 2.5vw, 3.5rem);
}
The middle value is the preferred size and it combines a fixed component with a viewport-relative one. Including the fixed rem component matters for accessibility: a purely viewport-based size ignores the user’s font size preference, whereas a mixed expression continues to respond to it.
The same technique applies to spacing, which is frequently overlooked. Section padding that scales fluidly between a comfortable minimum on phones and a generous maximum on wide screens removes another category of media query.
A caution worth stating: pure vw units for typography are an accessibility failure. Text that does not respond to browser zoom or user font settings excludes people who need larger text, and a viewport-only expression does exactly that. Always include a rem term.
Where Media Queries Still Belong
Container queries and intrinsic sizing do not replace media queries entirely. The cases where viewport or device information is genuinely the right input:
Page-level layout structure. Whether a sidebar exists at all, or whether navigation is a horizontal bar or a drawer, is a viewport-level decision.
User preference queries. prefers-reduced-motion, prefers-color-scheme, and prefers-contrast are about the person rather than the layout, and they matter for accessibility.
Input capability. hover and pointer queries distinguish devices with precise pointers from touch devices, which affects whether hover-dependent interactions are appropriate.
Print styles. A genuinely different medium.
Orientation. Occasionally relevant for specific layouts, though less often than it is used.
The reduced-motion query deserves specific mention as the one most frequently omitted. Respecting it is a small amount of CSS and it matters considerably for users who experience motion sickness from animation.
The pattern that emerges: media queries for page structure and user preferences, container queries and intrinsic sizing for component layout. That division assigns each mechanism the question it can actually answer.
Logical Properties and Internationalisation
A related modernisation that removes another class of conditional CSS.
Physical properties — margin-left, padding-right, text-align: left — assume left-to-right text. Supporting right-to-left languages with physical properties requires overriding every one of them, which is a substantial and error-prone body of code.
Logical properties describe position relative to the writing direction instead:
| Physical | Logical |
|---|---|
margin-left |
margin-inline-start |
padding-right |
padding-inline-end |
top / bottom |
inset-block-start / inset-block-end |
width / height |
inline-size / block-size |
text-align: left |
text-align: start |
border-left |
border-inline-start |
Written with logical properties, a layout mirrors correctly when the document direction changes, with no additional rules. For any project that might support Arabic, Hebrew, Persian, or Urdu, this is the difference between a direction switch and a rewrite.
Two additional benefits: margin-inline: auto centres horizontally more clearly than margin-left: auto; margin-right: auto, and shorthand forms like padding-block and padding-inline are more concise than their physical equivalents.
Adopting these is nearly free in new code and worth doing by default, even in projects with no current internationalisation plan, because retrofitting is the expensive path.
Migrating an Existing Codebase
Replacing a breakpoint-based system wholesale is rarely justified. An incremental path:
Start with new components. Build them with container queries and intrinsic sizing. This establishes the pattern without touching working code.
Replace grid and card layouts first. These are where auto-fit produces the largest simplification, frequently deleting several media queries per layout.
Convert typography to clamped scales. A contained change with visible improvement, since the jumps at breakpoints disappear.
Adopt logical properties in new code and during edits. No behavioural change, and it accumulates internationalisation readiness.
Refactor components with many context variants. Those are the ones container queries help most — the variant classes were compensating for exactly this gap.
Leave page-level media queries alone. They are doing the right job.
Delete breakpoint variables that become unused. The reduction in a mature codebase is frequently substantial and is worth measuring, because it demonstrates the value concretely.
The order matters because the early steps establish the pattern on new code where there is no risk, and the largest wins arrive in the second and third steps.
Common Pitfalls
Media queries for component layout. Asks about the viewport when the component needs its own space.
Pure vw typography. Ignores user font preferences and breaks zoom.
Forgetting container-type on the parent. Container queries silently do nothing without it.
Querying the component itself. A container query cannot query the element that establishes the container; it queries an ancestor.
Physical properties in internationalised layouts. Requires duplicating every rule for right-to-left.
Ignoring prefers-reduced-motion. An accessibility failure that costs a few lines to fix.
Breakpoints chosen from device names. Devices change; content-driven breakpoints do not.
Conclusion
Breakpoint-based responsive design solved a real problem with the tools available, and the tools changed. Media queries ask about the viewport, while components need to know their own available space — which is why breakpoint codebases accumulate variant classes and context-specific overrides to bridge the gap.
Container queries close it directly, letting a component adapt to its container regardless of where it appears. Intrinsic sizing goes further: an auto-fitting grid, flex items with a preferred basis, and clamped dimensions express intent rather than enumerating conditions, so they handle sizes nobody anticipated.
Keep media queries for what they answer well — page-level structure, user preferences like reduced motion and colour scheme, and input capability. Add logical properties by default, because they make direction changes free rather than expensive.
The result is substantially less CSS, components that work in contexts you did not plan for, and no more layouts that break at widths between your chosen breakpoints.
Frequently Asked Questions
Are media queries obsolete? No. They remain correct for page-level structure and for user preference queries, which are about the person rather than the layout. What changes is that component-level layout should generally not use them.
What does container-type: inline-size actually do? Establishes the element as a query container and applies size containment on the inline axis. Its children can then query its width. Without it, container queries have no container to match against.
Can an element query its own size? No. A container query matches against an ancestor that declared itself a container. This is why the usual pattern is a wrapper element establishing the container and the component inside it querying.
Is clamp() safe for accessibility? Yes, provided the preferred value includes a rem component. A purely viewport-based expression ignores user font size settings, which is an accessibility failure.
Should logical properties be adopted without internationalisation plans? Yes. They cost nothing in new code, read no worse, and make a future direction change trivial rather than a project. Retrofitting is the expensive alternative.
How should breakpoints be chosen when they are needed? From where the content stops working, determined by resizing until the layout breaks. Device-name breakpoints go stale as devices change; content-driven ones do not.
Do container queries have performance implications? Containment can improve performance by limiting layout recalculation scope. The queries themselves are efficiently implemented. This is not a meaningful concern in practice.



