Cross-Browser Inline-Block

Ah, inline-block, that elusive and oh so tempting display declaration that promises so much, yet delivers so little. Too many times have I received PSD files like this:

Gallery Design

and begin to cry.

Normally, this type of layout would be a cakewalk. Fixed width, fixed height, float:left and you’re done. Buuuuut, the design needs to work with variable amounts of content, which means if one of these blocks has more content than the others, it will break the layout:

Broken Layout with float:left

Because the first gallery item is taller than the rest, the 5th item is floated left against it instead of below it. Basically we want a layout with the flexibility of a table, but proper, semantic markup.

We start with a simple page with an unordered list and display set to inline-block:

<ul>
    <li>
        <h4>This is awesome</h4>
        <img src="http://farm4.static.flickr.com/3623/3279671785_d1f2e665b6_s.jpg"
        alt="lobster" width="75" height="75"/>
    </li>
...
<ul>

<style>
    li {
        width: 200px;
        min-height: 250px;
        border: 1px solid #000;
        display: inline-block;
        margin: 5px;
    }
</style>

And it looks ok in Firefox 3, Safari 3 and Opera:

Step 1

Obviously, something is wrong with the vertical alignment. Well, not exactly wrong, because this is the correct behavior, but it’s not what we want.

What’s going on here is the baseline of each <li> is being aligned with the baseline of the parent <ul>. What’s a baseline, you ask? A picture is worth a thousand words:

Baseline

The baseline is the black line running through the text above. Putting it as simply as possible, the default vertical-align value on inline or inline-block element is baseline, which means the element’s baseline will be aligned with its parent’s baseline. Here’s the first inline-block attempt with baselines shown:

Baseline illustration

As you can see, each baseline is aligned with the baseline for the text ‘This is the baseline’. That text is not in a <li>, but simply a text node of the parent <ul>, to illustrate where the parent’s baseline is.

Anyway, the fix for this is simple: vertical-align:top, which results in a great looking grid:

Inline block 2

Except it still doesn’t work in Firefox 2, IE 6 and 7.

inline-block-ff2

Let’s start with Firefox 2.

Firefox 2 doesn’t support inline-block, but it does support a Mozilla specific display property ‘-moz-inline-stack’, which displays just like inline-block. And when we add it before display:inline-block, FF2 ignores that declaration and keeps -moz-inline-stack because it doesn’t support inline-block. Browsers that support inline-block will use it and ignore previous display property.

<style>
    li {
        width: 200px;
        min-height: 250px;
        border: 1px solid #000;
        display: -moz-inline-stack;
        display: inline-block;
        vertical-align: top;
        margin: 5px;
    }
</style>

Unfortunately, it has a small bug:

Inline Block in Firefox 2

Honestly, I don’t know what causes this bug. But there is quick fix. Wrap everything inside the <li> with a <div>.

<li>
        <div>
            <h4>This is awesome</h4>
            <img src="http://farm4.static.flickr.com/3623/3279671785_d1f2e665b6_s.jpg"
            alt="lobster" width="75" height="75"/>
        </div>
</li>

This seems to ‘reset’ everything inside the <li>’s and makes them display appropriately.

Inline block 2

Now, on to IE 7. IE 7 does not support inline-block, but we can trick it into rendering the <li>s as if they were inline-block. How? hasLayout, a magical property of IE that allows for all sorts of fun! You can’t set hasLayout explicity on an element with hasLayout:true; or anything easy like that, but you can trigger it with other declarations like zoom:1.

Technically, what hasLayout means is an element with hasLayout set to true is responsible for rendering itself and its children (combine that with a min-height and width, and you get something very similar to display:block). It’s kinda like magical fairy dust you can sprinkle on rendering issues and make them disappear.

When we add zoom:1 and *display:inline (star hack to target IE6 & 7) to the <li>s, we make IE 7 display them as if they were inline-block:

<style>
    li {
        width: 200px;
        min-height: 250px;
        border: 1px solid #000;
        display: -moz-inline-stack;
        display: inline-block;
        vertical-align: top;
        margin: 5px;
        zoom: 1;
        *display: inline;
    }
</style>

inline-block-ie7

Phew! Almost done. Just IE 6 left:

inline-block-ie6

IE 6 doesn’t support min-height, but thanks to its improper handling of the height property, we can use that instead. Setting _height (IE6 underscore hack) to 250px will give all <li>s a height of 250px, and if their content is bigger than that, they will expand to fit. All other browsers will ignore _height.

So after all that work, here’s the final CSS and HTML:

<style>
    li {
        width: 200px;
        min-height: 250px;
        border: 1px solid #000;
        display: -moz-inline-stack;
        display: inline-block;
        vertical-align: top;
        margin: 5px;
        zoom: 1;
        *display: inline;
        _height: 250px;
    }
</style>

<li>
        <div>
            <h4>This is awesome</h4>
            <img src="http://farm4.static.flickr.com/3623/3279671785_d1f2e665b6_s.jpg"
            alt="lobster" width="75" height="75"/>
        </div>
</li>

94 responses

  1. Fred Wenzel wrote on :

    Thanks for the elaborate post on inline-block — but honestly, you’ve got to be kidding me. What bad have we done, having to suffer through this on a daily basis? ๐Ÿ˜‰

  2. Jeff Balogh wrote on :

    It’s very cool to see not just the fixes, but an explanation of what’s going on. And when can I borrow your handbook of hacks?

  3. Eli wrote on :

    Wow… -moz-inline-stack. The reason that you need the extra div is that you’re using what’s essentially an XUL stack element. I’m actually rather surprised it works reliably.

  4. Dao wrote on :

    What -moz-inline-stack did is not a bug — it’s what stacks do. See https://developer.mozilla.org/en/XUL_Tutorial/Stack_Positioning

  5. jan wrote on :

    Thanks mate!

  6. marcoos wrote on :

    You don’t need -moz-inline-stack and the additional div, Gecko 1.8 has -moz-inline-box which is more-less like inline-block. ๐Ÿ™‚

    Also, old IEs do support inline-block on elements that are originally inline (like span).

  7. Andrew wrote on :

    Wow, thanks for the great post and workarounds. I’m finally grokking the possibilities of inline-block. ๐Ÿ™‚

  8. Brian wrote on :

    inline-block also works in IE8.

  9. chris wrote on :

    really, that looks pretty ugly, design wise. I’d just make them all a fixed height… truncate the text if you have to.

  10. Reality Sammich wrote on :

    Good thing you don’t just use a table, since it’s TABULAR data and all.

  11. jimmy jj wrote on :

    Thanks – you’ve perfectly illustrated why tables ARE the answer. Pages and pages of complicated, browser-specific hacks in order to come up with something that’s visually uglier than a table (cause at least the row heights would align in a table).

    Go ahead, keep convincing yourself that your “pure” markup is somehow better than tr/td’s – meanwhile i’ll keep running circles around you by coding up better looking pages faster, better looking and easier to understand.

  12. Michael wrote on :

    When will be the day web-browsers will behave the same way?
    i doubt it will be in our life-time ๐Ÿ™

  13. Ronny wrote on :

    Nice explanation. I wouldn’t spend time doing hacks for that old shitty Firefox 2 anymore though, it’s market share has dropped enough to finally neglect it.

  14. Tom B. wrote on :

    Nice, but if you want your code to be validated you need some more tweaking here:

    1. there are better more standard ways to set hasLayout in IE7 then zoom:1 and according to http://www.satzansatz.de/cssd/onhavinglayout.html “min-height” is enough, if it dosn’t (because of display:inline) then overflow:hidden or position:fixed should do the trick.

    2. instead of using “_height” you can use the fact that IE6 ignores “!important” when the same property is declared twice for the same selector.
    so “height:auto !important; height:250px” can replace the non-standard underscore hack.

    3. did you try to set “display:inline” before the “-moz-inline-stack” decleration? it is supposed to work just like the trick you do with FF2 ignoring “display:inline-block”

    btw – how important it is to still support FF2?

  15. Renato Carvalho wrote on :

    Wow! It seems a great solution, thank you for sharing…

    I’ll play a little bit with you code ๐Ÿ˜‰

  16. James John Malcolm wrote on :

    Hey great work on display:inline-block! It’s the first time I’ve seen such a comprehensive solution:)

    One thing though: IE targeting is best done with Conditional comments.* It’s far neater, cleaner and has some chance of not messing up the future.

    * http://www.quirksmode.org/css/condcom.html

  17. AndyW wrote on :

    The sooner they get inline-block working properly in all browsers the better – the functionality has worked on images for years so how hard can it be to replicate that functionality for other elements?

    Sometimes I think about giving it all up and becoming a farmer.

  18. Francois wrote on :

    Why all this hacky mess, and not simply table, tr, td, for things who looks like tabular data …

  19. George wrote on :

    -moz-inline-box wouldn’t work for Firefox 2? Seems a bit messy to have to add markup to make it work, and I thought -moz-inline-box would have done the trick. Usually does for me, anyways. Let me know why that’s wrong ๐Ÿ™‚

  20. Kai Chan Vong wrote on :

    Loving the description for fixing (*cough* hacking) required for IE6/7.

    hasLayout is kinda like LOLcat talk vs. proper use of the English language. Cept not funny after the 100th time.

  21. ionut popa wrote on :

    nice explanation

  22. Rich wrote on :

    Wow – exactly the problem I was trying to fix today! Useful and informative.

  23. Chris Snyder wrote on :

    Very clever, and even heroic.

    But seriously, I got the assignment at the same time as you, and I used a table. Then I left work, ate a cheeseburger, and was well into my second pint of beer while you were still looking up that IE6 _height hack. Ok not really, as I had to write a little more logic to insert the s in the right places, but I was at least done with the cheeseburger.

    When I look at that design comp, I don’t see an unordered, bulleted list. I see a table of thumbnails and descriptions. From my point of view, table IS a semantic solution, and it has the wonderful side effect of being both backwards and forwards compatible with any web browser ever produced.

    I totally appreciate the value of what you’ve done, and I definitely learned some things about inline-block that I never knew, but jumping through these hoops to avoid a when the design is really a table? No thanks.

  24. Jeff Milner wrote on :

    I thought you were looking for “proper, semantic markup”. The CSS above does not validate.

  25. Tim wrote on :

    FINALLY, something to bookmark. I’ve been keeping them stashed away in the back of my head, but one of them generally escapes me every time I need this. Many thanks!

  26. Lucian Lature wrote on :

    Did you try to use the Dustin Diaz’s “min-height fast hack”?…I think it is a more elegant solution than using the “underscore hack”: http://www.dustindiaz.com/min-height-fast-hack/

  27. Anraiki wrote on :

    Eww, you just did the nasty of the nasty in cross browser CSS styling.

    If anything, I would have done it the lazy way:

    -Set a Character Limit.
    -Overflow: Hidden.

  28. Foamy wrote on :

    Or… wait for it … you could just use a table.

    Granted if the content is the entire page, then it would violate the rules on using tables for layout.

    However, if the content is–as it appears to be–*part* of the larger page, then using tables to layout that *portion* of the content is acceptable to me and more importantly to my sanity.

    http://www.w3.org/TR/html401/struct/tables.html#h-11.1

  29. Jon B wrote on :

    Can I ask, why -moz-inline-stack and not -moz-inline-block?

  30. Nosredna wrote on :

    >>What bad have we done, having to suffer through this on a daily basis?

    Obviously, we were all wicked, wicked children.

  31. at9t wrote on :

    Thanks for sharing this trick.

  32. Maximilian Bรถhm wrote on :

    Now I only miss a js-solution for creating equal heights fรผr each li dependent on the heighest li ๐Ÿ™‚

    Max

  33. sunil wrote on :

    This very good. Especially on galleries

  34. Jay wrote on :

    This reminds me of the age-old (internet years anyway) table vs. CSS debate. Should we always go to great lengths to use CSS even if something that Looks like a table can be implemented using a table very easily? Forms can look like tables (not all of them), and this does too. Hey, we’ve even got a table header here.

    Pro table: it’s easier, will scale nicer, and we’re setting fixed widths anyway;
    Pro CSS: the number of columns is a presentation issue, and is not supposed to be hardcoded in the HTML

    I guess it’s a trade-off between slightly non-standards-compliant ‘hacky’ code, and content/presentation separation purism.

  35. Neil Rashbrook wrote on :

    Basically the special case of the behaviour of a stack when it contains a single element is to size itself to the same size as that element or to stretch the element to its size depending on CSS size constraints, which is exactly what you need to simulate an inline block.

  36. djerdos wrote on :

    Easier to use a , innit?

  37. Emma wrote on :

    Surely setting a fixed width will trigger hasLayout, without requiring zoom:1 as well. But thanks for a great post, should help me fix a legacy bug.

  38. Felipe wrote on :

    May I suggest … Hacks like *display: inline; and _height should disappear and be replaced by conditional comments calling stylesheets only parsed by IE.
    That being said, -moz-inline-stack is a very interesting discovery I’m impatient to test, thanks!

  39. Magnar wrote on :

    How about -moz-inline-block to avoid those extra divs?

  40. karf wrote on :

    There is one weak point in this method. If you want pixel-perfect spacing between boxes, you need to control word-spacing somehow. It depends on source code formatting – if you have some white space between LI tags, then the boxes will be separated by normal space depending on specified font and font size. This is very dificult to eliminate across browsers. The only safe method I have found is to add { font-size: 0; letter-spacing: -1px; } on UL element and then revert it on LI elements (but then font-size can only be specified in px).

  41. Daniela wrote on :

    @jimmy jj, Francois, Chris Snyder, Foamy, Jay,

    1. Although I’m all for using tables when tabular data is to be displayed or when I’m too lazy to think CSS for a small thing I can do with a simple table, this is *not* tabular data. Where would you place row and column headers? This is a list of images. You don’t *have* rows and columns, you could place they one beside another and you’ll have exactly the same content semanticly* speaking (*is that a word?).

    2. What would you do if you have to implement a sortable? That’s exactly what happened to my layout of a photo album. First iteration it was only a list of photos, easy as cake, I used a table and off we go. But then they asked me to implement sorting of photos. You cannot use a table for that. Think about for a minute then think again if a table makes sense.

  42. Gmyster wrote on :

    For those who think table layout of this design is proper I have this to say… maintainability…. let’s say that your boss all of sudden says I want this to be 2 columns not 4… so you go into your html and change your tables… then he later says no I want it 6 columns… you then have to go into your html again to change it… this can go on and on… And since I coded it in a list with css controlling layout all I have to do is change one line of css… I don’t have to fuss with however many HTML pages I have with this code.

    If the design above is truly tabular data then the html should be coded with a table, however the information above is not tabular data. Therefore the implementation on this site is correct. Please take a look at why tables suck for layout here : http://www.hotdesign.com/seybold/everything.html

  43. matt wrote on :

    It works, but is definitely not pretty…I would agree completely with what Chris had to say: http://blog.mozilla.org/webdev/2009/02/20/cross-browser-inline-block/#comment-188047

    Either truncate, or use jquery/JS to make all the li’s have the same height as the tallest item, in conjunction with the inline-block fix for a nice degradation.

  44. fluidByte wrote on :

    I agree that it’s important to stick to “standards” and use CSS for everything you can, but it seems that people try to hard to hack the code and lose track of things like, I dunno – deadlines.

    Why not a table? This type of application of code is most likely going to be an issue with tabular data anyways, and that’s what tables are for.

    Or, since it’s most likely being generated dynamically – why not just throw a “clear” in there after 4 items? You’ll be using a loop, just add a counter.

  45. Eric wrote on :

    Um, this is NOT tabular data, for those table junkies in the room. There is no row-column relationship; every item is at the same hierarchical level.

    If this content is delivered from a database, the effort to create a control that will calculate and render the right number of table rows and columns is much greater than a control that will simply output a list, leaving the presentation to CSS. It’s worth the extra effort to figure out the CSS. And I’ll go for that beer a little later, when I’m done, and it will taste much better.

  46. Joe wrote on :

    @Maximilian Bรถhm: Have you looked at http://www.filamentgroup.com/lab/setting_equal_heights_with_jquery/ ?

  47. fluffyb wrote on :

    All you people suggesting these elaborate table layouts are a bunch of arcane time-wasters. By far the quickest way to do this is to save the original mock as a gif.
    Now I’ve got time for a nice lobster thermador and a bottle of bollinger while you scrabble around for your cheeseburgers.

  48. Dan Eastwell wrote on :

    The designers and business people now say they want sixteen on a row, and the image size has halved, whilst the copy has doubled in amount.

    This technique will still work.

    Actually, no, now the images are double the size, the copy’s four times as much and on the right hand side. They want the picture and text items to be stacked vertically:

    This technique will still work, as the content is separate from how it looks.

  49. Jeff wrote on :

    A very informative post, but…I agree with Reality Sammich, IMHO a table is more suited.

    The data is tabular, whether it includes images or not. And as such, should be presented to users that way. For the purist who thinks it is impeding the separation of content and presentation, the columns are a side effect of the content, not an added visual decoration. In this case, columns are part of the content, and not a presentation issue.

  50. Austin King wrote on :

    Amazing work!

    Table vs List markup: If you have the requirement that items dynamically appear and disappear (inplace editing, drag n drop, or realtime updates) then this solution would be a much better fit than a Table based layout, as you would have to do a lot of DOM manipulations as items entered and left the page.

    pedantic on:
    I disagree that a table layout would capture more of the semantics of the data; neither the columns nor the rows organize the data in a particular way. The periodic table of elements obviously meet this requirements, but a big list of T-shirt designs does not.

More comments:1 2