Cubit: A More Flexible Media Query
This post and idea were developed by Themeco's lead developers Kory Wakefield and Alexander Rohmann.
Much has been written about the humble media query. In the world of modern web development it is a necessary tool for most all projects, while being equally venerated (yay for responsive design!) and denounced (we need container queries!) at the same time. Media queries do a lot of things very well; however, they also inherently possess shortcomings that pose unique challenges, particularly when constructing tools for others to utilize in a site-building workflow.
Here at Themeco we're always trying new things, and today we'd like to share an idea we've been experimenting with: Cubit. Cubit is a JavaScript library we developed to address some of the pain points mentioned above, which we will walk through in more detail throughout this article.
When you're done reading, check out the interactive demo and GitHub repo at the bottom of this page.
Specificity vs. Abstraction
As developers, we like our abstractions. It's always helpful when we can tuck certain details away in a nice, orderly fashion somewhere else and not have to think about the specifics of each layer. With regards to media queries, there isn't a clean way to do this directly in our stylesheets as we don't have a dynamic layer to work with:
The code above will always fire when a device's viewport is at least 1000px
wide. This is nice, it's predictable and works, but it is a rigid construct without any flexibility. What if there were some way to remove this specificity out of the stylesheet? Are there any inherent benefits to doing so?
This infers a similar result, but isn't tied to any precise figure. Wouldn't it be powerful to be able to change the meaning of what .lg
is expressing behind the scenes from one place? “But muh pre-processors!” I hear you saying. True, tools like Sass allow us to leverage the usefulness of variables and mixins throughout our projects and keep things relatively DRY, but you're still tied into a specific value. This abstraction could allow you to explore some of the following:
- Let's say for the most part you want
.lg
to represent the same value on your site, but on certain pages you need it to break slightly higher or lower..lg
could represent different values on different pages, but all of your styles can be written in the same syntax throughout your website. - If you have a design team that needs to create pages on the fly but doesn't have access to your development tooling, they can easily style elements as needed using classes like
.lg
, and if you needed to change the value of what that represents down the road, you could do so without breaking their work or having a mixup of old and new values. - If you create products for end-users to build out their own websites (e.g. WordPress themes, et cetera), providing your customers with a way to specify what
.lg
is and how it works for their needs is a powerful concept. Furthermore, if a user wanted to jump in and make additional customizations beyond the options you provide, it would be a much simpler learning curve for non-technical users to understand our second example from above rather than the first.
Syntax
While it certainly isn't the most difficult to understand once you get the hang of it, media query syntax can be a bit daunting for newcomers:
I remember having my fair share of moments in my early explorations of responsive design firing up the ol' Google Machine for a quick refresher on the computational incantations needed to achieve the ideas floating around in my head. Ah, how much simpler the following would have been to experiment with:
Not only that, but let us not forget that ever-important rule when designing for the web: every byte counts! As we all know, in this world nothing can be said to be certain, except death, taxes, and unwieldy stylesheets (Benjamin Franklin was a huge CSS nerd). As time goes on, teams grow, et cetera, unused and unnecessary styles inevitably creep into projects. Sometimes the blessing of pre-processors can also be a curse when left unchecked. You know that awesome mixin you're using to scatter breakpoints all throughout your stylesheet? It's spitting out that big mess of characters every time you use it, and in particularly large and complex projects, that adds up. Utilizing a smaller, more abstract syntax allows us to more easily ascertain what is going on with our styles and keeps file size to a minimum (even if handled poorly).
A Thought Experiment
The situations described above (along with a few others) are what prompted us to begin exploring and see if there might be some way to introduce more flexibility into our projects. Specifically for us, the possibility of providing our users with the ability to declare their site-wide breakpoints in one place and then utilize a much cleaner and approachable class-based syntax as needed was a powerful thought. It also meant that we would be able to style site layouts and functionality within a static stylesheet but allow the user to specify what the abstract values we'd be using meant.
A solution like this would obviously require a little JavaScript magic to make use of these techniques, which we only wanted to explore with the following conditions:
- All styles should be declared mobile-first so that clients with JavaScript turned off are still able to achieve a good user experience and easily access content.
- The script would need to be small and run early enough to output the dynamic responsive classes before our content is visible to avoid FOUC.
The Implementation
As mentioned previously, this is JavaScript powered. If you've dabbled in using JavaScript to set styling based on the viewport width, you'll quickly realize that the units you get from JavaScript do not perfectly match how the viewport is handled in CSS media queries. Libraries like verge help with more accurate approximations (nearly perfect) by normalizing JavaScript's values depending on the browser.
Enter getComputedStyle
! This gem of a function allows you to grab the CSS being applied to any element, at any given moment. Imagine you have this CSS:
Now run this JavaScript:
Depending on your viewport width, style.content
will contain: xl
, lg
, md
, sm
, or xs
! Wrap all this up in a handler on the resize event and you can use media queries to trigger JavaScript changes. The next step would be taking that value and applying it as a class on the html
element.
With just a little bit of code, you've just abstracted away media queries into simple CSS classes. Now that we're in JavaScript land, we can take things just a bit farther and bundle this up into a pleasant, little library with one additional goal: have the library generate the required media query CSS from a data
attribute, allowing the breakpoints to be determined programmatically when needed.
This opens a new realm of possibilities. Without changing stylesheets themselves, we can change the ranges at which breakpoints trigger. Setting them in a data
attribute would allow them to be customized in the options of a theme or plugin.
What's Next?
It should be noted that Cubit is currently an experimental library. It has significant implications, but isn't battle tested. Can you break it? Do you see any potential issues with this particular method? Let us know in the comments, or get involved on the GitHub repo.
October 15, 2016 Update:
In response to some of the initial questions we've received please read the following.
Wait, doesn't this violate best practices? Yes, admittedly this introduces another script and breaks one of the old web conventions of not depending on JavaScript. We're not overly concerned by this, for the following reasons.
Regarding the requiring JavaScript:
- The internet these days can't really be used without JavaScript. Each day this becomes less and less of a concern.
- With this technique, the site could still degrade gracefully to any applied mobile first styling.
Regarding performance concerns:
- If this is used inline, it will add 1kb to your head. Keep in mind this is smaller than the JavaScript WordPress core (version 4.2) introduced to the head of every site to add the ever so helpful…wait for it…emoji parsing.
- When used inline, there's no measurable speed difference that we can tell in our testing.
- Alternatively, it could load as one of the first script requests. This might bump your load time by 50-100ms on the very first load until the browser caches the file for future pages.
What's the tradeoff? Quick recap:
- Less code required in your stylesheets.
- Your breakpoints values can change at a site configuration level, without affecting the stylesheets. For example, the same stylesheet is loaded, but different site pages have slightly different/optimized breakpoint values.
Other concerns?
As this is more of an experiment, there's still some discovery to go before we can decide if this is production worthy. It's looking promising, but our hope and goal here is that other developers with a grasp of responsive styling challenges will chime in with their thoughts and further testing. We also welcome contributions to our GitHub repo.
October 17th, 2016 Update:
It looks like others are working to solve similar problems in this space! Here are a few links that have been brought to our attention:
Cubit takes this getComputedStyle
innovation, packaging it up in a way that will swap any number of classes for you, which can correlate to any breakpoint you wish. We add an additional (and much needed) layer of dynamically generating the stylesheet holding the media queries. This allows for the breakpoints to be set at load, and altered in real time—creating a new dimension of flexibility.
Got anymore links for us? We'd love to know! Obviously this is a pain point that many are working to solve, and we'd love to see some more discussion come from this.