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:

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:

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:

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:

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:

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:

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

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:

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.

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>

Phew! Almost done. Just IE 6 left:

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>

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?
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?
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.
What -moz-inline-stack did is not a bug — it’s what stacks do. See https://developer.mozilla.org/en/XUL_Tutorial/Stack_Positioning
Thanks mate!
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).
Wow, thanks for the great post and workarounds. I’m finally grokking the possibilities of inline-block.
inline-block also works in IE8.
really, that looks pretty ugly, design wise. I’d just make them all a fixed height… truncate the text if you have to.
Good thing you don’t just use a table, since it’s TABULAR data and all.
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.
When will be the day web-browsers will behave the same way?
i doubt it will be in our life-time
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.
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?
Wow! It seems a great solution, thank you for sharing…
I’ll play a little bit with you code
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
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.
Why all this hacky mess, and not simply table, tr, td, for things who looks like tabular data …
-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
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.
nice explanation
Wow – exactly the problem I was trying to fix today! Useful and informative.
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.
I thought you were looking for “proper, semantic markup”. The CSS above does not validate.
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!
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/
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.
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
Can I ask, why -moz-inline-stack and not -moz-inline-block?
>>What bad have we done, having to suffer through this on a daily basis?
Obviously, we were all wicked, wicked children.
Thanks for sharing this trick.
Now I only miss a js-solution for creating equal heights für each li dependent on the heighest li
Max
This very good. Especially on galleries
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.
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.
Easier to use a , innit?
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.
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!
How about -moz-inline-block to avoid those extra divs?
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).
@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.
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
It works, but is definitely not pretty…I would agree completely with what Chris had to say: http://blog.mozilla.com/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.
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.
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.
@Maximilian Böhm: Have you looked at http://www.filamentgroup.com/lab/setting_equal_heights_with_jquery/ ?
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.
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.
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.
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.