Izak Filmalter wrote to me:
On the site I am working on there is a navigation bar with a few elements in it: a search input, the user’s full name, and some icons. The icons are of fixed with, the user’s name varies, and the search bar should take up the rest of the usable space.
No matter what I try, I can not get the search bar to use all the available space and no more when the user name length is dynamic.
I already sent Izak a response, but I’ll share that solution here. Flexbox! I’m sure it comes as no surprise that flexbox is a wonderful layout tool. I have a guide to flexbox here that makes for a pretty good reference.
The good news here is that this is a pretty easy thing to do with flexbox layout. We’ll get more detailed, but these few lines of code basically get it done:
.bar {
display: flex;
}
.search {
flex: 1;
}
Step by Step
Let’s assume markup like this. No surprises here really. I just wrapped the search input in a <div>
mostly because I’m weirded out by an input becoming a flex item, but you’d probably want a wrapping element (like a <form>
) anyway, because you’d probably have a <label>
and whatnot.
<div class="bar">
<div class="icon icon-1"></div>
<div class="icon icon-2"></div>
<div class="icon icon-3"></div>
<div class="username">
Emily Blunt
</div>
<div class="search">
<input type="search" placeholder="search..." />
</div>
</div>
To get them aligned horizontally and centered:
.bar {
display: flex;
align-items: center;
}
Let’s make sure the bar is as wide as the browser window. We can set 100% width easily enough. But whenever I do that, I’m reminded we should be rocking box-sizing: border-box; – otherwise any padding or border on that container and it would be wider than 100% and that’s the worst.
*, *:before, *:after {
box-sizing: inherit;
}
html {
box-sizing: border-box;
}
.bar {
display: flex;
align-items: center;
width: 100%;
background: #eee;
padding: 20px;
}
For the final trick:
.search {
/* take up the rest of the remaining space */
flex: 1;
}
.search input {
width: 100%;
}
It’ll work like this:
Now that we’re in flexbox-land, we can even flop the order of things around all we want and get the same good spacing action. The order property is 0 by default, so any positive value will put that flex item at the end or negative value at the beginning. And then in order, just like z-index
.
.bar-2 .username {
order: 2;
}
.bar-2 .icon-3 {
order: 3;
}
.bar-3 .search {
order: -1;
}
.bar-3 .username {
order: 1;
}
Bonus RWD!
Flexbox is super friendly toward responsive design, because we can swap around ordering and sizing, and even wrapping, really easily. Let’s make our demo wrap, and make the search and username full-width:
@media (max-width: 650px) {
.bar {
flex-wrap: wrap;
}
.icon {
order: 0 !important;
}
.username {
order: 1 !important;
width: 100%;
margin: 15px;
}
.search {
order: 2 !important;
width: 100%;
}
}
Demo
Here we go:
See the Pen Flexbox reordering by Chris Coyier (@chriscoyier) on CodePen.
Browser Support
You can’t mention flexbox these days without some rabble-rabble browser support. As always, you can consult Can I Use. IE 9 is the big limiter left. If you need to support that, you’ll have to find another way. Otherwise, between old / new / tweener syntaxes, you’ll be fine. We aren’t using anything weird here.
The trick is just to use Autoprefixer which handles flexbox wonderfully. When using that, the flexbox properties/values in this demo become:
.bar {
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-align-items: center;
-ms-flex-align: center;
align-items: center;
}
.search {
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
}
.bar-2 .username {
-webkit-order: 2;
-ms-flex-order: 2;
order: 2;
}
But I need a better fallback!
One possibility is to use Modernizr to detect flexbox support. It’ll put a class name on the html element, like:
<html class="no-flexbox">
You can use that to use a different layout technique:
.no-flexbox .bar {
display: table;
border-spacing: 15px;
padding: 0;
}
.no-flexbox .bar > * {
display: table-cell;
vertical-align: middle;
white-space: nowrap;
}
.no-flexbox .username {
width: 1px;
}
Works pretty good:
It’s not the only possible way. You could mess with inline-block or float and measuring things with JavaScript. Or you could use an actual table. The web, yo, there is always a way.
I see a JavaScript plugin idea! Flexbox syntax, but if it detects you’re on an old browser it automatically translates Flexbox to table layout.
James you look like Mark Zuckerberg
*gives flexbox a big hug for being lovely*
So, my question is this then: 99% of the time you will keep the order of the elements… So why use flexbox with it’s limited support if you can achieve the same with table-cell with a lot more browser support?
This seems a really legitimate question. I also am waiting for some more explanation here.
I don’t think the meaning of this post is to force people to use flexbox. But rather provide them with a more modern solution that the table-cell “hacky way”. Personally I would implement flexbox and learn while I’m at it. And provide a fallback trough Modernizr.
I agree that you can learn from this, I never said you coulnd’t. But I disagree with the “hacky” word for table-cell display, it’s a perfectly viable and non-hacky way to do it in fact…
Yeah, you are right its not hacky, used the wrong word :) Lets just say its a less known / underdog way of layouting. (i use it all the time btw)
Here’s some things that come to mind:
Hi there Chris, I agree with your point #1, although it is common to do that. On your #2 point that’s correct, although as I mentioned my question is regarding most cases when you will NOT need all that flexibility. About your #3 point, I wouldn’t put IE6/7 into the table, but flexbox is not supported by IE9 which still has a lot of users compared to the ones you mention.
I would argue that anything used in a way that it’s not intended to be used is by nature a hacky usage. display: table-cell is intended only for table cells; it’s hacky simply because a toolbar is not a table. I would even say using floats for layout is a hack, despite the fact that it’s been the de facto standard for years – it’s just not what it was intended for, which is why it works so unintuitively. There simply wasn’t any non-hacky way to do it for a long time.
That’s not to say hacky is always wrong. Sometimes hacks are far more practical even when there is a “correct” way to do things. But they’re still hacks, and you should always be aware that they may behave unpredictably in hard-to-find scenarios.
Hi Javier, just a thing about flexibility, I don’t think that you may be that sure about needing it or not, since things change on time. So I think it’s better to have an option for ordering things with pure CSS.
I’m aware of the compatibility issues with older browsers, but you could’ve said the same in the past about the “box-sizing: border-box;” model, that we are using a lot today.
I thought it was Schmidt. John Jacob Jingleheimer Schmidt.
It is… it’s Schmidt. But now I’m just showing my age. But hey, Schmidt happens, right?
The main reason I have moved to flexbox rather than display:table is that the latter disallows any use of positioning.
For example, recently I needed a search bar (very similar to the example given here) with dropdown /flyout menus… very, very tricky to do with display:table. With flexbox, a doddle.
Works flawlessly: http://davidwalsh.name/table-cell-position-absolute
I don’t call that flawless. Having to specify height 100% on a parent to get it working in FF is going to cause all manner of trouble.
i am in love with flex <3
Flexbox works in IE9+ or in IE10+?
Someone made fallbacks for Flexbox work in old browsers? I don’t think no solution =(
Well, it’s a tricky one. I think the best way is something like a combined way of Modrenizr and table-cell instead of flexbox.
Why is a
!important
used here?Because of specificity. I don’t recommend what Chris does here when using
!important
, but I understand why he does it.If you look at the previous code blocks, you’ll see descendant selectors like “.bar-3 .username”…. In order to override those, he’s chosen to keep it simple with just “.username” in the MQ styles. But that won’t override the previous selector. So he uses
!important
to make it override.Again, I don’t recommend that, because you can just repeat the selector and it’s all good… but if you’re working alone and you understand what you’re doing, then I suppose it’s one way to do it. I just don’t see how adding
!important
is any different than just repeating the selector.Mostly because this way it hits all three of the bars. Just a cheap way out of not writing three selectors in a compound selector to override all of them.
It’s definitely not recommended in a big production app. But I do prefer it over artificially increasing specificity. At least with
!important
you know it’s a problem you need to fix.Nice article…. Easy to understand
Nice post, thanks chris, flex ftw :)
Very nice Chris.
Perfect timing as ever.
Just starting a site rewrite site after 7 years, god it looks soooo dated.
I’m currently assuming it’ll be another 7 before I do it again.
This article has me convinced.
Purist HTML 5, with Flexbox replacing floated divs, is the way I’m going to rock.
Thanks dude.