Here's a question I get asked a lot: "What breakpoints should I use?" My answer always disappoints people. It doesn't matter nearly as much as you think. The specific pixel values are the least interesting part of media queries — and if breakpoints are the only thing you're using media queries for, you're missing out on about 80% of what they can do in 2026.
Media queries have evolved significantly. Container queries, preference queries, range syntax — there's a lot to cover. Let me walk through how I approach responsive CSS on modern projects.
Mobile-First: Not Just a Buzzword
Write your base styles for mobile, then add complexity as the screen grows. Not the other way around.
I know this sounds basic, but I still review codebases where the default styles target a 1440px desktop, and everything below is undone with max-width queries. It's like building a house and then tearing off rooms for smaller lots. Start small and add.
/* Base: mobile styles */
.grid {
display: grid;
grid-template-columns: 1fr;
gap: 16px;
}
/* Tablet and up */
@media (min-width: 768px) {
.grid {
grid-template-columns: repeat(2, 1fr);
gap: 24px;
}
}
/* Desktop */
@media (min-width: 1024px) {
.grid {
grid-template-columns: repeat(3, 1fr);
gap: 32px;
}
}
Mobile styles are the default — no media query needed. More capable devices get progressively enhanced layouts. This also means if a media query somehow fails to load, users still get a functional (if basic) layout.
Picking Breakpoints
Don't chase device dimensions. iPhones, Samsungs, iPads — they change every year. Instead, set breakpoints where your design breaks. Open the browser, resize slowly, and when the layout starts looking weird, that's your breakpoint.
That said, I typically use three: 640px, 768px, and 1024px. If you're using Tailwind, sm, md, and lg are close enough. Four breakpoints max. More than that and your CSS becomes a maintenance nightmare.
The New Range Syntax
For years we wrote min-width and max-width. It works, but the logic always felt backwards. "Minimum width of 768 pixels" means "768 and above" — not exactly intuitive. The new range syntax fixes this:
/* Old way */
@media (min-width: 768px) and (max-width: 1023px) { /* tablet only */ }
/* New way — so much clearer */
@media (768px <= width < 1024px) { /* tablet only */ }
/* Just "above this width" */
@media (width >= 1024px) { /* desktop */ }
/* Just "below this width" */
@media (width < 768px) { /* mobile */ }
It reads like a math expression. Width is greater than or equal to 1024? Desktop styles. Width is between 768 and 1024? Tablet. No more mental gymnastics translating min and max.
Browser support is excellent — all modern browsers ship this. I've been using it in production since mid-2024 with zero issues.
Tip: The range syntax also works with height, aspect-ratio, and other numeric features. @media (aspect-ratio > 16/9) targets ultrawide screens — great for cinematic layouts or adjusting video containers.
Container Queries: The Game Changer
This is the feature I'm most excited about. Media queries respond to the viewport size. Container queries respond to the size of a parent element. That distinction is massive for component-based design.
Think about a card component. In a full-width layout, it has room to show an image, title, and description side by side. In a narrow sidebar? It needs to stack vertically. With media queries, you'd need to know where the card is used and write different rules. With container queries, the card adapts to its own container:
/* Define a containment context */
.card-wrapper {
container-type: inline-size;
container-name: card;
}
/* Base styles — narrow container */
.card {
display: grid;
grid-template-columns: 1fr;
}
/* When the container is wide enough */
@container card (min-width: 400px) {
.card {
grid-template-columns: 200px 1fr;
align-items: start;
}
}
@container card (min-width: 600px) {
.card {
grid-template-columns: 280px 1fr;
gap: 24px;
}
}
Drop this card anywhere — main content area, sidebar, modal, email template — and it adapts to whatever space it has. No parent-specific overrides. No JavaScript resize observers. Pure CSS.
Container Query Units
You also get new units based on the container's dimensions:
.card-title {
font-size: clamp(1rem, 3cqi, 1.5rem);
}
.card-image {
height: 40cqb; /* 40% of container block size */
}
cqi is 1% of the container's inline size (width in horizontal writing). cqb is 1% of block size (height). These let you create truly proportional components.
Preference Queries: Respecting User Choices
Modern CSS lets you detect and respond to user preferences at the OS level. This isn't optional nicety — it's accessibility.
prefers-color-scheme
Detect whether the user has set their system to dark mode:
:root {
--bg: #ffffff;
--text: #0f172a;
--surface: #f8fafc;
}
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f172a;
--text: #f1f5f9;
--surface: #1e293b;
}
}
Combine this with a manual toggle (using a data-theme attribute) so users can override their system preference. The media query provides the default; the attribute provides the override.
prefers-reduced-motion
This one's crucial. Some users experience motion sickness, seizures, or just find animations distracting. When they enable "Reduce motion" in their OS settings, respect it:
/* Default: animations on */
.card {
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
}
/* User prefers reduced motion — remove animations */
@media (prefers-reduced-motion: reduce) {
.card {
transition: none;
}
.card:hover {
transform: none;
}
}
Don't just set animation: none globally — that removes all visual feedback. Instead, replace motion-based feedback with non-motion alternatives. A color change on hover instead of a transform, for example.
prefers-contrast
Less commonly used but valuable for accessibility:
@media (prefers-contrast: more) {
:root {
--text-muted: #1a1a1a; /* darker than usual */
--border-color: #333; /* more visible borders */
}
.btn {
border: 2px solid currentColor; /* explicit borders */
}
}
Users with low vision may crank up contrast in their OS. This query lets you respond to that with higher-contrast colors and stronger visual boundaries.
Tip: You can combine preference queries with other media features using and. For example, @media (prefers-color-scheme: dark) and (min-width: 1024px) targets dark mode users on desktop screens. Useful for dark-mode-specific layout tweaks on larger screens.
Other Modern Media Features Worth Knowing
hover and pointer
Not all devices have hover capability. Touch devices don't. Use this to adjust interactive patterns:
/* Only show hover effects on devices that support hover */
@media (hover: hover) {
.nav-item:hover .dropdown {
display: block;
}
}
/* Larger touch targets on coarse pointer devices (touchscreens) */
@media (pointer: coarse) {
.btn {
min-height: 48px;
padding: 12px 24px;
}
}
This is way better than guessing device type from screen width. A 13-inch iPad has a wide viewport but no hover. A tiny desktop window has a narrow viewport but supports hover. These queries detect the actual capability.
prefers-reduced-data
Still experimental in some browsers, but worth watching:
@media (prefers-reduced-data: reduce) {
.hero {
background-image: none; /* skip the heavy background */
}
video {
display: none; /* hide decorative videos */
}
}
Users on metered connections or data-saver mode can opt into a lighter experience.
Organizing Your Media Queries
Where you put your media queries matters for readability. I've seen two approaches that work well:
Approach 1: Colocated with the component — each component has its own media queries right next to its base styles. This is what I prefer. When you're editing the card component, all its responsive behavior is right there.
.card { /* base */ }
@media (width >= 768px) { .card { /* tablet */ } }
@media (width >= 1024px) { .card { /* desktop */ } }
Approach 2: Grouped at the bottom — all media queries collected at the end of the file. Some people find this cleaner. In my experience, it gets unwieldy as the file grows.
For most projects, especially component-based ones (React, Vue, Svelte), colocated queries win. Each component file contains everything about that component, including how it responds to different contexts.
Preview Responsive Designs as Images
Capture your responsive HTML layouts at different viewport sizes and export them as PNG images.
Try HTML to PNG Converter →Putting It All Together
Here's a snippet from a real project showing several modern query techniques combined:
.dashboard {
container-type: inline-size;
display: grid;
grid-template-columns: 1fr;
gap: 16px;
padding: 16px;
}
@container (min-width: 600px) {
.dashboard {
grid-template-columns: repeat(2, 1fr);
gap: 24px;
}
}
@container (min-width: 900px) {
.dashboard {
grid-template-columns: repeat(3, 1fr);
}
}
@media (prefers-color-scheme: dark) {
.dashboard {
--card-bg: #1e293b;
--card-border: #334155;
}
}
@media (prefers-reduced-motion: reduce) {
.dashboard * {
transition-duration: 0.01ms !important;
}
}
Container queries handle the layout. Preference queries handle the visual adaptation. Media queries handle the viewport-level stuff. Each tool does what it's best at.
Media queries in 2026 are about much more than breakpoints. They're about building UIs that adapt to their container, respect user preferences, and degrade gracefully across every device and context. Master all three layers — viewport, container, and preference — and you'll build interfaces that feel right everywhere.