Flex Menu

Here we will follow on from the page describing how to build a hamburger menu. The same HTML will be used here as in that explanation. It would be useful to open that page in a separate browser window so you can see the HTML as you follow this explanation.

Here is how the browser renders the HTML for the navigation list without any CSS applied:

display: flex;

This HTML was written to enable a hamburger menu on small screens and a horizontal flex menu on larger screens. Here we are making the flex menu and we do not need the hamburger button so we will use nav #hamburger-button {display: none;} so it is not displayed. We will also target the .top-level-list element for display: flex;. The CSS is shown below:


      
      #nav-1 #hamburger-button {
        display: none;
      }
      #nav-1 .top-level-list {
        display: flex;
      }
      
    

The button for the hamburger has gone and now the top level list ul element has its display property set to flex and so that list is horizontal. It can be seen that the second level ul elements are still displayed vertically. Later we will hide these and only show them when their parent li element from the top level list receives focus but first lets space the top level list out and deal with vertical alignment.

Spacing and Alignment

For horizontal alignment we will use the flex associated justify-content property and set its value to space-around. We will also use another flex associated property, align-items, to ensure the horizontal list elements are aligned vertically:


    #nav-2 #hamburger-button {
      display: none;
    }
    #nav-2 .top-level-list {
      display: flex;
      justify-content: space-around;
      align-items: center;
    }
    

So, now our navigation looks like this:

It can be seen that there is more space around the top level list items. Paradoxically the items now seem less aligned vertically but this is because the items with lists in them are being considered as a single item containing a key word (for example INFO) as well as a list and so they are aligned vertically given this consideration. We will later make the second level lists' position properties set to absolute which will take them out of the normal flow of the document and cause the horizontal items to be properly aligned vertically.

display: none / block

Here we hide the second level lists and redisplay them when the list element they are part of receives focus. In the web page on building a hamburger menu it was explained why :focus-within and not :focus is used and why it is important that it is used on a common parent element of both the element that when clicked causes the dropdown and the dropdown list itself. That is the top level list items. Here is the CSS:


    #nav-3 #hamburger-button {
      display: none;
    }
    #nav-3 .top-level-list {
      display: flex;
      justify-content: space-around;
      align-items: center;
    }
    #nav-3 .top-level-list ul {
      display: none;
    }
    #nav-3 .top-level-list li:focus-within ul {
      display: block;
    }
    

Note that when we click on one of the dropdowns and the second level list associated with it is displayed, the alignment of the top level list items changes. This is because the newly displayed second level list is still in normal flow and so now the whole block of content is being aligned with the other single words. We will address this now.

Absolute Position

In order to take the dropdown lists out of normal flow so they don't cause content to be rearranged when they are displayed we make them absolutely positioned.


    #nav-4 #hamburger-button {
      display: none;
    }
    #nav-4 .top-level-list {
      display: flex;
      justify-content: space-around;
      align-items: center;
    }
    #nav-4 .top-level-list ul {
      display: none;
      position: absolute;
      background-color: lightgrey;
    }
    #nav-4 .top-level-list li:focus-within ul {
      display: block;
    }
    

Normally an absolutely positioned element will be positioned relative to the browser window unles one of its parent elements has relative position. In this case our absolutely positioned second level lists seem to be positioned relative to their containing top level list items even though I have not given these relative position. It may be that flex items are relatively positioned or behave, in this respect, as if they were.

Keyboard Tabbing

Some users may by using the tab key on the keyboard for accessing web content. Tabbing with keyboard will only move to elements that can take focus. Anchor elements can take focus by default but our top level list items which are not links but have dropdown lists associated with them do not take focus through keyboard tabbing by default. They can be made to take focus by giving them a tabindex attribute. Assigning a value of 0 to tabindex results in them being focussed in the same order as their appearance in the code with respect to the elements that take focus by default.

If the page is refreshed and then the tab key depressed the navigation items should take focus in the order they appear in the HTML. This should mean they are accessible to people using the keyboard to navigate the webpage.

Conclusion

The basic flex navigation is now implemented. A lot more work could be done to enhance the appearance but that would obscure the fundamental requirements of the code and so has not been done here.

Precisely the same HTML code was used for this flex layout as the hamburger example described separately. In the next explanation we combine the two layouts to create a responsive layout with the flex CSS code contained in a media query.