8

I am creating a large dynamic nav menu that I want to look like this:

[----------- 100% PAGE WIDTH -----------] | GROUP A | GROUP C | GROUP F | GROUP G | | item | item | item | item | | item | | item | item | | | GROUP D | item | item | | GROUP B | item | item | | | item | | | | | item | GROUP E | | | | | item | | | |---------------------------------------| | | | | [------------- END OF PAGE -------------]

See my JS Fiddle Example.

* {
  padding: 0;
  margin: 0;
}
body {
  background: #ccc;
  font-family: helvetica, arial;
  color: #444;
}
ul {
  list-style: none;
}
.mega-menu {
  display: flex;
  flex-direction: column;
  flex-wrap: wrap;
  padding: 15px;
  height: 50vh;
  background: #fff;
}
.mega-menu > li {
  display: flex;
  flex-direction: column;
  font-size: .7rem;
  padding-bottom: 15px;
}
.title {
  font-size: .7rem;
  font-weight: bold;
  line-height: 1;
  padding-bottom: 5px
}
<ul class="mega-menu">
  <li>
    <a class="title">News</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Shows</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
      <li>CNN</li>
    </ul>
  </li>

  <li>
    <a class="title">Topics</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Shows</a>
    <ul>
      <li>CBS</li>
      <li>NBC</li>
    </ul>
  </li>

  <li>
    <a class="title">Networks</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
      <li>CNN</li>
    </ul>
  </li>

  <li>
    <a class="title">Groups</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Sections</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Pilots</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
    </ul>
  </li>

  <li>
    <a class="title">Locations</a>
    <ul>
      <li>Denver</li>
      <li>Baltimore</li>
      <li>LA</li>
      <li>New York</li>
      <li>San Francisco</li>
      <li>New Orleans</li>
      <li>Jacksonville</li>
      <li>Calvery</li>
      <li>August</li>
    </ul>
  </li>

  <li>
    <a class="title">Cities</a>
    <ul>
      <li>Denver</li>
      <li>Baltimore</li>
      <li>LA</li>
      <li>New York</li>
      <li>San Francisco</li>
      <li>New Orleans</li>
      <li>Jacksonville</li>
      <li>Calvery</li>
      <li>August</li>
    </ul>
  </li>

  <li>
    <a class="title">News</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Shows</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
      <li>CNN</li>
    </ul>
  </li>

  <li>
    <a class="title">Topics</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Shows</a>
    <ul>
      <li>CBS</li>
      <li>NBC</li>
    </ul>
  </li>

  <li>
    <a class="title">Networks</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
      <li>CNN</li>
    </ul>
  </li>

  <li>
    <a class="title">Groups</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Sections</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Pilots</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
    </ul>
  </li>

  <li>
    <a class="title">Locations</a>
    <ul>
      <li>Denver</li>
      <li>Baltimore</li>
      <li>LA</li>
      <li>New York</li>
      <li>San Francisco</li>
      <li>New Orleans</li>
      <li>Jacksonville</li>
      <li>Calvery</li>
      <li>August</li>
    </ul>
  </li>

  <li>
    <a class="title">Cities</a>
    <ul>
      <li>Denver</li>
      <li>Baltimore</li>
      <li>LA</li>
      <li>New York</li>
      <li>San Francisco</li>
      <li>New Orleans</li>
      <li>Jacksonville</li>
      <li>Calvery</li>
      <li>August</li>
    </ul>
  </li>

  <li>
    <a class="title">News</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Shows</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
      <li>CNN</li>
    </ul>
  </li>

  <li>
    <a class="title">Topics</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Shows</a>
    <ul>
      <li>CBS</li>
      <li>NBC</li>
    </ul>
  </li>

  <li>
    <a class="title">Networks</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
      <li>CNN</li>
    </ul>
  </li>

  <li>
    <a class="title">Groups</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Sections</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Pilots</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
    </ul>
  </li>

  <li>
    <a class="title">Locations</a>
    <ul>
      <li>Denver</li>
      <li>Baltimore</li>
      <li>LA</li>
      <li>New York</li>
      <li>San Francisco</li>
      <li>New Orleans</li>
      <li>Jacksonville</li>
      <li>Calvery</li>
      <li>August</li>
    </ul>
  </li>

  <li>
    <a class="title">Cities</a>
    <ul>
      <li>Denver</li>
      <li>Baltimore</li>
      <li>LA</li>
      <li>New York</li>
      <li>San Francisco</li>
      <li>New Orleans</li>
      <li>Jacksonville</li>
      <li>Calvery</li>
      <li>August</li>
    </ul>
  </li>

  <li>
    <a class="title">News</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Shows</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
      <li>CNN</li>
    </ul>
  </li>

  <li>
    <a class="title">Topics</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

  <li>
    <a class="title">Shows</a>
    <ul>
      <li>CBS</li>
      <li>NBC</li>
    </ul>
  </li>

  <li>
    <a class="title">Networks</a>
    <ul>
      <li>HBO</li>
      <li>CBS</li>
      <li>NBC</li>
      <li>CNN</li>
    </ul>
  </li>

  <li>
    <a class="title">Groups</a>
    <ul>
      <li>Top Stories</li>
      <li>Trending Stories</li>
      <li>Sports</li>
      <li>U.S.</li>
      <li>Global</li>
    </ul>
  </li>

</ul>

Requirements

  1. If the width of the page grows, I want there to be more columns, if it shrinks I want less columns.

  2. If I were to add twice as many items, I want the height of the columns to grow taller so there is room for the additional items.

  3. The padding/whitespace between each group should be the same.

  4. Ideally I will not use JavaScript (but will if that is the only way).

Problem

When I shrink the screen height/width, the content overflows off the right side of the menu.

Thoughts

I have searched for a CSS solution but cannot find one that doesn't add spacing in between the uneven groups. (E.g. Uneven whitespace)

All I can think of is to use JavaScript to dynamically set the height of the flexbox parent (increasing the height until the last item group (TITLE F) is fully shown and not overflowing off the screen.

Michael Benjamin
  • 346,931
  • 104
  • 581
  • 701
Justin
  • 26,443
  • 16
  • 111
  • 128
  • Have you tried media queries to change the layout? – SilentLupin Sep 21 '16 at 19:09
  • Since the amount of content is dynamic and can change the needed height of the menu, I don't think media queries help. – Justin Sep 21 '16 at 19:27
  • 1
    I need to keep thinking about this, but this is a really cool problem. – SilentLupin Sep 21 '16 at 19:36
  • 2
    Flex `column wrap` has lots of problems in most browsers. It's not really ready for prime time IMO. See [**here**](http://stackoverflow.com/q/33891709/3597276), [**here**](http://stackoverflow.com/q/39095473/3597276), [**here**](http://stackoverflow.com/q/39617628/3597276) and [**here**](http://stackoverflow.com/q/34480760/3597276). If you can, stick with `row wrap`: https://jsfiddle.net/wc8c4ccm/2/ (just a basic template / not tallored to your content) – Michael Benjamin Sep 21 '16 at 19:42
  • I agree with @Michael_B. You can try flex or multi-column layout, but it doesn't sound like either really covers what you are going for completely. – SilentLupin Sep 21 '16 at 20:21
  • @Michael_B `row wrap` doesn't work because I don't want the excess whitespace below each group. Since the content is dynamic I could have one group with 20 items and another with 2 which would look awful using row wrap. – Justin Sep 21 '16 at 20:36
  • I think you're talking about [**this**](http://stackoverflow.com/a/34481128/3597276), and yes, I agree with you. But my point is that `row wrap` is stable and reliable. `column wrap` is not. – Michael Benjamin Sep 21 '16 at 20:40
  • multi-column https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Columns/Using_multi-column_layouts is what you need. – user943702 Sep 21 '16 at 23:00
  • CSS columns are a great idea but I wasn't able to get them working without breaking up the groups. E.g. if a group is long, keep it in one column. – Justin Sep 23 '16 at 15:07
  • @Justin, is there a min/max width/height requirement here? What do you expect to happen when you shrink the size? Are you ok with "max-width" for the groups? Or does the width of them need to by dynamic? – Dekel Sep 25 '16 at 01:38
  • Min width/height is 320px x 320px. Max width/height is 4K (3840 x 2160) – Justin Sep 26 '16 at 20:42
  • What determines the height of the columns? How much space should be at the bottom? Can you give more examples of different cases. – skyline3000 Sep 28 '16 at 16:19
  • The height would be determined by how many items/groups there were. Since the menu content is dynamic, the goal is for it to look good in all cases. We could say the min number of groups is 4 and the max is 20. Min items under a group is 1 and max is 20. – Justin Sep 28 '16 at 16:50
  • I did run across https://codyhouse.co/gem/mega-dropdown/ which seems to be a very robust although heavy-handed solution. – Justin Sep 30 '16 at 16:56

1 Answers1

6

Maybe something like this (jsfiddle):

.mega-menu {
  -webkit-column-count:1;
  -moz-column-count:1;
  column-count:1;
  padding: 15px 15px 0;
  background: #fff;
}
@media (min-width: 200px) {.mega-menu{-webkit-column-count:2;-moz-column-count:2;column-count:2;}}
@media (min-width: 300px) {.mega-menu{-webkit-column-count:3;-moz-column-count:3;column-count:3;}}
@media (min-width: 400px) {.mega-menu{-webkit-column-count:4;-moz-column-count:4;column-count:4;}}
// ...
@media (min-width: 1800px) {.mega-menu{-webkit-column-count:18;-moz-column-count:18;column-count:18;}}
@media (min-width: 1900px) {.mega-menu{-webkit-column-count:19;-moz-column-count:19;column-count:19;}}
@media (min-width: 2000px) {.mega-menu{-webkit-column-count:20;-moz-column-count:20;column-count:20;}}
.mega-menu > li {
  display:inline-block;
  font-size: .7rem;
  padding-bottom: 15px;
}

It'd probably need to be extended to at least the width of a 4K monitor. If you have SASS or similar, that would make things less tedious.

It doesn't give content-aware column widths, leaving you to guess a likely minimum column width.

The issue you mentioned about the menu falling off the right isn't really going to be solved with any solution when you have a lot of items to show but no scrolling. Consider that narrower display widths usually also have shorter display heights. The smaller the screen, the less you'll be able to fit. It may be necessary to arrange for smaller screens to have fewer menu options.

Ouroborus
  • 16,237
  • 4
  • 39
  • 62
  • At 775x500 ish some of the columns that should stack on top of eachother double up in a single column. What is happening there? – Justin Sep 26 '16 at 21:08
  • I just added `li { width: 100% }`and it looks promising so far. https://jsfiddle.net/f18cfLxk/ – Justin Sep 26 '16 at 21:39
  • So far the biggest shortcomming I see is if you reduce the amount of groups to three or four, it still creates a bunch of needless columns. – Justin Sep 26 '16 at 21:47
  • 1
    @Justin It does create unused columns if there isn't enough content. The alternative is creating ultra-wide columns (though not really useful in conjunction with columns layout). Unfortunately, your question falls into the cracks between two possible CSS solutions, columns or flexbox. Because of this, a Javascript helper may be your best option. – Ouroborus Sep 26 '16 at 22:49
  • I am going to go with a JavaScript solution but this is still a good answer. Thanks! – Justin Sep 30 '16 at 19:50