Logic in CSS Media Queries (If / Else / And / Or / Not)

Avatar of Chris Coyier
Chris Coyier on (Updated on )

Just in case you have brain farts about the logic in CSS media queries about this constantly, as I do.

If

That’s what media queries are: logical if statements. “If” these things are true about the browser, use the CSS inside.

/* IF the viewport is 550px or smaller, do this */
@media (max-width: 550px) {
  html { background: hsl(0 0% 0% / 0.5); }
}

Media Queries Level 4 allows for a comparison syntax like this, but the browser support is much lower right now:

@media (width <= 30em) {
  html { background: rgb(0 0 0 / 0.5); }
}

And

The keyword and.

/* IF the viewport is 550px or smaller, do this */
@media (min-width: 600px) and (max-width: 800px) {
  html { background: red; }
}

The Media Queries Level 4 syntax really shines here:

@media (600px <= width <= 800px) {
  html { background: red; }
}

Or

Comma separate.

@media (max-width: 600px), (min-width: 800px) {
  html { background: red; }
}

Technically these are treated like two separate media queries, but that is effectively or.

Not

Reverse the logic with the keyword not.

@media not all and (max-width: 600px) {
  html { background: red; }
}

Just doing not (max-width: 600px) doesn’t seem to work for me, hence the slightly funky syntax above. Perhaps someone can explain that to me. Note that not only works for the current media query, so if you comma separate, it only affects the media query it is within. Also note that not reverses the logic for the entire media query as a whole, not individual parts of it. not x and y = not (x and y) ≠ (not x) and y.

Exclusive

To ensure that only one media query is in effect at a time, make the numbers (or whatever) such that that is possible. It may be easier to mentally manage them this way.

@media (max-width: 400px) {
  html { background: red; }
}
@media (min-width: 401px) and (max-width: 800px) {
  html { background: green; }
}
@media (min-width: 801px) {
  html { background: blue; }
}

Logically this is a bit like a switch statement, only without a simple way to do “if none of these match do this” like default.

This is future CSS stuff, but the @when syntax will help make this better:

@when media(min-width: 600px) {
  /* ... */ 
}
@else {
  /* ... */ 
}

Overriding

There is nothing preventing more than one media query from being true at the same time. It may be more efficient to use this in some cases rather than making them all exclusive.

@media (min-width: 400px) {
  html { background: red; }
}
@media (min-width: 600px) {
  html { background: green; }
}
@media (min-width: 800px) {
  html { background: blue; }
}

Media queries add no specificity to the selectors they contain, but source order still matters. The above will work because they are ordered correctly. Swap that order and at browser window widths above 800px the background would be red, perhaps unintuitively.

Mobile First

Your small screen styles are in your regular screen CSS and then as the screen gets larger you override what you need to. So, min-width media queries in general.

html { background: red; }

@media (min-width: 600px) {
  html { background: green; }
}

Desktop First

Your large screen styles are in your regular screen CSS and then as the screen gets smaller you override what you need to. So, max-width media queries in general.

html { background: red; }

@media (max-width: 600px) {
  html { background: green; }
}

Gettin Wacky

You can be as complex as you want with this.

@media 
  only screen and (min-width: 100px),
  not all and (min-width: 100px),
  not print and (min-height: 100px),
  (color),
  (min-height: 100px) and (max-height: 1000px),
  handheld and (orientation: landscape)
{
  html { background: red; }
}

Note: the only keyword was intended to prevent non-media-query supporting browsers to not load the stylesheet or use the styles. Not sure how useful that ever was / still is.