I've got a requirement to build a site header with three components:
- Brand (company logo...) - left
- Menu - center
- Shortcuts (settings, search) - right
The result should be looking like this:
Now, with Bootstrap is really easy to get the brand (and the menu) justified to the left and the shortcuts justified to the right. However, it is surprisingly difficult to get the layout above due to several reasons:
- The collapsible structure separates the brand from the rest, resulting in a 2-level nesting (i.e. the two navbar lists are in their own
<div>
at the same level as the<div>
containing the branding) - Bootstrap has some !important settings in the default style; overwriting them would defeat the purpose
- Mixing display:block and display:flex is bad for you :)
Consequently, I've tried the { margin: auto } trick with no success (maybe a more patient soul would have more success).
After a couple of hours of digging, I've reached a somewhat stable solution using flexbox.
Note: The code outlined below is SASS and contains some angular bits, which normally you should be able to ignore.
Solution
My solution has has a navbar-fixed-top layout. The preamble is quite standard:
<div class="navbar navbar-default navbar-fixed-top" ng-controller="NavBarCtrl">
<div class="container-fluid"></div>
</div>
The brand and toggle are grouped for better mobile display. This is a bit of code I've seen in a lot of samples:
<div class="navbar-header">
<button
type="button"
class="navbar-toggle collapsed navbar-brand"
data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1"
>
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand page-scroll" href="#page-top" update-title>
Brand
</a>
</div>
The two lists have their own group:
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1"></div>
Since the collapse style has a block display, I'm adding another layer with a flex one:
<span class="container-flex"></span>
Now, I'm adding the flexbox items:
<span class="navbar-flex"> </span>
<ul class="nav navbar-nav navbar-middle navbar-flex">
<li><a ui-sref="people.list">List</a></li>
<li><a ui-sref="people.chart">Chart</a></li>
</ul>
<ul class="nav navbar-nav navbar-right navbar-flex">
<li>
<div ng-include="'static/tpl/search-nav.tpl.html'"></div>
</li>
<li>
<div class="navbar-center navbar-text navbar-spacer">
<span class="glyphicon glyphicon-user"></span>
</div>
</li>
</ul>
As you can see, there are three items:
- A
to provide a reference point for the flex justification. Otherwise, the first item (the menu) would be at the beginning of the box and we need it to be in the middle of the page. - The menu - we want this to be in the middle of the page. The angular template contains a list similar to the third item
- The common operations - this provides a settings link and a search button
Now all we need to do is to close the tags:
</span>
</div>
</div>
</div>
The SASS classes relevant to the above code are:
.navbar-brand {
margin-left: 10px;
//padding-left: 55px;
//background-image: url("../img/erc-sl.png");
background-repeat: no-repeat;
background-position: left center;
}
.navbar-center {
//position: absolute;
color: $theme-primary;
width: 100%;
top: 0;
text-align: center;
margin-left: auto;
margin-right: auto;
//padding-right: 15px;
//padding-left: 15px;
}
.navbar-spacer {
padding-left: 10px;
padding-right: 10px;
}
@media (min-width: 768px) {
.navbar-flex {
display: inline-flex;
}
.navbar-middle {
float: none;
vertical-align: top;
}
.container-flex {
display: flex;
justify-content: space-between;
}
.navbar .navbar-collapse {
text-align: center;
}
}
They are pretty self-explanatory.
Conclusion
The good thing is that it works nicely right now. The bad thing is that you need to wrap things up to get around the !important tags present in bootstrap. It reminds me of the nightmare that was (still is?) nested tables.
HTH,
Member discussion: