offer jobs schedule amazon media file explorer download upload index folder image licenses menu widget Play Pause profile-settings more dots-two-horizontal dots-two-vertical more-vertical pending google-plus hangouts facebook instagram whatsapp spotify telegram twitter vine renren rss youtube twitch vimeo flickr dribble behance deviantart 500px steam github soundcloud skype reddit linkedin lastfm delicious stackoverflow pinterest xing flattr foursquare yelp World

A Practical Examples of Building An Advance Flexbox Layout

Flexbox needs no introduction, and this tutorial isn't an intro to FlexBox either, it is a practical guide on using flexbox. Well, if you want a basic intro to flexbox, then...

The Flexible Box Layout or simple flexbox is a one-dimensional layout model, with flexbox you can lay out page components by specifying how you want the spaces distributed, and elements ordered. So, if you do care about responsive sites, then flexbox is your best bet.

I know, the intro isn't enough, well, there are zillions of guides online about flexbox, read them.

So, in this guide, we would be working on practical examples of using a flexbox with a couple of use cases, let's get started...

Different Responsive Flexbox Menus

This section would contain instructions on how to create a responsive, and advanced nav menu with flexbox, go cracking...

Responsive Single Menu Layout With Flexbox

Single menus are menus with no drop-down, they have only a single level, that is they have no sub-menus, let's see an example of how we can create one real quick with flexbox.

I have the following HTML structure:

<!DOCTYPE html>
<html lang="en-US">

<head>
  <meta charset="utf-8">
  <title></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="css/normalize.css">
  <link rel="stylesheet" href="css/main.css">
  <meta name="theme-color" content="#fafafa">
</head>

<body>

<!--
     RESPONSIVE SINGLE NAVIGATION MENU
-->

  <header class="site-header">
    <div class="header">

      <section class="menu-section">
        <nav id="site-navigation" class="single-nav menu" role="navigation">
          <ul>
            <li><a href="#">Home</a></li>
            <li><a href="#">Beats</a></li>
            <li><a href="#">Plugins</a></li>
            <li><a href="#">Deals</a></li>
            <li><a href="#">About</a></li>
            <li><a href="#">Contact US</a></li>
          </ul>
        </nav><!-- #site-navigation .single-nav -->
      </section><!-- menu-section -->

    </div><!-- div.header -->
  </header><!-- site-header -->

</body>

</html>

Which result to this by default:

1. Single Level Flex Menu

So, we have an header element as you might have guessed, which represents the navigation aids of our application. Inside the header, we have a div with the class header, and inside the div we have a section that holds our navigation element.

Finally, inside the navigation element, we have an unordered list that contains a series of list items. This markup is what we would be using across our menu layout.

Create a css folder in your app root, and add a new file name main.css, that is where we would add our styles.

We are doing a "mobile-first" approach, where we start the design with the mobile version, we then adapt it to larger screens, this way, we can really focus on what matters the most. So, I'll use this CSS for my mobile design:

.header {
  margin: 0 auto;
  padding: 0 1em;
}

/* Base styles that apply to all menus */
.menu-section {
  padding-bottom: 2em;
  margin-bottom: 2em;
}

.menu ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  background-image: linear-gradient(150deg,#f0f3f1,#f0f3f1);
}

.menu li a {
  display: block;
  text-decoration: none;
  color: #001f3f;
  padding: .6em 2em;
  font-size: 18px;
}

.menu li a:hover {
  background: hsl(140deg 3% 73%);
  color: #4b1f3f;
}

This gives us the following result:

2. Mobile version -- viewing from desktop

and if you view from the mobile, you should get:

3. Mobile version -- viewing from mobile

Cool.

For the desktop version, we can do something like so:

@media screen and (min-width: 900px) {

  #site-navigation ul {
    display: flex;
    flex-wrap: wrap;
  }

  #site-navigation li {
    flex: 1 0 auto;
    text-align: center;
  }

}

You'll get the following result on screen that is >=900px:

4. Desktop view

With just the above media query, we are delivering a tailored styles for whatever screen width we are targeting. So, in the above example, we are targeting screens that is 900px or higher. So, what exactly are we doing when the screen is 900px or higher?

We targeted the ul element under the site-navigation: #site-navigation uland gave it a display: flex;and flex-wrap: wrap;We are basically defining the ul container as a flex, the flex-wrap:wrap CSS property sets the flex items to wrap onto multiple lines if there isn't enough space to hold the item in the container.

For example, if we have multiple list item that the UL container cannot hold, if we use flex-wrap: wrap;it would wrap onto multiple lines, and if we didn't set the wrap, it would cause an overflow, here is an illustration:

5. flex-wrap illustration

This makes things responsive when reducing the screen, it wraps it down if it can't contain the the UL container, and I think that sums up the wrap. For the <li>, which is under the ul element, we added:

  #site-navigation li {
    flex: 1 0 auto;
    text-align: center;
  }

flex 1 0 auto is equivalent to:

flex-grow: 1;
flex-shrink: 0;
flex-basis: auto;

The flex-grow allows a flex-item to fill any available space, if for example you have the following, then  the second flex-item grow three times wider than the rest:

#site-navigation li:nth-of-type(1) {flex-grow: 1;}
#site-navigation li:nth-of-type(2) {flex-grow: 3;}
#site-navigation li:nth-of-type(3) {flex-grow: 1;}

I mean, the 2nd li item in the ul element.

The flex-shrink sets the flex shrink factor of a flex item. If the size of all flex items is larger than the flex container, items shrink to fit according to flex-shrink, this can avoid unnecessary overflow, and lastly the flex-basis:auto sets the initial main size of a flex item. It sets the size of the content box unless otherwise set with box-sizing.

The  text-align: center;is used to center the text in the flex-item, and that is it.

The bottom line of this section is that Flexbox works like a parent and a child relationship, so, the parent is the ul element, which is why we activated it with the display: flex, and thus becomes a flex container, the children of this flex container,  become flex items, in our case, the children are the li element, and it is the children because it is nested within the ul element.

Note that, once you set an element to be a flex container (in our case, the <ul>), it will only flex its immediate((<li>) children and not further descendants (the a tag is the further descendants in this case).

You can make those descendants flex containers as well, and we would do that in the next section.

Let's see another example...

Responsive Single Menu Layout (Plus Icons) With Flexbox

Create a new HTML file and a new css file for this

The HTML markup is similar to the first one we had, but with a slight twist:

<!DOCTYPE html>
<html lang="en-US">

<head>
  <meta charset="utf-8">
  <title></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="stylesheet" href="css/normalize.css">
  <link rel="stylesheet" href="css/main.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />

  <meta name="theme-color" content="#fafafa">
</head>

<body>

  <header class="site-header">
    <div class="header">

<!--
     RESPONSIVE SINGLE NAVIGATION MENU WITH ICON
-->

      <section class="menu-section">
        <nav id="site-navigation" class="single-nav menu" role="navigation">
          <ul>
            <li>
              <a href="#">
                <div class="icon">
                  <i class="fa fa-home"></i>
                </div>
                <div class="menu-text">
                  Home
                  <span>The Journey Starts From Here</span>
                </div>
              </a>
            </li>
            <li>
              <a href="#">
                <div class="icon">
                  <i class="fas fa-compact-disc" aria-hidden="true"></i>
                </div>
                <div class="menu-text">
                  Beats
                  <span>Latest Banging Beats</span>
                </div>
              </a>
            </li>
            <li>
              <a href="#">
                <div class="icon">
                  <i class="fas fa-wrench"></i>
                </div>
                  <div class="menu-text">
                  Plugins
                  <span>Sparking Music Plugins</span>
                </div>
              </a>
            </li>
            <li>
              <a href="#">
                <div class="icon">
                  <i class="fas fa-bullhorn"></i>
                </div>
                  <div class="menu-text">
                  Deals
                  <span>Music Deals</span>
                </div>
              </a>
            </li>
            <li>
              <a href="#">
                <div class="icon">
                  <i class="fas fa-address-book"></i>
                </div>
                <div class="menu-text">
                  Contact US
                  <span>We are few seconds away to reach us</span>
                </div>
              </a>
            </li>          
          </ul>
        </nav><!-- #site-navigation .single-nav -->
      </section><!-- menu-section -->

    </div><!-- div.header -->
  </header><!-- site-header -->

</body>

</html>

The twist here is, I added a link to fontawesome library:

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />

Which is what we would use for the icons, and I extended the li item to include an icon and a span:

<li>
  <a href="#">
    <div class="icon">
      <i class="fa fa-home"></i>
    </div>
    <div class="button-text">
      Home
      <span>The Journey Starts From Here</span>
    </div>
  </a>
</li>

The li element includes an href tag, within the href tag, we have two divs, one contains the icon, and the other contains our menu name with a span that holds the description about the menu, we would you same style we used in the first example for our base styles:

.header {
  margin: 0 auto;
  padding: 0 1em;
}

/* Base styles that apply to all menus */
.menu-section {
  padding-bottom: 2em;
  margin-bottom: 2em;
}

.menu ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  background-image: linear-gradient(150deg,#f0f3f1,#f0f3f1);
}

.menu li a {
  display: block;
  text-decoration: none;
  color: #001f3f;
  padding: .6em 2em;
  font-size: 18px;
}

.menu li a:hover {
  background: hsl(140deg 3% 73%);
  color: #4b1f3f;
}

and that looks like this:

5. single nav with icon -- mobile view

Since most of the items above have different containers for different purposes, we should start by giving the "a tag" a flex container, we then make the icon the flex item:

nav#site-navigation li a {
  display: flex;
  justify-content: flex-start;
}

.icon {
flex: 0 0 40px;
font-size: 30px;
}

So, a is the parent, and the icon or the div we give the icon class is the child, doing the above gives us this:

6. a tag and icon as parent and chile

The flex: 0 0 40px;in the icon class set the space between the icon and the menu text content, we can actually use margin to do this, but flex also gave us a nice option.

We should now properly position the main menu name, and the description (the span element), before I should you how I'll do it, note that, I added:

 <div class="button-text">
Home
<span>The Journey Starts From Here</span>
</div>

This gives me a way to target specific element, and I'll do it like so:

.menu-text {
  font-size: 30px;
}

.menu-text span {
  display: block;
  font-size: 40%;
}

I increase the font-size of the main menu text, and I set the display of the span tag to block which displays the element as a block element, it starts on a new line, and takes up the whole width, which is similar to <p> element. We then reduce the percentage of the font to make it a smaller in percentage than the actual menu tex, and that gives us:

7. the menu-text and span in single nav with icon

Easy right! This is our mobile version.

For the desktop version, we can do something like so:

@media screen and (min-width: 900px) {

  nav#site-navigation ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
  }

  nav#site-navigation li {
    display: flex;
  }

}

Well, we want the ul element to be flex, and we want to wrap whenever there is no space to contain the whole flex item. The justify-content property aligns the flexible container's items when the items do not use all available space on the main-axis (horizontally), so, in our case, I am justifying the flex-content to center, and here is the output:

8. Justify content example-min

Without the justify-content been set to center, all the flex-items would be aligned to the left as I reduce the screen size.

The reason why I gave the li flex is that I want the anchor link to scale vertically and fill up the available space, the following is an example of without the  display:flex on the li and display:flex on the li:

Before:

9. anchor in li item not spanning vertically

As you can see, if the content in the anchor tag is shorter, it won't scale vertically, and that is why I added the display:flex to it:

10. anchor in li scaling vertically

You can see the differences right ;)

Let's see an advanced example...

Multi-Level Menu Layout (Plus Icons) With Flexbox

Let's extend our previous example to support a multi-level dropdown menu, this would require a bit of JS to program the toggle functionality, don't worry, I won't use jquery or frameworks, we would do it with Vanilla JS in few steps, and I'll explain every step.

As usual, create a new HTML file and css for this. We would use the following HTML markup:

<!DOCTYPE html>
<html lang="en-US">

<head>
  <meta charset="utf-8">
  <title>My Name</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="stylesheet" href="css/normalize.css">
  <link rel="stylesheet" href="css/main.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />

  <meta name="theme-color" content="#fafafa">
</head>

<body>

  <header class="site-header">
    <div class="header">

<!--
     RESPONSIVE SINGLE NAVIGATION MENU WITH ICON
-->

      <section class="menu-section">
        <nav id="site-navigation" class="single-nav menu" role="navigation">
          <ul>
            <li class ="first-menu-li">
              <a href="#">
                <div class="icon">
                  <i class="fa fa-home"></i>
                </div>
                <div class="menu-text">
                  Home
                  <span>The Journey Starts From Here</span>
                </div>
              </a>
            </li>
            <li class="first-menu-li">
              <a href="#" class="menu-link">
                <div class="menu-text">
                  <i class="icon fas fa-compact-disc" aria-hidden="true"></i>
                  Beats
                  <button class="dropdown-toggle" aria-expanded="false" aria-label="Expand child menu">
                    <i class="fas fa-caret-down"></i>
                  </button>
                  <span>Latest Banging Beats</span>
                </div>
              </a>
              <!-- The child menu-->
              <ul class="sub-menu">
                <li class="second-menu-li"><a href="#">Afro Beats</a></li>
                <li class="second-menu-li"><a href="#">House Music</a></li>
                <li class="second-menu-li"><a href="#">Folklore</a></li>
              </ul>
            </li>
            <li class="first-menu-li">
              <a href="#" class="menu-link">
                  <div class="menu-text">
                    <i class="fas fa-wrench" aria-hidden="true"></i>
                    Plugins
                    <button class="dropdown-toggle" aria-expanded="false" aria-label="Expand child menu">
                      <i class="fas fa-caret-down"></i>
                    </button>
                  <span>Sparking Music Plugins</span>
                </div>
              </a>
              <!-- The child menu-->
              <ul class="sub-menu">
                <li class="second-menu-li"><a href="#">Trending Plugins</a></li>
                <li class="second-menu-li"><a href="#">Newly Added</a></li>
                <li class="second-menu-li"><a href="#">Outdated Plugins</a></li>
              </ul>
            </li>
            <li class="first-menu-li">
              <a href="#">
                <div class="icon">
                  <i class="fas fa-bullhorn"></i>
                </div>
                  <div class="menu-text">
                  Deals
                  <span>Music Deals</span>
                </div>
              </a>
            </li>
            <li class="first-menu-li">
              <a href="#">
                <div class="icon">
                  <i class="fas fa-address-book"></i>
                </div>
                <div class="menu-text">
                  Contact US
                  <span>We are few seconds away to reach us</span>
                </div>
              </a>
            </li>
          </ul>
        </nav><!-- #site-navigation .single-nav -->
      </section><!-- menu-section -->

    </div><!-- div.header -->
  </header><!-- site-header -->

</body>

</html>

It looks like this by default:

11. Multilevel nav no style

Ugly right? Using the base style we've been using for all of our nav and a bit of flex, we can do like so for our mobile version:

/*--------------------------------------------------------------
            Base Layout That Apply To The Menu
--------------------------------------------------------------*/

.header {
  margin: 0 auto;
  padding: 0 1em;
}

.menu-section {
  padding-bottom: 2em;
  margin-bottom: 2em;
}

.menu ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  background-image: linear-gradient(150deg,#f0f3f1,#f0f3f1);
}

.menu li a {
  display: block;
  text-decoration: none;
  color: #001f3f;
  padding: .6em 2em;
  font-size: 18px;
}

.menu li a:hover {
  background: hsl(140deg 3% 73%);
  color: #4b1f3f;
}
.menu ul li a {
  display: flex;
  justify-content: flex-start;
}

.icon {
flex: 0 0 40px;
font-size: 30px;
}

.menu-text {
  font-size: 30px;
}

.menu-text span {
  display: block;
  font-size: 40%;
}

/*--------------------------------------------------------------
            CHILD MENU or SUB MENU STYLES
--------------------------------------------------------------*/

nav#site-navigation ul ul {
  display: none;
  flex-direction: column;
  background: #fff;
  margin-left: 0;
  -webkit-box-shadow: 0 3px 3px rgba(0,0,0,.2);
  box-shadow: 0 3px 3px rgba(0,0,0,.2);
}

nav#site-navigation li:hover {
  transform: scale(1.02);
  z-index: 5;
}

nav#site-navigation ul ul.toggle-on {
  display: flex;
  z-index: 100;
}

In the child menu section of the CSS, I first hide the child sub menu container, here:

nav#site-navigation ul ul {
display: none;
flex-direction: column;
background: #fff;
margin-left: 0;
-webkit-box-shadow: 0 3px 3px rgba(0,0,0,.2);
box-shadow: 0 3px 3px rgba(0,0,0,.2);
}

The toggle style would be used for JS later, we would use it to style the toggle class when user clicks the toggle button. here:

nav#site-navigation ul ul.toggle-on {
display: flex;
z-index: 100;
}

and all the styles we've applied so far, looks like so in the browser:

12. Multilevel nav mobile version style

Clicking the toggle button won't do anything just yet, to enable that functionality, you can either create a new JS file and include the below code or just embed the following code before the closing body tag:

<script>
    /* DROPDOWN TOGGLE for the MUTLINAV MENU */
    const dropdowntoggle = document.querySelectorAll('.dropdown-toggle');

    // Loop over all the dropdown toggle and add event listener to each of them
    for(let i = 0, len = dropdowntoggle.length ; i < len ; i++) {

        dropdowntoggle[i].addEventListener('click', function (e){

            dropdowntoggle[i].classList.toggle("toggle-on");
            dropdowntoggle[i].ariaLabel = 'Expand child menu';
            dropdowntoggle[i].ariaExpanded = 'false';
            dropdowntoggle[i].lastElementChild.classList.toggle("fa-caret-up");
            dropdowntoggle[i].parentElement.nextSibling.parentElement.nextElementSibling.classList.toggle("toggle-on");

            if (dropdowntoggle[i].classList.contains("toggle-on")) {
                dropdowntoggle[i].ariaLabel = 'Collapse child menu';
                dropdowntoggle[i].ariaExpanded = 'true';
            }

            e.preventDefault();
        });
    }
</script>

</body>

It looks a bit difficult to read but trust me it is super easy, so, to understand this, I'll explain it a step at a time:

I started by selecting all the dropdown button that has a class of dropdown-toggle

const dropdowntoggle = document.querySelectorAll('.dropdown-toggle');

Since a querySelectorAll would return a nodelist, we use for loop to loop through each one of the dropdown button, here:

for(let i = 0, len = dropdowntoggle.length ; i < len ; i++) {

So, the for loop is saying as long as i is lesser than the length of items (in our case, the item is all the dropdown button class) in the collection, we then do something, in our case, we set an event listener to listen for a click, here:

for(let i = 0, len = dropdowntoggle.length ; i < len ; i++) {
// We add the event listner to the dropdown button
  dropdowntoggle[i].addEventListener('click', function (e){
  // We add functionality here to do something when a click occurs in the button
}

So, any click that happens when the toggle button is clicked, the event listener would capture it, if you look at the button toggle structure of our button in the HTML markup, we have two elements, one which is the button wrapper, and the other is the icon, here:

<button class="dropdown-toggle" aria-expanded="false" aria-label="Expand child menu">
  <i class="fas fa-caret-down"></i>
</button>

To properly enable, the toggle functionality, we need to target both, and since we already are listening to the event that occurs with the button, we can build the following functionality in out event function like so:

To properly enable the toggle functionality, we only need to target the dropdowntoggle we selected, and we walk down the element to change the icon or aria attributes if necessary:

    dropdowntoggle[i].addEventListener('click', function (e){

        dropdowntoggle[i].classList.toggle("toggle-on");
        dropdowntoggle[i].ariaLabel = 'Expand child menu';
        dropdowntoggle[i].ariaExpanded = 'false';
        dropdowntoggle[i].lastElementChild.classList.toggle("fa-caret-up");
        dropdowntoggle[i].parentElement.nextSibling.parentElement.nextElementSibling.classList.toggle("toggle-on");

        if (dropdowntoggle[i].classList.contains("toggle-on")) {
            dropdowntoggle[i].ariaLabel = 'Collapse child menu';
            dropdowntoggle[i].ariaExpanded = 'true';
        }

        e.preventDefault();
    });
}

This is really simple, the first set of the "dropdowntoggle[i]." before the if condition is used to set the initial values, the important one is the dropdowntoggle[i].classList.toggle("toggle-on"); and the others that have the toggle function. The rest are used to set the initial values of the attributes.

So, as soon as the user clicks the toggle button, we add a class of "toggle-on" to the button, here: dropdowntoggle[i].classList.toggle("toggle-on"); if you re-click the button, it removes it; just like a switch; "ON and OFF". No need for unnecessary if conditions.

All the other places you see ".toggle" would remove whatever class you are adding as soon as you re-click the button, I mean here:

...
dropdowntoggle[i].lastElementChild.classList.toggle("fa-caret-up");
dropdowntoggle[i].parentElement.nextSibling.parentElement.nextElementSibling.classList.toggle("toggle-on");

The first one changes the icon from caret-down to caret-up (^), that is an up icon indicating something is open, and the second one would change the sub-menu container from "sub-menu" to sub-menu toggle-on" which would activate the toggle-on styles in our CSS file. Remember, all these are done with the toggle function, so, it would switch them off or it would remove whatever you are adding as soon as you re-click the button.

For the if condition, we are simply changing the aria attributes as soon as our dropdowntoggle contains the class "toggle-on"

Easy peasy, even if you have thousands of menus on the same page, and you follow the menu structure, the toggle is gonna work with no issue whatsoever. You are welcome ;)

Here is out it looks:

13. Multinav bar toggle functionality

I navigated with the keyboard just to be sure everything is in place, and it works superbly.

Since we are done with the mobile version, I'll program the desktop version like so:

@media screen and (min-width: 900px) {

  nav#site-navigation ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
  }

  .first-menu-li {
    transition: all .3s ease-in-out;
    display: flex;
    position: relative;
  }

  /*

  Since we set the submenu element to position: absolute, it would remove it from the normal content flow
  The size of the the element will be the size of the content inside,

  The fix is to set relevant top (not necessary in our case), right, bottom and left values as I have done below

*/

  nav#site-navigation ul ul.toggle-on {
    display: flex;
    z-index: 100;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
  }

}

This enables a row layout for our menu in the desktop view:

  nav#site-navigation ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
  }

Nothing special at all.

We then target the first-menu-li, not the submenu li but the first menu li:

  .first-menu-li {
    transition: all .3s ease-in-out;
    display: flex;
    position: relative;
  }

The first one is a transition effect, and the second property would let the li item scale vertically and fill up the available space like the ones we had in the other examples:

14. The first li iten multinav scale vertically

The last property is "position:relative" which sets first-menu-li to its relative position without changing the layout around it. If we didn't do this, the li item would get a "positon:static" by default, this would make the element stick to the normal page flow, left/right/top/bottom and z-index won't even work on it. Setting this to relative won't change the element layout and we can have a sub-menu underneath without having it to jump around, here is an example of how it would look if we hadn't added a position:relative:

15. Positon relative showcase

As you can see the sub-menus are jumping around, and with position:relative, we were able to keep it in position.

The last style is this:

 nav#site-navigation ul ul.toggle-on {
display: flex;
z-index: 100;
position: absolute;
left: 0;
right: 0;
top: 100%;
}

This enable

  • flex on the submenu container,
  • we added a z-index of 100 to make it stack forward
  • we use positon absolute to take it out of the normal document flow, without doing this, it would stack next to the a tag that holds the first  li element like so:
    16. No positon absolute for the second container
  • left 0, and right: 0; would make the sub-menu 100% width of the parent container, see:
    17. Left and right zero for submenuYou see that right
  • The last property, which is "top: 100%;" in a "position:absolute" element sets the top edge of an element to a unit above/below the top edge of its nearest positioned ancestor (in our case, the nearest position is the anchor link that holds first li element), we gave it 100% to properly position it below the bottom edge of the anchor, see:
    18. top 100 percent in a positon absoluteYou see that right

The following is the preview of the desktop functionality:

19. Full previw of multinav menu

Interesting, I navigated with the keyboard just to be sure everything is in place. One thing that you might notice is that you need to click the toggle button before the sub-menu opens, which to me is the best approach, items in the sub-menu should be secondary, and it should require intent before it is open.

But if you insist on wanting the sub-menu to open on hover, and you also want to make it accessible to keyboard users, you can use the adjacent sibling selector (+) on hover to open the sub-menu when your user mouse hover the menu. Since the anchor tag is next to the sub-menu, we can do it like so:

.menu-link:hover + .sub-menu {
    display: flex !important;
    z-index: 100;
    position: absolute;
    left: 0;
    right: 0;
    top: 100%;
}

This means when the menu link is mouse hover, we add the above styles to the .sub-menu, it was set to display:none before, so, the styles would open it, this would work because the .sub-menu (which is the ul)  is directly next to the menu-link, see here:

-------------------------
------------------------
<a href="#" class="menu-link">
------------
-----------
    </div>
  </a>
  <!-- The child menu is next to the a tag-->
  <ul class="sub-menu">
----------------
---------------
  </ul>
</li>

Let's do something more interesting....

Creating a Responsive Social Menu With Flexbox

In this section we would create a responsive social menu with flexbox, we then take whatever that is we created here and combine it to our multinav menu making it a cool mixed menu, this would be super interesting.

For the HTML markup, we would use the following:

<!DOCTYPE html>
<html lang="en-US">

<head>
  <meta charset="utf-8">
  <title>My Name</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="stylesheet" href="css/normalize.css">
  <link rel="stylesheet" href="css/main.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />

  <meta name="theme-color" content="#fafafa">
</head>

<body>

  <header class="site-header">
    <div class="header">

<!--
     RESPONSIVE SOCIAL MEDIA MENU WITH ICON
-->

      <section class="menu-section">
        <h2 class="menu-heading">Social Menu</h2>
        <nav id="social-menu" class="social-menu menu" role="navigation">
          <ul>
            <li><a href="http://youtube.com"><span class="screen-reader-text">YouTube</span></a></li>
            <li><a href="http://twitter.com"><span class="screen-reader-text">Twitter</span></a></li>
            <li><a href="http://facebook.com"><span class="screen-reader-text">Facebook</span></a></li>
            <li><a href="http://pinterest.com"><span class="screen-reader-text">Pinterest</span></a></li>
            <li><a href="http://instagram.com"><span class="screen-reader-text">Instagram</span></a></li>
          </ul>
        </nav><!-- .social-menu -->
      </section><!-- .menu-section -->

    </div><!-- div.header -->
  </header><!-- site-header -->

</body>

</html>

We would add our default base CSS styles like so:

/*--------------------------------------------------------------
            Base Layout That Apply To The Menu
--------------------------------------------------------------*/

.header {
  margin: 0 auto;
  padding: 0 1em;
}

.menu-section {
  padding-bottom: 2em;
  margin-bottom: 2em;
}

.menu ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  background-image: linear-gradient(150deg,#f0f3f1,#f0f3f1);
}

.menu li a {
  display: block;
  text-decoration: none;
  color: #001f3f;
  padding: .6em 2em;
  font-size: 18px;
}

.menu li a:hover {
  background: hsl(140deg 3% 73%);
  color: #4b1f3f;
}

.menu-text {
  font-size: 30px;
}

.menu-text span {
  display: block;
  font-size: 40%;
}

20. Social menu nav preview

For the icon and CSS we would use fontawesome like so:

/*-----------------------------------------------------------------
                    SOCIAL MENU STYLES
 -----------------------------------------------------------------*/
/* This would append the following styles to all the icon*/
.social-menu li a:before {
  font-family: "Font Awesome 5 Brands";
  -webkit-font-smoothing: antialiased;
  display: inline-block;
  font-style: normal;
  font-variant: normal;
  text-rendering: auto;
  line-height: 1;
  content: '\f08e';
}

/*
TThe below which is the contents would be added to the ::before of each icon, this would make the icon visible
*/

.social-menu li a[href*="facebook.com"]::before { content: '\f09a'; }

.social-menu li a[href*="github.com"]::before { content: '\f09b'; }

.social-menu li a[href*="instagram.com"]::before { content: '\f16d'; }

.social-menu li a[href*="linkedin.com"]::before { content: '\f0e1'; }

.social-menu li a[href*="pinterest.com"]::before { content: '\f0d2'; }

.social-menu li a[href*="reddit.com"]::before { content: '\f1a1'; }

.social-menu li a[href*="stackoverflow.com"]::before { content: '\f16c'; }

.social-menu li a[href*="twitter.com"]::before { content: '\f099'; }

.social-menu li a[href*="vimeo.com"]::before { content: '\f194'; }

.social-menu li a[href*="youtube.com"]::before { content: '\f167'; }

The first declaration block would insert the declared styles before the social-menu li a element, which is why I used ":before". The other styles are just fontawesome icon that targets differents social sites, for example, the following would add the content "\fo9a" before href ="facebook.com", which would display the fontawesome icon for facebook.

.social-menu li a[href*="facebook.com"]::before { content: '\f09a'; }

This is similar to the rest of the sites.

and that gives us the following:

21. Social icons with social labels

You can choose to leave it like so, if you want but who doesn't know major social platform logos. Let's remove it but we should also make it accessible for screen readers, we can do it like so:

/*------------------------------------------------------------------------------------------
     Accessibility - We add this for visual users, but would only be read by screen reader
-------------------------------------------------------------------------------------------*/
.screen-reader-text {
  clip: rect(1px, 1px, 1px, 1px);
  position: absolute !important;
  height: 1px;
  width: 1px;
  overflow: hidden;
}

.screen-reader-text:hover,
.screen-reader-text:active,
.screen-reader-text:focus {
  background-color: #f1f1f1;
  border-radius: 3px;
  box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.6);
  clip: auto !important;
  color: #21759b;
  display: block;
  font-size: 14px;
  font-weight: bold;
  height: auto;
  left: 5px;
  line-height: normal;
  padding: 15px 23px 14px;
  text-decoration: none;
  top: 5px;
  width: auto;
}

This would remove the text but would make it accessible to screen readers, this way, it is still accessible, here is the result so far:

22. Social Icon with screen reader text

To make it horizontal, you can use flexbox like so:

/*
Flexbox for scoial nav
 */

.social-menu ul {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

Which was result to:

23 .social icon nav center

If you want to play with the position, you can easily play with the justify-content, here is an illustration:

24. Justify content for scial nav ilustration

Cool.

Note that this is responsive as is, if you reduce the screen width, it would wrap the icon if it can't contain the flex container.

Now, let's combine this with the multi nav menu...

Combining Multinav Menu With The Social Menu

We would combine the markup of both menu, but instead of putting them in a different section, we would put them in one section element, we would then use a div as the parent container like so:

<!DOCTYPE html>
<html lang="en-US">

<head>
  <meta charset="utf-8">
  <title>My Name</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="stylesheet" href="css/normalize.css">
  <link rel="stylesheet" href="css/main.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" crossorigin="anonymous" />

  <meta name="theme-color" content="#fafafa">
</head>

<body>

  <header class="site-header">
    <div class="header">

<!--
     RESPONSIVE SINGLE NAVIGATION MENU WITH ICON
-->

      <section class="menu-section">
        <div class="combined-menu">
    <!-- The Multinav Menu -->
        <nav id="site-navigation" class="single-nav menu" role="navigation">
          <ul>
            <li class ="first-menu-li">
              <a href="#">
                <div class="icon">
                  <i class="fa fa-home"></i>
                </div>
                <div class="menu-text">
                  Home
                  <span>The Journey Starts From Here</span>
                </div>
              </a>
            </li>
            <li class="first-menu-li">
              <a href="#" class="menu-link">
                <div class="menu-text">
                  <i class="icon fas fa-compact-disc" aria-hidden="true"></i>
                  Beats
                  <button class="dropdown-toggle" aria-expanded="false" aria-label="Expand child menu">
                    <i class="fas fa-caret-down"></i>
                  </button>
                  <span>Latest Banging Beats</span>
                </div>
              </a>
              <!-- The child menu-->
              <ul class="sub-menu">
                <li class="second-menu-li"><a href="#">Afro Beats</a></li>
                <li class="second-menu-li"><a href="#">House Music</a></li>
                <li class="second-menu-li"><a href="#">Folklore</a></li>
              </ul>
            </li>
            <li class="first-menu-li">
              <a href="#" class="menu-link">
                  <div class="menu-text">
                    <i class="fas fa-wrench" aria-hidden="true"></i>
                    Plugins
                    <button class="dropdown-toggle" aria-expanded="false" aria-label="Expand child menu">
                      <i class="fas fa-caret-down"></i>
                    </button>
                  <span>Sparking Music Plugins</span>
                </div>
              </a>
              <!-- The child menu-->
              <ul class="sub-menu">
                <li class="second-menu-li"><a href="#">Trending Plugins</a></li>
                <li class="second-menu-li"><a href="#">Newly Added</a></li>
                <li class="second-menu-li"><a href="#">Outdated Plugins</a></li>
              </ul>
            </li>
            <li class="first-menu-li">
              <a href="#">
                <div class="icon">
                  <i class="fas fa-bullhorn"></i>
                </div>
                  <div class="menu-text">
                  Deals
                  <span>Music Deals</span>
                </div>
              </a>
            </li>
            <li class="first-menu-li">
              <a href="#">
                <div class="icon">
                  <i class="fas fa-address-book"></i>
                </div>
                <div class="menu-text">
                  Contact US
                  <span>We are few seconds away to reach us</span>
                </div>
              </a>
            </li>
          </ul>
        </nav><!-- #site-navigation .single-nav -->
 <!-- The Social Menu -->
        <nav id="social-menu" class="social-menu menu" role="navigation">
          <ul>
            <li><a href="http://youtube.com"><span class="screen-reader-text">YouTube</span></a></li>
            <li><a href="http://twitter.com"><span class="screen-reader-text">Twitter</span></a></li>
            <li><a href="http://facebook.com"><span class="screen-reader-text">Facebook</span></a></li>
            <li><a href="http://pinterest.com"><span class="screen-reader-text">Pinterest</span></a></li>
            <li><a href="http://instagram.com"><span class="screen-reader-text">Instagram</span></a></li>
          </ul>
        </nav><!-- .social-menu -->

        </div><!-- combined-menu -->
      </section><!-- .menu-section -->

    </div><!-- div.header -->
  </header><!-- site-header -->

<script>
  const dropdowntoggle = document.querySelectorAll('.dropdown-toggle');

  // Loop over all the dropdown toggle and add event listener to each of them
  for(let i = 0, len = dropdowntoggle.length ; i < len ; i++) {
    dropdowntoggle[i].addEventListener('click', function (e){
      if (e.target.className === 'dropdown-toggle') {
        e.target.className = 'dropdown-toggle toggle-on' // for the button
        e.target.ariaLabel = 'Collapse child menu';     // change the aria label to collapse child menu when the toggle is on
        e.target.ariaExpanded = 'true';
        e.target.parentElement.nextSibling.parentElement.nextElementSibling.className = 'sub-menu toggle-on'
        e.target.lastElementChild.className = 'fas fa-caret-up'
      }
      else if (e.target.className === 'fas fa-caret-down') {
        e.target.parentElement.className = 'dropdown-toggle toggle-on'
        e.target.parentElement.ariaLabel = 'Collapse child menu';     // change the aria label to collapse child menu when the toggle is on
        e.target.parentElement.ariaExpanded = 'true';
        e.target.parentElement.parentElement.nextSibling.parentElement.nextElementSibling.className = 'sub-menu toggle-on'
        e.target.className = 'fas fa-caret-up'
      }
      else if (e.target.className === 'dropdown-toggle toggle-on') {
        e.target.className = 'dropdown-toggle'
        e.target.ariaLabel = 'Expand child menu';  // change the aria label to expand child menu when the toggle is off
        e.target.ariaExpanded = 'false';
        e.target.parentElement.nextSibling.parentElement.nextElementSibling.className = 'sub-menu'
        e.target.lastElementChild.className = 'fas fa-caret-down'
      }
      else {
        e.target.className = 'dropdown-toggle'
        e.target.parentElement.ariaLabel = 'Expand child menu';  // change the aria label to expand child menu when the toggle is off
        e.target.parentElement.ariaExpanded = 'false';
        e.target.parentElement.parentElement.nextSibling.parentElement.nextElementSibling.className = 'sub-menu'
        e.target.className = 'fas fa-caret-down'
      }

      e.preventDefault();
    });
  }
</script>

</body>

</html>

This would give us the following:

24. Mixed menu structure

We give the combine menu class that is holding the two nav menu the following style:

@media screen and (min-width: 900px) {

/*-----------------------------------------------------------------
          COMBINE MENU STYLE -- THE CLASS HOLDING THE TWO NAV
 -----------------------------------------------------------------*/
.combined-menu {
  display: flex;
  background: hsl(140deg 11% 95%);
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-evenly;
}

Put the CSS in the media query as we are targeting the desktop view.

This won't do anything yet because the multinav menu is to wide, so, let's readjust and reduce a couple of component there, change this in the media query:

@media screen and (min-width: 900px)
nav#site-navigation ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
}

To:

@media screen and (min-width: 900px)
nav#site-navigation ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-start;
}

This would positon the first nav to the left, it still won't position, just hold on...

Now, change the social nav from:

.social-menu ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
}

to:

.social-menu ul {
    display: flex;
    flex-wrap: wrap;
    justify-content: flex-end;
}

This would position the social nav to the right, this also won't because the multinav is too wide (this would probably depend on the screen, mine isn't too wide), so, let's now reduce a couple of component:

Change the menu li a class from:

.menu li a {
    display: block;
    text-decoration: none;
    color: #001f3f;
    padding: .6em 2em;
    font-size: 18px;
}

to:

.menu li a {
    display: block;
    text-decoration: none;
    color: #001f3f;
    padding: .6em .8em;
    font-size: 18px;
}

As you can see I change the padding, so, this would affect the padding left and padding right of all the menu links, it would apply to both the multinav link and the submenu link, and that is because we are targeting: "menu li a"

This is the end result:

25. Completed mixed menu with multinav nav and social nav

Card Layout

Now, let's create a card layout to supplement our nav menu, I won't be creating different kinds of card layout, I'll just give you a simple example of how you can do it easily with flexbox, you can then expand on that if you like.

I'll re-adjust the markup a little to fit in with the new layout like so

<!DOCTYPE html>
<html lang="en-US">

<head>
  <meta charset="utf-8">
  <title>My Name</title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">

  <link rel="stylesheet" href="css/normalize.css">
  <link rel="stylesheet" href="css/main.css">
  <link crossorigin="anonymous" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"
        integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="
        rel="stylesheet"/>

  <meta name="theme-color" content="#fafafa">
</head>

<body>

<header class="site-header">
    <!--
         RESPONSIVE SINGLE NAVIGATION MENU WITH ICON
    -->
    <section class="menu-section">
      <div class="combined-menu">
        <!-- The Multinav Menu -->
        <nav id="site-navigation" class="single-nav menu" role="navigation">
            -------------
        </nav><!-- #site-navigation .single-nav -->
        <!-- The Social Menu -->
        <nav id="social-menu" class="social-menu menu" role="navigation">
            --------------
        </nav><!-- .social-menu -->

      </div><!-- combined-menu -->
    </section><!-- .menu-section -->

</header><!-- site-header -->

<main class="main-area">
  <div class="site-content">
  <!--
      ENTRY CONTENT
  -->

<section class="cards">

  <article class="card">
    <a href="#">
      <figure class="thumbnail">
        <img src="https://via.placeholder.com/450" alt="Placeholder image">
      </figure>
      <div class="card-content">
        <h2>Creating a Tiny PHP MVC Framework From Scratch</h2>
        <p>Mautic doesn’t support sending plain-text email yet, but you can still mimic plain-text kinda email, here is an example: I’ll show you two ways to use this...</p>
      </div><!-- .card-content -->
      Visit Page
    </a>
  </article><!-- .card -->

  <article class="card">
    <a href="#">
      <figure class="thumbnail">
        <img src="https://via.placeholder.com/450" alt="Placeholder image">
      </figure>
      <div class="card-content">
        <h2>Creating a Tiny PHP MVC Framework From Scratch</h2>
        <p>Mautic doesn’t support sending plain-text email yet, but you can still mimic plain-text kinda email, here is an example: I’ll show you two ways to use this...</p>
      </div><!-- .card-content -->
      Visit Page
    </a>
  </article><!-- .card -->

  <article class="card">
    <a href="#">
      <figure class="thumbnail">
        <img src="https://via.placeholder.com/450" alt="Placeholder image">
      </figure>
      <div class="card-content">
        <h2>Creating a Tiny PHP MVC Framework From Scratch</h2>
        <p>Mautic doesn’t support sending plain-text email yet, but you can still mimic plain-text kinda email, here is an example: I’ll show you two ways to use this...</p>
      </div><!-- .card-content -->
      Visit Page
    </a>
  </article><!-- .card -->

  <article class="card">
    <a href="#">
      <figure class="thumbnail">
        <img src="https://via.placeholder.com/450" alt="Placeholder image">
      </figure>
      <div class="card-content">
        <h2>Creating a Tiny PHP MVC Framework From Scratch</h2>
        <p>Mautic doesn’t support sending plain-text email yet, but you can still mimic plain-text kinda email, here is an example: I’ll show you two ways to use this...</p>
      </div><!-- .card-content -->
      Visit Page
    </a>
  </article><!-- .card -->

  <article class="card">
    <a href="#">
      <figure class="thumbnail">
        <img src="https://via.placeholder.com/450" alt="Placeholder image">
      </figure>
      <div class="card-content">
        <h2>Creating a Tiny PHP MVC Framework From Scratch</h2>
        <p>Mautic doesn’t support sending plain-text email yet, but you can still mimic plain-text kinda email, here is an example: I’ll show you two ways to use this...</p>
      </div><!-- .card-content -->
      Visit Page
    </a>
  </article><!-- .card -->

  <article class="card">
    <a href="#">
      <figure class="thumbnail">
        <img src="https://via.placeholder.com/450" alt="Placeholder image">
      </figure>
      <div class="card-content">
        <h2>Creating a Tiny PHP MVC Framework From Scratch</h2>
        <p>Mautic doesn’t support sending plain-text email yet, but you can still mimic plain-text kinda email, here is an example: I’ll show you two ways to use this...</p>
      </div><!-- .card-content -->
      Visit Page
    </a>
  </article><!-- .card -->

  <article class="card">
    <a href="#">
      <figure class="thumbnail">
        <img src="https://via.placeholder.com/450" alt="Placeholder image">
      </figure>
      <div class="card-content">
        <h2>Creating a Tiny PHP MVC Framework From Scratch</h2>
        <p>Mautic doesn’t support sending plain-text email yet, but you can still mimic plain-text kinda email, here is an example: I’ll show you two ways to use this...</p>
      </div><!-- .card-content -->
      Visit Page
    </a>
  </article><!-- .card -->

</section><!-- .cards -->

</div><!-- div.site-content -->
</main>

</body>

</html>

Note: You still need to include the script to make the toggle work, I removed it here to make it easier to read the markup

The changes I did was I removed the div.header element in the header section as that was unnecessary, so, now, the navigation only stays in the header element. I am trying to create a semantic markup, which describes the page meaning to both the browser and the developer, non semantic markup is using div or span which tells nothing about what you are doing except that it is a container.

An example of semantic markup is "<header>" you see what I am saying, that tells us we are dealing with the header section, likewise, a <nav> tells us we are dealing with the navigation.

For the card layout, I added the <main> element, which specifies the main content of a document, and inside the <main> tag, I added a <div>, I know you might say, Uhm, that is not semantic, but this is just a container that holds the section, so, that is fine.

Inside the <section>, we have an <article> element that specifies independent, and self-contained content, we have an anchor link that surrounds the element in the article tags, this would make it clickable as a whole.

So, what do we have inside the a tag? We have the <figure> element which specifies self-contained content, like images, code listings, etc. Inside it, we have the <img> element, and that defines the actual image.

Lastly, the <figure> sibling is a div container with a class name "card-content" this holds our header and paragraph of the content, and here is the result we get from that:


26. Card layout structure

We would use the following CSS to clean it up a bit:

/*--------------------------------------------------------------
                      CARD LAYOUT STYLES
--------------------------------------------------------------*/

.card {
  background: white;
  margin-bottom: 2em;
  border-radius: 8px;
  box-shadow: 0 4px 34px 0 rgba(93,100,148,.2);
}

.card a {
  color: black;
  text-decoration: none;
}

.card a:hover {
  box-shadow: 3px 3px 8px hsl(0, 0%, 70%);
}

.card-content {
  padding: 1.4em;
}

.card-content h2 {
  margin-top: 0;
  margin-bottom: .5em;
  font-weight: normal;
}

.card-content p {
  font-size: 95%;
}

/*This aligns the image properly in the card container*/
figure.thumbnail {
  margin: 0;
}

/* STYLES FOR THE IMAGES */

img {
display: block;
border: 0;
width: 100%;
height: auto;
}

Which results to:

27. Card layout clean up mobile version

Cool, this is our mobile version but the issue we have now is if we view it in the desktop view, the image widens along as you increase the screen size. We can use media query to set the maximum width when it reaches a screen width of 800px and above, use this:

@media screen and (min-width: 800px)
  .site-content {
    max-width: 800px;
  }
}

This would set a max-width of 800px to the div.site-content when the screen is equal or greater than 800px.

Now, I'll set a media query that splits up our card in two when the display is 950px and above, remember we are all in for mobile first approach, so, we move from a smaller screen design to bigger.

Now, we would make the section.cards container a flex container, which is the section element that holds all individual tag, we the set a flex basis which is the width of each individual card to 50%:


  @media screen and (min-width: 950px) {
    .cards { /* The section element that holds all individual cards */
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
    }

    .card { /* The individual card */
      flex: 0 1 50%;
    }

  }

When we get to that viewport, it looks like so:

29. flex-item basis for media query 950px

Looks weird right? But that is actually what we described for, we did "flex: 0 1 50%" which means grow by a factor of zero (0), shrink by a factor of one (1), and distribute the size by 50% which is how wide each of the item would be, so, there is only space for 2 items.

We can reduce the width so, we can have room for a bit space, and since the parent flex is already using "justify-content: space-between" it would space it appropriately.

Now, we can change this:

  @media screen and (min-width: 950px) {

    .card { /* The individual card */
      flex: 0 1 50%;
    }

  }

To:

  @media screen and (min-width: 950px) {

    .card { /* The individual card */
      flex: 0 1 calc(50% - 1em);
    }

  }

The calc is used to perform simple operation, here we are simply saying, each item width should be the result of "50% - 1em" (1em is 16px), you might think why not just add 49%. The thing is we are substracting an em from a percentage value, so, the .card would always have a width that is 1em or 16px less than 50%. As you can see we are substracting using em and not just percentage, so, the browser would grab 50% of each item space and remove 1em from it.

30. flex-item basis for media query 950px spacing using css calc

That is that, let's split up the item in 3's when we get to a viewport of maybe around 1020px and above, so, the other one we set would break when it gets to 1020px, and this one would take it from there, here is how I'll do that:

  @media screen and (min-width: 1020px) {
    .cards { /* The section element that holds all individual cards */
      display: flex;
      flex-wrap: wrap;
      justify-content: space-between;
    }

    .card { /* The individual card */
      flex: 0 1 calc(33% - 1em); /* set each item to 33% width and remove 1em from each of them */
    }

  }

Which gives us the following:

31. flex-item basis for media query 1020px

Now, the website is responsive, but we have one simple issue where the link is not spanning across the item, that is some portions are clickable and some aren't clickable, to fix this, we simply add flex to the card itself, and it would span across the parent container making it clickable, so, change:

@media screen and (min-width: 950px) {
  .cards { /* The section element that holds all individual cards */
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
  }

  .card { /* The individual card */
    flex: 0 1 calc(50% - 1em);
  }
}

To:

@media screen and (min-width: 950px) {
  .cards { /* The section element that holds all individual cards */
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
  }

  .card { /* The individual card */
    display: flex; /* <- The changes */
    flex: 0 1 calc(50% - 1em);
  }
}

That's it...let's get more pratical

Implementing a Responsive Holy Grail Layout With Flexbox

Well, you are currently on a web page that implements holy grail. Holy Grail is a web layout pattern that consists of a header, a main content area with fixed-width navigation on the left, content in the middle and a fixed-width sidebar on the right and then a footer.

We would create a simple Holy Grail layout, and in the coming section, we would use it to compliments the stuff we've been working on so far.

I'll use the following markup for my Holy grail:

<!DOCTYPE html>
<html lang="en-US">

  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Simple Holy Grail Layout</title>

    <link rel="stylesheet" href="main.css" type="text/css" media="all">
    <link crossorigin="anonymous" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css" integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA==" rel="stylesheet" />
  </head>

  <body>

    <div class="layout-wrapper">

      <header class="site-header">
        <!-- Header content -->
        <div class="header">

          <section class="menu-section">
            <nav id="site-navigation" class="single-nav menu" role="navigation">
              <ul>
                <li><a href="#"></a></li>
              </ul>
            </nav><!-- #site-navigation .single-nav -->
          </section><!-- menu-section -->

        </div><!-- div.header -->
      </header> <!-- site-header -->

      <div class="content">

        <main class="main-area">

          <!-- Main content -->
          <article class="entry-content">
            <h2>Creating a Tiny PHP MVC Framework From Scratch</h2>
            <p></p>
          </article><!-- .card -->

        </main>
        
         <div class="sidebar">

        <aside>
          <!-- Sidebar / Ads -->
          <nav class="example-menu">
            <ul>
              <li><a href="#"></a></li>
            </ul>
          </nav>
        </aside>

      </div> <!-- div.sidebar -->
      
      </div>

      <footer class="footer-area">
        <!-- Footer content -->
        <p></p>
      </footer>

    </div> <!-- div.layout-wrapper -->

  </body>

</html>

This is the just the layout, and as you can see, immediately after the body element, we wrapped all of our elements in a "div.layout-wrapper" inside there we have the header element which obviously would hold our menu.

The adjacent sibling next to the header is a div container that wraps the content and the sidebar, since the sidebar is part of our content, they should be in one div, in that "div.content" we have the <main> element for our actual content and a <aside> for the sidebar.

The adjacent sibling next to the "div.content" is the footer, and that would off course hold the footer content, the following is the HTML structure with no styles:

32. Holy Grail Layout HTML Structure

Let's start by adding the styles for the header menu, I'll use the one we did in the "Responsive Single Menu Layout With Flexbox":

/*--------------------------------------------------------------
            Base Layout That Apply To The Menu
--------------------------------------------------------------*/
body {
    margin: 0;
}

.site-content {
  margin: 0 auto;
  padding: 0 1em;
}

.menu-section {
  margin-bottom: 2em;
}

.menu ul {
  list-style-type: none;
  padding: 0;
  margin: 0;
  background-image: linear-gradient(150deg,#f0f3f1,#f0f3f1);
}

.menu li a {
  display: block;
  text-decoration: none;
  color: #001f3f;
  padding: .6em .8em;
  font-size: 18px;
}

.menu li a:hover {
  background: hsl(140deg 3% 73%);
  color: #4b1f3f;
}
.menu ul li a {
  display: flex;
  justify-content: flex-start;
}

.menu-text {
  font-size: 30px;
}

.menu-text span {
  display: block;
  font-size: 40%;
}


@media screen and (min-width: 900px) {

  #site-navigation ul {
    display: flex;
    flex-wrap: wrap;
  }

  #site-navigation li {
    flex: 1 0 auto;
    text-align: center;
  }

}


The only thing different here is the "margin:0" that I added to the body element, and that would make sure the body doesn't have any unnecessary margins, this way, the footer or whatever can sit properly in its place.

and for the entry-content, the footer, and the sidebar, I'll use the following styles:

/*--------------------------------------------------------------
            Entry and Footer Content
--------------------------------------------------------------*/

/* Entry Content */

.entry-content {
	padding: 0 1.4em 1.4em;
}

/* Footer Style */

.footer-area {
	padding: .4em 1em;
	text-align: center;
  background-image: linear-gradient(150deg,#f0f3f1,#f0f3f1);
}

/* Sidebar */
.sidebar {
  padding: .5em 1.4em 1em;
  margin-bottom: 2em;
  background-image: linear-gradient(150deg,#f0f3f1,#f0f3f1);
}

and the result is:


33. Holy Grail Mobile Versiony

The image looks as though I added a border-bottom, which isn't actually a border-bottom, it is a demarcation between my windows 10 taskbar and my web browser, this is to show you that the footer isn't sitting at the bottom properly, and that is where flexbox comes in...

Since we have wrapped the header, content, and the footer in a div.layout-wrapperwe can turn that into a flex container, and make it stack next to each other, if we hadn't wrapped the header, content, and footer in div.layout-wrapper, we would have used the body element, but I guess you get my point for using the main container, having done that, we then give then "div.layout-wrapper" a  height of 100vh and margin:0.The 100vh means 100% of the viewport height, in this case, we are stretching the container to at least the full height of the screen which will not trigger overflow if the content is short and it will allow content to continue stretching the height as needed.

Lastly, we then give footer:  margin-top:auto;this would automatically push the footer to the bottom of the screen, all together we have:

/*--------------------------------------------------------------
   DIV Container that wraps header, content and footer
--------------------------------------------------------------*/

.layout-wrapper {
  display: flex;
  flex-direction: column;
}

.layout-wrapper {
  height: 100vh;
  margin: 0;
}

.footer-area {
  margin-top: auto;
}

The result is as follows:

34. Holy Grail Mobile Version_footer stick to bottom

and the complete desktop version styles:

Note: I changed the min-width from 900px to 700px

@media screen and (min-width: 700px) {

  .content {
    display: flex;
  }

  .main-area {
    flex: 1 1 auto;
  }

  .entry-content {
    max-width: 45em;
    margin: 0 auto;
  }

  .sidebar {
    flex: 0 0 12em;
  }

  #site-navigation ul {
    display: flex;
    flex-wrap: wrap;
  }

  #site-navigation li {
    flex: 1 0 auto;
    text-align: center;
  }

}

Before I explain how it works, the result is as follows:

35. Holy Grail Desktop Version

I deliberately added margin spaces below and ontop the sidebar, if you don't want that, then remove the  margin-bottom: 2em;we added to the ".menu-section" and also remove the  margin-bottom: 2em;we added to the "sidebar".

Another fancy stuff you can do with this layout is to re-order there position, and you can do that easily with flexbox without changing any markup, before I explain how it works, here is a visual illustration:

36. Flex order illustration

Note that, this won't change the order in which the items are navigated to using keyboard or screen reading, this kinda make sense, since it doesn't change the markup, it is merely a visual re-ordering.

So, the items are assigned an integer that represents their group. The items are then placed in the visual order according to that integer, lowest value comes first, as you can see in the illustration above, I gave sidebar "order:1" and the main-area "order:2", and that would position the sidebar first, and the main-area second.

However, If more than one item has the same integer value, then within that group the items are laid out as per source order, meaning they are placed the way they are laid down in the markup.

This is a long guide, I wrote it back in 2020 and I just decided to publish it now, I hope you learnt one or two things from this guide, see ya!