Layered HTML LayoutJune 11, 2011

Another day, another Photoshop layout with drop shadows, gradients, rounded corners, whatnot, your designers gives you to slice. You do and something changes, like – column gets wider – and you slice it again, and again. And then background color changes. In the end of the day you ask yourself, isn’t there a better way? A way where you can keep all the pieces separate and change them independently, easy and fast. Well, there is.

Following I introduce you to layers in HTML and I do it backwards, I’ll show how I construct my sites and then I’ll explain how it is done in HTML & CSS.

Ok, let’s start with part of the actual design of actual website. This is what I received. Being professional slicer, you know how to handle this - top piece, center piece (set as repeatable background), bottom piece. But wait, this is resizable news box, isn’t it? And that stupid designer did vertical gradient. Hey, this CAN’T be done! Oh and by the way, before mentioning that there are -rounded-corners- etc, this layout is supposed to work even in IE6.

How does it look like?

I would like to mention that this particular layout was delivered in Indesign, so I had an opportunity not to guess but to know exact numbers (corner radiuses, stroke widths, gradient points, transparency used) of building parts and that made my work a lot easier. Following I concentrate on the right news column only.

How this is achieved?

CSS magic first

I’m using two specifically CSS-crafted div elements – layers and fills.

Layer is absolute positioned div, layer positions itself below or above the parent element and by default takes his exact size. Layer position is defined by z-index, usually, as by drawing backgrounds, you position layer below the actual content, If you need links to be work, DO NOT mess with z-index on ALL other elements (leave z-index intact). And as you can see I use !important on CSS properties forbidden to change for everything to work as intended.

div.lhl-layer {
    margin: 0!important;
    padding: 0!important;
    border: none!important;
    position: absolute!important;
    overflow:hidden!important;

    top:0; 
    left:0;
    width:100%;
    height: 100%;
}

Fill is a box that as the name suggest fills the outer box to his dimensions. On fill you need to set padding and borders. And here’s The Heureka that allows all this - CSS property box-sizing - set box-sizing to border-box! Now 100% of width and height includes padding and borders! Again margin must be 0, position relative and overflow must be hidden. You may ask what the difference between layers and fills? Fill flows naturally (no absolute positioning) and can have padding and borders, while layer cannot. Layer is the foundation, fill is the walls. Why not use box-sizing on layer too? Well, IE6 is the reason. YES everything I describe here works on IE6 too, to achieve that CSS-expressions must be used and the fewer the better is the best practice. So, let’s keep layer’s box model a traditional one.

div.lhl-fill, div.lhl-fill-width, div.lhl-fill-height, img.lhl-fill {
    margin: 0!important;
    box-sizing: border-box!important;
    -webkit-box-sizing: border-box!important;
    -moz-box-sizing: border-box!important;
    -ms-box-sizing: border-box!important;
    overflow:hidden!important;
}

div.lhl-fill, div.lhl-fill-width, img.lhl-fill {
    width:100%;
}

div.lhl-fill, div.lhl-fill-height, img.lhl-fill {
    height: 100%;
}

img.lhl-fill {
    /* image must be positioned absolute in Safari, I don't know why*/
    position:absolute!important;
}

Additionally I need an container box into where to place all fills and layers. Position absolute or relative and overflow hidden, this again is important, thanks to these settings container box accommodates everything you put inside it, it is important to set borders, padding and margin 0, otherwise width and height in % inside it will not work.

div.lhl-container {
    overflow:hidden!important;
    margin: 0!important;
    padding: 0!important;
    border: none!important;
    position: relative;
}

A propos! If you need, you can set paddings and margins and borders for layers and fills, but then you MUST set explicit widths and heights. This reduces HTML tag count, but adds CSS complexity and on most cases this is not a reasonable thing to do. Best is to let layers and fills get widths and heights “magically” from their parent elements.

Lets have some html.

First, container box. Set “master” width for container, all other widths inside this container will inherit for that one.

<div class="lhl-container" style="width: 200px;">
    ...
</div>

For our container to have height first we must place box with inside it first - float the text in. Text box is special fill, it fills 100% in width, but grows freely in height, as much text we have.

<div class="lhl-container" style="width: 200px;">
    <div class="lhl-fill-width">
        ...some text...
    </div>
</div>

Now when our container has dimensions let’s add some layers. Heres the important part:Layer must be containers child and contents sibling.

<div class="lhl-container" style="width: 200px;">
    <div class="lhl-fill-width">
        ...some text...
    </div>
    <div class="layer" style="z-index:-10"> ... </div>
    ...
    <div class="layer" style="z-index:-10"> ... </div>
</div>

It is wise to let layer intact and not add any CSS properties. The only properties you may use are background and opacity, for anything else use fills. You can do what you like with fills, except, remember, margins. How to overcome that limitation? Easy, use fill inside fill with padding. Like this:

<div class="fill" style="padding: 10px">
    <div class="fill">
        ... this fill now has 10px margin ...
    </div>
</div>

Continuing with our example, everything in HTML example code should be understandable now.

<!-- master layer -->
<div class="lhl-layer" style="z-index: -100;">

    <!- adding 2px margin for the next fill with help of this  -->
    <div class="lhl-fill" style="padding: 2px;">
        <div class="lhl-fill">

            <!-- 15% white background layer -->
            <div class="lhl-layer box-background" style="z-index: -50;"></div>

            <!-- header image on layer where height is set exactly, 
                image fills the space --> 
            <div class="lhl-layer" style="z-index: -50; height: 24px;">
                <img src="f/nws_header.png" class="lhl-fill">
            </div>

            <!-- box built using sprite image -->
            <div class="lhl-layer" style="z-index: -50;">
                <div class="lhl-fill lhl-sprite-box px2-box">

                    <!-- those 4 parts of box each have 50% width height and they 
                        are aligned to 4 different corners -->
                    <div class="lhl-layer bottom-right" style="z-index: -50;"></div>
                    <div class="lhl-layer bottom-left" style="z-index: -50;"></div>
                    <div class="lhl-layer top-right" style="z-index: -50;"></div>
                    <div class="lhl-layer top-left" style="z-index: -50;"></div>
                </div>
            </div>

            <!-- left-right gradient stretched -->
            <div class="lhl-layer" style="z-index: -50;">

                <!-- NB! this layer actually flows 24px outside of the box borders 
                from bottom, but thanks to overlay:hidden we can not see this -->
                <img src="f/nws_gradient.png" class="lhl-fill" style="top: 24px;">
            </div>

            <!-- top-bottom gradient stretched -->
            <div class="lhl-layer" style="z-index: -50;">
                <img src="f/nws_gradient2.png" class="lhl-fill">
            </div>

        </div>
    </div>
</div>

For clarity, here’s CSS behind sprite-box above

div.lhl-sprite-box {
    padding: 0;
    margin: 0;
}

div.lhl-sprite-box div.top-left,
div.lhl-sprite-box div.top-right,
div.lhl-sprite-box div.bottom-left,
div.lhl-sprite-box div.bottom-right {
    padding: 0;
    margin: 0;
    background-color:transparent;
    background-repeat:no-repeat;
    width: 50%;
    height: 50%;

}

div.lhl-sprite-box div.top-left {
    background-position: left top;
}

div.lhl-sprite-box div.top-right {
    left: 50%;
    background-position: right top;
}

div.lhl-sprite-box div.bottom-left {
    top: 50%;
    background-position: left bottom;
}

div.lhl-sprite-box div.bottom-right {
    top: 50%;
    left: 50%;
    background-position: right bottom;
}

Here, have a cake.

Finished example. Grab a stylesheet from here.

Pretty awesome, isn’t it, even if I say it by myself? And what’s more important, easily manageable. Content (text) is fully separated from background and independent, you do not have to worry about insets etc. HTML code is manageable and understandable, when compared to the usual tag soup for example in here http://www.schillmania.com/projects/dialog2/. All graphics are simple and reusable. A fully working website using this technique can be observed here, be aware, its not in English (yet), but this shouldn’t matter.

That’s it. Enjoy XXI century of HTML layout and if everything goes well, the follow-up on how to use layers with grid based layout and what great and impossible things you can do, is coming.