A few weeks ago Wes Oudshoorn, a UI/UX designer we’re fortunate to work with on AppSignal, asked if I knew a way to make media queries relative to an element instead of the viewport. I did not, but it didn’t take long for me to understand that this is a feature that would make writing responsive, reusable modules so much better.
Right now, media queries will determine which CSS to use when the viewport has certain minimum, maximum or exact dimensions (or device characteristics; irrelevant to this article). This aids in creating better responsive layouts, for instance when a sidebar with a width of 35% and a main content container with a width of 65% stops making sense at some viewport sizes. In narrow viewports you may want both to be at 100% for readability, so your sidebar simply gets positioned below your main content.
I’m a proponent of writing modular and reusable front-end modules. An important aspect is the decoupling of module dimensions/positioning and other styling, so modules can be used in any context. When a module always has a width of 100% it can be easily used within fluid containers, like a sidebar and content container. But, maybe we want the module to have a different appearance when it’s more narrow than 200 pixels, for instance to hide some icons that won’t fit anymore. Wouldn’t it be great to have a media query that could target the rendered width of an element?
Right now there’s the situation where we need multiple media queries for this. Let me illustrate with this example. What we have here is a simple layout with an article on the left and a sidebar on the right. The article has a width of 65% and the sidebar is 35% wide. It’s fluid up till a maximum viewport width of 1000px, after which the whole thing will be centered. When the viewport is more narrow than 600px, the article and sidebar will be rendered on top of each other for readability. This may be one of the most used lay-outs that involve media queries. So far, so good.
Now let’s say we want the article to have the text in two columns as long as that particular element is wide enough. In this case we’re not doing mobile first, so the default is to have those two columns. When the article element width is 500px or less we want to reset that to a single column. Right now we need to resize our browser window until the article is 500px wide, get the viewport size and put that in a media query:
@media (max-width: 815px). This is getting complicated, because we need to figure out that viewport width again if we ever change the article:sidebar (65:35) ratio and change all media queries accordingly.
When we hit the 600px viewport width, the sidebar gets positioned below the article. Because of that the article is now 600px wide as well, so we want to have those two columns. We have to change our media query to match
(min-width: 600px) and (max-width: 815px) and start resizing again. To make sure we have a single column in a narrow viewport we add
(max-width: 515px) as well (that’s the 500px wide article element plus a scrollbar on OSX). Our complete media query ends up being
@media (max-width: 515px), (min-width: 600px) and (max-width: 815px).
That’s a lot of work to apply styling to an element based on its size, while having to reference the viewport dimensions! On top of that we’re also throwing in some guesstimates on scrollbar width, and for every other context the element is used in we need to determine when it hits that 500px mark again. Our media query will get seriously bloated over time.
The solution would be simple: nest media queries inside elements. Of course that would mean running into compatibility issues with CSS pre-processors such as Sass & LESS, which already allow nesting (with the nested media queries bubbling up). Maybe we should settle on a different syntax for these “element media queries” or re-think the way pre-processors bubble (which is based on the current limitations).
Either way, we need smarter media queries if we want our code to be truly modular.