1

I just discovered CSS3's display: flex; property and tried to apply it to my website's sticky header, but I ran into a problem.

The goal was to evenly space all links across the full width of the header, but this seems impossible when semantically grouping elements with, for example, a <nav> tag.

See jsfiddle example: https://jsfiddle.net/9bua0n7o/

<header>
  <div class="logo">
   Logo
  </div>

  <nav class="navigation">
    <a href="#">Home</a>
    <a href="#">FAQ</a>
    <a href="#">Contact</a>
  </nav>

  <div class="search">
    <a href="#">Search</a>
  </div>

  <div class="login">
   <a href="#">Register</a>
   <a href="#">Login</a>
  </div>

</header>

CSS:

header {
    width: 100%;
    height: 60px;
    line-height: 60px;

    display: flex;
    flex-flow: row wrap;    
    justify-content: space-between;

    position: fixed;
    top: 0;
    left: 0;

    padding: 0 25px;
    box-sizing: border-box;

    background: #ccc;
}
Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Swen
  • 767
  • 1
  • 9
  • 31

3 Answers3

1

You can do like this.

Here I gave each wrapper (navigation, search, etc.) a flex value matching the amount of children they have.

That basically means that the wrapper will occupy that much of the available space and make the items evenly spread.

Then I set each link to flex: 1 making them take its full space.

header {
  width: 100%;
  height: 60px;
  line-height: 60px;
  display: flex;
  flex-flow: row wrap;
  position: fixed;
  top: 0;
  left: 0;
  padding: 0 25px;
  box-sizing: border-box;
  background: #ccc;
}

header .logo,
header .search {
  flex: 1;
  display: flex;
}

header .navigation {
  flex: 3;
  display: flex;
}

header .login {
  flex: 2;
  display: flex;
}

header .navigation a,
header .search a,
header .login a {
  flex: 1;
}
<header>
  <div class="logo">
    Logo
  </div>

  <nav class="navigation">
    <a href="#">Home</a>
    <a href="#">FAQ</a>
    <a href="#">Contact</a>
  </nav>

  <div class="search">
    <a href="#">Search</a>
  </div>

  <div class="login">
    <a href="#">Register</a>
    <a href="#">Login</a>
  </div>

</header>
Asons
  • 84,923
  • 12
  • 110
  • 165
  • This worked for my case! But it leaves me wondering... Isn't there a better way to do this? What if the amount of links inside `.navigation` element changes? I would have to manually update the `flex: 3;` property to make it work again. – Swen Jul 31 '16 at 15:48
  • @Swen Since they are wrapped, their wrapper need to know how big it should be compared to the rest. If you don't wrap them and give them all `flex: 1` it will work independent of amount of links, like this: https://jsfiddle.net/LGSon/804fhnj6/ – Asons Jul 31 '16 at 15:54
  • 1
    @Swen For older browsers you can actually do this with `display: table-cell` as well: https://jsfiddle.net/LGSon/804fhnj6/1/ – Asons Jul 31 '16 at 15:57
  • 1
    @Swen If you want to keep the wrappers, an alternative is to give each link a width, which again will have to be maintained when new links get added, as they can't "know" how many they are and dynamically adjust – Asons Jul 31 '16 at 16:01
1

The solution I came up with is similar (though not quite identical) to LGSon's above.

1) I added a link to .logo:

<div class="logo">
<a href="#">Logo</a>
</div>

2) Then I gave all the elements in the header the same flex style:

.logo,
.logo a,

.navigation,
.navigation a,

.search,
.search a,

.login,
.login a {
flex: 1 0 auto;
}

3) Finally, I overrode the flex-grow styles of .navigation and .login:

.navigation {
flex-grow: 3;
}

.login {
flex-grow: 2;
}

Example in full:

header, .logo, .navigation, .search, .login {
display: flex;
justify-content: space-between;
}

.logo, .navigation, .search, .login, .logo a, .navigation a, .search a, .login a {
flex: 1 0 auto;
}

.navigation {
flex-grow: 3;
}

.login {
flex-grow: 2;
}

header {
width: 100%;
height: 60px;
line-height: 60px;

position: fixed;
top: 0;
left: 0;

padding: 0 25px;
box-sizing: border-box;
background: #ccc;
}
<header>

<div class="logo">
<a href="#">Logo</a>
</div>

<nav class="navigation">
<a href="#">Home</a>
<a href="#">FAQ</a>
<a href="#">Contact</a>
</nav>

<div class="search">
<a href="#">Search</a>
</div>

<div class="login">
<a href="#">Register</a>
<a href="#">Login</a>
</div>

</header>
Rounin
  • 27,134
  • 9
  • 83
  • 108
1

Though (as of July 2016) certainly not a production ready solution, the CSS Display Module Level 3 Working Draft defines the display: contents property, which dictates:

The element itself does not generate any boxes, but its children and pseudo-elements still generate boxes as normal. For the purposes of box generation and layout, the element must be treated as if it had been replaced with its children and pseudo-elements in the document tree.

Applied to your solution, it might look something like this (currently only supported in Firefox):

header {
  width: 100%;
  height: 60px;
  line-height: 60px;
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
  position: fixed;
  top: 0;
  left: 0;
  padding: 0 25px;
  box-sizing: border-box;
  background: #ccc;
}

.header-section {
  display: contents;
}

.header-section a {
  flex: 1;
}
<header>
  <div class="logo">
    Logo
  </div>

  <nav class="header-section navigation">
    <a href="#">Home</a>
    <a href="#">FAQ</a>
    <a href="#">Contact</a>
  </nav>

  <div class="header-section search">
    <a href="#">Search</a>
  </div>

  <div class="header-section login">
    <a href="#">Register</a>
    <a href="#">Login</a>
  </div>
</header>

Hopefully this at least provides some education on proposed new CSS features

Ian Clark
  • 9,237
  • 4
  • 32
  • 49
  • 1
    This is actually what I was looking for. Too bad it's not widely supported yet. Very interesting! – Swen Jul 31 '16 at 17:50
  • @Swen yeh, it clearly has a great purpose in situations like this! Web development is always full of these "there's a solution, but you can't use it" outcomes! – Ian Clark Jul 31 '16 at 17:55