Responsive web design is showing us that designing content is more important than designing containers. But if you’ve given RWD a serious try, you know that shifting your focus from the container is surprisingly hard to do. There are many factors and
instincts working against you, and one culprit is a perpetrator you’d least suspect.
The media query is the ringmaster of responsive design. It lets us establish the rules of the game and gives us what we need most: control. However, like some kind of evil double agent, the media query is actually working against you.
Its very nature diverts your attention away from content and forces you to focus on the container.
The very act of choosing a media query value means choosing a screen size.
Look at the history of the media query—it’s always been about the container. Values like screen, print, handheld and tv don’t have anything to do with content. The modern media query lets us choose screen dimensions, which is great because it makes RWD possible. But it’s still the act of choosing something that is completely unpredictable.
Content should dictate our breakpoints, not the container. In order to get our focus back to the only thing that matters, we need a reengineered media query—one that frees us from thinking about screen dimensions. A media query that works for your content, not the window. Fortunately, Sass 3.2 is ready and willing to take on this challenge.
Thinking in Columns
Fluid grids never clicked for me. I feel so disoriented and confused by their squishiness. Responsive design demands their use though, right?
I was ready to surrender until I found a grid that turned my world upright again. The Frameless Grid by Joni Korpi demonstrates that column and gutter sizes can stay fixed. As the screen size changes, you simply add or remove columns to accommodate. This made sense to me and armed with this concept I was able to give Sass the first component it needs to rewrite the media query: fixed column and gutter size variables.
$grid-column: 60px;
$grid-gutter: 20px;
We’re going to want some resolution independence too, so let’s create a function that converts those nasty pixel values into ems.
@function em($px, $base: $base-font-size) {
@return ($px / $base) * 1em;
}
We now have the components needed to figure out the width of multiple columns in ems. Let’s put them together in a function that will take any number of columns and return the fixed width value of their size.
@function fixed($col) {
@return $col * em($grid-column + $grid-gutter)
}
With the math in place we can now write a mixin that takes a column count as a parameter, then generates the perfect media query necessary to fit that number of columns on the screen. We can also build in some left and right margin for our layout by adding an additional gutter value (remembering that we already have one gutter built into our fixed function).
@mixin breakpoint($min) {
@media (min-width: fixed($min) + em($grid-gutter)) {
@content
}
}
And, just like that, we’ve rewritten the media query. Instead of picking a minimum screen size for our layout, we can simply determine the number of columns needed. Let’s add a wrapper class so that we can center our content on the screen.
@mixin breakpoint($min) {
@media (min-width: fixed($min) + em($grid-gutter)) {
.wrapper {
width: fixed($min) - em($grid-gutter);
margin-left: auto; margin-right: auto;
}
@content
}
}
Designing content with a column count gives us nice, easy, whole numbers to work with. Sizing content, sidebars or widgets is now as simple as specifying a single-digit number.
@include breakpoint(8) {
.main { width: fixed(5); }
.sidebar { width: fixed(3); }
}
Those four lines of Sass just created a responsive layout for us. When the screen is big enough to fit eight columns, it will trigger a fixed width layout. And give widths to our main content and sidebar. The following is the outputted CSS…
@media (min-width: 41.25em) {
.wrapper {
width: 38.75em;
margin-left: auto; margin-right: auto;
}
.main { width: 25em; }
.sidebar { width: 15em; }
}
Demo
I’ve created a Codepen demo that demonstrates what we’ve covered so far. I’ve added to the demo some grid classes based on Griddle by Nicolas Gallagher to create a floatless layout. I’ve also added a CSS gradient overlay to help you visualize columns. Try changing the column variable sizes or the breakpoint includes to see how the layout reacts to different screen sizes.
Responsive Images
Responsive images are a serious problem, but I’m excited to see the community talk so passionately about a solution. Now, there are some excellent stopgaps while we wait for something official, but these solutions require you to mirror your breakpoints in JavaScript or HTML. This poses a serious problem for my Sass-generated media queries, because I have no idea what the real values of my breakpoints are anymore. For responsive images to work, JavaScript needs to recognize which media query is active so that proper images can be loaded for that layout.
What I need is a way to label my breakpoints. Fortunately, people much smarter than I have figured this out. Jeremy Keith devised a labeling method by using CSS-generated content as the storage method for breakpoint labels. We can use this technique in our breakpoint mixin by passing a label as another argument.
@include breakpoint(8, 'desktop') { /* styles */ }
Sass can take that label and use it when writing the corresponding media query. We just need to slightly modify our breakpoint mixin.
@mixin breakpoint($min, $label) { @media (min-width: fixed($min) + em($grid-gutter)) {// label our mq with CSS generated content body::before { content: $label; display: none; }.wrapper { width: fixed($min) - em($grid-gutter); margin-left: auto; margin-right: auto; } @content } }
This allows us to label our breakpoints with a user-friendly string. Now that our media queries are defined and labeled, we just need JavaScript to step in and read which label is active.
// get css generated label for active media query
var label = getComputedStyle(document.body, '::before')['content'];
JavaScript now knows which layout is active by reading the label in the current media query—we just need to match that label to an image. I prefer to store references to different image sizes as data attributes on my image tag.
<img class="responsive-image" data-mobile="mobile.jpg" data-desktop="desktop.jpg" />
<noscript><img src="desktop.jpg" /></noscript>
These data attributes have names that match the labels set in my CSS. So while there is some duplication going on, setting a keyword like ‘tablet’ in two places is much easier than hardcoding media query values. With matching labels in CSS and HTML our script can marry the two and load the right sized image for our layout.
// get css generated label for active media query var label = getComputedStyle(document.body, '::before')['content'];// select image var $image = $('.responsive-image');// create source from data attribute $image.attr('src', $image.data(label));
Demo
With some slight additions to our previous Codepen demo you can see this responsive image technique in action. While the above JavaScript will work it is not nearly robust enough for production so the demo uses a jQuery plugin that can accomodate multiple images, reloading on screen resize and fallbacks if something doesn’t match up.
Creating a Framework
This media query mixin and responsive image JavaScript are the center piece of a front end framework I use to develop websites. It’s a fluid, mobile first foundation that uses the breakpoint mixin to structure fixed width layouts for tablet and desktop. Significant effort was focused on making this framework completely cross-browser. For example, one of the problems with using media queries is that essential desktop structure code ends up being hidden from legacy Internet Explorer. Respond.js is an excellent polyfill, but if you’re comfortable serving a single desktop layout to older IE, we don’t need JavaScript. We simply need to capture layout code outside of a media query and sandbox it under an IE only class name.
// set IE fallback layout to 8 columns $ie-support = 8;// inside of our breakpoint mixin (but outside the media query) @if ($ie-support and $min <= $ie-support) { .lt-ie9 { @content; } }
Perspective Regained
Thinking in columns means you are thinking about content layout. How big of a screen do you need for 12 columns? Who cares? Having Sass write media queries means you can use intuitive numbers for content layout. A fixed grid means more layout control and less edge cases to test than a fluid grid. Using CSS labels for activating responsive images means you don’t have to duplicate breakpoints across separations of concern.
It’s a harmonious blend of approaches that gives us something we need—responsive design that feels intuitive. And design that, from the very outset, focuses on what matters most. Just like our kindergarten teachers taught us: It’s what’s inside that counts.


Comments
Comments are ordered by helpfulness, as indicated by you. Help us pick out the gems and discourage asshattery by voting on notable comments.
Got something to add? You can leave a comment below.
13/12/2012
Les – thank your for your article.
However, the major drawback I see with this approach is that if a device has a screen resolution that is just shy of what is needed to add another column, then you are left with quite a bit of redundant space either side.
This become particularly a concern on smaller devices where screen real estate is already at a premium.
Barry
Vote Helpful or Unhelpful
13/12/2012
Hmmm, based on the title I had hoped for another plea to ditch media queries based on browser and/or device sizes, instead focusing on the actual available space (width/height) a component has. Article 11 (Paul Robert Lloyd) mentioned this issue already, but I believe it really needs more backing to gain some traction.
More about this particular subject:
http://blog.andyhume.net/responsive-containers/
http://www.onderhond.com/blog/media-queries-based-on-elements
Vote Helpful or Unhelpful
14/12/2012
Love this concept. I love that this shifts your thinking away from specific widths and allows you to design from the content out.
I do agree with @barry above, the “fixed-width” nature of this is kind of a bummer. So… I made a few tiny changes to the codepen (see my fork here: http://codepen.io/bencallahan/full/mCwBe) so that it uses the fixed width column/gutter definitions to calculate the breakpoints but still uses percentages for the widths.
Good stuff @lesjames! Thanks for sharing.
Vote Helpful or Unhelpful
03/01/2013
The trick with @body::before@ got me thinking—if you’re going to use JavaScript based on the window size, why use media queries at all?
JavaScript has access to window size, pixel density, resize and orientationchange events, activating alternate stylesheets and setting class names on the @<body />@, and these are all techniques that have been around for a long time.
Just a thought.
Vote Helpful or Unhelpful
16/12/2012
This was a very interesting read… I still have some fundamental problems with the concept of RWD in its application to complex web apps, but this still was interesting to me. I’ll be tweeting.
My thoughts on RWD as a developer of web apps can be found here:
http://tinyurl.com/cpkhk5x
Vote Helpful or Unhelpful
14/12/2012
The 1140 grid already does this. It uses percentages and almost no media queries and it works right out of the box.
Also, the css is human-friendly.
Vote Helpful or Unhelpful
13/12/2012
I love this theory and your right, content should be king, however content is not always the sole purpose of a website.
Without going off on a tangent I have 2 main points that I think are worth considering.
1 – Always know what the purpose of your site is, what your users / visitors are and what they use and create a site that is fit for the user base and purpose.
2 – Is RWD removing choice from a user? I believe so. Would a mobile site that is responsive to a certain point with the choice for a user to select the full website, which is again responsive to a certain point better? Even I find sites frustrating when I’m forced to view content or less content on a responsive site in some cases.
Anyway, always worth a thought.
Vote Helpful or Unhelpful
13/12/2012
The title is a bit misleading as it fails to mention Sass, which this article exclusively talks about.
Vote Helpful or Unhelpful
13/12/2012
To be more precise, I meant that it detracts from what the actual message of the article is. Sorry, I should have been clearer in my first comment.
Vote Helpful or Unhelpful
Impress us