Cross Browser Web Design – CSS Positioning instead of floats


Over the years one of the most constant things i’ve been witness to in the web design world is people struggling with html layouts. They first struggle with them in their browser of choice and then they struggle making them cross browser compatible. This all comes from a design which is pixel perfect as it was laid out in photoshop.

The web designer usually starts by creating some html and then starts the complex job of using a combination of floats and clears to get everything sitting in the right places. Because of the way floats work more and more html markup ends up going in to handle all the different combination of right and left floats needed to get the layout just right. Then cross browser starts. I know it’s not so much of an issue these days but it still matters.

For the past 4 years i’ve not used this approach at all as I feel not only does it make my life so hard trying to replicate what the designer has created but it then makes my life hard tweaking everything for all the different browser engines. Instead i’d used an approach that is cross browser compatible but for some reason large portions of the web design community don’t believe in and actually seem the need to tell me it’s impossible!

Float Based Layout

So first off i’m going to tackle a really simple layout with floats. This is an a typical website header with a logo, navigation bar, search box and login and register buttons. Here is the really basic html

<div id="header">
    <div class="logo"></div>
    <div class="util">
        <form class="search">
            <input />
            <button>Search</button>
        </form>
        <ul class="login">
            <li>Login</li>
            <li>Register</li>
        </ul>
    </div>
    <ul class="nav">
        <li>Item 1</li>
        <li>Item 1</li>
        <li>Item 1</li>
        <li>Item 1</li>
        <li>Item 1</li>
    </ul>
</div>

What usually happens at this point is the developer floats the logo to the left then floats the util div to the right. The nav is set to clear both and then the list items inside are floated left, eventually through a combination of right and left floats and some clears you end up with something resembling the design below (graphics designers behold the beauty!!)

The actual CSS will look something like this

@CHARSET "UTF-8";

#header {
    padding: 10px;
    height: 100px;
    background-color: yellow;
}

#header div.logo {
    width: 60px;
    height: 60px;
    background-color: blue;
    float: left;
}

#header div.util {
    float: right;
}

#header div.util form.search {
    float: right;
    height: 30px;
}

#header div.util form.search * {
    float: left;
}

#header div.util ul.login {
    float: right;
    clear: right;
}

#header div.util ul.login li {
    float: left;
}

#header ul.nav {
    clear: both;
    padding-top: 20px;
}

#header ul.nav li {
    float: left;
}

The graphics designer when they created their design specified the heights of all the elements and the developer implemented those using a combination of margins and padding and clears. If this design was more complex it would start causing problems on older browsers due to their usually broken box models and various other unique behaviours.

Ultimately the biggest problem with this design is because all the positioning of elements is reliant upon the large amount of above floats and clears if the design is ever changed this CSS rapidly becomes a string of hacks and modifications to get around maybe removing some floats or adding new ones in. After a few changes the code is almost unusable and really starts to cause cross browser problems.

Generally at this point lots of bugs start creeping in and developers start making excuses and get annoyed and you enter the CSS washing machine of hell.

Position Based Layout

However…. there is an alternative approach which allows you to decouple the page elements to enable easy design changes, and which is vastly more cross browser compatible. The achieve this you need to do two things:

  1. Lose the floats
  2. Lose the whitespace

Okay lets address the first one. Most developers will now be used to absolute and relative positioning in html. Actually they are more familiar with the absolute and use a horrible combination of float and clear hacks to make absolute behave how they expect. In reality they should work like this:

Absolute – Position the element relative the top left hand corner of the first parent element that has a position set and is a block element. If no parent has a position set this defaults to body which is usually (0,0)

Relative – Position the element relative to it’s own position & (this bit is important) if no left, right, etc is specified behave as normal but set the absolute position for all immediate child elements to it’s top left hand corner

So lets say for example we have a div which has content above it so ends up 200 pixels down the page. We then have another div inside that which we have set to be absolutely positioned with a left of 0px and top of 0px. By default this child div will sit in the top left hand corner of the page as it will be positioned relative to the body. But if we set position relative on it’s parent div it sits in the top left of the parent div and nothing happens to the parent. It’s like magic.

What I generally do is set all standard block elements (div, ul, li) to have position relative by default

div, ul, li {
    position: relative;
}

Now I can use positioning inside those elements by default without any problems, the next thing I use is inline-block instead of left floats. Inline block is good because the element is not taken out of the dom height calculation, this allows for really good multi column layouts that don’t then require lots of clears to deal with. There is one caveat for good old IE, it doesn’t know what to do with inline-block, so anywhere I use an inline-block I follow it up with an IE specific hack

display: inline-block;
*display: inline;

Once this hack is no longer needed in the future i’ll just do a search and replace on the CSS to remove it. In addition to this for IE’s box model to behave you need to trigger hasLayout, which is a special hidden feature in IE which is only triggered in certain circumstances and without it you’ll need to add loads of hacks. There are number of ways to enable this but by far the easiest is the following CSS

* {
    zoom: 1;
}

This is the first part of being able to write cross browser CSS. There is one final part that nobody seems to know about, or they do know about but for some reason find the most bizarre ways of dealing with.

The problem stems from the fact that browsers render whitespace. This is the whitespace between tags and it’s really annoying. It’s the reason why you see a lot of really odd sized padding and margins in a layout when everything should really be nice and neatly organised. And there is one really easy way to fix it. All you need is a bit of regex and to post process your HTML.

In PHP this is usually quite easy, there will be some point in your code where everything is either sent to the screen like a print command (so it will already be in a variable of some type) or you will be outputting the HTML as your PHP script goes along (which is usually bad). If you are outputting you HTML you can wrap your code in the following to get it into a variable:

ob_start();
///HTML GOES HERE
$output = ob_get_clean();

Once you have your code into a variable there are a few lines of regex that can be run on it to remove the whitespace between the tags.

$output=preg_replace('/>[\n\t\r ]+</', '><', $output);
$output=preg_replace("/&([^a-z#])/", "&amp;\\1", $output);
$output=str_replace('><html', ">\n<html", $output);

With the whitespace removed all the elements on your page will sit next to each other, no more random 4 pixel gaps and no more random padding and margins! The CSS to provide the same layout as the float based one above now looks like the following

@CHARSET "UTF-8";

* {
    zoom: 1;
}

div, ul, li {
    position: relative;
}

#header {
    padding: 10px;
    height: 100px;
    background-color: yellow;
}

#header div.logo {
    width: 60px;
    height: 60px;
    background-color: blue;
}

#header div.util {
    position: absolute;
    right: 0px;
    top: 0px;
    width: 100%;
}

#header div.util * {
    text-align: left;
}

#header div.util form.search {
    position: absolute;
    top: 10px;
    right: 10px;
}

#header div.util form.search * {
    display: inline-block;
    *display: inline;
}

#header div.util ul.login {
    position: absolute;
    top: 40px;
    right: 10px;
}

#header div.util ul.login li {
    display: inline-block;
    *display: inline;
}

#header ul.nav {
    position: absolute;
    bottom: 10px;
    left: 10px;
}

#header ul.nav li {
    display: inline-block;
    *display: inline;
}

In this each element is positioned absolute in the main header div. The main benefit of this is it should look the same on all browsers, and from this point onwards with the exception of remembering to add the inline display for IE7 all the CSS you use should work the same on all browsers, saving lots of time.

 

, , , , , ,

  1. #1 by Luke on March 21, 2012 - 10:22 am

    Nice write up, makes a great deal of sense, and i have started to implement it on a current project. more please.

  2. #2 by Andy on April 6, 2012 - 4:10 pm

    The removing of whitespace regex is great. Very useful!

(will not be published)