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.
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:1pxsolid#cccccc;}/**
* We apply a top borer to the navigation
* items except the first one.
*/.navigation__item:not(:first-child){border-top:1pxsolid#ccc;}/**
* We turn all links to block level elements
* then we applie some padding to make them
* clickable.
*/.navigation__link{display:block;padding:0.8em1em;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.
*/@mediascreenand(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.
*/@mediascreenand(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;}}
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:
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 tagletlink=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";}
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.
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__namea{color:#ffffff;}
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.
@mediascreenand(max-width:40em){/**
* All code for our overlay navigation will live
* inside this media query.
*/}
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.
@mediascreenand(max-width:40em){.header{display:flex;justify-content:space-between;/* for the space between the elements */}}
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.
@mediascreenand(max-width:40em){/* All previous code remain the same */.hambuger__icon{font-size:30px;cursor:pointer;}}
Save and refresh your browser. The hambuger icon should look bigger. But the navigation menu meant for the overlay 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.
@mediascreenand(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;}}
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 IDletnavigation=document.getElementById('navigation');/**
* The openNav function will set the width
* of the navigation to 100 therefore, it will
* show on screen.
*/functionopenNav(){navigation.style.width="100%";}/**
* The closeNav function will set the width
* of the navigation to an empty value therefore, it will
* close up.
*/functioncloseNav(){navigation.style.width="";}
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 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.
@mediascreenand(max-width:40em){/* All previous code remain the same */.overlay.closebtn{position:absolute;top:20px;right:45px;font-size:3.75em;}}
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.
@mediascreenand(max-width:40em){/* All previous code remain the same */.overlaya{display:block;font-size:2.3em;padding:8px;color:#818181;text-decoration:none;transition:0.3s;}.overlaya:hover,.overlaya:focus{color:#f1f1f1;}}
Save your file and observe the changes.
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.
@mediascreenand(max-width:40em){/* All previous code remain the same */.overlay__content{position:relative;top:25%;width:100%;margin-top:30px;text-align:center;}}
Save your files and try the 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.
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.
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.
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 sizeletclientWidth=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="";}});
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.