When two media-queries overlap, it is a source of complexity and even bugs. How can we create two media-queries that follow each other and are totally exclusives?
This article follows the “What is a double breakpoint?” article. The conclusion was to not have the same values for “min” & “max” media-queries. Here, I will try to bring solutions adapted to different kind of projects.
The simplest way to have different values for “min” & “max” media-queries, is to eliminate the usage of one them.
We can have a “mobile-first” approach and prohibit “max” media-queries:
/* Mobile styles applied everywhere */
@media (min-width: 320px) {
/* Override above and at 320px */
}
Play with this example on jsbin
Or, we can have a “desktop-first” approach and prohibit “min” media-queries:
/* Desktop styles applied everywhere */
@media (max-width: 320px) {
/* Override at and below 320px */
}
Play with this example on jsbin
The main problem with these solutions is the maintainability. When you will have a lot of breakpoints, you will not be able to fix something in a specific resolutions range. For example, In a mobile-first approach, fix something for the tiniest resolution will have repercussions on all designs.
This is the technique that I personally use most of the time. It is just two simple rules:
@media (max-width: 319px) {
/* Applied below and at 319px */
}
@media (min-width: 320px) {
/* Applied above and at 320px */
}
Play with this example on jsbin
This rocks because it's simple!
The problem with this solution is that it works only with pixel media-queries. It's better to have your breakpoints in em values. You will be able to create web designs that's adapt them selves to the user text zoom.
1px is normally 0,0625em. But if our visitor has a text zoom, 0,0625em could be more than 1px.
@media (max-width: 19.9375em) {
/* Applied below and at 319px with zoom 1 */
/* Applied below and at 350.9px with zoom 1.1 */
}
@media (min-width: 20em) {
/* Applied above and at 320px with zoom 1 */
/* Applied above and at 352px with zoom 1.1 */
}
/* No media-query applied at 351px with zoom 1.1 */
Play with this example on jsbin
So we have to use a tinier separation between our two media-queries to prevent a gap between them. If we choose a too small separation, for example 0.001em, there is a risk that the browser rounded the two media-queries to the same values:
@media (max-width: 19.999em) {
/* Applied below and at 319,984px with zoom 1 */
/* Applied below and at 160px with zoom 0.5 */
}
@media (min-width: 20em) {
/* Applied above and at 320px with zoom 1 */
/* Applied above and at 160px with zoom 0.5 */
}
/* Both applied at 160px with zoom 0.5 */
Play with this example on jsbin
For me, the best solution is a difference of 0.01em
@media (max-width: 19.99em) {
/* Applied below and at 319,84px with zoom 1 */
}
@media (min-width: 20em) {
/* Applied above and at 320px with zoom 1 */
}
Play with this example on jsbin
So we can adapt our two simple rules:
The problem with all the previous solutions is the communication part. You have to document it & say it loud to every people that could modify the CSS.
After discussing it with Kaelig, he presented me the sass-mq project of the gardian. The great advantage of a preprocessor solution is that everything will be automatic. It reduce a lot this communication problem.
The following nice Sass code:
$mq-breakpoints: ((mobile 320px) (tablet 640px));
.element {
@include mq($from: mobile, $to: tablet) {
color: white;
}
}
will be computed in exactly what we want:
@media all and (min-width: 20em) and (max-width: 39.99em) {
.element {
color: white;
}
}
It is quite hard to create good exclusive media-queries with the current specifications. So we are obliged to find a hacky way to do it.
I presented all the solutions that I have used or tried. They are all adapted to a kind of projects. But clearly, my preference will go to the preprocessor solution. Because I think we should never underestimate the importance of the communication problems in CSS maintenance.
Do you have another great solution to share?
Use a gap of 1px or 0.01em between your media-queries or use the sass-mq project.