I learned a new CSS trick from one of the best insane inventor webdevs I know. The pieces have been out there for some time, it seems, but I hadn’t ever seen this spelled out as simply and elegantly as he did it.
Inline-block layout solves a lot of problems. It lets you do some cool stuff previously thought impossible with CSS. It makes vertical alignment work properly. And sadly, it’s supported pretty badly.
Mozilla doesn’t support inline-block at all, but they have -moz-inline-stack which is about the same. Fair enough, since no one else understands -moz-inline-block, you can just do this:
If you put inline-block after -moz-inline-stack, then Moz will start using the “right” one when it supports it.
IE supports inline-block, but only for elements that are natively inline. So, if you really want to use inline-block, you’re restricted to spans and strongs and ems, when a list or paragraph would perhaps make more semantic sense (and also degrade more nicely for non-CSS users.)
However, if you trigger hasLayout on a block element, and then set it to display:inline, it magically becomes an inline-block in IE! By using the *property hack (which I love so well), you can hide the display:inline from all non-IE browsers effortlessly.
Here’s the code, in all its brief loveliness:
Inline block elements can be vertically centered like display:table-cell, but they wrap when they get to the end of their parent. Also, it’s supported across browsers using this hack, whereas display:table-cell is not supported in IE.
This technique allows for some very interesting layout approaches that would have required a lot of very tricky use of floats previously.
If an element is inside of an inline block, and lies outside the line box, then it won’t be clickable in Mozilla. Give the child element
position:relative to correct the problem.
Elements treated this way will have hasLayout set in MSIE. This is a weird and esoteric aspect of MSIE’s CSS engine that has potentially unforeseen consequences. Beware.
If an IMG element is directly inside an inline block element in Mozilla, it will stretch to the full width of that element. Wrap all IMG tags in a block-level container element to avoid the problem.
Since inline block elements wrap and flow like inline content, that means that they also respect white space like words on a page would. That is, if there is any whitespace between two inline-block elements, then a single space will be added between two inline-block elements. If this causes a problem, you can either remove the whitespace or comment it out like so:
All in all, the caveats are pretty easy to work around, and the benefits allow for some really cool stuff that would be almost impossible or very difficult otherwise.
The 2 Faces of vertical-align
In a classic CSS blunder, vertical-align can mean 2 extremely different things, depending on whether an element is display:inline-block or display:table-cell.
Align the element’s contents according to the element’s vertical-align property. IE, if the cell’s vertical-align is set to “middle”, then vertically center the element’s contents. The height and position of the element itself is determined by the containing display:table element.
Align the element according to the element’s vertical-align property. IE, if the inline-block’s vertical-align is set to “middle”, then the element is vertically centered in the line-block. The height and position of the element’s contents are determined by the standard block-level flow rules.
While I personally believe that this was a stupendously bad and confusing approach to take, I believe that the reasoning comes from backwards compatibility. Inline blocks emulate the behavior of the IMG tag, and the vertical-align CSS property thus mimics the old valign attribute. Table cells emulate the behavior of the TD tag, and the vertical-align CSS property thus mimics the behavior of the valign attribute on TDs. In other words, in this way, CSS faithfully reproduces the sloppy errors of HTML. It would have been better to use two different properties to achieve this effect; after all, vertical-align:baseline hardly makes sense for table cells. Perhaps the inline-block type of vertical alignment would have been better called “line-position” or some such, since it is less like a vertical version of the text-align property.
But, you write code with the language you have, not the language you wish you had, and CSS is what it is, at least for the foreseeable future.
Recently, I had to achieve an effect that was extremely tricky by standard methods, but extremely easy using tables. I decided to test out a display:table approach, and then try to hack it into place for IE, since it is the only browser that does not support this approach.
The result uses a fairly large number of DIVs, but still fewer than the straight table approach, and without the semantic rubble of tables. The dialog is vertically and horizontally centered, but if you resize the viewport too small, the dialog will not be hidden permanently, due to the “collapse to fit” nature of the table display style. Doing this will an inline-block would have been quite a bit more difficult.
For IE, I used the 50/50 hack. Create a position:absolute element at top:50%. Then, create a position:relative child at top:-50%. The negative top rule on the position:relative element will be misinterpreted, and result in a vertically centered box. The downside is that IE gets a scrollbar if the viewport is less than twice the height of the dialog. But, that’s a pretty acceptable down side, in my opinion.
Sadly, unlike with display:inline-block, it doesn’t look like there’s any real consistency to support display:table across browsers. (Except, of course, using table tags.) You basically just have to hack something for IE that achieves the same effect, and which approach you use varies on the effect you’re going for. In this case, I exploited an IE positioning bug to achieve vertical centering, but other situations would require different approaches. If you’re doing complex layouts using display:table, which in a perfect world would indeed be a great way to do it, you’re going to have a lot of work cut out for you hacking away at IE.
Let’s pray that IE 8 supports display:table!