Articles: 843 | Categories: 148   
   
   
Home Articles Contact Us
 
 
 
 
Making Simple Work of Complex CSS Layouts (0 Comments)
Admin: Posted Date: April 4, 2010

A flexible layout, one which is happy to contort to the requirements of its contents and surroundings.Decent browser support (...or support for decent browsers, at least!).Graceful degradation in legacy browsers.

Site in an Hour

a big purple eye

Making Simple Work of Complex CSS Layouts

What are we trying to create?

  • A flexible layout, one which is happy to contort to the requirements of its contents and surroundings
  • Decent browser support (...or support for decent browsers, at least!)
  • Graceful degradation in legacy browsers
  • Something that will make the bosses and marketing types happy while still allowing even access to all customers — current and potential

The Design

Here's what our designer has given us to work with: A simple, clean design with three columns, horizontal navigation and a variable-width header. For the purposes of this demonstration, we will assume our designer has sliced any graphics into the necessarry pieces.

design mock up

Design Notes - Basic Elements

  • Simple header/banner with vertically centered text
  • Horizontal menu
  • 3 columns, with a main column which is 200% the width of the side columns

Rough Code Map

design mock up

Mark-up Plan — Containers

While it is always preferable to directly style semantic (x)html, using containers is often necessarry due to varying content and a willingness to avoid adding box-model hacks for IE<6

Which sections will require containers (divs)?

  • Header - Two images needed, therefore a container is required as an extra ‘hook’
  • Menu - not necessarry, due to ability to style <ul>
  • Columns - individual columns are required due potentially varying content on each page. If the left column was a sub-menu, a container may not be necessarry.
  • Footer - Multiple elements that need to sit horizontally aligned. A container will help with this.
  • Outer Container - IE<6 doesn't support styling of the <body>, so an outer wrap is needed.

Further Code Considerations

  • We need a 'page level' anchor menu for people using assistive technologies (AT’s)
  • We want our main content as high in the source order as possible, which is where it starts getting tricky...

    diagram on required nesting


Bring on the Code!

Now that we've planned out how we are going to make the design a reality, it's time to get get your hands dirty.

The first thing that needs to be added to our basic XHTML shell is our (empty) CSS file.

How will we import our CSS? That decision is guided by our intended browser support. Since we don't want to send any CSS to Netscape 4 or IE mac, we will use the following @import method...

CSS Negotiation

<style type="text/css" media="screen,projection">
/* backslash hack hides from IEmac \*/
@import url(default.css);
/* end hack */

Setting the Limits

I mentioned previously that we will use a wrapper div to hold our design together, but this div also fills a much more important role — it will control the scaling of the design.

% or em? BOTH!

In an ideal world, our design would expand and contract to fit within the available screen real-estate while still maintaining a legible line-length regardless of font-size or resolution.

He's gone mad with power!

Not at all (well, maybe a little); this utopian goal is achievable — even in IE5!

</style>

What does that do?

By setting the media attribute to screen,projection (note comma seperated list — do not add a space), we are locking out NN4 and also telling Opera to use this style sheet in projection mode (full-screen mode).

Because we don't want to feed IE/mac a style sheet which it cant handle, we include the ‘backslash hack’ to send that browser on its merry way.

Now we are serving our CSS to all intended browsers, while legacy browsers are left with unstyled mark-up.

Part 1: Holding it Together; Letting it Flow

div#outer {
width:94%;
min-width:40em;
max-width:70em;
}

By combining a percentage width with em min. and max. widths, we can be assured of the following:

  • Our design will fill the available screen space, but will not expand further than 70em, keeping line lengths manageable.
  • Our design will fit perfectly on an 800x600 res. screen, our minimum requirement for this site.
  • Because em is a relative unit and is controlled by the user, font-size will always take precedance over screen-width for deciding the width of the layout. If a user with a res. of 800x600 increases their font-size to 'xx-large', the design will expand and cause horizontal scroll. Is this a Good Thing™? I believe so.
  • ...and then there's IE, our backwards friend that we keep inviting to every party. IE doesn't understand min/max-width, so we will employ a great little script to add that functionality. Whilst it is far from best practice to use javascript for layout purposes, the page will still function like any other fluid-width design sans-script.

Part 2: Setting the Standard

There are three properties I set at the beginning of every style sheet; padding, margins and a base font-size.

My preferred method is to globally reset all margins and padding to zero and assign them to elements as needed. I prefer this over instances of margin:0;padding:0; all through my CSS and I have repeatedly found it to cut the development time required for CSS layouts.

For font sizing, I use the method of setting a base size for the body element and em for all other elements, starting at 1em for the content text. For more information regarding the issues relating to even font-scaling, take a look at this.

* {
margin:0;
padding:0;
}
body {font-size:90%;}

The reason the font-size property is not declared within the global ruleset is that it has undesirable affects in relation to nested elements — the deeper you go, the smaller they get!

Setting the Standard - Links

There is one part of styling links that many fail to remeber — hovering with a mouse is not the only method of focusing on a link. Many users, myself included, use the tab key to skip through links on a page. To accomodate these users, we add a visual cue to the :focus pseudo-class.

Once again, IE/PC comes to the party and leaves a horrid mess; in this instance the problem is that IE does not render the :focus pseudo-class, but it does treat :active as :focus

Here's our basic link styles, with the :focus and :active pseudo-classes added:

a {
color:#4C53E0;
}
a:focus, a:hover, a:active {
color:#EB8518;
}

If you have trouble remembering the order for these declarations; this lymeric is a great help:
Lord Vader's Former Handle, Aniken

Part 3: The Banner

Time to get serious — our designer has presented us with a design that contains a fluid-width banner with vertically centered text. Since we can't use the v-align attribute in XHTML, we'll use line-height to achieve the same effect.

XHTML

<div id="header">
<h1>GeneriCo.</h1>
</div>

CSS

#header {
background:#EBEBE9 url(img/banner-bg.jpg) repeat-x left bottom;
}
#header h1 {
font:bold 3em/2.5 "Lucida Bright", Georgia, Times, serif;
background: transparent url(img/banner.jpg) no-repeat right bottom; 
}

By using line-height to achieve even vertical padding and attaching our non-repeating image (the buildings) to the bottom right corner of the h1, we have reliable vertical spacing and image placement regardless of font-size. Get used to that term, I love anything that works regardless of font-size.

Part 4: Navigation

The design for the menu is reasonably simple, so I won't go into excessive detail.

Here is the basic CSS:

ul#nav {
list-style:none;
text-align:center;
background:#fff;
}
#nav li {
width:25%;
float:left;
display:block;
text-align:center;
background:#EAF0E6 url(img/mnu-btm.gif) repeat-x left bottom;
}
#nav a {
display:block;
font:bold 1em/1.8 'Lucida Grande', Arial, tahoma, verdana, sans-serif;
text-decoration:none;
background:transparent url(img/mnu-top.gif) repeat-x left top;
}

Each of the four menu items is assigned a width of 25% and floated, causing them to line up horizontally. To create the button effect in from the intial design, two small gradient images are tiled along the x-axis of the link and its parent li. Once again, line-height is employed for the purpose of vertical alignment.

Part 5: Columns

As we discussed earlier, to place our content in the optimal position within the source (ie: as early as possible) we will need to add an extra div (hereby known as #sub), which wil be floated to the left. The first div within #sub will be floated right and will then sit in the center of the comlete layout.

Our design dictates the following widths for the columns:

layout map
  • #left: 25%
  • #center: 50%
  • #right: 25%

Columns Continued

Simple, right? Not quite — because of our newly aquired #sub wrapper, we have to calculate the widths of #left and #center according to their parent element.

layout map
  • #sub: 75%
  • #left: 33%
  • #center: 66%
  • #right: 25%

While it may seem confusing at first, it's actually pretty straight forward. Our #sub wrap takes up 75% percent of our content area. Within, #center takes up 66% of it's parent (#sub), which translates to 50% of our content area. #left fills the remaining 33% of #sub and #right is assigned a width of 25% due to the fact it is not nested within #sub, so it's width is calculated accroding to our entire content area.

To avoid the need for box-mdel hacks (which become nightmare-ish when dealing with this sort of layout), I have reduced the width of each column slightly, using the remaining gaps between them as the column ‘gutters’.

Part 6: Content

Now that we've got three well-proportioned columns, it's time to fill them up with content. The main point I want to make regarding content is in relation to units of measure for padding and margins.

The best way to achieve a consistant design regardless of resolution or font size is by using percentage values for all horizontal spacing and em for all vertical spacing.

Here is a simple example:

p { margin:1em 5%; }

Enhancing Legibility

By default, web text is rather messy. The leading (line-spacing) is tiny and the words are crammed together in an unappealing manor. Here's how we fix that:

#sub, #right {
font: 1em/1.5 'Lucida Grande', arial, verdana, sans-serif;
word-spacing: 0.1em;

By defining our word-spacing in em, we are assured of consistency regardless of font size.

Part 7: Left Column

left column

The left column contains an image which our designer assures us draws the eye towards the 'Catalogue' link in the menu.
We think he's had one too many lattes, but we'll keep him happy anyway.

This image is assigned as the background for #left and is anchored to the top right corner.

To overcome the problem of 'running out of image' as the column expands vertically, we asked our designer to make up a version of the image which fade to white at the left edge. While the gradient can often be less than ideal visually, I find this approach to be good balance between having 'image to spare' and saving every bit of bandwidth.

Part 8: Right Column - Form Madness

Forms provide a wealth of styling hooks without extraneous mark-up, so that isn't a concern. What is a concern is the width of the input elements. Once again, our simple rule of setting all horizontal measures using % saves the day.

To make maximum use of the space available, we will assign a width of 98% to the text inputs, with the extra 2% allowing for padding within the form.

To differentiate between various input types, we have to add a class attribute (.txt in this case) — this will not be necessary once all common browsers support the full CSS2 selector set.

The ideal method of differentiating between input types would be to use attribute selectors:

input[type=text] { /* text input styles */ }
input[type=submit] { /* submit input styles */ }

Part 9: The Footer

The footer is a simple affair, with a horizontal menu and a short copyright notice.

Contrary to the main navigation, the footer nav. uses inline lis to minimise the horizontal space required. Because we don't add presentaional mark-up, the 'pipe seperator' is emulated using a CSS border.

Here is the CSS for the footer menu

#footer ul { list-style:none; margin-top:0.7em; } #footer li { display:inline; border-right: 1px solid #C8DCC2; padding:.3em 2%; } Although this breaks our strict grid layout, it also reduces the probability of the copyright notice causing the menu to line break.

Part 10: Adding a Behaviour Layer

Semantic XHTML is thing of beauty, perhaps; but a dash of unobtrusive javascript is a great way to tip the usability scales in your favour.

Text inputs often contain examples of intended input or further instructions relating to the field. This used to be required due to early browsers having issues with empty input elements.

A common addition to pre-filled input elements is this.select() attached to the onfocus event — this causes all text within the field to be selected upon focus of the element, allowing the user to start typing immediately without first deleting the existing content.

If javascript is disabled, the user will need to first delete the existing content, so why don't we use javascript to add it in the first place?

Behaviour Layer — Continued

This script will do exactly what we're after — add the filler text and also attach the onfocus behaviour.

function prepInput( id , text ) {
//check that getElementById is supported and element exists
if (!document.getElementById(id)) { return false; }	
var elem = document.getElementById(id);
elem.setAttribute('value', text);
elem.onfocus = function() {
this.select();
}
}

To call the function, we add this script block to the head of the page:

<script type="text/javascript">
// <![CDATA[
window.onload = function() {
prepInput('searchQ', 'Enter search term');
prepInput('subEmail', 'you@youraddress.com');
}
// ]]>
</script> 

But we can take this one step further...

More Behaviour Layer

Another nice usablilty feature of CSS is the ability to add styling chages for currently focused form elements using the :focus pseudo-class. IE, of course, can't cope with such advanced CSS (bahaha!), but we can make it obey our commands by addinga few lines to the existing script

function prepInput( id,text ) {
if (!document.getElementById(id)) { return false; }	
var elem = document.getElementById(id);
elem.setAttribute('value', text);
// store class name
var origClass = elem.className;
elem.onfocus = function() {
// append 'focus' to class, emulates :focus pseudo-class for IE 
this.className += " focus";
this.select();
}
// return class name to origClass after element loses focus
elem.onblur = function() {
this.className = origClass;	
}
}

Then in our CSS, we have this:

input:focus, input.focus { border-style:inset; }

Done!

Anything Else?

So it's fluid, it's elastic, it's simple and it works. Did we miss anything, or have we met all our goals?

We've written clean, accessible code, we've made a flexible layout that will accomodate any combination of resolution and font size, so I guess we get the WAI-AAA stamp of approval...

Wrong!

There's one crucial element that our designer overlooked, one which we then also overlooked — contrast.

There isn't sufficient contrast between the content header anxc the white background, or between the menu text and its background.

Accessible code does not equal accessible design.

For more details on specific sections of the CSS, XHTML and Javascript, please refer to the source code. It is heavily commented and includes URLs of further reading.

 

 

 

 
 

 

 

 

 

 

 

 

 

 
 
Add a Comment:
 
(You must be signed in to comment on an article. Not a member? Click here to register)
   
Title:

Comments: