CSS media queries are the backbone of responsive web design, allowing you to apply styles based on device characteristics like viewport width, orientation, and user preferences. In 2025, CSS media queries and breakpoints have evolved significantly with new range syntax, container queries, and preference-based features. This comprehensive guide covers everything you need to build truly responsive websites.
Convert CSS units with our CSS Unit Converter →
Build responsive layouts visually with our Flexbox Generator →
Media Query Syntax
A media query consists of a media type and one or more expressions that check for conditions. When the conditions are true, the styles inside the query are applied.
/* Basic media query syntax */
@media media-type and (media-feature) {
/* CSS rules here */
}
/* Examples */
@media screen and (min-width: 768px) {
.container { max-width: 720px; }
}
@media print {
.no-print { display: none; }
}
/* Without media type (applies to all) */
@media (min-width: 768px) {
.sidebar { display: block; }
}You can combine conditions using logical operators: and requires all conditions to be true, or (comma-separated) requires at least one, and not negates the entire query.
/* AND — all conditions must be true */
@media screen and (min-width: 768px) and (max-width: 1024px) {
/* tablet only */
}
/* OR — comma-separated, at least one must be true */
@media (max-width: 480px), (orientation: portrait) {
/* mobile OR portrait */
}
/* NOT — negates the entire query */
@media not print {
/* everything except print */
}
/* ONLY — prevents older browsers from applying styles */
@media only screen and (min-width: 768px) {
/* modern browsers only */
}Recommended Breakpoints for 2025
The device landscape has shifted in 2025. These breakpoints cover the most common screen sizes based on current usage data. The key is to design for content first, then add breakpoints where your layout breaks — but these values are an excellent starting point.
| Device | Breakpoint | CSS (Mobile-First) | Common Devices |
|---|---|---|---|
| Mobile (portrait) | 0 – 479px | Default styles (no query) | iPhone SE, Galaxy S series |
| Mobile (landscape) | 480px | @media (min-width: 480px) | Phones in landscape |
| Tablet | 768px | @media (min-width: 768px) | iPad Mini, iPad Air, Galaxy Tab |
| Laptop | 1024px | @media (min-width: 1024px) | iPad Pro, small laptops |
| Desktop | 1280px | @media (min-width: 1280px) | Standard desktop monitors |
| Large Desktop / 4K | 1536px | @media (min-width: 1536px) | Large monitors, 4K displays |
/* Complete mobile-first breakpoint system */
/* Base: Mobile portrait (0–479px) */
.container { padding: 0 16px; }
/* 480px: Mobile landscape */
@media (min-width: 480px) {
.container { max-width: 460px; margin: 0 auto; }
}
/* 768px: Tablet */
@media (min-width: 768px) {
.container { max-width: 720px; }
}
/* 1024px: Laptop */
@media (min-width: 1024px) {
.container { max-width: 960px; }
}
/* 1280px: Desktop */
@media (min-width: 1280px) {
.container { max-width: 1200px; }
}
/* 1536px: Large desktop / 4K */
@media (min-width: 1536px) {
.container { max-width: 1400px; }
}Mobile-First vs Desktop-First
There are two main approaches to writing responsive CSS: mobile-first (using min-width) and desktop-first (using max-width). Mobile-first is the industry standard and recommended approach for 2025.
Why mobile-first? Mobile traffic accounts for over 60% of global web traffic. Starting with mobile styles means your base CSS is simpler, loads faster on low-powered devices, and you progressively enhance for larger screens.
/* ========== MOBILE-FIRST (Recommended) ========== */
/* Base styles: mobile */
.grid {
display: flex;
flex-direction: column;
gap: 16px;
}
.grid-item {
width: 100%;
}
/* Tablet: 2 columns */
@media (min-width: 768px) {
.grid {
flex-direction: row;
flex-wrap: wrap;
}
.grid-item {
width: calc(50% - 8px);
}
}
/* Desktop: 3 columns */
@media (min-width: 1024px) {
.grid-item {
width: calc(33.333% - 11px);
}
}
/* ========== DESKTOP-FIRST (Less common) ========== */
/* Base styles: desktop */
.grid {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
.grid-item {
width: calc(33.333% - 11px);
}
/* Tablet: 2 columns */
@media (max-width: 1023px) {
.grid-item {
width: calc(50% - 8px);
}
}
/* Mobile: 1 column */
@media (max-width: 767px) {
.grid {
flex-direction: column;
}
.grid-item {
width: 100%;
}
}Width Queries: Classic and New Range Syntax
Width-based media queries are the most commonly used. In 2025, all modern browsers support the new range syntax introduced in Media Queries Level 4, which is more readable and less error-prone.
The new range syntax uses mathematical comparison operators (>=, <=, >, <) and is supported in Chrome 104+, Firefox 63+, Safari 16.4+, and Edge 104+.
/* ===== Traditional syntax (widely supported) ===== */
@media (min-width: 768px) { /* 768px and up */ }
@media (max-width: 767px) { /* below 768px */ }
@media (min-width: 768px) and (max-width: 1023px) { /* 768–1023px */ }
/* ===== New range syntax (2025 — all modern browsers) ===== */
@media (width >= 768px) { /* 768px and up */ }
@media (width < 768px) { /* below 768px */ }
@media (768px <= width < 1024px) { /* 768–1023px */ }
/* More examples of range syntax */
@media (width >= 480px) {
/* mobile landscape and up */
}
@media (width >= 768px) and (width < 1024px) {
/* tablet only */
}
@media (height >= 800px) {
/* tall viewports */
}
/* Range syntax is more intuitive for ranges */
/* Old: min and max are confusing with "and" */
@media (min-width: 768px) and (max-width: 1279px) { ... }
/* New: reads naturally as a mathematical range */
@media (768px <= width <= 1279px) { ... }Other Media Features
Beyond width, CSS media queries can detect many device characteristics and user preferences. These features are essential for building accessible, user-friendly interfaces.
orientation
Detects whether the viewport is in portrait (height > width) or landscape (width > height) mode.
/* Portrait mode */
@media (orientation: portrait) {
.gallery {
grid-template-columns: 1fr;
}
}
/* Landscape mode */
@media (orientation: landscape) {
.gallery {
grid-template-columns: repeat(3, 1fr);
}
}
/* Combine with width */
@media (orientation: landscape) and (max-height: 500px) {
/* landscape on a phone — reduce vertical padding */
.hero { padding: 20px 0; }
}hover and pointer
Detects the primary input mechanism's capabilities. Essential for distinguishing touch devices from mouse-based devices.
/* Device has hover capability (mouse/trackpad) */
@media (hover: hover) {
.button:hover {
background-color: #2563eb;
transform: translateY(-2px);
}
}
/* Device does NOT have hover (touchscreen) */
@media (hover: none) {
.button {
/* larger touch target */
padding: 16px 24px;
font-size: 18px;
}
}
/* Fine pointer (mouse) */
@media (pointer: fine) {
.input { height: 32px; }
}
/* Coarse pointer (touch/finger) */
@media (pointer: coarse) {
.input { height: 48px; } /* larger touch target */
}
/* Combine for precise detection */
@media (hover: hover) and (pointer: fine) {
/* desktop with mouse — show tooltips on hover */
.tooltip:hover .tooltip-text { display: block; }
}prefers-color-scheme
Detects the user's preferred color scheme (light or dark). This is critical for implementing dark mode.
/* Define color tokens with CSS custom properties */
:root {
--bg: #ffffff;
--text: #1a1a1a;
--card-bg: #f8fafc;
--border: #e2e8f0;
--primary: #2563eb;
}
/* Dark mode overrides */
@media (prefers-color-scheme: dark) {
:root {
--bg: #0f172a;
--text: #f1f5f9;
--card-bg: #1e293b;
--border: #334155;
--primary: #60a5fa;
}
}
/* Use the tokens everywhere */
body {
background-color: var(--bg);
color: var(--text);
}
.card {
background-color: var(--card-bg);
border: 1px solid var(--border);
}
/* Manual toggle override with data attribute */
[data-theme="dark"] {
--bg: #0f172a;
--text: #f1f5f9;
--card-bg: #1e293b;
--border: #334155;
--primary: #60a5fa;
}prefers-reduced-motion
Detects if the user has requested reduced motion in their system preferences. Important for accessibility — always respect this preference.
/* Default: add animations */
.element {
transition: transform 0.3s ease, opacity 0.3s ease;
}
.element:hover {
transform: scale(1.05);
}
@keyframes slide-in {
from { transform: translateX(-100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
.animated {
animation: slide-in 0.5s ease-out;
}
/* Reduce or remove motion when user prefers it */
@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;
}
}
/* Alternative: opt-in approach (safer) */
@media (prefers-reduced-motion: no-preference) {
.element {
transition: transform 0.3s ease;
}
}aspect-ratio
Targets viewports with a specific aspect ratio. Useful for ultra-wide monitors or specific device form factors.
/* Ultra-wide monitors (21:9 or wider) */
@media (min-aspect-ratio: 21/9) {
.hero {
max-width: 1600px;
margin: 0 auto;
}
}
/* Standard widescreen (16:9) */
@media (aspect-ratio: 16/9) {
.video-container { padding: 0; }
}
/* Tall/narrow viewports (phones) */
@media (max-aspect-ratio: 3/4) {
.sidebar { display: none; }
}Container Queries
Container queries are one of the biggest CSS additions in recent years. Instead of querying the viewport size, they let you style elements based on the size of their parent container. This makes components truly reusable across different layout contexts.
container-type: inline-size enables width-based container queries. container-type: size enables both width and height queries but is more expensive for the browser to calculate. Use inline-size in most cases.
/* Step 1: Define a container */
.card-wrapper {
container-type: inline-size; /* enable width-based queries */
container-name: card; /* optional: name for targeting */
}
/* Shorthand */
.card-wrapper {
container: card / inline-size;
}
/* Step 2: Query the container */
@container card (min-width: 400px) {
.card {
display: flex;
flex-direction: row;
}
.card-image {
width: 40%;
}
.card-content {
width: 60%;
}
}
@container card (min-width: 600px) {
.card {
gap: 24px;
}
.card-title {
font-size: 1.5rem;
}
}
/* Container query with range syntax */
@container (width >= 300px) {
.component { padding: 24px; }
}
/* Container query units */
.card-title {
/* cqw = 1% of container width */
font-size: clamp(1rem, 3cqw, 1.5rem);
}
/* Practical example: sidebar vs main content */
.sidebar .card-wrapper {
/* Container is narrow → card stacks vertically */
container-type: inline-size;
}
.main .card-wrapper {
/* Container is wide → card goes horizontal */
container-type: inline-size;
}
/* Same @container rules work in both contexts! */Popular Framework Breakpoints Comparison
Here is how popular CSS frameworks define their breakpoints in 2025. Understanding these helps when migrating between frameworks or choosing one for your project.
| Breakpoint | Tailwind CSS | Bootstrap 5 | Material UI |
|---|---|---|---|
| Extra Small | — | <576px | 0px (xs) |
| Small | 640px (sm) | 576px (sm) | 600px (sm) |
| Medium | 768px (md) | 768px (md) | 900px (md) |
| Large | 1024px (lg) | 992px (lg) | 1200px (lg) |
| Extra Large | 1280px (xl) | 1200px (xl) | 1536px (xl) |
| 2X Large | 1536px (2xl) | 1400px (xxl) | — |
/* Tailwind CSS — using classes directly */
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
<!-- responsive grid -->
</div>
/* Bootstrap 5 — using column classes */
<div class="row">
<div class="col-12 col-sm-6 col-lg-4 col-xl-3">
<!-- responsive column -->
</div>
</div>
/* Custom: matching Tailwind breakpoints in plain CSS */
/* sm */ @media (min-width: 640px) { ... }
/* md */ @media (min-width: 768px) { ... }
/* lg */ @media (min-width: 1024px) { ... }
/* xl */ @media (min-width: 1280px) { ... }
/* 2xl */ @media (min-width: 1536px) { ... }Responsive Typography
Responsive typography ensures text is readable across all screen sizes. In 2025, the clamp() function is the gold standard for fluid typography, eliminating the need for multiple media query breakpoints.
The clamp(min, preferred, max) function takes three values: a minimum size, a preferred (fluid) size, and a maximum size. The preferred value typically uses viewport units to scale with screen size.
/* ===== clamp() — fluid typography (recommended) ===== */
h1 {
/* min: 2rem, preferred: 5vw, max: 3.5rem */
font-size: clamp(2rem, 5vw, 3.5rem);
}
h2 {
font-size: clamp(1.5rem, 3.5vw, 2.5rem);
}
p {
font-size: clamp(1rem, 1.2vw, 1.25rem);
line-height: 1.6;
}
/* ===== Complete fluid type scale ===== */
:root {
--text-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
--text-sm: clamp(0.875rem, 0.8rem + 0.35vw, 1rem);
--text-base: clamp(1rem, 0.9rem + 0.5vw, 1.125rem);
--text-lg: clamp(1.125rem, 1rem + 0.6vw, 1.25rem);
--text-xl: clamp(1.25rem, 1rem + 1.2vw, 1.75rem);
--text-2xl: clamp(1.5rem, 1rem + 2.5vw, 2.5rem);
--text-3xl: clamp(2rem, 1.2rem + 4vw, 3.5rem);
}
/* ===== Traditional breakpoint approach ===== */
h1 { font-size: 1.75rem; }
@media (min-width: 768px) {
h1 { font-size: 2.25rem; }
}
@media (min-width: 1024px) {
h1 { font-size: 3rem; }
}
/* ===== Viewport units (use with caution) ===== */
h1 {
/* Never use vw alone — inaccessible (ignores zoom) */
font-size: 5vw; /* BAD */
/* Combine with calc or clamp for accessibility */
font-size: calc(1rem + 2vw); /* OK */
font-size: clamp(1.5rem, 4vw, 3rem); /* BEST */
}Image Responsiveness
Responsive images are crucial for performance and user experience. The srcset attribute, sizes attribute, and <picture> element work together to serve the right image for each device.
srcset provides the browser with a list of image sources and their sizes, allowing it to choose the best one.
<!-- srcset with width descriptors -->
<img
src="image-800.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w,
image-1600.jpg 1600w
"
sizes="
(min-width: 1280px) 1200px,
(min-width: 768px) 90vw,
100vw
"
alt="Responsive image example"
loading="lazy"
/>
<!-- srcset with pixel density descriptors -->
<img
src="logo.png"
srcset="
logo.png 1x,
logo@2x.png 2x,
logo@3x.png 3x
"
alt="Company logo"
/>The <picture> element provides art direction — serving entirely different images based on media conditions.
<!-- Art direction with <picture> -->
<picture>
<!-- Wide screens: panoramic hero -->
<source
media="(min-width: 1024px)"
srcset="hero-wide.jpg"
/>
<!-- Tablet: cropped version -->
<source
media="(min-width: 768px)"
srcset="hero-medium.jpg"
/>
<!-- Mobile: square crop -->
<source
media="(max-width: 767px)"
srcset="hero-mobile.jpg"
/>
<!-- Fallback -->
<img src="hero-medium.jpg" alt="Hero image" />
</picture>
<!-- Format switching with <picture> -->
<picture>
<source type="image/avif" srcset="image.avif" />
<source type="image/webp" srcset="image.webp" />
<img src="image.jpg" alt="Modern format with fallback" />
</picture>
/* CSS: responsive images */
img {
max-width: 100%;
height: auto;
display: block;
}
/* Object-fit for fixed-aspect containers */
.thumbnail {
width: 300px;
height: 200px;
object-fit: cover; /* crop to fill */
object-position: center; /* crop from center */
}Testing Responsive Designs
Testing your responsive layouts across devices is essential. Here are the most effective tools and techniques for 2025.
Chrome DevTools Device Toolbar: Press Ctrl+Shift+M (Windows/Linux) or Cmd+Shift+M (Mac) to toggle the device toolbar. You can select preset devices, set custom dimensions, throttle network speed, and simulate touch events.
Firefox Responsive Design Mode: Press Ctrl+Shift+M to enter responsive design mode. Firefox offers DPR simulation and touch simulation.
Test at your actual breakpoints, not just at preset device sizes. Slowly resize the browser window and watch for layout breaks.
/* Debug helper: show current breakpoint */
body::after {
content: 'MOBILE';
position: fixed;
bottom: 8px;
right: 8px;
background: #ef4444;
color: white;
padding: 4px 8px;
font-size: 12px;
border-radius: 4px;
z-index: 9999;
}
@media (min-width: 480px) {
body::after { content: 'MOBILE-L'; background: #f97316; }
}
@media (min-width: 768px) {
body::after { content: 'TABLET'; background: #eab308; }
}
@media (min-width: 1024px) {
body::after { content: 'LAPTOP'; background: #22c55e; }
}
@media (min-width: 1280px) {
body::after { content: 'DESKTOP'; background: #3b82f6; }
}
@media (min-width: 1536px) {
body::after { content: 'LARGE'; background: #8b5cf6; }
}
/* Remove in production! */
@media print {
body::after { display: none; }
}Common Responsive Patterns
Here are the most frequently used responsive design patterns that you will need in real projects.
Show/Hide on Mobile
Toggle element visibility based on screen size. Use with caution — hidden content should not be essential for mobile users.
/* Hide on mobile, show on desktop */
.desktop-only {
display: none;
}
@media (min-width: 768px) {
.desktop-only {
display: block; /* or flex, grid, etc. */
}
}
/* Show on mobile, hide on desktop */
.mobile-only {
display: block;
}
@media (min-width: 768px) {
.mobile-only {
display: none;
}
}
/* Visually hidden but accessible to screen readers */
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}Stack Columns on Mobile
Convert a multi-column layout to a single stacked column on smaller screens.
/* Stacked on mobile, side-by-side on tablet+ */
.two-column {
display: flex;
flex-direction: column;
gap: 24px;
}
@media (min-width: 768px) {
.two-column {
flex-direction: row;
}
.two-column > .main-content {
flex: 2;
}
.two-column > .sidebar {
flex: 1;
}
}
/* Three columns with container queries */
.card-grid {
container-type: inline-size;
display: grid;
gap: 16px;
grid-template-columns: 1fr;
}
@container (min-width: 500px) {
.card-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@container (min-width: 800px) {
.card-grid {
grid-template-columns: repeat(3, 1fr);
}
}Responsive Navigation
Transform a horizontal navigation into a hamburger menu on mobile devices.
/* Mobile: hamburger menu */
.navbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
}
.nav-links {
display: none; /* hidden by default on mobile */
flex-direction: column;
position: absolute;
top: 60px;
left: 0;
right: 0;
background: var(--bg);
padding: 16px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.nav-links.active {
display: flex; /* toggled by JavaScript */
}
.hamburger {
display: block;
cursor: pointer;
}
/* Desktop: horizontal nav, no hamburger */
@media (min-width: 768px) {
.nav-links {
display: flex;
flex-direction: row;
position: static;
background: none;
box-shadow: none;
gap: 24px;
padding: 0;
}
.hamburger {
display: none;
}
}
/* Responsive font sizes in nav */
.nav-link {
font-size: 1rem;
padding: 12px 0;
}
@media (min-width: 768px) {
.nav-link {
font-size: 0.875rem;
padding: 8px 12px;
}
}Frequently Asked Questions
What are the best CSS breakpoints for 2025?
The recommended breakpoints for 2025 are: 480px (mobile landscape), 768px (tablet), 1024px (laptop), 1280px (desktop), and 1536px (large desktop/4K). However, the best practice is to set breakpoints where your content actually breaks rather than targeting specific devices. Use these values as a starting point and adjust based on your design.
Should I use min-width or max-width for media queries?
Use min-width (mobile-first approach) in most cases. This is the industry standard because it starts with the simplest styles for mobile devices and progressively enhances for larger screens. Mobile-first CSS tends to be cleaner and more performant. The only exception is when retrofitting responsive styles onto an existing desktop-only site, where max-width (desktop-first) may be more practical.
What is the difference between media queries and container queries?
Media queries respond to the viewport (browser window) size, while container queries respond to a parent element's size. Container queries make components truly reusable because a card component can adapt based on where it is placed (sidebar vs main content) rather than only the viewport size. Use media queries for page-level layouts and container queries for component-level responsiveness.
How do I implement dark mode with CSS media queries?
Use the prefers-color-scheme media feature: @media (prefers-color-scheme: dark) { /* dark styles */ }. Define CSS custom properties (variables) for your color tokens and swap them in the dark mode query. Also provide a manual toggle using a CSS class or data attribute that overrides the system preference, giving users full control.
Is the new media query range syntax safe to use in production?
Yes, the new range syntax (e.g., @media (width >= 768px)) is supported in all modern browsers: Chrome 104+, Firefox 63+, Safari 16.4+, and Edge 104+. As of 2025, this covers over 95% of global users. If you need to support older browsers, use the traditional min-width/max-width syntax as a fallback or use a PostCSS plugin to transpile the range syntax automatically.
CSS media queries and breakpoints are essential tools for building responsive websites in 2025. With new range syntax, container queries, and preference-based features, you have more power than ever to create adaptive layouts. Use a mobile-first approach, test thoroughly, and always respect user preferences for the best experience.
Convert between CSS units with our CSS Unit Converter →
Build responsive layouts visually with our Flexbox Generator →