Prevent double breakpoint

See comments

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.

Only upwards or downwards breakpoints

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.

A separation of 1px

This is the technique that I personally use most of the time. It is just two simple rules :

  1. A “min-” media-query is always an even number
  2. A “max-” media-query is always an odd number
@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.

A separation in em

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 :

  1. A “min-” media-query is always an integer
  2. A “max-” media-query always ends with “.99em”

With a preprocessor

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;
    }
}

Conclusion

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?

tl;dr

Use a gap of 1px or 0.01em between your media-queries or use the sass-mq project.

Happy coding, Thomas Zilliox.

comments powered by Disqus
That's my face!

Thomas ZILLIOX

I am a freelance web developer. I help web teams to reduce CSS maintenance & evolution costs.