How to shrinkwrap and center elements horizontally

When you use CSS to float an element that does not have an explicit width, the element’s width becomes its “shrink-to-fit” width. This is useful when you’re floating elements that you don’t know how wide they need to be. A very common example is a navigation bar, where you often want the width of the links to vary depending on how much text they contain.

Not knowing an element’s width makes it harder to center it horizontally, however. Take the navbar example. What if you have a list of a few items, you want them to shrinkwrap, but you also want to shrinkwrap and center the whole navbar horizontally? Shrinkwrapping it is easy – you just need to float it. But centering is not quite that straightforward. You can try margin:0 auto;, but unless you give the navbar an explicit width it won’t do anything. And that’s the problem – you don’t know how wide the navbar is.

If you could specify float:center that would probably do the trick. Sadly you can’t do that (not yet anyway). Fortunately there are several other ways to solve this problem. I’ve found no less than five different techniques, all of which are used on the shrinkwrap and center demo page. View source on that page for HTML and CSS. Which one you use depends on what markup you have, if you can and want to change it, and which browsers it needs to work in.

display:inline-block

If the navbar has a parent that you can set text-align:center on, you can use display:inline-block to make the navbar shrinkwrap. Here’s the HTML structure:

<div class="navbar">
    <ul>
        <li><a href="/">Home</a></li>
        …
    </ul>
</div>

And here’s the necessary CSS:

.navbar {
    text-align:center;
}
.navbar ul {
    display:inline-block;
}
.navbar li {
    float:left;
}
.navbar li + li {
    margin-left:20px;
}

Depending on the design you may need to add text-align:left (or right) to the .navbar ul {} rule.

Browser support

This works pretty much everywhere. For IE, it works in IE8+ without hacks, and even in IE7 if you trigger haslayout and change display:inline-block to display:inline:

.navbar ul {
    display:inline;
    zoom:1;
}

Use conditional comments or CSS hacks, whatever your preference is, to target this at IE7 only.

position:relative

Another method (which my colleague @martinjansson showed me) is to perform a bit of position:relative trickery. This trick requires two parent elements that you can use. One is for positioning and one to avoid getting a horizontal scrollbar. The HTML:

<div class="navbar">
    <div>
        <ul>
            <li><a href="/">Home</a></li>
            …
        </ul>
    </div>
</div>

And the CSS:

.navbar {
    overflow:hidden;
}
.navbar > div {
    position:relative;
    left:50%;
    float:left;
}
.navbar ul {
    position:relative;
    left:-50%;
    float:left;
}
.navbar li {
    float:left;
}
.navbar li + li {
    margin-left:20px;
}

The trick is to float the inner wrapper, which makes it shrink to fit its content, relatively position it 50% to the left, and then position the ul minus 50% to the left to pull it back to its original position. Due to how relative positioning works this causes a horizontal scrollbar if the browser window is narrower than twice the navbar’s width. The overflow:hidden declaration on the outer wrapper takes care of this and contains the floated div.

Browser support

Like the display:inline-block method this works pretty much everywhere, but IE7 needs a little extra help again, this time to make overflow:hidden work properly:

.navbar {
    position:relative;
}

display:table

If you want to use the minimal amount of markup, display:table is a good choice since it requires no wrapper element:

<ul class="navbar">
    <li><a href="/">Home</a></li>
    …
</ul>

Tables automatically adjust their width to their content, and centering them with auto margins works without declaring an explicit width. Here’s the relevant CSS:

.navbar {
    display:table;
    margin:0 auto;
}
.navbar li {
    display:table-cell;
}
.navbar li + li {
    padding-left:20px;
}

Browser support

This method (like anything that uses display:table and friends) does not work in IE7 or older, but everywhere else. You can either live with a somewhat broken layout in IE7- or patch it up by floating the ul and li elements in your IE7-targeted CSS.

width:fit-content/intrinsic

Another method, which Catalin Rosu writes about in Horizontal centering using CSS fit-content value is to use width:fit-content.

The fit-content keyword is described in the CSS Intrinsic & Extrinsic Sizing Module Level 3.

Like the display:table method, this needs no wrapper element:

<ul class="navbar">
    <li><a href="/">Home</a></li>
    …
</ul>

The shrinkwrapping is achieved by setting the width of the ul element to fit-content (with vendor prefixes for Firefox and Chrome). Safari uses the non-standard keyword intrinsic, so you may want to include that as well. Once the width is shrinkwrapped, setting the left and right margins to auto works for centering:

.navbar {
    width:intrinsic; /* For Safari, see https://developer.mozilla.org/en-US/docs/CSS/width */
    width:-moz-fit-content;
    width:-webkit-fit-content;
    width:fit-content;
    margin:0 auto;
    overflow:hidden; /* Contain the floated li elements */
}
.navbar > li {
    float:left; /* You could use display:inline-block instead */
}
.navbar li + li {
    margin-left:20px;
}

Browser support

Browser support is pretty bad for this one currently, but you can get Firefox, Safari and recent Chrome versions to play along. Currently no IE or Opera as far as I know.

display:inline-flex

The final technique is to use flex layout, more specifically display:inline-flex. This is pretty similar to display:inline-block and needs the same HTML:

<div class="navbar">
    <ul>
        <li><a href="/">Home</a></li>
        …
    </ul>
</div>

The CSS (with vendor prefixes and a mix of old and new flexbox syntax):

.navbar {
    text-align:center;
}
.navbar > ul {
    display:-webkit-inline-box;
    display:-moz-inline-box;
    display:-ms-inline-flexbox;
    display:-webkit-inline-flex;
    display:inline-flex;
}
.navbar li + li {
    margin-left:20px;
}

Browser support

This technique also pushes browser requirements quite a bit, though it’s better supported than fit-content. It currently requires vendor prefixes in all browsers except Opera 12.1, and does not work in IE9 or older. See the Can I use Flexible Box Layout Module support chart for details.

Many ways to…

With all of these different approaches available, which one is the best? You probably already know the answer: it depends. I think the display:inline-block method is probably my favourite due to its wide browser support and since you can use the white-space property to control whether list items wrap or not if the navbar becomes wider than the available space. You can do that with the flexbox method too by using flex-wrap:wrap, but browser support isn’t the same.

If you don’t already have a favourite method to do this, you now have a bunch to choose between (and there may be even more). Hopefully at least one will be right for you.

Posted on March 4, 2013 in CSS

Comments are disabled for this post (read why), but if you have spotted an error or have additional info that you think should be in this post, feel free to contact me.