<!-- 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>
@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;
}
}
// 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));
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.