Tag Archives: html

A few notes on Responsive Design

I’ve been working on a few responsive design projects recently and an old friend of mine asked what I had leaned on my journey, below is my response (pardon the pun):

Basically, it’s seen as bad practice to target specific devices, instead you should just target the breakpoints that make sense i.e. scale the browser down until something looks wrong, then add a media query to fix it, rinse and repeat. That way you’re future proofing yourself as it will look good at any width, even on that device that hasn’t come out yet.

Ideally you should do ‘mobile first’ design whereby you start with the mobile version (duh!) and work your way up, this ensures that you focus on the content and that you don’t get into a situation where something won’t fit the mobile version. The problem with this approach is that old versions of IE will display the mobile version, as they don’t understand media queries without including something like respond.js.

I have not been following the ‘mobile first’ approach on the projects I’ve been involved in due to the IE factor. I have instead been starting with the full width version (keeping in mind how it will respond for narrow screens) and adding media queries where required to adjust the layout for a given breakpoint:


/* All */
.main .module   { float: left; width: 33.333%; }

/* 600 and down */
@media screen and (max-width:600px) {
  .main .module { float: none; width: auto; }
}

The design process becomes an interesting one, do you show the client a full width mockup, mobile, or both? Or should this be done at the wire-framing stage? I went with showing the client a full width mockup and just keeping in the back my mind how it would respond.

I am keen to try the ‘mobile first’ approach, however I will attempt this first on a personal project so that I can nail the workflow before unleashing it on a client project.

As an aside, the css and media queries for ‘mobile first’ would look something like this:


/* Mobile */
.main .module     { }

/* 600 and up */
@media screen and (min-width:600px) {
  .main .module   { float: left; width: 33.333%; }
}

So there we have it, my notes from the trenches of responsive design.

The Solution to Fluid Inconsistencies and Equal Height Columns

Update: There is a follow up article to this one addressing issues raised in the comments.

An introduction

Being an early adopter of Web Standards back in 2001 and thanks to Jeffrey Zeldman’s Designing with Web Standards released in 2003, I strived for a life without tables and 1 pixel shims and instead one filled with semantic markup and a clean separation of content from presentation. Early on in the transition to Web Standards came frustration, the things we used to do with tables without batting an eyelid became a series of CSS hacks and erroneous markup.

To this day there are two core concepts which still make us standards freaks wince. The first is equal height columns and the second is percentage width consistency across browsers. Chris Coyier of CSS Tricks recently rounded up the current solutions for Fluid Width Equal Height Columns but all have their limitations. We have a new flexible box model in CSS3, however it will be a number of years before we can use this in practice due to the slow uptake of new browsers.

A couple of weeks ago, I decided that enough was enough and that it was time to sit down and come up with a solid answer to the problem. Combining a number of CSS tricks that I have amassed over my years in the industry, I have come up with what I believe to be a solution.

Fluid Inconsistencies

Just today I read a post by Steffan Williams on his frustrations with “Fluid Inconsistencies“, so I will address this first.

As documented by John Resig, the reason for inconsistencies with percentage widths comes down to the fact that different browsers round in different ways. Webkit rounds down to the nearest pixel, IE rounds up and Firefox does a bit of both in order to spread the difference across the containing element.

Surprisingly, Webkit’s rounding causes me the biggest problem. As an example, lets say we have 12 equal width columns butted up together. When the container is at 800 pixel in width, in an ideal world each column would be 66.666 pixels, the problem being that there is no such thing as a fraction of a pixel, so Webkit rounds each column down to 66 pixels. This gives a total width of 792 pixels meaning there is a gap of 8 pixels between the right of the container and the right of the last column.

So what are our options? Absolute positioning could work as we would manually set the left edge of each column, negating the cumulative rounding problem. The obvious problem is that absolute positioning would take the columns out of the flow of the document meaning we couldn’t have anything below them and they wouldn’t force their container to grow. What we need is absolute positioning only on the x axis so that we can position each column a specific distance from the left edge. We can achieve this effect with a bit of css trickery…

Absolute positioning only on the x axis

To simulate absolute positioning on just the x axis, we will combine floats, negative margins and relative positioning. Relative positioning is like absolute positioning in that you can specify a left and top value, however it differs in that the element is positioned relative to where it would have been without relative positioning. If we could just get all the columns to start at the same x position, we could use relative positioning from there to shift the columns into the correct positions.

Given the following markup and bearing in mind that you should normally use semantic class names instead of col1, col2 etc.


<div class="content">
  <div class="col1"><p>I am column 1</p></div>
  <div class="col2"><p>I am column 2</p></div>
  <div class="col3"><p>I am column 3</p></div>
  etc...
</div>

I turns out that with the use of floats and negative margins we can get all the columns positioned at the same starting x position.


.content        { position: relative; overflow: hidden; background: #f5f5f5; }
.content div    { float: left; margin-left: -100%; position: relative; width: 8.333%; }

Note the “overflow: hidden;” on the container, this serves two purposes. The first is to ensure it expands to the height of the tallest float (a simple clearfix), the second is so that IE, who we have established rounds up, doesn’t overlap the right edge of the container. Also note that the column width is set to 8.333% (100% ÷ 12).

At this stage we have all columns stacked on top of each other and positioned at -100%. The reason that they are now all in the same place is that when the first element is shifted to the left by 100% (due to the negative margin), the second element’s x coordinate is now 0, it in turn gets shifted left by 100% and so on.

This leaves us in a great position (pardon the pun) as we can now position the columns anywhere horizontally by just adding 100% to the desired x position.


.content .col1   { left: 100%;     background: #eee; }
.content .col2   { left: 108.333%; background: #ddd; }
.content .col3   { left: 116.666%; background: #ccc; }
.content .col4   { left: 124.999%; background: #bbb; }
etc...

View example 1

The real beauty of this technique is that the column order is independent of the order they appear in the markup. You can swap 1 with 3, 9 with 2 etc. you can even span multiple columns by doubling the width. Below is an example of changing the column order.


.content .col3   { left: 100%;     background: #eee; }
.content .col2   { left: 108.333%; background: #ddd; }
.content .col1   { left: 116.666%; background: #ccc; }
.content .col4   { left: 124.999%; background: #bbb; }
etc...

Notice how all we have done here is change the class names around.

View example 2

So there you have it, we have addressed the “Fluid Inconsistencies” problem and it works in Safari, Chrome, Firefox and right the way back to IE6. The keen eyed among you will have noticed the occasional 1 pixel gap between the columns on scaling the browser window, I will address this in the next section.

Equal Height Columns

This has long been a subject for debate and is a hot topic for the “Just Use Tables!” clan. Something caught my eye when reading Chris Coyier’s Fluid Width Equal Height Columns and that was the “Nicolas Gallagher Method”. He basically uses “:before” and “:after” pseudo selectors to add blank content to the container element which can then be styled, pure genius. The only downside to his technique however, is that you are limited to 3 equal height columns.

Based on Nicolas’s method, I found that it was possible to add “:after” pseudo selectors to the columns themselves to achieve the effect of equal height columns. The benefit of this is that we can tell the styled content to inherit the background properties of it’s parent element. This way you only have to style the column itself and all will be inherited correctly.

So how do we do it I hear you shout! Well it’s almost disappointingly simple, I will show the code and then explain what is going on.


.content            { z-index: 1; }
.content div:after  { content: ""; position: absolute; z-index: -1; background: inherit;
                      left: 0; top: 0; right: -1px; bottom: -9999px; }

As with Nicolas’s method, we setup an “:after” with blank content, position it absolutely and set it’s z-index to -1 so that it appears under the column element who’s z-index is 1. The difference comes with the actual positioning. As our column is positioned relatively, we set it’s left and top to 0 so that the top left corner matches that of the column. We set the the right to -1px to solve the occasional gap between columns as promised. The bottom value is where the magic happens, we set it to -9999px. This makes it drop way below the bottom of the column and as long as the difference in height between the tallest and shortest columns is less than that value, you are fine, a pretty safe bet I would say.

View example 3

And that’s it, equal height columns with flexible widths. We could end here, as everything works in Safari, Chrome, Firefox and IE9. In IE8 and below, the page will degrade gracefully and just not have equal height columns. We can however add IE 6/7/8 support using CSS expressions and I would recommend including the fixes with conditional comments should you need to support these versions.

Fixing Internet Explorer IE6/7/8


/* IE 6/7/8 fixes */
* html .content { height: 1%; /* IE6 hasLayout */ }
.content div    { clear: expression(innerHTML += '<u class="ieafter"></u>', style.clear = 'none', 0); }
.ieafter        { width: expression(parseInt(parentNode.offsetWidth) + 1 + 'px');
                  height: expression(parseInt(parentNode.parentNode.offsetHeight) + 'px');
                  background-color: expression(parentNode.currentStyle.backgroundColor);
                  background-image: expression(parentNode.currentStyle.backgroundImage);
                  background-repeat: expression(parentNode.currentStyle.backgroundRepeat);
                  background-position: expression(parentNode.currentStyle.backgroundPosition); }

View example 4

Things to consider

When using this method, if you need padding on columns, I would suggest adding it to an element within the column as opposed to the column itself as otherwise Webkit has two more opportunities to round down (one for the left padding and one for the right). If you really need to add padding to the columns themselves then you may want to add the following Webkit fix.


/* If we have left and right padding on columns, Safari has 2 more opportunities to round down so compensate here */
@media screen and (-webkit-min-device-pixel-ratio:0) { .content div:after { right: -3px; } } 

Something else to consider is explicitly setting the z-index for each column in order from left to right as the additional width we are giving our “:after” content has the potential to overlay the next column. With the z-indexes it looks like this:


.content .col3  { left: 100%;     background: #eee; z-index: 1; }
.content .col2  { left: 108.333%; background: #ddd; z-index: 2; }
.content .col1  { left: 116.666%; background: #ccc; z-index: 3; }
etc...

View example 5

You will notice in my examples I have used divs. These can be easily substituted for more semantic HTML5 elements such as “section” but I wanted to keep the examples simple without the need for HTML5 shivs etc. My use of ‘col1, col2′ etc. class names are purely for this example, you should use more semantic class names in real development.

I hope you like my new technique, feel free to leave a comment with any questions.

Acknowledgements

Update 08/12/10: As pointed out by Paul O’Brien in the comments, the equal height column technique shown here suffers the same side effect as the One True Layout solution, in that if you have a link to an anchor in a column, that column will scroll when the link is clicked. If you need to link to anchors in your columns, this may not be the best technique and the only other option without adding additional markup is to equalise the column height with javascript. The only benefit of this technique over the One True Layout option is that it fixes gaps between columns. I will continue my search for a solution to the anchor issue.

Super simple jQuery templating

Working with jQuery, it is common to find yourself needing some kind of templating solution. There are plenty out there, however sometimes the use-case is simple enough that you don’t really need a fully blow templating plugin.

We’ll take a task list as an example (just to be original). Lets say you have the following task objects in an array:


var tasks = [
  { id: 1, name: 'A todo item' },
  { id: 2, name: 'Another todo' }
];

Now you want to display them in an unordered list with checkboxes etc. What I like to do is create a ‘templates’ object, which contains my basic templates:


var templates = {
  taskList: $('<ul class="task-list">'),
  taskItem: $('<li><input type="checkbox" /> <span /></li>')
}

With these templates in place, it’s a simple case of cloning them when required and inserting the relevant data. jQuery’s ‘text‘ and ‘attr‘ methods are ideal for this as they handle the escaping of html entities. Based on this we can iterate over our tasks array and insert the relevant items in the dom:


var list = templates.taskList.clone();
$.each(tasks, function() {
  templates.taskItem.clone()
    .attr('id', this.id)
    .find('span').text(this.name).end();
  list.append();
});

$('body').append(list);

And that’s it, by using jQuery’s ‘text‘ and ‘attr‘ functions, you have complete control over your templates, without having to remember to escape html. Your templates can be stored away in an external javascript file and everyone’s happy.