Method 1: Floating list items
Of all of the techniques I’m going to describe, this one uses the cleanest XHTML markup and some of the simplest CSS. Unfortunately, its flaws prevent it from becoming my method of choice.
XHTML:
<div> <ol ><li><a href="#">Aloe</a></li ><li><a href="#">Bergamot</a></li ><li><a href="#">Calendula</a></li ><li><a href="#">Damiana</a></li ><li><a href="#">Elderflower</a></li ><li><a href="#">Feverfew</a></li ><li><a href="#">Ginger</a></li ><li><a href="#">Hops</a></li ><li><a href="#">Iris</a></li ><li><a href="#">Juniper</a></li ><li><a href="#">Kava kava</a></li ><li><a href="#">Lavender</a></li ><li><a href="#">Marjoram</a></li ><li><a href="#">Nutmeg</a></li ><li><a href="#">Oregano</a></li ><li><a href="#">Pennyroyal</a></li ></ol> <br /> </div><!-- .wrapper -->
CSS:
ol { width: 30em; /* room for 3 columns */ } ol li { float: left; width: 10em; /* accommodate the widest item */ } /* stop the floating after the list */ br { clear: left; } /* separate the list from what follows it */ div.wrapper { margin-bottom: 1em; }
/* anchor styling */ ol li a { display: block; width: 7em; text-decoration: none; } ol li a:hover { color: #FFF; /* white */ background-color: #A52A2A; /* brown */ }
Method 2: Numbering split lists with HTML attributes
This tactic—perhaps born of a desperate desire to tame web design to be as obedient as print design—is to split the list into multiple sub-lists and arrange them side by side.
See Example 2.
HTML (deprecated):
<div> <ol ><li><a href="#">Aloe</a></li ><li><a href="#">Bergamot</a></li ><li><a href="#">Calendula</a></li ><li><a href="#">Damiana</a></li ><li><a href="#">Elderflower</a></li ></ol> </div> <div> <ol start="6" ><li><a href="#">Feverfew</a></li ><li><a href="#">Ginger</a></li ><li><a href="#">Hops</a></li ><li><a href="#">Iris</a></li ><li><a href="#">Juniper</a></li ></ol> </div> <div> <ol ><li value="11">Kava kava</a></li ><li><a href="#">Lavender</a></li ><li><a href="#">Marjoram</a></li ><li><a href="#">Nutmeg</a></li ><li><a href="#">Oregano</a></li ><li><a href="#">Pennyroyal</a></li ></ol> </div> <br />
CSS:
/* position list chunks side by side */ div.wrapper { float: left; width: 10em; } /* clear float after lists */ br { clear: left; }
/* anchor styling */ ol li a { display: block; width: 7em; text-decoration: none; } ol li a:hover { color: #FFF; /* white */ background-color: #A52A2A; /* brown */ }
Method 3: Numbering split lists with CSS generated content
If you’re splitting your list into chunks, another tool for numbering the items contiguously is CSS content generation, wherein a stylesheet causes text to appear that doesn’t exist in the XHTML markup.
See Example 3.
XHTML:
<div> <div> <ol ><li><a href="#">Aloe</a></li ><li><a href="#">Bergamot</a></li ><li><a href="#">Calendula</a></li ><li><a href="#">Damiana</a></li ><li><a href="#">Elderflower</a></li ></ol> </div><!-- .wrapper --> <div> <ol ><li><a href="#">Feverfew</a></li ><li><a href="#">Ginger</a></li ><li><a href="#">Hops</a></li ><li><a href="#">Iris</a></li ><li><a href="#">Juniper</a></li ></ol> </div><!-- .wrapper --> <div> <ol ><li><a href="#">Kava kava</a></li ><li><a href="#">Lavender</a></li ><li><a href="#">Marjoram</a></li ><li><a href="#">Nutmeg</a></li ><li><a href="#">Oregano</a></li ><li><a href="#">Pennyroyal</a></li ></ol> </div><!-- .wrapper --> <br /> </div><!-- .outerwrap -->
CSS:
/* separate lists from subsequent content */ div.outerwrap { margin-bottom: 1em; } /* position list chunks side by side */ div.wrapper { float: left; width: 10em; } /* clear float after lists */ br { clear: left; }
/* remove default spacing to promote cross-browser consistency */ ol { margin: 0; padding: 0; } /* suppress normal list item numbering */ ol li { list-style-type: none; } /* generate new item numbers that continue from one list to the next */ ol li:before { content: counter(item) ". "; counter-increment: item; }
/* anchor styling */ ol li a { text-decoration: none; } ol li a:hover { color: #FFF; /* white */ background-color: #A52A2A; /* brown */ }
Method 4: Wrapping a single list with XHTML
If you’re willing to control where the column-wrap occurs using the XHTML markup, it’s easy enough to mark each item according to which column it belongs to. An additional class name marks the first item of each column.
See Example 4.
XHTML:
<ol ><li><a href="#">Aloe</a></li ><li><a href="#">Bergamot</a></li ><li><a href="#">Calendula</a></li ><li><a href="#">Damiana</a></li ><li><a href="#">Elderflower</a></li ><li><a href="#">Feverfew</a></li ><li><a href="#">Ginger</a></li ><li><a href="#">Hops</a></li ><li><a href="#">Iris</a></li ><li><a href="#">Juniper</a></li ><li><a href="#">Kava kava</a></li ><li><a href="#">Lavender</a></li ><li><a href="#">Marjoram</a></li ><li><a href="#">Nutmeg</a></li ><li><a href="#">Oregano</a></li ><li><a href="#">Pennyroyal</a></li ></ol>
CSS:
/* separate the list from surrounding elements */ ol { margin: 0 0 1em 2em; padding: 0; } ol li { /* Stipulate the height of each item so that vertical return = items * height */ line-height: 1.2em; /* Clear the default margins & padding so we can style the list from scratch */ margin: 0; padding: 0; } /* If li position is left static, Internet Explorer disables hyperlinks in the list in all but the final column. This rule will be processed only by Internet Explorer because only IE believes that there’s a level above HTML: */ * html ol li { position: relative; }
/* horizontal position of each column */ ol li.column1 { margin-left: 0em; } ol li.column2 { margin-left: 10em; } ol li.column3 { margin-left: 20em; }
/* Bring the first item of each column back up to the level of item 1. Vertical return = items * height. Here, 5 items * 1.2em line-height = 6em */ li.reset { margin-top: -6em; }
/* anchor styling */ ol li a { display: block; width: 7em; text-decoration: none; } ol li a:hover { color: #FFF; /* white */ background-color: #A52A2A; /* brown */ }
Method 5: Wrapping a single list using absolute positioning
Because we’ve now identifiedeach item uniquely within the list, one possible approach is simply to position each item explicitly.
See Example 5.
Note that this is not a practical cross-browser solution today for ordered lists, since neither Internet Explorer 6 nor Opera 7 will display list markers when list items are styled {position: absolute;}
.
To make this work for the rest of the browsers, the entire list must be enclosed within a div
with {position: relative}
: this gives the absolutely-positioned list items a frame of reference so we can prevent them from simply flying up to the top of the page. Then it’s simply a matter of assigning vertical positioning to every element. We can do this in rows:
li { position: absolute; } li.aloe, li.feve, li.kava { top: 0.0em; } li.berg, li.ging, li.lave { top: 1.2em; } li.cale, li.hops, li.marj { top: 2.4em; } li.dami, li.iris, li.nutm { top: 3.6em; } li.elde, li.juni, li.oreg { top: 4.8em; } li.penn { top: 6.0em; }
How to edit: When the number of items per column changes, the stylesheet will need to be changed. When items are added to or removed from the list, the stylesheet must be edited to re-determine which items reside in which columns. Every new list item must be assigned a unique class in XHTML.
Absolutely positioning every item in a list is a control-freak’s dream, the sort of approach many of us took when we first came to the web from print design and hadn’t yet learned to let go. To create multiple-column lists it isn’t necessary to position every item, as Method 6 will demonstrate, but I’m including it here for the sake of completeness. It does ’break’ differently from Method 6 and that might be one basis for choosing it:
If any list item is long enough to wrap around to a second line, how does that affect the layout of the list? When list items are absolutely positioned as in Method 5, the overall layout will remain unchanged, but the list item that wraps will be overlaid by the next item in the list which will claim its position without regard to preceding text, since absolute positioning takes each item out of the flow. In contrast, in Method 6 below each column of list items descends by normal flow; a list item that wraps will push down subsequent items, lengthening the column it’s in. Because that method assumes a fixed column height, the vertical return will then be insufficient to bring the next column back up to the top, creating a staggered layout.
There may be other ramifications of absolute positioning that will affect our choice: for example, some browsers don’t permit the user to highlight text in absolutely-positioned blocks.
Method 6: Wrapping a single list using normal flow
Finally, here’s the technique I prefer to use: a single semantically-logical list whose column-wrapping is controlled entirely from CSS, relies on normal flow, and works in most modern browsers.
See Example 6.
What differentiates this method is that here we use those unique item classes to bring the first item of each column back up to the top using a negative margin-top:
li { line-height: 1.2em; } li.feve, li.kava { margin-top: -6em; }
Again, vertical return = number of items in a column ? height of each item. In this case, 5 items ? 1.2em line-height = 6em.
How to edit: when items are added to or removed from the list, the uniqueness of item class names must be maintained in XHTML and the stylesheet must be tweaked to shift items to their proper columns.
Gettin’ fancy
With a little extra styling and some background images, the list can get ready to party without breaking the multi-column flow.
See Example 7.
Where to now?
There are many ways to display a multiple-column list. As we’ve seen, many of them require a compromise of web standards or browser-compatibility, as well as some pretty hairy markup and styling. The best choice, in my opinion, is to give the XHTML markup sufficient “hooks” to allow the column-wrapping to be controlled entirely from CSS.
What we really need is for some bright bulb to come along and figure out how to do this with spare markup—and actually claim that holy grail.