Comments on CSS Swag: Multi-Column Lists

59 Reader Comments

Back to the Article
  1. Some of these methods will get very messy if the text of some of the list items is long enough to wrap lines. Just something to lookout for before committing to a method.

    Copy & paste the code below to embed this comment.
  2. I posted about this problem a while ago on my website. It may be relevant to study the comments I got when I asked how to do a split list suitable for Netscape 4:

    Copy & paste the code below to embed this comment.
  3. Rather than eliminating all whitespace inside a list element I prefer to just set the <li> tag to display: inline;

    This may not be suitable in all instances (if you just want a plain list say). However as this list is to be styled further with floats and possibly other display settings (on the links in each li for example) I think it is a more elegant solution.

    Copy & paste the code below to embed this comment.
  4. Could we not maintain semantic integrity, simple CSS, AND get our nice, beautiful, multi-column layout by using Javascript to convert lists into a multi-column variant?

    In this way, everything degrades to a simple list; but for virtually all users, you get your multi-column list with none of the caveats mentioned.

    Copy & paste the code below to embed this comment.
  5. It seems a little funny to use a class to uniquely identify an item.

    Classes tag an item as being part of a group. If the aim is to uniquely identify something on a page, then assigning an ID to the item is probably a better route to go.

    Copy & paste the code below to embed this comment.
  6. Boy, is that ever hairy markup… and absolutely positioning everything?

    I’d rather use multiple lists than have to break DRY like this. I mean, what a disaster when you have to add a nav item in your markup and _then_ track down and fiddle with all these numbers in your stylesheet.

    After all, as stated in the article, it’s like three lines of PHP and an associative array to have complete control. Alternatively, if the markup is sacred, have a tiny server-generated stylesheet that auto-calculates the menu item positions.

    Copy & paste the code below to embed this comment.
  7. Method 4 would be the useful when you are generating content using sever side scripting (i.e. PHP), you could have a simple little function that takes a list (i.e. from an array) and splits it into N numbers of columns by assigning class=“columnN” and class=“columnN reset” through a for loop. This removes the need to ‘tweak’ the source each time there’s an item added or removed from the list.

    Only useful for generated content, and probably only in certain situations at that, but once implemented it looks after itself, as long as you can live with presentation vs semantics use of CSS class names.

    Interesting article Paul, thanks for the different ideas.

    Copy & paste the code below to embed this comment.
  8. Like most other readers of articles on this site, I’m always interested in experimentation and exploration of issues related to the implementation of proper standards and markup. And I absolutely enjoyed the exploration of so many different options presented here.

    However, with each new example this article seemed to get farther and farther away from our true “holy grail” of generating pages that utilize the simplest, most compatible markup.

    If all the content were contained within the markup (i.e. not supplied via a CMS), my question is why not use CSS to simply style these lists manually, and avoid using OL & LI tags entirely?

    Certainly the auto-numbering of OL tags is a strong tool and like other aware developers I too long to have numbered columns that can “swagger.” However, if I’m going to build custom code on a list-by-list basis, it seems to make a lot more sense—and is ultimately much more compatible—to do so within the XHTML markup than CSS.

    Copy & paste the code below to embed this comment.
  9. Actually, Konqueror does just fine with the example in Method #3.  I would assume Safari does as well.  And Firefox handles generated content as well—it’s the counters and counter increments that it doesn’t seem to support.  Firefox 1.5 is “supposed to”:’s_New_in_Deer_Park_Alpha handle counters, but when I load the example they all show up as “1. whatever.”  It looks like it’s using the counter, but not incrementing it.

    Come to think of it, IE is pretty much the only major browser that doesn’t support the :before pseudo-element.  Unfortunately, last I looked it wasn’t on the “list of additions for IE7”:

    Copy & paste the code below to embed this comment.
  10. I found a description of what’s different about Firefox 1.5’s implementation of counter-increment.  (The release notes just say it matches an upcoming draft of CSS 2.1 rather than the current one, without saying what’s changed.)  Apparently you need to put counter-increment “in the tag’s style”: instead of in the :before pseudoelement’s style.  This still works fine in Opera.

    There is one further step to getting it to work as expected in Firefox, since “scope is an issue”: in the new way of handling counters.  Ending the LI’s parent—the OL—ends the counter’s scope unless you define it somewhere further up the tree, and you get each column starting over at 1.  If you apply “counter-reset: item” to div.outerwrap, it sets the scope so that the counter doesn’t reset on each OL, and you get continuous numbering in both Opera and Firefox 1.5.

    Copy & paste the code below to embed this comment.
  11. I can confirm “Kelson Vibber’s assumption”: that Safari handles at least some generated content (such as plain text).  It cannot handle the counters and I’m sure there are a few other things in the ways of generated content it doesn’t handle.  So it’s finicky at best.

    Copy & paste the code below to embed this comment.
  12. Nice in-depth analysis. However, there are some errors in one of your examples. Under “Wrapping a single list with CSS” you have a bunch of orphaned closing anchor tags:

      ><li class=“aloe”>Aloe</a></li
      ><li class=“berg”>Bergamot</a></li
      ><li class=“cale”>Calendula</a></li
      ><li class=“oreg”>Oregano</a></li
      ><li class=“penn”>Pennyroyal</a></li

    Otherwise, great article!

    Copy & paste the code below to embed this comment.
  13. I recently had to do something similar. I wanted to keep the HTML simple so that the list was easily editable. I relied on a combination of JavaScript and CSS. Sometimes heavy DOM augmentation is necessary to get what you want, but I don’t think it’s necessary for this particular trick.

    My method is similar to #4 in your article, but did a lot of the heavy lifting in JS. One of the benefits is that the JS will determine the height of the list and dynamically create two columns (currently it’s limited to two columns).

    I will note that one problem I encountered that I have not yet had time to fix is the list element marker (number or bullet). IE has a particularly nasty problem where the marker for multi-line list elements shows up next to the last line instead of the first (something to do with “hasLayout” I suppose). Becuase of this the CSS removes the markers, which luckily I did not need.

    The code still requires quite a bit of clean-up, I still have some improvements to make, there are some problems that I have yet to figure out, and the documentation is pretty much non-existent. Anyone interested can find a demo page at <>.

    Copy & paste the code below to embed this comment.
  14. Any easy way to force IE to render without list-items without whitespace is to just define a line-height. I use:

    li {
    line-height: 150%;

    This styles the spacing to match my paragraphs, and seems to get IE to play nice with the other browsers. It sure beats having to do line-breaks in your HTML. ;)

    Copy & paste the code below to embed this comment.
  15. Tweak this so that instead of having a unique class (.name) for each value, just have a class that indicates what column it should sort into…

    and so on, and then just toggle through those when adding to your list.  You could automate this even further by running this through a server for the numbering party (and even for defining which class each <li> would be, as the author suggests.

    Let me know if this is tomfoolery.

    Copy & paste the code below to embed this comment.
  16. In the 2nd method, what is the purpose of using ‘display : block’ for the a element?

    Copy & paste the code below to embed this comment.
  17. As Avril Lavigne puts it, “Life is complicated”. So is this. Wouldn’t it be easier just to produce the list with the server-side script?

    Copy & paste the code below to embed this comment.
  18. As “Kelson Vibber”: noted above, Firefox 1.5 beta can be made to work with Example 3. One just have to use the correct syntax. Another browser that supports this perfectly is little brother iCab 3.0. Safari (1.3 and 2.0) isn’t there yet.

    One problem to note with those various techniques (i.e. example 3): the second and third column are not perfectly aligned horizontally. Each column starts a few pixels higher or lower than the previous one. That is a consequence of the way browser round of values to the nearest pixel. On the Linux box, Firefox is correct, but Opera 8.5 was off by a few pixels. On my OS X box, the only browser to get it correctly is Firefox 1.5b.

    Copy & paste the code below to embed this comment.
  19. Great article.

    Thanks also for the insight into where the ‘jolly swagman’ term comes from.

    Copy & paste the code below to embed this comment.
  20. Paul, there’s no argument from me when it comes to the importance of using OL/UL for the purposes of defining structure and the nature of the content. And don’t get me wrong—I love this article and I’m not trying to criticize you or its content in any way. I’m just looking to further the discussion on this subject, because I think you’ve brought up a significant conern.

    Hopefully I can make myself a bit more clear in my thoughts. :)

    Wrapping OLs across multiple columns is only a “holy grail” of the *visual* presentation aspects of XHTML/CSS. Yet this barely registers on the radar if we’re talking about presenting content to a screen reader.

    More importantly, as visually pleasing as multiple-column OL may be, the truth is that when presented on-screen, it tests miserably with reagards to usability (according to Edward Tufte and NN/G). This is not to say we should abandon our little Grail quest in this regard. On the contrary, I think this brings into question how important it is that certain content be presented in numerical order.

    While this article is presented from a technical/development standpoint, but isn’t the underlying message of ALA that development and design need to go hand in hand?

    We all love CSS and we all dream of heightened standards for both development and display. But our hopes for the future shouldn’t derail us from our primary responsibility as developers, and that is delivering universally readable content that maintains visual presentation.

    Copy & paste the code below to embed this comment.
  21. Another potential methodology is to not create the content in xhtml and stick to xml and use a xslt to style things. This allows a programatically neutral backend that can be reproduced on many levels. It also lends it self to transformation to other formatting that can be machine readable (RDF or otherwise). The negative of course is it does require scripting of some sort.

    A larger is issue to consider is that it brings forth whether or not semantic markup really matters. It is no where as close to being as usable or parseable as something like RDF or some other user created xml format. I am not saying of course that we should use xml documents to return to tons of tables and the like, but rather that our consideration for what is really “semantic” or proper should be considered in context. If an application drives this example, then it seems to be a moot point IMHO. Of course, if someone had to edit the pages (be it javascript, xhtml, css or otherwise) the strategies become much more important.

    Thanks for the interesting article.

    Copy & paste the code below to embed this comment.
  22. Very interesting article. Until the CSS 3 columns module gains wide support, I still prefer to use Method 1; I think I first saw it used on Eric Meyer’s site a year or two ago. I like the simplicity of the XHTML and CSS with no extra scripting. Actually I was surprised how quick and easy it is to recognize that the sequence is running horizontally and make the visual adjustment (using numeric markers might be a different story, though). I haven’t seen any of the usability data sited earlier that suggests problems with this approach (anyone willing to site a URL?). And I haven’t used this method with item markers, so that hasn’t been a problem for me either.

    Copy & paste the code below to embed this comment.
  23. Paul,
    Isn’t that just the funniest thing! I was so excited because I had actually thought of something to post on ALA.  I was too tired to realize it was your idea!  My head is hung in shame.

    Oh well, I shall console myself with the idea that at least I’m *thinking* like an ALAer.  Sigh.

    Copy & paste the code below to embed this comment.
  24. Paul (re: “28”: ). Actually, I was wrong in pointing to example 3; that one works correctly, as far as vertical alignment is concerned. The problem applies to those examples where you use negative top margins. When you first published your experiments on CSS-D, I did some testing on this. Never could them right across browser land, except when using either pixels for values, or using a line-height like ‘1’ or ‘2’. It is all due to way browsers attempt to slice pixels or rather the way they convert to screenpixels to render on screen when the line box does not have a round number of pixels for computed height.

    Copy & paste the code below to embed this comment.
  25. Great article but I’m only brave enough to use example 1 until css 3 is more widely supported. Nice work!

    Copy & paste the code below to embed this comment.
  26. in example 6 you use different “unique” li classes to set up the margin’s. Is there really a need for unique handlers? I don’t get it. I guess in this case are 3 class-names enough.

    However, nice idea:)

    Copy & paste the code below to embed this comment.
  27. This may be incredibly pedantic but I thought an ‘ex’ was the measure of font height whereas an em is the measure of font width.

    But I found the article and subsequent comments very intersting and learnt something, so thanks.

    Copy & paste the code below to embed this comment.
  28. Thanks for a nice article, even though I don’t think I will be using the techniques. I prefer the (unobtrusive) javascript approach, with the ol or ul having a class “multiplelist” or whatever. The possible problem with multiple-line / different height-li’s seems to be catered for with a little help from the linked source in Brian Sweeney’s comment (#16).

    I can see a real potential use of multiple column lists in e.g. feature lists on product information pages.

    Copy & paste the code below to embed this comment.
  29. *Re: #41*
    Paul! I’m sorry, I was probably too quick with my comment. It’s true, I would still have to choose the way to do the actual column wrapping.

    My apologies if the rest of this post is not understandable due to my language. English isn’t my mother tongue. Please correct me or ask if anything seems unclear.

    The technique I choose would have to fulfill at least the following: it has to be degradable (“unobtrusive”), not use deprecated elements or attributes, and *not force me to manually change the js, css or (x)html* when adding or removing items to/from the list (except the manual change of adding or removing the items, of course). This removes method #2 from the list. I would use method #1 if I only used unordered lists (giving the ul a class of “multipleUnorderdList” and styling accordingly). If :before and content properties where widely supported, I would probably prefer method #3 (with the “sublists” created by js), and accept the extra list elements even though they might be unsemantic. My critique against method #5 and #6 is mainly because I would have to change the css (add classnames) on every change on the list (also, the use of classes on every element looks ugly! :).

    So I was thinking about method #4, and accept the “presentation intrusion on the content”. The columnN and reset classes would be set by js, making it easy to add or remove items. The ol would get a class “multipleOrderedList” or something similar for the js to “see” and work upon. I would still have to update the css when adding/removing items though, so I have to throw this solution away, too. This leaves me with nothing. If being forced by a customer to make a multicolumn ol, I would use something like method #6 and be angry about the solution not being dynamic. I would consider the problem unsolvable on my criterias until I saw “:before” and the “content-property” being widely supported, or read another ALA-article telling me how to do it. :)

    Still, this was a nice article. It got me thinking. Thanks. And if someone could point me to a solution that fulfills my criterias, thanks again!

    Copy & paste the code below to embed this comment.
  30. Uhu. The pseudoclass “:before” should be after the word saw in the long link in my previous post (#42). It was parsed as a textile command for “link”. I can’t see how I could miss that in the preview (which btw is a great thing!). I’m sorry.

    Copy & paste the code below to embed this comment.
  31. Paul, thanks for your explanation. But I still don’t think any of the methods will work for me (please correct me if I’m wrong!). I can see what you mean with inline styles “on-the-fly” being acceptable for the sake of function, but I definitely wouldn’t like to mix css into either the html/content file or the js-file. That would make it too hard for a designer to change the styles. Restyling the site should, in my opinion, only be a matter of replacing one or a few css files for some other. I think the only way the script should be able to modify styles is by assigning class names and ids to the elements. Then it’s up to the designer to style for it.

    But you already know that. It’s me not understanding that we were looking at two different things. You wrote an article about different (experimental) ways to handle a problem. I was looking for the perfect solution, to use in a “write once, use anywhere”-script that could automate the process for me while being modular. Your article was great, I just have to accept that it didn’t provide the solution for me. It still was some interesting reading, and your answer to my comment and questions did clear a few things up. Thanks again!

    About individual classes on the list items: Yea, it’s true I would never have to see it if I assigned them from a script. It would offend me knowing they were there ;) but I could probably accept the usage. Still, the problem of having to update the css file manually matters more to me. As I surely have said to many times now. :)

    Looking forward to see your next article, hopefully on ALA.

    Regards, Pär.

    Copy & paste the code below to embed this comment.
  32. I read this article with pleasure & pain. I don’t get it why I should declare every item an own stylesheet just to get it in columns when I can do it simple with boxes?!
    Whatever, I guess the only possibility is to enact the Firefox 1.5 to every user until we got CSS3 as a standard ;)

    It would solve all problems with dynamic cols.

    (to be honest, every browser should integrate the column-css that we can create appropriately layouts!!)

    Copy & paste the code below to embed this comment.
  33. It would seem to me that this is a clear case where Tables should be used, at least for now.

    Yes, I’ve burnt the midnight oil creating entirely table free layouts using CSS2, but sometimes I can’t help wondering if I’m being too much of a purist, or rather, a prophet.

    In a world where we constantly innovate, we can often forget the simplicity of older innovations. This can result in brilliant ideas, but more often than not, turns into an overly complex “because I can” hack.

    The simplicity of multi-column layouts in standard HTML is so profoundly accessible, I see no point in trying to complicate the issue. It is, in essense, tabular data.

    The basic table tags are accessible by the vast majority of browsers and browsing devices and can be augmented with CSS2.

    Perhaps I’m missing the point entirely ?

    Although I will attempt these various CSS2 hacks, the humble HTML table remains to me, a vision of simplicity and elegance.

    Copy & paste the code below to embed this comment.
  34. The weekend the article was released I wrote a “script to automate the process”: I then submitted it to ALA and waited for ... well till now. So that’s why this comes so late. But for anyone who’s interested, it’s done.

    Copy & paste the code below to embed this comment.
  35. If we’re talking which is more Semantically Pure™, I’m really not convinced that an attribute-encrusted nightmare of IDs beats a two-celled table.  Never mind, fer-God’s-sakes, unique _classes_.  Yeah, they’re attributes and not structure.  They also blow out the character count sonmething fierce.

    That said, I probably wouldn’t be keen on splitting an _ordered_ list into columns of any sort anyway, what with it being a colossal pain in the bum to read.  If I do, however?  Ara’s JavaScript example looks pretty good right now.

    Copy & paste the code below to embed this comment.
  36. I used multi-column method for my list but i have a problem:
    my list have many rows and when i print it, it is printed only in one page and is not divided in multi-pages and i lost the last rows.

    Copy & paste the code below to embed this comment.
  37. using the column part:

    ul li {
    column-count: 3;
    Column-gap: 1em;


    Copy & paste the code below to embed this comment.
  38. I’ve been messing with the CSS and HTML for awhile, but I can’t figure out how to get text to show up like:

    Text 1     Text 2
              Text 3
              Text 4

    and then repeat. Any help would be nice. (

    Copy & paste the code below to embed this comment.
  39. On my previous one, the spacing didn’t go through…

    Text 1 aligned left, then Text 2, 3, and 4 aligned right, with Text 1 and 2 on the same line.

    Copy & paste the code below to embed this comment.
  40. I used to design with HTML tables but then I found out the drawbacks of table and use of Divs instead. However my Float Left divs wrap underneath one another when they dont fit in the borwser’s size, for eg
    [first div] [second div] [third div]
    if you customize the size of browse to less 800 i get something like:
    [first div] /* second wraps */
    [second div] /* third wraps */
    [third div]

    Any idea as to How I need to solve this issue? Thanks.

    Copy & paste the code below to embed this comment.
  41. Nice post, one that I’ve only just stumbled upon….so I’ve read it, and im pretty sure that my navigation isn’t going to change, so I’ve opted for Method 4.

    And it works…that is until I changed it to use a bulleted list and instead of using a bullet image, I used a background image. Adding the code below to the “ul li” class works for all but the top two items in each subsequent list after the first.  Removing the “position:relative” ie/hack solves it, but then the ol’ link problem reoccurs..

    background-image: url(../images/bullet.gif);
    background-repeat: no-repeat;
    background-position: 10px;

    Any ideas how I can get round this?

    Copy & paste the code below to embed this comment.
  42. I used method 6 for a 2-column list but noticed that in IE the second column inherited the background from the parent element.  It has something to do with the negative margins as using a positive or zero margin resulted in no background for the second column.  The easiest way around this bug is to set the background to “none” for all li elements.

    Copy & paste the code below to embed this comment.
  43. You can create multicolumn lists with my “List Splitter”: application. Cool animation too.

    Copy & paste the code below to embed this comment.
  44. Sorry, commenting is closed on this article.