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.
- Previous post: Fieldset, legend, border-radius and box-shadow
- Next post: Letting users disable responsive layout