//PEN HEADER
.header
h1.header__title Tabs Navigation UI
.header__btns.btns
a.header__btn.btn(href="https://github.com/nat-davydova/tab-navigation" title="Check on Github" target="_blanc") Check on Github
//PEN CONTENT
.content
//content title
h2.content__title Click to any navigation link
//content inner
.content__inner
//tabs
.tabs
//tabs navigation
.tabs__nav
ul.tabs__nav-list
li.tabs__nav-item.js-active Profile
li.tabs__nav-item Settings
li.tabs__nav-item About Us
li.tabs__nav-item FAQ
//tabs panels
.tabs__panels
//single panel
.tabs__panel
.tabs__panel-card
.tabs__panel-avatar
.tabs__panel-content
.tabs__panel-card
.tabs__panel-content
.tabs__panel-card
.tabs__panel-content
//single panel
.tabs__panel
.tabs__panel-card
.tabs__panel-content
.tabs__panel-content
.tabs__panel-card
.tabs__panel-content
.tabs__panel-card.tabs__panel-card--spaced-between
.tabs__panel-img
.tabs__panel-img
.tabs__panel-img
//single panel
.tabs__panel
.tabs__panel-card
.tabs__panel-content
.tabs__panel-card
.tabs__panel-content
.tabs__panel-img
.tabs__panel-card
.tabs__panel-img
.tabs__panel-content
//single panel
.tabs__panel
.tabs__panel-card
.tabs__panel-content
.tabs__panel-content
.tabs__panel-card
.tabs__panel-content
.tabs__panel-card
.tabs__panel-content
.tabs__panel-content
View Compiled
//mixins
@mixin transition-mix ($property: all, $duration: 0.2s, $timing: linear, $delay: 0s) {
transition-property: $property;
transition-duration: $duration;
transition-timing-function: $timing;
transition-delay: $delay;
}
@mixin position-absolute ($top: null, $left: null, $right: null, $bottom: null) {
position: absolute;
top: $top;
left: $left;
right: $right;
bottom: $bottom;
}
//variables
$theme-font-color: #2c2c2c;
//common styles
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font: {
family: 'Lato', sans-serif;
size: 16px;
}
color: $theme-font-color;
a {
color: inherit;
text-decoration: none;
}
}
.btn {
@include transition-mix;
padding: 10px 20px;
margin-right: 10px;
background-color: #fff;
border: 1px solid $theme-font-color;
border-radius: 3px;
cursor: pointer;
outline: none;
&:last-child {
margin-right: 0;
}
&:hover,
&.js-active{
color: #fff;
background-color: $theme-font-color;
}
}
//header styles
.header {
max-width: 500px;
margin: 50px auto;
text-align: center;
}
.header__title {
margin-bottom: 30px;
font: {
weight: 500;
}
}
//content styles
.content {
max-width: 700px;
margin: auto;
}
.content__title {
margin-bottom: 20px;
font: {
size: 18px;
weight: 500;
}
text-align: center;
}
.content__inner {
width: 375px;
height: 550px;
margin: auto;
box-shadow:
0 8px 17px 2px rgba(0,0,0,0.14),
0 3px 14px 2px rgba(0,0,0,0.12),
0 5px 5px -3px rgba(0,0,0,0.2);
}
//tabs styles
$tabs-padding: 15px;
.tabs {
position: relative;
padding: $tabs-padding;
height: 100%;
overflow: hidden;
}
//tabs nav styles
$theme-accent-color: #119DA4;
.tabs__nav {
position: relative;
}
//nav decoration is appended by js
.tabs__nav-decoration {
position: absolute;
top: 0;
left: 0;
height: 100%;
transition: width .2s linear 0s,
transform .2s ease-out 0s;
background-color: $theme-accent-color;
border-radius: 3px;
z-index: 1;
}
.tabs__nav-list {
position: relative;
display: flex;
justify-content: space-between;
list-style-type: none;
z-index: 5;
}
.tabs__nav-item {
@include transition-mix($delay: 0s);
padding: 15px;
cursor: pointer;
//active styles
&.js-active {
@include transition-mix($delay: .05s);
color: #fff;
}
}
$panels-bg-color: rgba(0,0,0,.15);
.tabs__panels {
position: relative;
margin-top: 30px;
}
.tabs__panel {
@include position-absolute($top: 0, $left: 0);
transition: none;
transform: scale(.8);
width: 100%;
opacity: 0;
//active panel styles
&.js-active {
transition: all .25s linear 0s;
transform: scale(1);
opacity: 1;
}
}
.tabs__panel-card {
display: flex;
margin-bottom: 30px;
padding: 15px;
box-shadow:
0 2px 2px 0 rgba(0,0,0,0.14),
0 3px 1px -2px rgba(0,0,0,0.12),
0 1px 5px 0 rgba(0,0,0,0.2);
&:last-child {
margin-bottom: 0;
}
}
.tabs__panel-card--spaced-between {
justify-content: space-between;
}
.tabs__panel-avatar {
flex-shrink: 0;
width: 100px;
height: 100px;
border-radius: 50%;
background-color: $panels-bg-color;
}
.tabs__panel-img {
flex-shrink: 0;
width: 80px;
height: 80px;
border-radius: 4px;
background-color: $panels-bg-color;
}
.tabs__panel-content {
width: 100%;
margin-left: 30px;
&:first-child {
margin-left: 0;
}
&:not(:last-child) {
margin-right: 30px;
}
&:before,
&:after {
display: block;
width: 100%;
height: 20px;
content: '';
background-color: $panels-bg-color;
}
&:before {
margin-bottom: 15px;
}
}
View Compiled
const DOM = {
tabsNav: document.querySelector('.tabs__nav'),
tabsNavItems: document.querySelectorAll('.tabs__nav-item'),
panels: document.querySelectorAll('.tabs__panel')
};
//set active nav element
const setActiveItem = elem => {
DOM.tabsNavItems.forEach(el => {
el.classList.remove('js-active');
});
elem.classList.add('js-active');
};
//find active nav element
const findActiveItem = () => {
let activeIndex = 0;
for(let i = 0; i < DOM.tabsNavItems.length; i++) {
if (DOM.tabsNavItems[i].classList.contains('js-active')) {
activeIndex = i;
break;
};
};
return activeIndex;
};
//find active nav elements parameters: left coord, width
const findActiveItemParams = (activeItemIndex) => {
const activeTab = DOM.tabsNavItems[activeItemIndex];
//width of elem
const activeItemWidth = activeTab.offsetWidth - 1;
//left coord in the tab navigation
const activeItemOffset_left = activeTab.offsetLeft;
return [activeItemWidth, activeItemOffset_left];
};
//appending decoration block to an active nav element
const appendDecorationNav = () => {
//creating decoration element
let decorationElem = document.createElement('div');
decorationElem.classList.add('tabs__nav-decoration');
decorationElem.classList.add('js-decoration');
//appending decoration element to navigation
DOM.tabsNav.append(decorationElem);
//appending styles to decoration element
return decorationElem;
};
//appending styles to decoration nav element
const styleDecorElem = (elem, decorWidth, decorOffset) => {
elem.style.width = `${decorWidth}px`;
elem.style.transform = `translateX(${decorOffset}px)`;
};
//find active panel
const findActivePanel = index => {
return DOM.panels[index];
};
//set active panel class
const setActivePanel = index => {
DOM.panels.forEach(el => {
el.classList.remove('js-active');
});
DOM.panels[index].classList.add('js-active');
};
//onload function
window.addEventListener('load', () => {
//find active nav item
const activeItemIndex = findActiveItem();
//find active nav item params
const [decorWidth, decorOffset] = findActiveItemParams(activeItemIndex);
//appending decoration element to an active elem
const decorElem = appendDecorationNav();
//setting styles to the decoration elem
styleDecorElem(decorElem, decorWidth, decorOffset);
//find active panel
findActivePanel(activeItemIndex);
//set active panel
setActivePanel(activeItemIndex);
});
//click nav item function
DOM.tabsNav.addEventListener('click', e => {
const navElemClass = 'tabs__nav-item';
//check if we click on a nav item
if(e.target.classList.contains(navElemClass)) {
const clickedTab = e.target;
const activeItemIndex = Array.from(DOM.tabsNavItems).indexOf(clickedTab);
//set active nav item
setActiveItem(clickedTab);
//find active nav item
const activeItem = findActiveItem();
//find active nav item params
const [decorWidth, decorOffset] = findActiveItemParams(activeItem);
//setting styles to the decoration elem
const decorElem = document.querySelector('.js-decoration');
styleDecorElem(decorElem, decorWidth, decorOffset);
//find active panel
findActivePanel(activeItemIndex);
//set active panel
setActivePanel(activeItemIndex);
}
});
View Compiled
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.