Menu

CSS Media Queries: Breakpoints, min-width and max-width

A media query applies CSS only when a condition is true, such as a minimum screen width, a dark color scheme or a mouse pointer. Learn the @media syntax, mobile-first breakpoints, the range syntax, prefers-color-scheme, matchMedia in JavaScript, and container queries.

This page includes runnable editors - edit, run, and see output instantly.

A media query in one example

A media query wraps CSS in a condition. The rules inside @media (min-width: 600px) only apply while the viewport is at least 600px wide:

The preview is a frame, and a media query inside it measures the frame, not your screen. On a desktop it is about as wide as this article column, usually more than 600px, so the box is blue. Make your browser window narrow enough that the frame drops under 600px and it turns amber; the line under the box reports the frame's width live. On a phone the frame is already narrow, so you see the amber version.

The @media syntax

@media (min-width: 600px) { ... }                        /* one condition */
@media (min-width: 600px) and (max-width: 899px) { ... } /* both must be true */
@media (max-width: 599px), print { ... }                 /* either one (comma = or) */
@media not print { ... }                                 /* negation */
@media screen and (orientation: landscape) { ... }       /* media type plus a feature */
@media (width >= 600px) { ... }                          /* range syntax */
@media (600px <= width < 900px) { ... }                  /* a range with both ends */

and needs a space on both sides: (min-width: 600px)and(max-width: 900px) is invalid and the whole query is ignored. The range syntax (width >= 600px) is supported in current Chrome, Firefox and Safari and reads more clearly than min-width, especially for "up to but not including".

The features you will use most:

FeatureMatches whenExample
min-width / width >=viewport is at least this wide(min-width: 768px)
max-width / width <=viewport is at most this wide(max-width: 767px)
orientationportrait (taller than wide) or landscape(orientation: landscape)
hoverthe main pointer can hover (hover) or not (none, most touch screens)(hover: hover)
pointerthe main pointer is fine (mouse) or coarse (finger)(pointer: coarse)
prefers-color-schemethe user chose a dark or light theme(prefers-color-scheme: dark)
prefers-reduced-motionthe user asked the system for less motion(prefers-reduced-motion: reduce)
print (a media type)the page is printed or saved as PDF@media print

Mobile first: min-width breakpoints

Write the phone layout as the base CSS, then add min-width queries that change the layout as space grows. Each query only has to describe what changes:

In a wide desktop window the preview is over 720px, so you see three columns; the line under the grid reports the width and the column count. Narrow the window and it drops to two under 720px and to one under 480px. The order of the two queries matters: they are both true on a wide screen, and the later one wins, so min-width queries go from smallest to largest. (With max-width queries it is the reverse: largest first.)

Choosing breakpoints

There is no official list. These are common values and what they usually separate:

BreakpointTypically
min-width: 640pxlarge phones in landscape, small tablets
min-width: 768pxtablets in portrait
min-width: 1024pxtablets in landscape, small laptops
min-width: 1280pxdesktops

A better method: start with the narrow layout, widen the window until something looks wrong (lines too long, cards too wide), and put a breakpoint there. Two or three breakpoints are enough for most pages. Often you need none at all: repeat(auto-fit, minmax(200px, 1fr)) in grid, flex-wrap, and clamp() adapt continuously.

Every page needs the viewport meta tag

Without this tag in the <head>, phone browsers lay the page out as if the screen were about 980px wide and then zoom out, so your min-width: 480px query is always true and your phone layout never shows:

<meta name="viewport" content="width=device-width, initial-scale=1">

The meta viewport page explains every value in it.

Dark mode with prefers-color-scheme

prefers-color-scheme follows the operating system's light or dark setting. The cleanest way to use it is to keep colors in CSS variables and change only the variables inside the query:

In this preview the query can print false even when your system is set to dark: Chrome does not pass the system setting into a sandboxed frame like this one. The button sets the same variables by hand, which is how a site's theme toggle works. :root[data-theme] is more specific than the plain :root inside the query, so the reader's choice wins over the system. Save the example as a file and open it to see the query follow your system.

The script shows the second half of media queries: matchMedia() runs the same query from JavaScript, returns whether it matches now, and fires a change event when the answer changes. Use it when behavior (not just styling) depends on the screen, such as opening a menu differently on touch devices.

Motion works the same way. Wrap large or looping animations in a check for prefers-reduced-motion, as shown on the animation page.

Container queries: when the component should decide

A media query only knows the viewport. A card that sits in a wide main column and in a narrow sidebar on the same page needs to know its own space instead. That is a container query: mark the parent with container-type: inline-size, then use @container with the same conditions.

Drag the corner of the gray box to resize it. The card switches layout at 360px of box width, whatever the window size:

At its starting 480px the picture sits beside the text. Drag the box narrower than 376px (360px of content plus 8px of padding on each side) and the picture moves above the text. Container queries work in all current major browsers.

Styles for printing

@media print applies only when the page is printed or saved as a PDF. Hide navigation and buttons, and use dark text on white:

@media print {
  nav, .ad, button { display: none; }
  body { color: black; background: white; }
  a::after { content: " (" attr(href) ")"; } /* print the URL after each link */
}

Common mistakes

  • No viewport meta tag. Phones then report a wide layout viewport and small-screen queries never match.
  • Queries in the wrong order. Two true queries with the same selector: the later one wins. Order min-width queries from small to large.
  • Overlapping or gapping boundaries. max-width: 768px and min-width: 768px both match at exactly 768px. Use 767px for one of them, or the range syntax (width < 768px) and (width >= 768px).
  • No space after and. and( is invalid and the query is dropped silently.
  • Testing only by resizing a desktop window. Also check a real phone or the browser's device mode, which applies the phone's viewport, pixel ratio and touch.
  • Too many breakpoints. If every component needs its own, try grid auto-fit, flex-wrap or container queries instead.

Frequently Asked Questions

What is a media query in CSS?

A rule that applies the CSS inside it only when a condition about the device or window is true. @media (min-width: 600px) { ... } applies its styles only when the viewport is at least 600px wide, which is how one page adapts from phones to desktops.

Should I use min-width or max-width media queries?

Most sites write mobile first: the base CSS is for small screens and min-width queries add layout as the screen grows. max-width queries work the other way, starting from the desktop layout. Either works; mixing both on the same element leads to overlaps and gaps at the boundaries.

What are the standard media query breakpoints?

There is no official standard. Common choices are around 640px, 768px, 1024px and 1280px, but the better rule is to add a breakpoint where your own layout starts to look wrong, and to keep the number of breakpoints small.

Why is my media query not working on a phone?

The most common cause is a missing viewport meta tag. Without <meta name="viewport" content="width=device-width, initial-scale=1">, mobile browsers pretend the page is about 980px wide, so small-width queries never match. Also check that the query comes after the rules it overrides.

How do I write a media query for dark mode?

Use @media (prefers-color-scheme: dark) { ... }. It matches when the user's operating system or browser is set to a dark theme, and you can override colors or CSS variables inside it.

What is the difference between media queries and container queries?

A media query tests the viewport (the whole window). A container query, written with @container, tests the size of a parent element that you mark with container-type: inline-size. Use container queries for a component that appears in both wide and narrow places on the same page.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED