Tech Blog :: css

Nov 25 '11 11:59am

Migrating a static grid to a responsive, semantic grid with LessCSS

The layout of Antiques Near Me (a startup I co-founded) has long been built using the sturdy grid system (implemented in Drupal 6 using the Clean base theme). Grids are very helpful: They allow layouts to be created quickly; they allow elements to be fit into layouts easily; they keep dimensions consistent; they look clean. But they have a major drawback that always bothered me: the grid-X classes that determine an element's width are in the HTML. That mixes up markup/content and layout/style, which should ideally be completely separated between the HTML and CSS.

The rigidity of an in-markup grid becomes especially apparent when trying to implement "responsive" design principles. I'm not a designer, but the basic idea of responsive design for the web, as I understand it, is that a site's layout should adapt automagically to the device it's viewed in. For a nice mobile experience, for example, rather than create a separate mobile site - which I always thought was a poor use of resources, duplicating the content-generating backend - the same HTML can be used with @media queries in the CSS to make the layout look "native".

(I've put together some useful links on Responsive Design and @media queries using Delicious. The best implementation of a responsive layout that I've seen is on the site of FourKitchens.)

Besides the 960 grid, I was using LessCSS to generate my styles: it supports variables, mix-ins, nested styles, etc; it generally makes stylesheet coding much more intuitive. So for a while the thought simmered, why not move the static 960 grid into Less (using mixins), and apply the equivalent of grid-X classes directly in the CSS? Then I read this article in Smashing on The Semantic Grid System, which prescribed pretty much the same thing - using Less with a library called - and I realized it was time to actually make it happen.

To make the transition, I forked and made some modifications: I added .alpha and .omega mixins (to cancel out side margins); for nested styles, I ditched's .row() approach (which seems to be buggy anyway) and created a .nested-column mixin instead. I added clear:both to the .clearfix mixin (seemed to make sense, though maybe there was a reason it wasn't already in).

To maintain the dimensions and classes (as an intermediary step), I made a transitional-960gs.less stylesheet with these rules: @columns: 16; @column-width: 40; @gutter-width: 20;. Then I made equivalents of the .grid_X classes (as Clean's implementation had them) with an s_ prefix:

.s_container, .s_container_16 {
  margin-left: auto;
  margin-right: auto;
  width: @total-width;
.s_grid_1 {
.s_grid_2 {
.s_grid_16 {

The s_grid_X classes were purely transitional: they allowed me to do a search-and-replace from grid_ to s_grid_ and remove the stylesheet, before migrating all the styles into semantic equivalents. Once that was done, the s_grid_ classes could be removed. and also implement their columns a little differently, one with padding and the other with margins, so what was actually a 1000px-wide layout with became a 960px layout with To compensate for this, I made a wrapper mixin applied to all the top-level wrappers:

.wide-wrapper {
  padding-right: 20px;
  padding-left: 20px;

With the groundwork laid, I went through all the grid_/s_grid_ classes in use and replaced them with purely in-CSS semantic mixins. So if a block had a grid class before, now it only had a semantic ID or class, with the grid mixins applied to that selector.

Once the primary layout was replicated, I could make it "respond" to @media queries, using a responsive.less sheet. For example:

/* iPad in portrait, or any screen below 1000px */
@media only screen and (max-device-width: 1024px) and (orientation: portrait), screen and (max-width: 999px) {
/* very narrow browser, or iPhone -- note that <1000px styles above will apply here too! 
note: iPhone in portrait is 320px wide, in landscape is 480px wide */
@media only screen and (max-device-width: 480px), only screen and (-webkit-min-device-pixel-ratio: 2), screen and (max-width: 499px) {
/* iPhone - portrait */
@media only screen and (max-device-width: 480px) and (max-width: 320px) {

Some vitals tools for the process:

  • (for Mac), or even better, the new CodeKit by the same author compiles and minifies the Less files instantly, so the HTML can refer to normal CSS files.
  • The iOS Simulator (part of XCode) and Android Emulator (with the Android SDK), to simulate how your responsive styles work on different devices. (Getting these set up is a project in itself).
  • To understand what various screen dimensions looked like, I added a simple viewport debugger to show the screen size in the corner of the page (written as a Drupal6/jQuery document-ready "behavior"; fills a #viewport-size element put separately in the template):
    Drupal.behaviors.viewportSize = function() {
      if (!$('#viewport-size').size()) return;
      Drupal.fillViewportSize = function() {
        $('#viewport-size').text( $(window).width() + 'x' + $(window).height() )
          .css('top', $('#admin-menu').height());
      $(window).bind('resize', function(event){

After three days of work, the layout is now entirely semantic, and the stylesheet is gone. On a wide-screen monitor it looks exactly the same as before, but it now adapts to narrower screen sizes (you can see this by shrinking the window's width), and has special styles for iPad and iPhone (portrait and landscape), and was confirmed to work on a popular Android tablet. It'll be a continuing work in progress, but the experience is now much better on small devices, and the groundwork is laid for future tweaks or redesigns.

There are some downsides to this approach worth considering:

  • Mobile devices still load the full CSS and HTML needed for the "desktop" layout, even if not all the elements are shown. This is a problem for performance.
  • The stylesheets are enormous with all the mixins, compounding the previous issue. I haven't examined in depth how much of a problem this actually is, but I'll need to at some point.
  • The contents of the page can only change as much as the stylesheets allow. The order of elements can't change (unless their visible order can be manipulated with CSS floats).

To mitigate these and modify the actual content on mobile devices - to reduce the performance overhead, load smaller images, or put less HTML on the page - would probably require backend modifications that detect the user agent (perhaps using Browscap). I've been avoiding that approach until now, but with most of the work done on the CSS side, a hybrid backend solution is probably the next logical step. (For the images, Responsive Images could also help on the client side.)

See the new layout at work, and my links on responsive design. I'm curious to hear what other people do to solve these issues.

Added: It appears the javascript analog to media queries is media query lists, which are event-able. And here's an approach with media queries and CSS transition events.

Jul 16 '10 10:05pm

CSS sprites made easy with LessCSS

I'm working on a project now that involves building a Drupal theme from scratch from a designer's PSDs. It's something I don't do often - the CSS I write is usually either in a modified stock theme or cut up by someone else. So I haven't had much experience creating CSS sprites from scratch before.

Basically a sprite is a single image file containing other images that appear on the site. Each individual image (a button, logo, background, whatever) is then written in CSS with the sprite as the background image, a background-position, and a fixed width and height, so each slice of the big image appears where it's needed. The main benefit is fewer HTTP requests, so the site loads faster. (With broadband it's often not the loading itself that takes time but rather the requesting of each item.) It's also easier to manage a few sprites than dozens of little files, and it's just elegant.

The tools I'm using in this case are Photoshop and LessCSS. I know Photoshop decently well but am not an expert. LessCSS is a recent addition to my workflow, and it basically makes coding CSS a lot more efficient and intuitive with nesting, "mixins," variables, calculations, and other little shortcuts. I use for Mac, so I just save my .less files in Textmate, and they get compiled (and minified) automatically.

I'm building the sprite with a simple photoshop grid of 60px horizontally and vertically. I align each picture with the top and/or left line of a grid square. So for each picture I just have to know: 1) the grid position (not pixel position), 2) pixel width and height.

In Less this works out like this:

@mastersprite-row: -60px;
#mastersprite {
  background-image: url('/path-to-theme/images/master-sprite.png');
  background-repeat: no-repeat;
  background-position: 0px 0px;

Then for the image at the 4th position down on the grid (the first being at 0px), I do:

#element {
  background-position: 0 @mastersprite-row*3;
  width: [X]px;
  height: [Y]px;

And it just works. Beautiful.

Also part of this theming process but not related to sprites: the client had a font they wanted to use, in OpenType Font (.otf) format. I uploaded it to Font Squirrel's Font-Face Generator and got a fully browser-compatible font package, including a CSS file which I simply @import'd into my .less file and voila, the font worked.