Inverted Media Queries and Breakpoints
The occasional breakpoint
Nowadays I lean on modern CSS solutions, fluid layout patterns, and intrinsic sizing over viewport dimension-based media queries – typically referred to as breakpoints – that adapt a design at particular screen sizes. Let's not forget that container queries will soon join our CSS toolset, expanding the exciting universe of parent context styling.
However, I do still find the occasional place to apply adaptive styles. Common example: a "desktop" menu (imagine a horizontal list of navigation items) that converts into its "mobile" counterpart (imagine a hamburger button that toggles a vertically stacked menu's visibility). After building out the foundational styles, I often prefer separating adaptive CSS properties versus having to override or unset them. This means I end up with rulesets that might look like this:
.menu {
/* base styles */
}
/* below 600px */
@media (max-width: 599px) {
.menu {
/* narrow viewport styles */
}
}
/* 600px and above */
@media (min-width: 600px) {
.menu {
/* wide viewport styles */
}
}
Notice the single pixel difference in the two values, 599px
and 600px
. If these min- and max-width queries shared the same value, then there would be a single pixel overlap where both styles would apply. Not ideal!
Invert the media query
One way around this is to negate the media query. The not
keyword in a media query will invert the query's meaning. To let both values in the previous example be the same, I could instead reuse a query and then invert it:
/* below 600px */
@media not all and (min-width: 600px) {
/* "... */
}
/* 600px and above */
@media (min-width: 600px) {
/* "... */
}
If your current browser window is large enough, you can resize the CodePen result window below to see the text and background color change based on their respective media query declarations:
Future CSS solutions
Level 4 media query range contexts, which are getting closer to full modern browser support, will allow use of the same value:
/* @media (max-width: 599px) becomes */
@media (width < 600px) {
/* ... */
}
/* @media (min-width: 600px) becomes */
@media (width >= 600px) {
/* ... */
}
I really dig that syntax. I personally find it easier to understand and maintain.
Another exciting solution involves custom media queries, which would allow us to store the media feature to a variable:
@custom-media --breakpoint (min-width: 600px);
/* below 600px */
@media not all and (--breakpoint) {
/* ... */
}
/* 600px and above */
@media (--breakpoint) {
/* ... */
}
It seems that this won't be available for a while as it's in a draft of the level 5 media queries spec, but the good news is that there's a PostCSS plugin that give us this power today. 👏