The cascade is what makes CSS special and powerful. But in the case of media queries, overlap can seem problematic.
Consider the following CSS (continuing rules for CSS media query overlap):
/* Standard - for all screens below 20em */
body { color: black; font-size: 1em; }
/* Query A - slightly wider, mobile viewport */
@media (min-width: 20em) and (max-width: 45em) {
body { color: red; } /* supposed to be unique for this width */
}
/* Query B - everything else */
@media (min-width: 45em) {
body { font-size: larger; } /* because viewport is bigger */
}
So when the screen is exactly 45em wide, the overlap at 45em will be treated according to the standard CSS cascade:
max-width: 45em
definitions will be applied first,min-width: 45em
will be applied thereafter.Consider these two conditions:
black
, but Query A is unique and has color: red
. font-size: larger
.Therefore, at a width of exactly 45em, we'd get big and red text. What would be the best solution to avoid this?
I see two possibilities:
Re-declare the text to have color: black
in Query B, but then you're managing two declarations if you choose to change the color
in the future. (Of course, not such a problem with this single line of code, but imagine there's a lot of other declarations and selectors.)
Avoid overlap by using pixel values like max-width: 799px
and min-width: 800px
, but then you're using pixels — I guess they could be 49.9375em and 50em, respectively. Though what if the default is no longer 16em and something gets rounded? And we're still not certain what happens at that gap. (A black hole that breaks the space-time continuum?)
Both have their drawbacks... any other ideas?
Media queries are used to apply some CSS instructions only when certain conditions are met. The conditions usually refer to the width of the viewport, since that's the main factor that determines how much can be displayed on a website.
Responsive Pixel — An Alternative to Media Query for Responsive Resizing.
You may use as many media queries as you would like in a CSS file. Note that you may use the and operator to require multiple queries to be true, but you have to use the comma (,) as the or operator to separate groups of multiple queries. The not keyword can be used to alter the logic as well.
The only reliable way to create two mutually exclusive @media
blocks for any given media query is to use not
to negate it in one of the blocks. Unfortunately, this means repeating your media query once for each @media
block. So, instead of this for example:
@media (max-width: 49.9375em) {
body {
color: red;
}
}
@media (min-width: 50em) {
body {
font-size: larger;
}
}
You would have this:
/*
* Note: Media Queries 4 still requires 'not' to be followed by a
* media type (e.g. 'all' or 'screen') for reasons I cannot comprehend.
*/
@media not all and (min-width: 50em) {
body {
color: red;
}
}
@media (min-width: 50em) {
body {
font-size: larger;
}
}
Interactive jsFiddle demo
This is very effective at closing the gap with range media features like width
and height
since it essentially turns this into an either-or scenario. But, like your first two options, it isn't perfect: as mentioned, you have to repeat the same media query twice, and add not
to one of them. There is no if/else construct for @media
as described in Conditional Rules 3.
Although I mention this in my answer to your previous question:
From my experiments it would seem Safari on iOS rounds all fractional pixel values to ensure that either one of
max-width: 799px
andmin-width: 800px
will match, even if the viewport is really 799.5px (which apparently matches the former).
It should be noted, still, that I've noticed some quirks when it comes to rounding. That said, I haven't been able to find a fractional value that would evade both media queries and end up not receiving styles from either set of rules (which, by the way, is the worst that can happen, so don't worry about potentially creating a space-time rift). That must mean browsers — at least, Safari as I've tested — do a reasonable job of ensuring they satisfy media queries even if you have values that differ (by exactly 1 CSS pixel).
When it comes to units with larger gaps that can be observed on desktop browsers, though, like ems, there is a much larger margin of error. For example, one comment suggests using 49.99999em instead of something more arbitrary than 49.9375em, but apparently there is a difference, at least with a default font size of 16px.
I simplified your code, changed the media queries to use decimal values, and put the code in jsFiddle:
@media (max-width: 49.9375em) {
body {
color: red;
}
}
@media (min-width: 50em) {
body {
font-size: larger;
}
}
If you resize the Result pane to exactly 800 pixels (the text will update to guide you along), you actually end up with different results depending on whether @media (max-width: 49.9375em)
is used, or @media (max-width: 49.99999em)
is used (I was surprised by this too)...
Either way, you're right: option 2 has its drawbacks too. I'm not particularly fond of it, to be honest, because I wouldn't want to crack my head over device and user agent quirks which are out of my control. If you're like me, I suppose it would be better to go through the inconvenience of redeclaring your rules at the cost (?) of being more vigilant around your code, as that's at least still within your control as an author.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With