Skip to main content
  • Created:
  • Updated:
  • Author:
    Takeshi Takatsudo

Media Query Best Practices

The Problem

AI agents use media queries as the default (and often only) responsive tool. They frequently use arbitrary device-based breakpoints, ignore user preference queries (prefers-reduced-motion, prefers-color-scheme, prefers-contrast), and never use feature queries (@supports). They also tend to write desktop-first styles and then override everything for mobile, leading to bloated CSS.

The Solution

Media queries should be one tool among many for responsive design. Use them for page-level layout changes and user preference detection. Prefer content-driven breakpoints over device-specific ones, and adopt a mobile-first approach.

Mobile-First Layout — Use viewport buttons to see breakpoints

Code Examples

Mobile-First vs. Desktop-First

Mobile-first uses min-width queries, starting from the smallest screen and adding complexity:

/* Mobile-first: base styles are for small screens */
.layout {
display: flex;
flex-direction: column;
gap: 1rem;
}

@media (min-width: 48rem) {
.layout {
flex-direction: row;
}
}

@media (min-width: 64rem) {
.layout {
max-width: 75rem;
margin-inline: auto;
}
}

Desktop-first uses max-width queries, starting from the largest screen and removing features:

/* Desktop-first: more overrides needed */
.layout {
display: flex;
flex-direction: row;
max-width: 75rem;
margin-inline: auto;
}

@media (max-width: 63.999rem) {
.layout {
max-width: none;
}
}

@media (max-width: 47.999rem) {
.layout {
flex-direction: column;
}
}

Mobile-first results in less CSS overall because you add styles as the viewport grows rather than removing them.

Content-Driven Breakpoints

Instead of targeting specific devices, add breakpoints where your layout breaks:

/* Let the content dictate the breakpoint */
.article {
max-width: 65ch; /* Optimal reading width */
margin-inline: auto;
padding-inline: 1rem;
}

.article-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
}

/* Add a second column when there is enough room */
@media (min-width: 55rem) {
.article-grid {
grid-template-columns: 1fr 20rem;
}
}

User Preference: prefers-color-scheme

:root {
--color-text: #1a1a1a;
--color-bg: #ffffff;
--color-surface: #f5f5f5;
--color-border: #e0e0e0;
}

@media (prefers-color-scheme: dark) {
:root {
--color-text: #e0e0e0;
--color-bg: #1a1a1a;
--color-surface: #2a2a2a;
--color-border: #3a3a3a;
}
}

body {
color: var(--color-text);
background-color: var(--color-bg);
}

Allow manual override with a data attribute:

[data-theme="light"] {
--color-text: #1a1a1a;
--color-bg: #ffffff;
--color-surface: #f5f5f5;
--color-border: #e0e0e0;
}

[data-theme="dark"] {
--color-text: #e0e0e0;
--color-bg: #1a1a1a;
--color-surface: #2a2a2a;
--color-border: #3a3a3a;
}

User Preference: prefers-reduced-motion

/* Remove transitions and animations for users who prefer reduced motion */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}

See the dedicated prefers-reduced-motion page for a more nuanced approach.

User Preference: prefers-contrast

@media (prefers-contrast: more) {
:root {
--color-text: #000000;
--color-bg: #ffffff;
--color-border: #000000;
}

.button {
border: 2px solid currentColor;
}
}

@media (prefers-contrast: less) {
:root {
--color-text: #333333;
--color-bg: #fafafa;
--color-border: #cccccc;
}
}

Interaction Media Queries: hover and pointer

/* Only apply hover styles on devices that support hover */
@media (hover: hover) {
.card {
transition: box-shadow 0.2s ease;
}

.card:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 0.15);
}
}

/* Increase touch targets on coarse pointer devices */
@media (pointer: coarse) {
.nav-link {
min-height: 44px;
padding-block: 0.75rem;
}
}

Feature Queries with @supports

/* Base layout */
.grid {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}

.grid > * {
flex: 1 1 300px;
}

/* Enhanced layout for browsers with grid subgrid support */
@supports (grid-template-columns: subgrid) {
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

.grid > * {
display: grid;
grid-template-rows: subgrid;
grid-row: span 3;
}
}

Combining Queries

/* Dark mode + reduced motion */
@media (prefers-color-scheme: dark) and (prefers-reduced-motion: reduce) {
.notification {
background-color: var(--color-surface);
/* No entrance animation, just appear */
}
}

Common AI Mistakes

  • Device-specific breakpoints: Using @media (max-width: 768px) because "that's the iPad width." Breakpoints should be driven by content, not device catalogs.
  • Desktop-first approach: Writing full desktop styles first and then stripping them away for mobile, creating unnecessary overrides.
  • Ignoring user preferences: Never including prefers-color-scheme, prefers-reduced-motion, or prefers-contrast queries.
  • Using media queries for component layouts: Using @media when @container would be more appropriate. Media queries are for page-level layout; container queries are for component-level adaptation.
  • Missing @media (hover: hover): Adding :hover styles that create sticky hover states on touch devices.
  • Not using @supports: Writing modern CSS features without fallbacks and without checking support.
  • Using px for breakpoints: Pixel breakpoints do not scale with user font-size preferences. Use rem values (e.g., 48rem instead of 768px).
  • Too many breakpoints: Creating 5+ breakpoints when clamp() or intrinsic sizing would handle the fluid range.

When to Use

  • Page-level layout changes: Switching between single-column and multi-column page layouts.
  • User preference detection: prefers-color-scheme, prefers-reduced-motion, prefers-contrast.
  • Input modality adaptation: hover, pointer for adjusting interactions to input type.
  • Feature detection: @supports for progressive enhancement with new CSS features.
  • Not for component layouts: Use container queries instead.
  • Not for fluid sizing: Use clamp() instead of breakpoint jumps.

References