How to make a horizontal sliding navigation
Tags: tutorial, html, css, javascript
Published: 2020-02-02 20:07:09

This tutorial is about making the navigation menu from Developer Bacon. This article is aimed for people that don't know Vue.js and has been stripped out of this tutorial. There is the use of SCSS but there will be examples with both SCSS and the compiled CSS. The JavaScript for this tutorial use the library [AnimeJs](https://animejs.com/). If you want to just look at the code you can find it in the Codepen example below or continue this article for the examples.

<iframe height="381" style="width: 100%;" scrolling="no" title="Sliding Navigation from Developer Bacon" src="https://codepen.io/brettanda/embed/preview/MWYMpvJ?height=381&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
  See the Pen <a href='https://codepen.io/brettanda/pen/MWYMpvJ'>Sliding Navigation from Developer Bacon</a> by Brett
  (<a href='https://codepen.io/brettanda'>@brettanda</a>) on <a href='https://codepen.io'>CodePen</a>.
</iframe>

## [The HTML](#the-html)

Let's get started with the HTML for the navigation menu. The HTML example has all the necessary tags and necessary for both the CSS and Js script to look the same as Developer Bacon. These can of course be changed to whatever you like (just make sure that you change it both in the CSS file and Js file).

```html
<header class="header">
	<a class="logo-link" href="#">
		Developer Bacon
	</a>

	<nav class="nav">
		<a class="nav__link" href="#">Search</a>
		<a class="nav__link" href="#">About</a>
		<a class="nav__link" href="#">Contact</a>
	</nav>

	<div class="nav__background nav__background-1"></div>
	<div class="nav__background nav__background-2"></div>

	<button class="nav-ham toggleNav">
		<span class="nav-ham__line"></span>
		<span class="nav-ham__line"></span>
		<span class="nav-ham__line"></span>
	</button>

	<div class="close-nav toggleNav"></div>
</header>
```

## [The SCSS and CSS](#the-scss-and-css)

This first example is the SCSS for the navigation menu. If you are not familiar with SCSS the second example is the compiled SCSS to CSS.

```scss
// Based on this [snippet](https://css-tricks.com/snippets/sass/mixin-manage-breakpoints/) by Hugo Giraudel.
@mixin break($point) {
	@if map-has-key($breakpoints, $point) {
		@media screen and (max-width: map-get($breakpoints, $point)) {
			@content;
		}
	} @else {
		@warn 'Unfortunately, no value could be retrieved from `#{$breakpoint}`. '
			+ 'Available breakpoints are: #{map-keys($breakpoints)}.';
	}
}

$breakpoints: (
	"sm": 800px,
	"md": 1000px
) !default;

.header,
.header * {
	box-sizing: border-box;
}

body {
	background-color: black;
}

nav[role="navigation"] {
	text-align: center;

	a {
		display: inline-block;
		margin: 1em 0.75em 2em;
	}
}

.header {
	background-color: red;
	display: flex;
	justify-content: space-between;
	margin-bottom: 20px;
	padding: 1rem;
	box-shadow: 0 2px 10px 1px rgba(0, 0, 0, 0.2);
}

img.logo-link {
	height: 1rem;
	width: auto;
}

.logo-link,
.nav__link {
	text-decoration: none;
	text-transform: uppercase;
	letter-spacing: 1px;
	color: black;
	transition: color 0.3s ease;

	&:hover,
	&:focus {
		text-decoration: none;
		color: white;
	}
}

.nav__link {
	margin-left: 20px;
	display: inline;

	@include break(md) {
		padding: 0.75rem;
	}
}

.nav-ham,
.nav__background {
	display: none;
}

.nav-ham {
	z-index: 2;
}

@include break(md) {
	.nav {
		display: none;

		&.is-active {
			display: flex;
			flex-direction: column;
			padding: 8rem 2rem 70vh 1rem;
			justify-content: space-around;
			text-align: right;
			position: fixed;
			background-color: #222;
			color: white;
			top: 0;
			height: 100vh;
			width: 15rem;
			z-index: 5;
			box-shadow: 0 2px 10px 1px rgba(0, 0, 0, 0.2);

			a {
				color: white;
				border-color: white;
			}
		}
	}

	.nav__background-1,
	.nav__background-2 {
		&.is-active {
			display: block;
			z-index: 3;
			background-color: gray;
			box-shadow: 0 2px 10px 1px rgba(0, 0, 0, 0.2);
			top: 0;
			height: 100vh;
			width: 18rem;
			position: fixed;
		}
	}

	.nav-ham {
		display: block;
		background: none;
		border: none;
		outline: none;
		height: 3rem;
		width: 3rem;
		position: absolute;
		right: 1rem;
		top: 0;
		z-index: 6;

		.nav-ham__line {
			border: 1px solid white;
			height: 0;
			width: 2rem;
			display: block;
			margin: 0.6rem 0 0.6rem auto;
			transform-origin: center right;
			transition: 0.3s;
		}

		& .is-active {
			&:first-child {
				transform-origin: center right;
				transform: rotate(-45deg);
			}

			&:nth-of-type(2) {
				width: 0;
				opacity: 0;
			}

			&:last-child {
				transform-origin: center right;
				transform: rotate(45deg);
			}
		}
	}
}

.close-nav {
	display: none;
	position: fixed;
	right: -100vw;
	top: 0;
	background: rgba(0, 0, 0, 0.7);
	height: 100vh;
	width: 100vw;
	opacity: 0;
	z-index: 2;

	@include break(md) {
		&.is-active {
			display: block;
		}
	}
}

.logo {
	vertical-align: bottom;
	width: 33px;
	padding: 0 1px;
}
```

### [The compiled CSS](#the-compiled-css)

This is the compiled CSS from the SCSS above.

```css
.header,
.header * {
	box-sizing: border-box;
}

body {
	background-color: black;
}

nav[role="navigation"] {
	text-align: center;
}
nav[role="navigation"] a {
	display: inline-block;
	margin: 1em 0.75em 2em;
}

.header {
	background-color: red;
	display: -webkit-box;
	display: flex;
	-webkit-box-pack: justify;
	justify-content: space-between;
	margin-bottom: 20px;
	padding: 1rem;
	box-shadow: 0 2px 10px 1px rgba(0, 0, 0, 0.2);
}

img.logo-link {
	height: 1rem;
	width: auto;
}

.logo-link,
.nav__link {
	text-decoration: none;
	text-transform: uppercase;
	letter-spacing: 1px;
	color: black;
	-webkit-transition: color 0.3s ease;
	transition: color 0.3s ease;
}
.logo-link:hover,
.logo-link:focus,
.nav__link:hover,
.nav__link:focus {
	text-decoration: none;
	color: white;
}

.nav__link {
	margin-left: 20px;
	display: inline;
}
@media screen and (max-width: 1000px) {
	.nav__link {
		padding: 0.75rem;
	}
}

.nav-ham,
.nav__background {
	display: none;
}

.nav-ham {
	z-index: 2;
}

@media screen and (max-width: 1000px) {
	.nav {
		display: none;
	}
	.nav.is-active {
		display: -webkit-box;
		display: flex;
		-webkit-box-orient: vertical;
		-webkit-box-direction: normal;
		flex-direction: column;
		padding: 8rem 2rem 70vh 1rem;
		justify-content: space-around;
		text-align: right;
		position: fixed;
		background-color: #222;
		color: white;
		top: 0;
		height: 100vh;
		width: 15rem;
		z-index: 5;
		box-shadow: 0 2px 10px 1px rgba(0, 0, 0, 0.2);
	}
	.nav.is-active a {
		color: white;
		border-color: white;
	}

	.nav__background-1.is-active,
	.nav__background-2.is-active {
		display: block;
		z-index: 3;
		background-color: gray;
		box-shadow: 0 2px 10px 1px rgba(0, 0, 0, 0.2);
		top: 0;
		height: 100vh;
		width: 18rem;
		position: fixed;
	}

	.nav-ham {
		display: block;
		background: none;
		border: none;
		outline: none;
		height: 3rem;
		width: 3rem;
		position: absolute;
		right: 1rem;
		top: 0;
		z-index: 6;
	}
	.nav-ham .nav-ham__line {
		border: 1px solid white;
		height: 0;
		width: 2rem;
		display: block;
		margin: 0.6rem 0 0.6rem auto;
		-webkit-transform-origin: center right;
		transform-origin: center right;
		-webkit-transition: 0.3s;
		transition: 0.3s;
	}
	.nav-ham .is-active:first-child {
		-webkit-transform-origin: center right;
		transform-origin: center right;
		-webkit-transform: rotate(-45deg);
		transform: rotate(-45deg);
	}
	.nav-ham .is-active:nth-of-type(2) {
		width: 0;
		opacity: 0;
	}
	.nav-ham .is-active:last-child {
		-webkit-transform-origin: center right;
		transform-origin: center right;
		-webkit-transform: rotate(45deg);
		transform: rotate(45deg);
	}
}
.close-nav {
	display: none;
	position: fixed;
	right: -100vw;
	top: 0;
	background: rgba(0, 0, 0, 0.7);
	height: 100vh;
	width: 100vw;
	opacity: 0;
	z-index: 2;
}
@media screen and (max-width: 1000px) {
	.close-nav.is-active {
		display: block;
	}
}

.logo {
	vertical-align: bottom;
	width: 33px;
	padding: 0 1px;
}
```

## [The magic JavaScript](#the-magic-javascript)

Before we get into the Javascript of this project we should add AnimeJs to the project. To do this use this command in your project directory.

```bash
yarn add animejs
```

I used AnimeJs because it is great for handleing animations with Javascript. The AnimeJs documentation can be found at [animejs.com](https://animejs.com).

```javascript
document.querySelectorAll(".toggleNav").forEach(item => {
	item.addEventListener("click", function() {
		console.log("clicked");
		toggleNav();
	});
});

function closeNav() {
	document.querySelectorAll(".nav-ham, .nav-ham span").forEach(function(item) {
		item.classList.remove("is-active");
	});
	anime({
		targets: ".nav",
		duration: 400,
		easing: "easeInOutSine",
		right: [0, "-15rem"],
		complete: function() {
			document.querySelector(".nav").classList.remove("is-active");
		}
	});

	anime({
		targets: ".nav__background",
		delay: 150,
		duration: 400,
		easing: "easeInOutSine",
		right: ["0rem", "-18rem"],
		complete: function() {
			document.querySelectorAll(".nav__background").forEach(item => {
				item.classList.remove("is-active");
			});
		}
	});

	anime({
		targets: ".close-nav",
		delay: 150,
		duration: 400,
		easing: "easeInOutSine",
		right: [0, "-100vw"],
		opacity: 0,
		complete: function() {
			document.querySelector(".close-nav").classList.remove("is-active");
		}
	});
}
function toggleNav() {
	document.querySelectorAll(".nav-ham, .nav-ham span").forEach(item => {
		if (item.classList.contains("is-active")) {
			item.classList.remove("is-active");
		} else {
			item.classList.add("is-active");
		}
	});

	if (document.querySelector(".nav").classList.contains("is-active")) {
		this.closeNav();
	} else {
		anime({
			targets: ".close-nav",
			duration: 400,
			easing: "easeInOutSine",
			right: ["-100vw", 0],
			opacity: 1,
			begin: function() {
				document.querySelector(".close-nav").classList.add("is-active");
			}
		});

		anime({
			targets: ".nav:not(.is-active)",
			duration: 400,
			delay: 150,
			easing: "easeInOutSine",
			right: ["-15rem", 0],
			begin: function() {
				document.querySelector(".nav").classList.add("is-active");
			}
		});

		anime({
			targets: ".nav__background:not(.is-active)",
			duration: 400,
			easing: "easeInOutSine",
			// right: ['-100vw',"-20vw"],
			right: ["-18rem", "0rem"],
			begin: function() {
				document.querySelectorAll(".nav__background").forEach(function(item) {
					item.classList.add("is-active");
				});
			}
		});
	}
}
```