$responsive-size: 576px;
@import url("https://fonts.googleapis.com/css?family=Montserrat:400,400i,700");
:root {
--animation-time: 300ms;
--app-font-family: Montserrat, sans-serif;
--app-background-image:
linear-gradient(to top left, #79a8bb, #b36dd4, #f06699);
--cpc-menu-background-color: rgba(0, 0, 0, 0.5);
--cpc-menu-max-width: 320px;
--cpc-menu-padding: 0.4rem;
--cpc-menu-border-radius: 0.7rem;
--cpc-menu-box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.5);
--cpc-menu-links-color: #ffffff;
--cpc-menu-links-padding: 0.9rem 0.7rem;
--cpc-menu-links-text-shadow: 0 -1px #000000;
--cpc-menu-links-border-radius: 0.7rem;
--cpc-menu-links-transition: background-color 300ms ease-in-out;
--cpc-menu-links-hover-background-color: rgba(0, 0, 0, 0.5);
--cpc-icon-margin-right: 0.5rem;
--cpc-caret-margin-left: 0.5rem;
--cpc-caret-transform: rotate(0deg) scale(1);
--cpc-caret-transforming: rotate(90deg) scale(1.2);
--cpc-caret-transformed: rotate(180deg) scale(1);
--cpc-sub-menu-background-color: rgba(0, 0, 0, 0.5);
--cpc-sub-menu-padding: 0.4rem;
--cpc-sub-menu-border-radius: 0.7rem;
--cpc-sub-menu-box-shadow: 0 1rem 3rem rgba(0, 0, 0, 0.5);
}
html, body, #app {
font-size: 16px;
width: 100%;
height: 100%;
}
@mixin flex-center() {
display: flex;
align-items: center;
justify-content: center;
}
#app {
@include flex-center();
font-family: var(--app-font-family);
background-image: var(--app-background-image);
padding: 2rem;
overflow: auto;
box-sizing: border-box;
}
.cpc-menu {
width: 100%;
max-width: var(--cpc-menu-max-width);
background-color: var(--cpc-menu-background-color);
padding: var(--cpc-menu-padding);
margin: auto;
box-sizing: border-box;
border-radius: var(--cpc-menu-border-radius);
box-shadow: var(--cpc-menu-box-shadow);
}
.cpc-main, .cpc-sub {
padding: 0;
margin: 0;
list-style-type: none;
& > li {
width: 100%;
& > a {
color: var(--cpc-menu-links-color);
font-weight: bold;
text-align: center;
text-decoration: none;
width: 100%;
padding: var(--cpc-menu-links-padding);
display: flex;
justify-content: space-between;
box-sizing: border-box;
text-shadow: var(--cpc-menu-links-text-shadow);
border-radius: var(--cpc-menu-links-border-radius);
transition: var(--cpc-menu-links-transition);
&:hover {
background-color: var(--cpc-menu-links-hover-background-color);
}
}
}
}
.cpc-main {
@include flex-center();
flex-direction: column;
}
.cpc-sub {
height: 0;
overflow: hidden;
visibility: hidden;
}
.cpc-caret {
margin-left: var(--cpc-caret-margin-left);
.cpc-active {
animation: caret-is-active var(--animation-time) linear forwards;
}
.cpc-inactive {
animation: caret-is-active var(--animation-time) linear forwards;
}
}
.cpc-icon {
margin-right: var(--cpc-icon-margin-right);
}
.cpc-hidden {
visibility: hidden;
}
@keyframes caret-is-inactive {
0% { transform: var(--cpc-caret-transform); }
50% { transform: var(--cpc-caret-transforming); }
100% { transform: var(--cpc-caret-transformed); }
}
@keyframes caret-is-active {
0% { transform: var(--cpc-caret-transformed); }
50% { transform: var(--cpc-caret-transforming); }
100% { transform: var(--cpc-caret-transform); }
}
@media (min-width: $responsive-size) {
.cpc-menu {
width: auto;
max-width: none;
}
.cpc-main {
flex-direction: row;
position: relative;
& > li > a {
position: relative;
}
}
.cpc-sub {
background-color: var(--cpc-sub-menu-background-color);
padding: var(--cpc-sub-menu-padding);
position: absolute;
top: 120%;
border-radius: var(--cpc-sub-menu-border-radius);
box-shadow: var(--cpc-sub-menu-box-shadow);
}
.cpc-hidden {
display: none;
}
}
View Compiled
// TODO: Activate caret active animation
class CpcNavigation extends React.Component {
constructor(props) {
super(props);
this.state = {
jsxData: [],
submenu: []
};
}
componentDidMount() {
this.setState({jsxData: this.createMenuJSX()});
}
menuClickEvent(i) {
let submenu = this.state.submenu;
let tmpmenu = submenu[i];
let sub = tmpmenu.sub.current;
let caret = tmpmenu.caret.current;
if (tmpmenu.active === false) {
tmpmenu.active = true;
TweenLite.to(caret, 1, {
transform: 'rotate(180deg)',
ease: Elastic.easeOut.config(1, 0.3)
});
TweenLite.to(sub, 1, {
height: sub.scrollHeight,
visibility: 'visible',
ease: Elastic.easeOut.config(1, 0.3)
});
} else {
tmpmenu.active = false;
TweenLite.to(caret, 1, {
transform: 'rotate(0deg)',
ease: Elastic.easeOut.config(1, 0.3)
});
TweenLite.to(sub, 0.5, {
height: 0,
ease: Bounce.easeOut
}).eventCallback('onComplete', () => {
TweenLite.to(sub, 0, {
visibility: 'hidden'
})
});
}
submenu[i] = tmpmenu;
this.setState({submenu: submenu});
}
createMenuJSX(menu = this.props.menu) {
let link = [];
for (let i in menu) {
let m = menu[i];
let ic = <i className="cpc-icon cpc-hidden fas fa-caret-down"></i>;
if (typeof m.icon !== 'undefined') {
ic = <i className={'cpc-icon ' + m.icon}></i>;
}
if (typeof m.menu === 'undefined') {
link.push(
<li>
<a href={m.link}>
{ic}
<span>{i}</span>
<i className="cpc-caret cpc-hidden fas fa-caret-down"></i>
</a>
</li>
);
} else if (typeof m.menu === 'object') {
let tmpSubmenu = this.state.submenu;
let tmpLength = tmpSubmenu.length;
tmpSubmenu.push({
'id': m.link,
'active': false,
'caret': React.createRef(),
'sub': React.createRef()
});
link.push(
<li>
<a
href={m.link}
onClick={this.menuClickEvent.bind(this, tmpLength)}
>
{ic}
<span>{i}</span>
<i
className="cpc-caret fas fa-caret-down"
ref={tmpSubmenu[tmpLength].caret}
></i>
</a>
<ul className="cpc-sub" ref={tmpSubmenu[tmpLength].sub}>
{this.createMenuJSX(m.menu)}
</ul>
</li>
);
this.setState({submenu: tmpSubmenu});
}
}
return link;
}
render() {
return (
<nav className="cpc-menu">
<ul className="cpc-main">
{this.state.jsxData}
</ul>
</nav>
);
}
}
// Navigation menu builder
const menu = {
'Home': {
'link': '#home',
'icon': 'fas fa-home'
},
'About': {
'link': '#about',
'icon': 'fas fa-info-circle'
},
'Clients': {
'link': '#clients',
'icon': 'fas fa-user-tie',
'menu': {
'Burger King': {
'link': '#burger_king',
'icon': 'fas fa-check'
},
'Southwest Airlines': {
'link': '#southwest_airlines',
'icon': 'fas fa-check'
},
'Levi Strauss': {
'link': '#levi_strauss',
'icon': 'fas fa-check'
}
}
},
'Services': {
'link': '#services',
'icon': 'fas fa-cogs',
'menu': {
'Print Design': {
'link': '#print_design',
'icon': 'fas fa-check'
},
'Web Design': {
'link': '#web_design',
'icon': 'fas fa-check'
},
'Mobile App Development': {
'link': '#mobile_app_development',
'icon': 'fas fa-check'
}
}
},
'Contact': {
'link': '#contact',
'icon': 'fas fa-phone-square'
}
};
ReactDOM.render(
<CpcNavigation menu={menu} />,
document.querySelector('#app')
);
View Compiled