Replicate this particular design for a mobile, or close to mobile, navigation bar.
Third project for the #weeklycodingchallenge.
A Pen by Matthew Daniel Brown on CodePen.
<!-- markup structure | |
nav, a wrapping container for a series of link elements | |
a, each anchor link element nesting an SVG icon and a span describing the link | |
--> | |
<nav> | |
<a class="active" href="#"> | |
<svg viewBox="0 0 100 100"> | |
<g transform="translate(10 5) scale(0.8 0.9)"> | |
<path d="M 0 30 v 70 h 100 v -70 l -50 -30 z" stroke="currentColor" stroke-width="10" fill="none" | |
stroke-linejoin="round" stroke-linecap="round" /> | |
</g> | |
</svg> | |
<span> | |
Home | |
</span> | |
</a> | |
<a href="#"> | |
<svg viewBox="0 0 100 100"> | |
<g transform="translate(5 5) scale(0.9 0.9)"> | |
<path d="M 50 35 a 20 20 0 0 1 50 0 q 0 25 -50 60 q -50 -35 -50 -60 a 25 25 0 0 1 50 0" stroke="currentColor" | |
stroke-width="10" fill="none" stroke-linejoin="round" stroke-linecap="round" /> | |
</g> | |
</svg> | |
<span> | |
Likes | |
</span> | |
</a> | |
<a href="#"> | |
<svg viewBox="0 0 100 100"> | |
<g transform="translate(5 5) scale(0.9 0.9)"> | |
<circle cx="45" cy="38" r="38" stroke="currentColor" stroke-width="10" fill="none" /> | |
<line x1="66" y1="65" x2="100" y2="100" stroke="currentColor" stroke-width="10" /> | |
</g> | |
</svg> | |
<span> | |
Search | |
</span> | |
</a> | |
<a href="#"> | |
<svg viewBox="0 0 100 100"> | |
<g transform="translate(5 5) scale(0.9 0.9)"> | |
<circle cx="50" cy="35" r="18" stroke="currentColor" stroke-width="10" fill="none" /> | |
<rect x="15" y="75" width="70" height="50" rx="25" stroke="currentColor" stroke-width="10" fill="none" /> | |
</g> | |
</svg> | |
<span> | |
Profile | |
</span> | |
</a> | |
</nav> |
// array describing the options of the navigation elements | |
// specifying the lower case option and the matching color | |
const navigationOptions = [ | |
{ | |
name: 'home', | |
color: '#5B37B7' | |
}, | |
{ | |
name: 'likes', | |
color: '#C9379D' | |
}, | |
{ | |
name: 'search', | |
color: '#E6A919' | |
}, | |
{ | |
name: 'profile', | |
color: '#1892A6' | |
} | |
]; | |
// target all anchor link elements | |
const links = document.querySelectorAll('nav a'); | |
// function called in response to a click event on the anchor link | |
function handleClick(e) { | |
// prevent the default behavior, but most importantly remove the class of .active from those elements with it | |
e.preventDefault(); | |
links.forEach(link => { | |
if (link.classList.contains('active')) { | |
link.classList.remove('active'); | |
} | |
}); | |
// retrieve the option described the link element | |
const name = this.textContent.trim().toLowerCase(); | |
// find in the array the object with the matching name | |
// store a reference to its color | |
const { color } = navigationOptions.find(item => item.name === name); | |
// retrieve the custom property for the --hover-c property, to make it so that the properties are updated only when necessary | |
const style = window.getComputedStyle(this); | |
const hoverColor = style.getPropertyValue('--hover-c'); | |
// if the two don't match, update the custom property to show the hue with the text and the semi transparent background | |
if (color !== hoverColor) { | |
this.style.setProperty('--hover-bg', `${color}20`); | |
this.style.setProperty('--hover-c', color); | |
} | |
// apply the class of active to animate the svg an show the span element | |
this.classList.add('active'); | |
// change the color of the background of the application to match | |
document.querySelector('body').style.background = color; | |
} | |
// listen for a click event on each and every anchor link | |
links.forEach(link => link.addEventListener('click', handleClick)); |
@import url("https://fonts.googleapis.com/css?family=Open+Sans:700"); | |
* { | |
box-sizing: border-box; | |
margin: 0; | |
padding: 0; | |
} | |
/* by default include the background of the option for the home navigation */ | |
body { | |
background: #5b37b7; | |
color: #010101; | |
/* center in the viewport */ | |
min-height: 100vh; | |
display: grid; | |
place-items: center; | |
font-family: "Open Sans", sans-serif; | |
/* transition for the change in bg color */ | |
transition: background 0.2s ease-out; | |
} | |
/* display the anchor link side by side */ | |
nav { | |
display: flex; | |
background: #fff; | |
/* considerable whitespace surrounding the navigation's items */ | |
padding: 2rem 3.15rem; | |
border-radius: 0 0 30px 30px; | |
box-shadow: 0 1px 15px rgba(0, 0, 0, 0.1); | |
} | |
/* remove default style and slightly separate the anchor links from one another */ | |
a { | |
color: inherit; | |
text-decoration: none; | |
margin: 0 0.2rem; | |
/* display the svg icon and span elements side by side, vertically aligned */ | |
display: flex; | |
align-items: center; | |
/* include padding for the background applied on the active item */ | |
padding: 0.75rem 1.25rem; | |
border-radius: 30px; | |
/* position relative for the pseudo element */ | |
position: relative; | |
/* custom properties for the colors picked up by the elements when clicked (and updated for each link in the script) */ | |
--hover-bg: #5b37b720; | |
--hover-c: #5b37b7; | |
} | |
/* include considerable negative margin to have the svg icon overlapping with the span element */ | |
a svg { | |
margin-right: -2.5rem; | |
width: 28px; | |
height: 28px; | |
pointer-events: none; | |
/* transition for the change in margin */ | |
transition: margin 0.2s ease-out; | |
} | |
/* by default hide the span element */ | |
a span { | |
opacity: 0; | |
visibility: hidden; | |
font-size: 0.9rem; | |
margin-left: 0.9rem; | |
} | |
/* include with a pseudo element relative to the anchor link a circle, with a fixed with and height */ | |
a:before { | |
position: absolute; | |
content: ""; | |
top: 50%; | |
left: 0; | |
width: 70px; | |
height: 70px; | |
border-radius: 50%; | |
/* positioned to the left of the anchor link and scaled to 0 */ | |
transform: translate(0%, -50%) scale(0); | |
visibility: visible; | |
opacity: 1; | |
} | |
/* when active */ | |
/* specify the colors dictated by the custom properties */ | |
a.active { | |
background: var(--hover-bg); | |
color: var(--hover-c); | |
} | |
/* using the color specified by the then updated custom property show the circle of the pseudo element increasing its size and highlighting it momentarily */ | |
a.active:before { | |
background: var(--hover-c); | |
opacity: 0; | |
visibility: hidden; | |
transform: translate(0%, -50%) scale(2); | |
/* transition only when the class is applied */ | |
transition: all 0.4s ease-out; | |
} | |
/* remove the margin applied to the svg to make it overlay atop the anchor link */ | |
a.active svg { | |
margin-right: 0; | |
} | |
/* show the span element */ | |
a.active span { | |
visibility: visible; | |
opacity: 1; | |
transition: all 0.2s ease-out; | |
} | |
/* on smaller viewports show the navigation bar on the side, attached to the left of the screen */ | |
@media (max-width: 500px) { | |
nav { | |
flex-direction: column; | |
justify-self: start; | |
border-radius: 0 30px 30px 0; | |
padding: 2rem 1.15rem 2rem 0.75rem; | |
} | |
/* change the margin separating the anchor link elements now dividing the elements vertically */ | |
nav a { | |
margin: 0.5rem 0; | |
} | |
/* remove the negative margin from the svg elements, as the width is to be taken in full */ | |
nav svg { | |
margin: 0; | |
} | |
} |