My WebDev Notes: Fullscreen overlay navigation

Habdul Hazeez - Apr 13 '20 - - Dev Community

Check the overlay navigation online: https://ziizium.github.io/my-webdev-notes/fullscreen-navigation/

Introduction

Navigation is an essential part of every website and on a normal day, it will contain links to other resources on a website. You will find them in various design and layout based on the website.

This experiment, as the title indicates is about creating a fullscreen overlay navigation using HTML, CSS, and JavaScript. HTML for the markup, CSS for the presentation and JavaScript for the behavior.

Some CSS properties are not supported by all browsers and we will have to do some testing before throwing some code at the browser. Luckily, CSS provides a way to do this via the @supports feature query.

In addition, the overlay navigation will be hidden from view by default and will be available on mobile viewport via JavaScript. But we should know that some users might have JavaScript turned off in their browser therefore, we have to make the navigation accessible when the user has JavaScript turned off.

The problem is: How can we make the navigation accessible to users with JavaScript turned off?.

I am pretty sure there are many ways to solve a problem but the solution I came up with might not be fancy. Here is what I did.

I created two stylesheets, one for browsers with JavaScript turned off and the other for a browser with JavaScript enabled. The stylesheet for the users with JavaScript turned off will be the default stylesheet. When the page loads we do a little check to see if the browser has javascript enabled then we switch to the second stylesheet.

All this is all in the spirit of progressive enhancement.

The HTML Code

The HTML for the navigation is short and easy to understand (nothing fancy). I actually took it from the fictional website I created for the series: FrontEnd Development: Zero to Hero with few additions.

<div class="top">
    <header class="header">
        <h1 class="header__name"><a href="index.html">Alice & Bob&trade;</a></h1>
        <span class="hambuger__icon" onclick="openNav()">&#9776;</span>
    </header>

    <nav id="navigation" class="navigation overlay">

        <a href="#" class="closebtn" onclick="closeNav()">&times;</a>

        <ul class="navigation__menu overlay__content">
            <li class="navigation__item">
               <a href="#" class="navigation__link">Web Design</a>
            </li>
            <li class="navigation__item">
               <a href="#" class="navigation__link">Graphics Design</a>
            </li>
            <li class="navigation__item">
               <a href="#" class="navigation__link">Contact Us</a>
            </li>
            <li class="navigation__item">
               <a href="#" class="navigation__link">Our Blog</a>
            </li>
        </ul>
    </nav>
</div>
Enter fullscreen mode Exit fullscreen mode

The CSS and JavaScript

Create a new stylesheet and link it with the HTML code above. In the stylesheet paste the snippet below. To be the best of my understanding the code is easy to understand as seen in the final project for the series on web development.

The CSS will create the default view for the navigation at different viewports and will be available to users who have JavaScript turned off in their browser.

/**
 * Save the code with any name of your choosing
 * preferably nojs.css
 */

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

a {
    text-decoration: none;
}

li {
   list-style-type: none;   
}

.top {
    background-color: #1f2a47;
    color: #ffffff;
}

.header {
    padding: 1.2em;
}

/**
 * Mobile navigation menu
 */
.navigation__menu {
    margin-top: 1.2em;
    border: 1px solid #cccccc;
}

/**
 * We apply a top borer to the navigation
 * items except the first one.
 */
.navigation__item:not(:first-child) {
    border-top: 1px solid #ccc;
}

/**
 * We turn all links to block level elements
 * then we applie some padding to make them
 * clickable.
 */
.navigation__link {
    display: block;
    padding: 0.8em 1em;
    font-weight: normal;
    color: #ffffff;
}

/**
  * Am pretty sure this is straighforwad :)
  */
.navigation__link:hover,
.navigation__link.active {
    background-color: #06162f;
    color: #ffffff;
}

.header__name {
    margin-bottom: 0.5em;
    font-weight: 700;
    color: #ffffff;
    text-align: center;
}

/**
 * We hide the hamburger icon and the close button
 * because if javascript is disabled in the user's browser
 * they will not be usable.
 */
.hambuger__icon,
.closebtn {
    display: none;
}

/**
 * From 560px (16 * 35em) upwards we turn the
 * the navigation to a grid container and then
 * arrange the navigation links in a grid template
 * area for browsers that support CSS Grid.
 */ 
@media screen and (min-width: 35em) {
    @supports (display: grid) {
        .navigation__menu {
            display: grid;
            grid-template-areas: "h1 h2" "h3 h4";
        }

        .navigation__item:nth-child(2) {
            border-top: none;
        }
    }
}

/**
 * Almost at a desktop view, we turn the navigation
 * into a flex container and we also remove the border
 * from the navigation items.
 */
@media screen and (min-width: 55em) {
    .top {
        display: flex;
        justify-content: space-between;
    }

    .navigation__menu {
        display: flex;
        border: none;
        margin-right: 4em;
    }

    .navigation__link {
        margin-right: 20px;
    }

    .navigation__item:not(:first-child) {
        border-top: none;
    }
}
Enter fullscreen mode Exit fullscreen mode

Save your file and load the HTML in your browser. Snap your browser to a mobile viewport, you should get the same output below as seen in Firefox 75:

A navigation mobile viewport of the navigation
Mobile viewport

You can resize your browser to see how the navigation adapts to different viewports.

When the page loads we need to perform a quick check to see if JavaScript is enabled then we switch to our second stylesheet that will contain code tailored to users that have JavaScript enabled including the overlay navigation.

Before writing the JavaScript below create a file named styles.css and save it in your current working directory.

/**
 * Save this JavaScript in a script tag at the bottom
 * of the HTML file before the closing <body> tag
 * or in another file saved with the .js extension.
 */

if (document.getElementById) {
    // The code in this section will only
    // execute if the user has JavaScript
    // enabled.

    // we get the link tag
    let link = document.getElementsByTagName('link');

    /**
     * The getElementsByTagName returns an HTMLCollection.
     * The code below assumes you have just one link tag
     * in your HTML file, therefore, we select the first
     * first using its index i.e zero.
     *
     * Link tags also have attributes which inturn contains
     * the href which has a nodeValue that contains the name
     * of the current CSS file.
     *
     * Since JavaScript is enabled for this code to run
     * we switch to the second stylesheet named styles.css
     */
     link[0].attributes.href.nodeValue = "styles.css";
}
Enter fullscreen mode Exit fullscreen mode

Save your file and refresh your browser, you should get an HTML with no style applied. This means we've successfully switched to the styles.css file. You can confirm this with the browser Developer Tools by performing a simple "Inspect Element" then observe the linked stylesheet in the link.

Firefox developer tools showing an HTML link tag referencing a file named styles.css
The styles.css file in Firefox Developer Tools

Let's change the view a little bit. The following snippet is similar to the code found in the alternate CSS file (the no javascript version created earlier) save it in the styles.css file.

/**
  * Save as styles.css
  */

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

a {
    text-decoration: none;
}

li {
    list-style-type: none;  
}

.top {
    background-color: #1f2a47;
    color: #ffffff;
}

.header {
    padding: 1.2em;
}

.header__name {
    margin-bottom: 0.5em;
    font-weight: 700;
    color: #ffffff;
    text-align: center;
}

.header__name a {
    color: #ffffff;
}
Enter fullscreen mode Exit fullscreen mode

Refresh your browser to see the changes (nothing much for now).

As I mentioned earlier the overlay navigation will be available for the mobile viewport only. So we'll have to use a media query to specify the viewport size that will use the overlay navigation.

Since we are not targeting desktop viewport it's best if we specify a maximum width of the viewport in the media query and any viewport size between zero and this maximum width will get our styles for the overlay navigation.

@media screen and (max-width: 40em) {
    /**
      * All code for our overlay navigation will live
      * inside this media query.
      */
}
Enter fullscreen mode Exit fullscreen mode

To follow along, kindly snap your browser to a mobile view or switch on Responsive Design mode in your browser (Ctrl + Shift + M in Firefox for Windows).

The first thing we'll do is to align the header name and the hambuger icon side-by-side with space between them. We'll achieve this with Flexbox. We'll set the display property to flex and to get the space between the header name and hambuger icon we use the justify-content property with a value of space-between.

@media screen and (max-width: 40em) {

    .header {
        display: flex;
        justify-content: space-between; /* for the space between the elements */
    }

}
Enter fullscreen mode Exit fullscreen mode

When you take a look at the result in your browser you'll notice changes but there is a tiny problem. The hambuger icon is small. All we have to do is increase its font size, while at it we can also add a cursor property with a value of pointer for mouse users.

@media screen and (max-width: 40em) {

    /* All previous code remain the same */

    .hambuger__icon {
        font-size: 30px;
        cursor: pointer;
    }

}
Enter fullscreen mode Exit fullscreen mode

Save and refresh your browser. The hambuger icon should look bigger. But the navigation menu meant for the overlay navigation is still visible.

Navigation menu meant for the overlay is still visible
Navigation is still visible

It's time we apply the overlay. The overlay navigation is hidden by default. Based on how we want it we can make it slide from top to the bottom of the screen or from left to right. We'll stick with the latter (left to right).

The overlay navigation will cover the entire screen hence its height is set 100%. Its position will be fixed so that we can position it off-screen using offset properties like top and left. And since it's an overlay it will appear over the entire page content and we can only achieve this by changing the stacking context of the page using CSS z-index

We'll also add aesthetics to indicate that it's over the page using a background color that'll be transparent so that we'll see the page content underneath. We'll resolve to rgba() color format and apply an alpha transparency less than one. To create the sliding effect we'll resolve to CSS transition using the transition property with our chosen value.

Since the navigation is sliding from left to right there is a tendency for overflow along the x-axis so it's better we hide the overflow using the overflow-x property with a value of hidden.

The snippet below is the resulting code for the explanation in the last three paragraphs.

@media screen and (max-width: 40em) {

    /* All previous code remain the same */

    .overlay {
        position: fixed;
        top: 0;
        left: 0;
        width: 0;
        height: 100%;
        z-index: 1;
        background-color: rgba(0,0,0,0.9);
        overflow-x: hidden;
        transition: 0.5s;
    }

}
Enter fullscreen mode Exit fullscreen mode

Save your files. The navigation should be out of sight but when you click the hambuger icon to reveal it nothing happens. Why? That's because we've not written the JavaScript that will make it work. Let's fix that.

Switch back to your JavaScript code. From our HTML the navigation has an HTML ID attribute named navigation.

We also have a span element inside the header that contains the hambuger icon and it has an onclick event set to a function named openNav().

There is also a tag in the navigation with an onclick event set to a function named closeNav().

We'll grab the navigation using its ID, and we'll have to create the openNav and the closeNav function.

// grab the navigation by  its ID
let navigation = document.getElementById('navigation');

/**
 * The openNav function will set the width
 * of the navigation to 100 therefore, it will
 * show on screen.
 */
function openNav() {
    navigation.style.width = "100%";
}

/**
 * The closeNav function will set the width
 * of the navigation to an empty value therefore, it will
 * close up.
 */
function closeNav() {
    navigation.style.width = "";
}
Enter fullscreen mode Exit fullscreen mode

Save your files. Now refresh your browser and click the hambuger icon the overlay menu should slide into view but you might find it difficult to close it because we have not styled the close button.

The incomplete overlay navigation as seen in Firefox Responsive Design Mode
Incomplete overlay navigation

The close button will be absolutely positioned in the top right corner of the overlay this is possible because of its parent element — the overlay having a position of fixed. If you would like to understand this in-depth you can read my post on CSS positioning or Ahmad Shadeed tutorial on CSS positioning.


@media screen and (max-width: 40em) {

    /* All previous code remain the same */

    .overlay .closebtn {
        position: absolute;
        top: 20px;
        right: 45px;
        font-size: 3.75em;
    }

}
Enter fullscreen mode Exit fullscreen mode

Save your files and refresh to see the changes. You should be able to open and close the overlay navigation but the navigation links still remain un-styled.

When styling the links we have to make them clickable on mobile by setting the links display property to block and then we add some padding.

@media screen and (max-width: 40em) {

    /* All previous code remain the same */

    .overlay a {
        display: block;
        font-size: 2.3em;
        padding: 8px;
        color: #818181;
        text-decoration: none;
        transition: 0.3s;
    }

    .overlay a:hover,
    .overlay a:focus {
        color: #f1f1f1;
    }

}
Enter fullscreen mode Exit fullscreen mode

Save your file and observe the changes.

Overlay navigation as seen in Firefox Responsive design mode
Overlay navigation

The navigation is almost done, we just need to adjust the position of the navigation menu by moving it to the center of the overlay.

@media screen and (max-width: 40em) {

    /* All previous code remain the same */

    .overlay__content {
        position: relative;
        top: 25%;
        width: 100%;
        margin-top: 30px;
        text-align: center;
    }

}
Enter fullscreen mode Exit fullscreen mode

Save your files and try the navigation.

The completed overlay navigation as seen in Firefox 75 Responsive design mode
The completed overlay navigation

To finish up we'll apply styles for two breakpoints. The first will set of styles that will take effect immediately after the 40em breakpoint of the navigation. After this breakpoint, we arrange the navigation in a Grid container and we hide the hambuger icon and close button.

At a desktop view, we rearrange the navigation using Flexbox. This is similar to the behavior seen in the CSS file available to users without JavaScript enabled.

@media screen and (min-width: 40.01em) {

    .closebtn,
    .hambuger__icon {
        display: none;
    }

    .navigation__link {
        display: block;
        padding: 0.8em 1em;
        font-weight: normal;
        color: #ffffff;
    }

    .navigation__item:not(:first-child) {
         border-top: 1px solid #ccc;
    }

    .navigation__link:hover {
        background-color: #06162f;
        color: #ffffff;
    }

    .navigation__menu {
         margin-top: 1.2em;
         border: 1px solid #cccccc;
    }

    @supports (display: grid) {
        .navigation__menu {
            display: grid;
            grid-template-areas: "h1 h2" "h3 h4";
        }
        .navigation__item:nth-child(2) {
            border-top: none;
        }
    }

}

@media screen and (min-width: 55em) {
    .top {
        display: flex;
        justify-content: space-between;
    }

    .navigation__menu {
        display: flex;
        border: none;
        margin-right: 4em;
    }

    .navigation__link {
        margin-right: 20px;
    }

    .navigation__item:not(:first-child) {
        border-top: none;
    }
}
Enter fullscreen mode Exit fullscreen mode

Save your file and perform the following steps:

  • Refresh your browser
  • Open the overlay navigation and leave it opened
  • Disable the Responsive Design mode or snap your browser viewport to a desktop view

What do you observe? There are problems with the navigation layout. One is evident (as seen in the image below), the other is not.

The navigation crashed as seen in Firefox 75
A crashed navigation

A quick investigation in the Developer tools reveals that while the overlay was opened there was an inline style applied via the openNav function and while we did not close the navigation the inline style was still active when the browser viewport was resized. This resulted in the weird layout of the navigation.

Firefox developer tools showing an element with a width of 100
The cause of weird behavior

You might not notice this if I did not mention it. The fix is simple.

We have to take this situation into consideration and observe when the user resizes the browser without closing the navigation. If this happens we remove the inline style applied by the openNav function.

// we get the user current viewport size
let clientWidth = document.documentElement.clientWidth;

/**
 * We add an event listener and observe the "resize"
 * behavior and if the client width is more than the
 * width of that enables the overlay navigation we remove
 * the inline style applied by the openNav function.
 */
window.addEventListener('resize', function() {
    if (clientWidth > 640) {
        navigation.style.width = "";
    }
});
Enter fullscreen mode Exit fullscreen mode

The second problem with the layout is when you reduce the height of the viewport, the font size of navigation link will cause the browser to add scroll bars in the overlay navigation.

The fix is to use a media query and check for the height of the viewport and change the font size accordingly.

@media screen and (max-height: 28em) {
    .overlay a {
      font-size: 1.25em;
    }

    .overlay .closebtn {
      font-size: 2.5em;
      top: 15px;
      right: 35px;
    }
}
Enter fullscreen mode Exit fullscreen mode

That's it. The code works but it can be improved. This was an experiment and there is an issue with the navigation that you should be aware of.

If you are on a desktop device perform the following steps:

  • Resize your browser window to mobile view to enable the overlay navigation
  • Now gently resize the browser height you'll realize the overlay navigation closes

I am going to figure out how to fix this, and if you know the fix, let me know in the comments section.

The Github repo:

GitHub logo ziizium / my-webdev-notes

Code snippets for series of articles on DEV about my experiments in web development

My WebDev Notes

This repositiory contains code snippets, and links for series of articles on DEV about my experiments in Web development.

List of articles






Have fun!

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .