Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flexbox Responsive Mega Menu with Dynamic Content

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.

like image 567
Justin Avatar asked Sep 21 '16 19:09

Justin


1 Answers

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.

like image 50
Ouroborus Avatar answered Nov 01 '22 06:11

Ouroborus