Journey to Highly Effective and Maintainable CSS Media Queries

Kaloyan Kosev
Dev Labs
Published in
8 min readApr 12, 2017

--

TL;DR: See the checklist for highly maintainable and effective CSS Media Queries at the bottom! ✅

Writing CSS Media Queries is so easy! But writing them in maintainable and highly effective manner… ahhh, not so much!

I’ll share my experience, the things I’ve struggled with and some ideas I read about. The solutions and conclusions below led me to the magical moment where I feel happy with the media queries I write!

I hope you learn from those or disprove in the comments below :-)

Where to place them?

When I was a newbie, I remember writing all my media queries at the bottom of my stylesheet.

.header {
...
}
.nav {
...
}
.footer {
...
}
/**
* ...
*
* bla bla, imagine a huge amount of styles here
*
* ...
*/
/** All style tweaks for screens < 1024px */
@media screen and (max-width: 1024px) {
.header { ... }
.nav { ... }
}

Later on, I realized the maintainability this way is a bit hard. The main reason: .header logic is spread across multiple unrelated lines of CSS. Therefore, I decided to keep the related styles together:

/** Header's styles and media queries */
.header {
...
}
@media screen and (max-width: 1024px) {
.header { ... }
}
@media screen and (max-width: 720px) {
.header { ... }
}
/** Nav's styles and media queries */
.nav {
...
}
@media screen and (max-width: 1024px) {
.nav { ... }
}
@media screen and (max-width: 720px) {
.nav { ... }
}
/** ... and so on */

That’s what I shared in this StackOverflow thread too. Do you agree?

However, repeating media queries max-width values all-around doesn’t sound maintainable enough. Using a CSS pre-processor like SASS could allow us to replace them with variables and define them once.

But there came another issue. What does min-width means? Does it start at the breakpoint or does it last until it? It was always fuzzy in my head.

Plus, combining max-width and min-width definitions gave me a real headache! Composing a more complex media query was never easy-enough-readable for me.

To boost up the maintainability and to make these definitions a lot more elegant, I started to use an abstraction on top of the Media Queries.

More Maintainable Media Query Definitions

Frankly, I can’t imagine an easy maintainable and readable media query definitions, without some kind of abstraction. Recently, I started using SASS MQ for this job.

/**
* Name your breakpoints in a way that creates a ubiquitous language
* across team members. It will improve communication between
* stakeholders, designers, developers, and testers.
*/

$mq-breakpoints: (
mobile: 375px,
tablet: 740px,
desktop: 1024px
);
@import 'mq';.header {
@include mq($from: tablet) {
...
}
@include mq($from: tablet, $until: desktop) {
...
}
}

It helps me compose media queries in an elegant and more human (programmer) readable way ($from: min-width boundary; $until: max-width boundary).

Additionally, the tool:

  • Compiles keywords and PX/EM values to EM-based queries
  • Provides well described various kinds of fancy configuration options
  • Provides fallbacks for older browsers (IE <= 8, Firefox <= 3, Opera <= 9… come on, does somebody still care for these dinosaurs?)

Sweet! ❤️

There are bunch of other similar tools. The point I’d like to make here is not about recommending a specific library! It is about using any kind of abstraction that solves the issue with the maintainability and readability I raised.

PX vs EM vs REM, which is the best?

Em-based media queries got very popular after Lyza Gardner detailed how breakpoints didn’t hit as you’d expect with browser zooming.

That’s not the case anymore! In modern browsers, there is no difference between em-based and pixel-based media queries if the browser handles the zoom correctly.

While zoom does not have an effect on modern browsers, base font size still does!

Zell Liew says:

I’m sorry to break your bubble, pixel fans, but it’s a no-go for pixel based queries. 😱😱😱

If you’re interested in more evidences, read this excellent case study shared in his blog. Here are my 3 reasons why:

  1. Respect user’s browser base font size! EM and REM are based on the font-size of the browser. Hence, their media queries should get updated when the user changes their default font-size setting. How often is the default font size in the browser not 16px? Nobody knows. But users do have the option to change their browser base font size! Therefore, it’s probably worth considering them. It is a question of accessibility.
  2. EMs are likely more future-proof. They will work for any device whose optimal default font size is not 16px (as well as those that are).
  3. Based on Zell’s tests, the only unit that performs bug-less across all browsers is EM.

Therefore, I stick to using EM values!

Choosing the Most Effective Breakpoints

Another popular misconception I’ve struggled with, is picking the most effective breakpoint values. A responsive design can have multiple breakpoints, say for a small-screen phone (portrait), then a large-screen phone (landscape), then a tablet, then a laptop/desktop and etc.

Approach 1: Do a research about the most popular device sizes in the world, and base my breakpoints on them.

This was my initial approach. Googling and searching for the most recent and most popular device screen sizes. The most popular screen resolution in 2017 is 1366px in width. I should totally base my wide-screen breakpoint to this one!

Approach 2: Many teams try to decide on breakpoints using average screen sizes. This approach sounds even more tempting if you upgrade it by comparing those with the visitor analytics reports for your project. Finally, plan the breakpoints based on your visitor’s most popular screen resolutions.

I’ve played around with both and I’ve come to a conclusion that both approaches are misleading.

On the one hand side, the device landscape is always changing, so today’s values might be relevant even just a year down the road.

On the other hand side, here’s an use-case:

Imagine if I have 2 breakpoints: at 768px and at 1024px. However, my content breaks at 900px.

If I want to be consistent and stick to the existing breakpoints I have, I must do a compromise and use the768px breakpoint. But the content is actually okay between 768px to 899px range. However, layout is switched at via an earlier breakpoint.

The second option I see is introducing a new breakpoint. Not an optimal solution, since we don’t stick to the main breakpoints for something so general as … our main content.

Therefore, my experience screams: breakpoints should be content-based, not device based.

Instead of being concerned with device breakpoints the best practice, plan according to your content. Or design for your smallest viewport first. As you expand that view there will come a point at which the design breaks. This is where you add a breakpoint!

Of course, you should keep track of and test for your visitors or general popular device dimensions (375px = iPhone portrait, 768px iPad portrait, etc). But remember: write content-based, not device-based breakpoints. Make all visitors equally happy, not just the one’s which visit your website most often.

Use Major Breakpoints And Minor Tweakpoints

Not every aspect of your design needs to neatly fit into small, medium or large breakpoints. That’s another key concept that goes along with the layout-based breakpoints.

Sure, there are the points at which the layout needs to change drastically — those media queries can legitimately be called breakpoints. But then there are the media queries that are used to finesse page elements without making any major changes to the layout.

Those technically are minor breakpoints, or tweakpoints. Jeremy Keith describes them:

It feels a bit odd to call them breakpoints, as though the layout would “break” without them. Those media queries are there to tweak the layout. They’re not breakpoints; they’re tweakpoints.

Following along with my SASS MQ configuration, here’s where they fit:

$mq-breakpoints: (
mobile: 375px,
tablet: 740px,
desktop: 1024px,
/** Tweakpoints */
desktopAd: 810px,
mobileNotificationsPanel: 480px
);

This approach helps me to do less compromises by introducing additional tweakpoints when needed. Plus, I keep track of all my minor tweaks.

Listing all tweakpoints in one place along with the main breakpoints is a bit more maintainable. Plus it gives a good overview how much effort each component causes.

The balance between major and minor breakpoints is up to you. While it’s important to utilize both kinds of breakpoints, you need to be careful not to overdo any of them. With responsive design, it’s easy to get caught up in all the media query magic and go crazy.

Media queries are certainly fun, but introducing a lot of complexity into your designs is for sure going to frustrate you! Balance wisely. 🙏🏼

Mobile-first manner

As part of a mobile-first responsive design strategy, you can consider authoring styles in a mobile-first manner.

Code for larger screens is usually more complicated than the codes for smaller screens. This is why coding mobile first helps simplify your code. Therefore, authoring mobile-first styles results in smaller, simpler, more maintainable code.

Not convinced yet? Zell Liew shared a great example in his blog post that convinced me!

Consider a situation where you have a content-sidebar layout for a website. The .content takes up 100% width on mobile, and 60% on desktop. Here’s how we could approach this, mobile-first (using SASS):

.content {
// Properties for smaller screens.
// Nothing is required here because we can use the default styles
// Properties for larger screens
@media (min-width: 740px) {
float: left;
width: 60%;
}
}

Alternatively, if we go with the desktop-first approach, we will have to restore the default properties for smaller viewports most of the time:

.content {
// Properties for larger screens.
float: left;
width: 60%;
// Properties for smaller screens.
// Note that we have to write two default properties
// to make the layout work.

@media (max-width: 740px) {
float: none;
width: 100%;
}
}

This example resonates a lot to my experience. For something so common, we managed to reduce twice the lines of code needed. Imagine how much time and effort this could save you in a bigger project! ☀️

Device Width is Only One Option out of Many

Speaking about effective media queries, I should mention that device width is not the only thing we could keep track of.

If you’re looking for a comprehensive list of all media query options, check out this MDN page. Additionally, there’s a nice CSS Tricks article focused on media queries that catch various standard devices.

But remember! Do not base your breakpoints on devices!

TL;DR: Checklist of highly maintainable and effective CSS Media Queries

  • Place the media queries next to the components they modify.
  • Use an abstraction like SASS MQ (or anything) to sum-up media queries and make them more readable.
  • Write (or compile to) media query values in EMs.
  • Breakpoints should be content-based, not device based.
  • Use (major) breakpoints and (minor) tweakpoints.
  • Simplify your stylesheets by coding in Mobile-first manner. It works.

--

--

Front-End Engineer, currently focused on React, React Native, and NodeJS. In the 10k+ reputation club on StackOverflow. Maker. Nano influencer. superkalo.com