Float Columns

Float your boat to stay afloat.

Table of Contents

There have been many ways to achieve column layouts with CSS positioning. Here is the best solution for me, that gives great cross-browser compatibility, source first, any order, equal height, fluid, responsive and mobile friendly column layouts


This Web Site

What follows is a demonstration of how I would build a site that requires all of the features above in a one, two or three column layout. This site does not use these techniques; it is a one column layout which, within the one column, offers many different  layout combinations on the same page. If you want the more traditional approach used in most web site designs but which also includes all of the above mentioned features, please read on.  


Design Over-View

The idea will be to progressively enhance some raw, semantic markup to adopt one feature at a time. This will allow me to explain why we do something, how it is done and for you to see the results in real time.

As we are building this layout within my site, it will inherit many of my CSS styles. You will need to style your own Semantics yourself. Our focus will be just on the layout design.

We will begin with three sections of content in raw, semantic mark-up. This linear layout will be fixed at the bottom of the page so that you can see the design take shape as we progess.


The Actual Markup

Here is the output of the actual markup that we will use to create the layout. We will return here after each progression to see the result.

Rainbow Layout

Main Content

Most important content first

Information

Other, less important side bar content and adverts.


Notes

  Note all styles are inherited from my site style-sheets.

  Click the buttons to expand and collapse additional information. Try this now.

  It will be a good idea to collapse code buttons when the content has been read. This will avoid having to scroll too much.


Markup Progressions

Source Order

The document flows from most to least important content but we will need to present the content in any order. This is good for SEO and accessibility.

Document Outline

The document is outlined by headers for easy scanning, accessibility and better SEO. Check out my dynamic Table of Contents on different pages. This is how an assisted technology device might use headers in the document outline for page navigation.

Accessible

Without any assisted technologies, this semantic markup is accessible to all devices. We are now in a position to start progressive enhancement.

Markup Outline

An -rb suffix is being used so as not to clash with styles from my site style-sheets.

The <html> and <body> elements are being emulated with target id attributes and will be used as part of the design.

<!-- Markup -->
<div id="html-rb">
  <div id="body-rb">
    <div id="header-rb">
      ...
    </div><!--#header-rb-->
    <div id="right-rb">
      <div id="centre-rb">
        <div id="left-rb">
          <div id="content-rb">
            ...
          </div><!--#content-rb-->
          <div id="menu-rb">
            ...
          </div><!--#menu-rb-->
          <div id="info-rb">
            ...
          </div><!--#info-rb-->
        </div><!--#left-rb-->
      </div><!--#centre-rb-->
    </div><!--#right-rb-->
    <div id="footer-rb">
      ...
    </div><!--#footer-rb-->
  </div><!--#body-rb-->
</div><!--#html-rb-->
HTML5

HTML5 markup can be used instead but be aware of the issues.

  • IE8 and below need all HTML5 elements to be set to display: block;.
  • IE8 and below need all HTML5 elements to be created with document.createElement('html5element');.
  • If JavaScript is not available then you will not be able to create elements and your design will fail in these old browsers.
  • An intrimin solution would be to use both divititus and HTML5 elements
<!-- HTML5 Markup -->
<html>
  <body>
    <header class="rainbow">
      <h1>Rainbow Layout</h1>
    </header>
    <section id="right">
      <section id="center">
        <section id="left">
          <article id="content">
            ...
          </article><!--#content-->
          <section id="menu">
            ...
          </section ><!--#menu-->
          <aside id="info">
            ...
          </aside><!--#info-->
        </section><!--#left-->
      </section ><!--#center-->
    </section><!--#right-->
    <footer class="rainbow">
      ...
    </footer>
  </body>
</html>

Additional Content

In order to test a layout, you will need to add additional content to each of the columns in turn. Let's simplify this task with a little JavaScript.

  Any code blocks that have the lightening stroke, execute code can be clicked to execute the code. Test this now.

  Use the browser back button to get back to this location.

/* Allow content to be added and removed from columns */
WW.Dev.filler('content-rb', 'menu-rb', 'info-rb');
location.href = '#html-rb'; /* Jump back to layout to see and test the effect */
/* JavaScript to add additional Content */
/* At bottom of page, as I don't think I'll be reusing this code */
(function(WW){

WW.Dev = { 
	words: ['lorem','ipsum','dolor','sit','amet','consectetuer','adipiscing','elit','suspendisse','eget','diam','quis','diam','consequat','interdum'],
	filler: function() { 
		var i,l,b,r,p;
		for(i=0;i<arguments.length;i++){
			if (document.getElementById(arguments[i])) {
				p=document.createElement("p");
				p.className="text2";
				l=document.createElement("a");
				l.href="#";
				l.appendChild(document.createTextNode("Add Text"));
				l.onclick=function(){WW.Dev.addText(this);return(false)};
				p.appendChild(l);
				b=document.createTextNode(" | ");
				p.appendChild(b);
				r=document.createElement("a");
				r.href="#";
				r.appendChild(document.createTextNode("Remove Text"));
				r.onclick=function(){WW.Dev.removeText(this);return(false)};
				p.appendChild(r);
				document.getElementById(arguments[i]).appendChild(p);
			}
		}
	},
	addText: function (el){
		var s="",n,i,t;
		n=WW.Dev.randomNumber(20,80);
		for(i=0;i<n;i++) {
			s+=this.words[WW.Dev.randomNumber(0,this.words.length-1)]+" ";
		}
		t=document.createElement("p");
		t.setAttribute('class','added');
		t.appendChild(document.createTextNode(s));
		el.parentNode.insertBefore(t,el);
	},
	removeText: function (el){
		var parent = el.parentNode, i, para;
		for(i=0;i<parent.childNodes.length;i++) {
			para = parent.childNodes[i];
			// XML will store node as "p" not "P"
			if((para.nodeName == "P" || para.nodeName == "p") && para.getAttribute('class')=='added') {
				parent.removeChild(para);
				break;
			}
		}
	},
	randomNumber: function (n1,n2){
		return(Math.floor(Math.random()*(n2-n1))+n1);
	}
}; 

})(window.WW); /* end namespace */

Layout Framework

jQuery Performence

It is always good practise to cache targets in the DOM whenever they are used more than once. Normally, I would use var but the variables need to be in global scope to be in context across my code blocks. Click this code to execute.

/* Cache Selectors */
html = $('#html-rb');
body = $('#body-rb');
header = $('#header-rb');
right = $('#right-rb');
centre = $('#centre-rb');
left = $('#left-rb');
content = $('#content-rb');
menu = $('#menu-rb');
info = $('#info-rb'); 
footer = $('#footer-rb');
alert('All jQuery selectors are now cached to global variables');
Viewport

A viewport will need to be emulated.

html.css( { /* emulate the html tag */
  'position':'fixed', /* Fix html container */
  'bottom':'0', /* to bottom */
  'left':'0', /* and left */
  'width':'100%', /* full width */
  'z-index': '3000', /* Bring to front */
  'background':'#999',
  'font-size':'50%' /* Reduce size so we can see progress */
});

  Now that we have an html viewport, let's give each region a background colour so that we can identify it.

  It is always good practise to identify the regions in the linear layout that will need to be positioned in the design. We will use background colours for this purpose.

Body

The body can be used as the page wrapper by setting a fluid width and auto-centring it.

/* emulate the body tag */
body.css( { 
  'background':'#fff', /* Just so we can identify it */
  'width': '40%', /* Layout fluid width. Very small to help with this tutorial */
  'margin': '1em auto' /* Auto Center and expose html background */
});
Header and Footer

How about adding rainbow backgrounds with CSS3 (No images) to header and footer. Hey! I know this is not practical but it is the Rainbow layout.

header
  .addClass('rainbow')
  .css( {
    'float':'left', /* Using addictive floating to left */
    'width':'100%', /* Floats shrink wrap by default so needs full layout width */
    'border-bottom':'1px solid #000' /* presentational seperation */
  });
$('h1', header).css( {
  'padding-left':'1em' /* Only give internal padding (box = w+p+b+m) */
});
  
footer
  .addClass('rainbow')
  .css( {
    'clear':'both', /* Clear all floats */
    'width':'100%',
    'border-top':'1px solid #000'
  });
$('p', footer).css( {
  'padding': '0 1em 1em'
});
/* Here is the rainbow class */
.rainbow {
  background: linear-gradient(left, red, orange, yellow, green, blue, indigo, violet);
}
Right Background

Refer to the markup and note that right is at the bottom of the stack in the structure.

By default, all block elements have a transparent background. Setting a background colour on right means we are looking through all higher elements in the stack to see this new background.

right.css( { 
  'background':'#e87dea',
  'position':'relative', /* We will be using relative positioning */
  'float':'left', /* addictive floats */
  'width':'100%' /* width of whole page - waiting to be exposed */  

  /* This chops off any overhanging divs that are positioned off this bottom level background */
  /* COMMENTED OUT FOR THIS TUTORIAL
  'overflow':'hidden'	
  */
});
Centre Background

Next in the stack is centre, which will overlay right.

centre.css( { 
  'background':'#fffa00',
  'position':'relative',
  'float':'left',
  'width':'100%'
  //'overflow':'hidden'
});
Left Background

The last background in the stack is left, which will overlay centre. Remember, all content regions are inside left and will therefor remain visible.

left.css( { 
  'background':'#ff9800',
  'position':'relative',
  'float':'left',
  'width':'100%'
  //'overflow':'hidden'
});
Content Regions

To aid identification of each of the content regions we will give them pastel background colours.

Setting background colours on content columns also demnostrates the non-equal height column backgrounds issue. As will be seen when widths are set shortly.

content.css({
  'background': '#eff',
});

menu.css({
  'background': '#fef',
});

info.css({
  'background': '#eef',
});
Float Content

As the layout is using addictive floating, we need to also float all content regions in the stack. Everything is now floated left down to the footer, which clears all floats.

/* We would use a multi-selector normally but ... */
content.css( { 
  'position':'relative',
  'float':'left',
  'width':'100%'
});
menu.css( { 
  'position':'relative',
  'float':'left',
  'width':'100%'
});
info.css( { 
  'position':'relative',
  'float':'left',
  'width':'100%'
});

One Column Layout

Working within a total width of 100%, adjust column widths and relative positions to create layout and margins.

One Column

Remember, at this time, all content regions are inside the left column so moving this column will also move all content and expose the underlying centre background.

/* All content content, menu and info) is contained in left parent container */
left.css({
  'left': '10%' /* Set a left margin */
});

Reduce width to expose right margin.

/* All content content, menu and info) is contained in left parent container */
left.css({
  'width': '80%' /* Reduce width accordingly ( 100 - 10 + 10 = 80) */
});

Obviously, these background colours are just to make it clear to you what the effect of these adjustments do to the layout. We would probably leave them all transparent on a white background for this particular layout.


Two Column Layout

It should now be clear that all content columns are contained within the left background container. The next task is to move the top, left background to expose an underlying background of the second, centre column of the layout. Then each of the content regions will need to be positioned over an appropriate background column.

Two Columns

Reset left background.

left.css({
  'left': 'auto', /* Reset left margin back to default */
  'width': '100%' /* Reset to default */
});

Expose underlying background which will act as right-hand column. Notice that all of the content regions would be partially hidden outside the vewport, If we had not commented out the overflow: hidden styles. These will need to be moved back over appropriate background columns.

left.css({
  'right': '30%' /* Push left background (and all content) to the left 30% */
});

First, bring the content over left and set the width to give nice margins.

content.css({
  'left': '32%', /* return main content back to right the 30%, plus 2% for a left margin */
  'width': '66%' /* Reduce width to fit nicely in the 70% available (100-30 =) 70 - (2% left/right margin =) 4 = 66% */
});

Next, position the menu to sit in the 30% gap available to the right of left so it sits nicely over the centre background.

Remember, when floating to the left, if there is enough room, a following float will move into the gap along-side the previous floated container.

All that is needed is to make the menu less than 30% and calculate margins.

Also remember the 30% that all content has been moved to the left (outside the viewport). This has to be postioned appropriately.

menu.css({
/* Push menu to right same as content 
(32) + the content right margin (2%) + the menu left margin (2%) = 36% */
  'left': '36%',
/* width small enough to float to right of content
and expose a right margin */  
  'width': '26%' 
});

Initally, we will position content and info to be in the same column.

info.css({
  'left': '32%', /* Same as content */
  'width': '66%' /* Same as content */
});

Move Information Right

As the info content is not important, let's move it over to the right.

info.css({
  'float': 'right',
  'left': '28%',
  'width': '26%'
});

Setting overflow: hidden and the content regions to background: transparent would complete the layout. I will demonstrate this after the three column layout is completed.


Width Calculations

The calculations for relative offsets, content widths and margins have been fairly straight forward so far. As the number of columns increase, a formula might help with the calculations. Following these steps should help the process.

  1. If you need more than three columns these would need to be added to the HTML structure. We'll stick with one to three columns.
  2. For one or two columns, modify the examples shown previously.
  3. For three columns, consider what percentage you would like for each background column. Say 25%/50%/25%.
  4. Offset the centre background 25% to expose the underlying right background column.
  5. Offset the left background 50% to expose the underlying centre background column.
  6. We now have a 25% left background, a 50% centre background and a 25% right background.
  7. All the content columns have moved out of the veiwport along with the left background. They now need to be given a width that fits within a targeted background, along with a margin value each side of the content. Finally, position over the target background.
  8. Apply the following formula for each column:
/*
Relative left value =
Total left Offset (we pushed centre 25% and left 50% giving a total of 75% left of viewport)
    minus
Total content widths (that you assign) to the left of the content region (if any) you want to move
    plus
Total background columns to skip to get to target background
    plus
Content left margin
*/

Simplified, this can be abbreviated to:

/*
offset - content widths (to left)
+  skipped backgrounds
+  Content left margin
*/

Three Column Layout

Now all three backgrounds need to be exposed and each content region moved into an appropriate column.

Three Column

Let's begin by a few resets so that you can focus better on the progressions.

left.css({
  'right': 'auto',
  'width': '100%'
});
content.css({
  'left': 'auto',
  'width': '100%'
});
menu.css({
  'left': 'auto',
  'width': '100%'
});
info.css({
  'left': 'auto',
  'width': '100%',
  'float': 'left'
});

Move centre to expose right, which becomes our right column.

centre.css({
  'right': '25%' /* Push all containers to the left 25% */
});

Adjust the centre column visibility to give a 25/50/25 percent column layout.

left.css({
  'right': '50%' /* Push all containers to the left another 50% */
});

The content regions are way off the viewport now. Time to bring them back into their columns. Starting with the main content region.

content.css({
/*
offset - content widths (to left)
+  skipped backgrounds
+  Content left margin
= left value
*/
/*
(75 - 0)  =  75
+ 25
+ 2
= 102
*/
  'left': '102%', 
/* center background width minus center left and right margins (50 - 2 - 2 = 46%) */
  'width': '46%' 
});

Now for the menu region.

menu.css({
/* 
(75 - 46)  =  29
+ 0
+ 2
= 31
*/  
'left': '31%',
/* 25% background minus margins (25 - 2 -2) = 21% */
  'width': '21%'
});

Finally, the info region.

info.css({
/* 
(75 - (46 + 21 = 67)  =  8
+ (25 + 50) = 75
+ 2
= 85
*/
  'left': '85%', 
  'width': '21%' /* same as menu */
});

Any Column Order

You have probably worked this out but let's demonstrate swapping around the menu and info content regions.

info-content-menu

menu.css({
/* 
75 - 46  =  29
+ (25 + 50) = 75
+ 2
= 106
*/
  'left': '106%'
});
info.css({
/* 
75 - (46 + 21)  =  8
+ 0
+ 2
= 10
*/
  'left': '10%' 
});

Equal Height Columns

It can now clearly be seen that if we were only working with the content region, we would have backgrounds of un-equal heights. Removing these temporary backgrounds will expose equal height columns.

Equal Heights

content.css({
  'background': 'transparent'
});

menu.css({
  'background': 'transparent'
});

info.css({
  'background': 'transparent'
});

Let's get rid of the over-flowed content.

right.css({
  'overflow': 'hidden'
});

Responsive Layouts

To achieve a fluid and responsive layout, media queries can be used to change the layout properties based on the width of the device viewport.

Rather than adding media queries in this tutorial, let's take our design, complete with all of these properties added and view the final  Responsive Layout.


.