Cross Browser Inline-Block

How many times have you used float:left to make a bunch of elements align horizontally? Usually this technique is used on lists (of both the ul and ol persuasion) and generally works just fine under a few preconditions. The first precondition is that you are comfortable using some form of a clear-fix hack. Either you apply overflow:hidden to the container, or you add a meaningless empty div with clear:both, or you apply some magic with the :after pseudo-class. While this isn’t a deal breaker, it is annoying.

The second precondition is that all floated elements are of the same (fixed) height. Of course, your original mock-ups with dummy data and lorem ipsum text all use the same element just repeated across the page, right? And then the client provides actual content (hopefully), and you find that some items have a really long title, or a super-tall product image. And what happens? The floats ‘snag’ items and you end up with this:

float_thumb

see fiddle

If the previous screenshot is what you going for, or both previous preconditions hold in your case, you may stop reading right now. Otherwise, read on and you’ll learn how to create this instead:

final_thumb

see fiddle

First, I must give credit where credit is due. The technique I’m about to describe (and improve upon) was covered by Ryan Doherty over at the Mozilla WebDev blog. I suggest you read his post first because I’m going to breeze through his foundation and jump into my improvements.

Foundation

Here is the HTML will be working with:


<ul>
  <li>
    <h3>Product 1</h3>
    Some product description.
  </li>
  <li>
    <h3>Product 2</h3>
    Some product description.
  </li>
  <li>
    <h3>Product 3</h3>
    Some really, super, long product description.
  </li>
</ul>

And some aesthetic styles not relevant to the inline-block layout:


ul {
  list-style:none;
  width:440px;
}
li {
  border:1px solid #888;
  -moz-border-radius:3px;
  width:75px;
  text-align:center;
  margin:5px;
}
h3 {
  font-size:14px;
  font-weight:bold;
  margin:.25em;
}
p {
  margin:.25em;
}

Compliant Browsers

The first step is to try an use the proper CSS property: display:inline-block. This property gives an inline element, block-like properties. Such as the ability to have width and margin. With just this property, we have support in Firefox 3+, Safari 3+, Chrome 1+, Opera 9+, and IE8+. Not too shabby.

li {
    display:inline-block;
}

valign_thumb

Fixing the Rest: Firefox 2

Aside from IE 6 and 7, only Firefox 2 fails to support inline-block. To get support in Firefox 2, simply add the mozilla-specific property display:-moz-inline-stack prior to the display:inline-block declaration. Non-Gecko browsers will ignore the -moz rule. Firefox 3+ supports inline-block so it will override the -moz-inline-stack rule. Using the -moz-inline-stack rule for Firefox 2 also necessitates a div be wrapped around the inline-block element’s contents. Given Firefox 2’s latest market share numbers (most give it under 1% overall), I would generally concede that messing with -moz-inline-stack and an inner-wrapping div is unnecessary. For this reason, I have removed the -moz-inline-stack rule from my Inline-Block gist on GitHub, however, you can see it in action at jsfiddle. Feel free to add it back in yourself if Firefox 2 support is necessary.

Fixing the Rest: IE 6/7

IE 6 and 7 both support inline-block natively but with a caveat; they only support it on elements that are inherently inline. Thus, block elements like div and list-item elements like li won’t apply inline-block. However, IE 6/7 has the concept of ‘layout’. (see On Having Layout). IE treats elements with layout triggered exactly the way inline-block elements are supposed to work; that is, block-level elements that are displayed inline. So for IE6/7 we reset the display to inline, and trigger hasLayout with zoom:1.

* Note: You can apply the IE 6/7 rules in any manner you wish. Conditional Comments paired with IE-only stylesheets is generally the preferred method. However, I have gone with the *hack in this case to make these utility classes copy/paste-able.

li {
    display:inline-block;
    *zoom:1;
    *display:inline;
}

The final touch is to set vertical-align:top to make the boxes line up across the top:

li {
    display:inline-block;
    *zoom:1;
    *display:inline;
    vertical-align:top;
}

final_thumb_3

Room for Improvement

So now we have our elements aligned horizontally without using floats. We have one last problem which is where I will improve on Ryan’s method. In the screenshot below I have changed the li margin to margin:5px 0; We would expect the left and right borders of each box to touch, no?

whitespace_thumb

The problem is due to the fact that white-space surrounding inline elements is displayed. Of course, this makes perfect sense. Imagine if the white-space between words in a sentence weren’t displayed! The trick is to take advantage of letter-spacing and word-spacing to counter the white-space between our inline-block elements. (This is unnecessary in IE6/7 which already ignores the white-space between boxes because the elements have hasLayout triggered and are not technically inline-block.)

Six of One…

My first attempt was to apply a negative word-spacing to the container. Word-spacing is inherited, so we must reset it to normal on our inline-block elements themselves so as to not affect their children. With word-spacing set to -1em, we have eliminated the offending white-space in Firefox and Opera.

nowhitespace_thumb

see fiddle

…Half a Dozen of the Other

In order to fix WebKit (Safari, Chrome) we must also apply negative letter-spacing. Interestingly, applying only letter-spacing actually fixes both WebKit and Firefox which means that either letter-spacing or word-spacing will work for Firefox. However, in order to appease Opera, we will apply both. see fiddle

All Together Now

I have pared down the necessary rules to a set of utility classes named ib-container and ib-block, as seen below. You can also find them in a gist on GitHub and as a fiddle on jsFiddle.

.ib-block {
    vertical-align:top;
    display:inline-block;
    *zoom:1; /* IE6/7 */
    *display:inline; /* IE6/7 */
}
.ib-container {
    letter-spacing:-.25em;
    word-spacing:-1em;
}
.ib-container .ib-block {
    letter-spacing:normal;
    word-spacing:normal;
}

Other Variations

They can be used on list items as seen in this example or any other set of sibling elements:


<div class="ib-container">
    <div class="ib-block">…</div>
    <div class="ib-block">…</div>
    <div class="ib-block">…</div>
</div>

They can be used without the ib-container class if the white-space between ib-block elements is not an issue for you:


<ul>
  <li class="ib-block">…</li>
  <li class="ib-block">…</li>
  <li class="ib-block">…</li>
</ul>

They can be nested so an ib-block element becomes an ib-container for other ib-block elements:


<ul class="ib-container">
  <li class="ib-block ib-container">
    <div class="ib-block">…</div>
    <div class="ib-block">…</div>
  </li>
  <li class="ib-block ib-container">
    <div class="ib-block">…</div>
    <div class="ib-block">…</div>
  </li>
  <li class="ib-block">…</li>
</ul>

CSS Reset

Why start with a blank slate? After years of web development and hundreds of sites, starting from scratch on each project really turns into a buzz kill. Nobody wants to spend time rehashing the same issues from site to site. So many of us have turned to CSS Resets. As we all know, CSS Resets are designed to fix cross-browser inconsistencies by rebasing all or most default styles to a common state. I’ve always had a problem with these resets. Many of the styles in these resets are never used (how often do you use q, ins, del, and table anymore, really?). Other styles are completely overridden. I would wager that by the end of a long project, one could probably remove the CSS Reset without affecting the design (save maybe the margin/padding rules). Jonathan Snook feels the same way. For these reasons, I’ve generally used the universal margin/padding reset:

* {margin:0; padding:0;}

There is quite a lot of contention around the subject both for and against as well as the reasoned centrist.

So, rather than continue to rail against their futility, performance penalty, or outright boorishness, I thought I’d actually use a CSS reset a few times and report my findings.

Decision Time!

CSS Reset by Eric Meyer or YUI Reset? Well, after watching this video (you should, too), my decision was firmly in the Meyer camp.

First Reactions

Who uses Firebug? Okay, sorry, who doesn’t use Firebug? If you don’t, you should, and if you do, you likely won’t like Meyer’s reset without it first being modified. Ladies and gentlemen of the jury, Exhibit A:

reset_thumb

Due to the first rule in the reset, the font-size property is applied to (nearly) every element. However, font-size is also an inherited property. Which means nearly every element inherits its value from its parent, while simultaneously being reset itself by the same rule it inherited! The first rule of nearly every stylesheet of mine usually includes a set of font properties (font-family, font-size, and line-height). With these properties already being set, there is no reason to have them in my CSS Reset, so let’s remove the offending rule and relieve some of Firebug pressure.

reset3_thumb

Whew, that’s better.

Don’t Lose Your Focus!

The most offending rule in Eric’s reset is his outline rule:

:focus { outline: none; }

Sure, he adds a comment to remind users to be sure to specify proper outlines for keyboard users. But you and I can both count on one hand the number of times a proper outline is reinstated for the :focus pseudo-class. Besides, I subscribe to the belief that frameworks and tools should make it easy to fall into the pit of success rather than making it harder to do things the right way. Luckily, Patrick H. Lauke has outlined (sorry, I couldn’t help it) a method to remove the outline during its less-useful moments, while retaining the outline as necessary for keyboard navigation. In brief, simply:

a:hover, a:active { outline: none; }

This will hide the ugly outline during the click action on a link as well as during the time the page loads (so long as the user doesn’t move their mouse). I think this fits nicely in the 80/20 category.

And Now?

So where does that leave us? I’m not sure. I’m still not entirely convinced of the utility of a CSS Reset. However, I believe my two minor modifications do bring Meyer’s Reset a bit further into the ‘’useful’ category without being a pain or downright harmful. My version of the reset is hosted at GitHub, so if you don’t like it, go fork it!

http://github.com/jasonkarns/css-reset

Roy.G.Biv alpha

I was recently on a project that called for a translucent background color over a an image similar to this:

rgba_thumb http://www.flickr.com/photos/yeowatzup/ / CC BY 2.0

Of course, being the conscientious web developers we are, we want to be as semantic as possible with our markup. This means that text should be marked up as text and not flattened into the image, forever to remain hidden from the world of web spiders, search engines, assistive technologies, and mash-up artists. We give the text a background color to keep the text readable over the background image but we still want the background image to be slightly visible through the text area. Before RGBa, we would resort to a 1 x 1px translucent PNG but this adds additional overhead (both with the extra HTTP request, the maintenance of the image should the color change, and a PNG fix for IE6). Another option would be the CSS opacity property. Unfortunately, the opacity property applies to an element and all of its descendants. This means the text itself would become translucent as well, something we would like to avoid if possible. So, let’s use some RGBa!

First, add the standard RGB background color so the text block will still be legible in browsers that don’t support RGBa:

  div {
    background: rgb(100, 100, 183);
  }

Now we can enhance this for conforming browsers:

  div {
    background: rgba(100, 100, 183, .75);
  }

We now have support in Firefox 3+, Webkit (Safari 3+, Chrome 1+). What about that other browser? To add support for IE6-IE7, we need to use IE’s proprietary filter property. As this is a proprietary property, it should be included via an IE-only stylesheet referenced using Conditional Comments.

  div {
    background:transparent;
    filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#BF6464B7,endColorStr=#BF6464B7);
    zoom: 1;
  }

A bit of an explanation is in order. First we set the background to transparent which overrides the solid color rgb declaration. Next we apply IE’s proprietary filter. Notice we set the startColorStr and the endColorStr to the same values. These values are not your standard HEX values. Instead of 0xRRGGBB, the first 2 digits are the alpha transparency. Converting our 75% into HEX (.75 * 255 –> 191.25 –> 0xBF). Lastly, we apply the zoom property to trigger hasLayout on the element. This is required for the filter to take effect.

Keen observers will note that the filter property is not supported in IE8 standards mode. As IE8 now properly follows the CSS grammar, we must add the vendor prefix and quote the value. The hasLayout trigger is no longer needed.

  div {
    background:transparent;
    -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#BF6464B7,endColorstr=#BF6464B7)";
  }

Combined, we have our main CSS:

  div {
    background: rgb(100, 100, 183);
    background: rgba(100, 100, 183, .75);
  }

and IE’s CSS:

  div {
    background:transparent;
    filter:progid:DXImageTransform.Microsoft.gradient(startColorStr=#BF6464B7,endColorStr=#BF6464B7);
    -ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#BF6464B7,endColorstr=#BF6464B7)";
    zoom: 1;
  }

We have now achieved cross-browser, CSS-only (no PNGs needed), alpha transparency!