Responsive Source-Order Shift Technique
Here's a pretty spiffy layout trick I learned this week. Let's say you find yourself with a two-column layout where you've floated both columns to get them to appear side-by-side. Maybe you've used display: inline-block
, but either way you have two columns. This looks great when the viewport is large but once it starts getting a little smaller perhaps you'd like to stack the columns on top of one another. Typically the way you would approach this is to de-float the columns (or switch to display: block
) in a media query that only takes effect once the viewport width drops below a certain threshold. The columns now each take up 100% of the width of their container and stack on top of one another naturally.
However, they now stack up in the same order as your source code with the first column in your source appearing on top and the last column in your source appearing on the bottom. Lots of times that's exactly what you are trying to achieve but sometimes you want the opposite order. For instance, maybe you have an image on the left and some text on the right. When the viewport gets small, you (or your designer/copy writer/etc.) may feel that it's more important to show the text on top, even though it comes last in the source.
This is a common design pattern and it seems like it should be a pretty easy thing to accomplish until you actually try it. So what to do? Well, at this point you could take a couple of approaches:
1. Duplicate the source and toggle the visibility of each copy
This approach would have you write out the same markup twice but re-order the source in the second markup block. Then you can style each one individually so that the first one is side-by-side and the other one is stacked. Lastly you set display: none/block
on each of your chunks of markup so that the side-by-side one shows when the viewport is large and the stacked one shows when the viewport is small. This is a common solution because it's very simple to implement, especially if you don't have a lot of markup to contend with. However, it's also a little controversial, as there is some concern about whether or not the duplicated content is harmful to SEO and/or screen readers. While I can't confirm that there is indeed a negative impact, I really can't see it improving matters so I would at least consider this approach a little risky. You also have to admit that there's something kind of dirty feeling about duplicate content - one of the best things about responsive design is that it frees us from the burden of having to create two entire sites to cover both desktop and mobile. Duplicating the content seems like a step backwards from the goal of serving one website with one set of content to all platforms and devices.
2. Use javascript
Technically you could use javascript to detect changes to the width of the viewport and then do some DOM manipulation magic to re-order the columns depending on the viewport width. window.innerWidth/innerHeight
will give you the dimensions of the viewport and you can check those each time the window resize event fires. Or even better, you could use window.matchMedia
to detect the changes. Once javascript reports that your viewport is below the width threshold, you would literally swap the positions of your elements in the DOM, using removeChild
and appendChild
to put them in whatever order you want. This works, but it's kind of a complicated solution. After all, isn't this essentially a presentational problem? Now here we are writing 20 or so lines of javascript to solve something we should really should be able to do with CSS. Also keep in mind that the extra complexity means that you may be more likely to accidentally introduce bugs while implementing this approach and because it has to wait for javascript to execute, it could wind up being slower than it needs to be. I can imagine that there are totally valid use cases for manipulating media queries with javascript, but personally I feel that this ain't one of 'em.
So what do we do? Table model to the rescue!
3. Do a source-order shift using the CSS table model
This is so simple it's actually kind of beautiful. Tables often have <caption>
elements that let you put some kind of descriptive text either on top or at the bottom of the table. We can make use of this in our small-screen media query by setting display: table
and caption-side: top
on the element that contains both of our columns. This tells the container to use the table model to display itself and, if there is a caption present, show it at the top and not the bottom. Finally, by specifying display: table-caption
on the last column, we essentially tell the browser that our last column should behave like a table caption (and thus be shown above the first column). That's it! 3 lines of CSS and we're done.
Here's a pen that illustrates the solution:
See the Pen Responsive Source Order Shift by Brad Marshall (@bmarshall) on CodePen.
Browser support
Support for this technique goes way back considering that these properties are not new at all. caption-side
, display: table
and display: table-caption
are all part of the CSS 2.1 table model and go all the way back to version 1.0 in Firefox, Chrome and Safari. Opera goes back to version 7 and even IE picked up support in version 8 so I'd feel totally comfortable using this everywhere. In the future, hopefully this kind of technique will be made obsolete by more powerful layout tools like flexbox but until then this is an easy and reliable solution to a common problem.