
Lưu ý: Phần chạy code phía trên có thể hoạt động không chính xác. Hãy copy code sang website của bạn để nó hoạt động đúng cách.
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nav-Bar using HTML CSS JS</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<nav class="tab-bar">
<ul class="tab-bar__tabs">
<li class="tab-bar__tab">
<a class="tab-bar__tab-link" href="#home" aria-current="page">
<svg class="tab-bar__tab-icon tab-bar__tab-icon--home" viewBox="0 0 24 24" width="24px" height="24px" aria-hidden="true">
<g class="tab-bar__tab-icon-1" fill="var(--focus-t)" stroke="currentColor" stroke-width="2" stroke-linejoin="round">
<polygon points="12 1,23 10,23 23,16 23,16 14,8 14,8 23,1 23,1 10" />
</g>
</svg>
<span class="tab-bar__tab-name">Home</span>
</a>
</li>
<li class="tab-bar__tab">
<a class="tab-bar__tab-link" href="#videos">
<svg class="tab-bar__tab-icon tab-bar__tab-icon--videos" viewBox="0 0 24 24" width="24px" height="24px" aria-hidden="true">
<g fill="var(--focus-t)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line class="tab-bar__tab-icon-1" x1="3" y1="1" x2="21" y2="1" />
<line x1="2" y1="5" x2="22" y2="5" />
<g class="tab-bar__tab-icon-2" transform="translate(1,9)">
<polygon points="9 3,15 7.5,9 11" />
<rect rx="2" ry="2" width="22" height="14" />
<polygon class="tab-bar__tab-icon-3" opacity="0" points="9 3,15 7.5,9 11" />
</g>
</g>
</svg>
<span class="tab-bar__tab-name">Videos</span>
</a>
</li>
<li class="tab-bar__tab">
<a class="tab-bar__tab-link" href="#books">
<svg class="tab-bar__tab-icon tab-bar__tab-icon--books" viewBox="0 0 24 24" width="24px" height="24px" aria-hidden="true">
<g class="tab-bar__tab-icon-1" fill="var(--focus-t)" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect class="tab-bar__tab-icon-2" x="1" y="1" rx="2" ry="2" width="11" height="19" />
<rect class="tab-bar__tab-icon-3" x="12" y="1" rx="2" ry="2" width="11" height="19" />
<line x1="12" y1="21" x2="12" y2="23" />
</g>
</svg>
<span class="tab-bar__tab-name">Books</span>
</a>
</li>
<li class="tab-bar__tab">
<a class="tab-bar__tab-link" href="#profile">
<svg class="tab-bar__tab-icon tab-bar__tab-icon--profile" viewBox="0 0 24 24" width="24px" height="24px" aria-hidden="true">
<g fill="var(--focus-t)" stroke="currentColor" stroke-width="2">
<circle class="tab-bar__tab-icon-1" cx="12" cy="6.5" r="5.5"/>
<path d="M20.473,23H3.003c-1.276,0-2.228-1.175-1.957-2.422,.705-3.239,3.029-8.578,10.693-8.578s9.987,5.336,10.692,8.575c.272,1.248-.681,2.425-1.959,2.425Z"/>
</g>
</svg>
<span class="tab-bar__tab-name">Profile</span>
</a>
</li>
</ul>
</nav>
<script src="script.js"></script>
</body>
</html>
/* Basic Reset */
* {
border: 0;
box-sizing: border-box;
margin: 0;
padding: 0;
}
/* Root Variables */
:root {
--hue: 223;
--bg: hsl(var(--hue), 10%, 90%);
--fg: hsl(var(--hue), 10%, 10%);
--focus: hsl(var(--hue), 90%, 50%);
--focus-t: hsla(var(--hue), 90%, 50%, 0);
--tab-bar-bg: hsl(0, 0%, 100%);
--trans-dur: 0.3s;
--trans-timing: cubic-bezier(0.65, 0, 0.35, 1);
font-size: calc(14px + (30 - 14) * (100vw - 280px) / (3840 - 280));
}
/* Body Styles */
body {
background-color: var(--bg);
color: var(--fg);
display: flex;
font: 1em/1.5 "Noto Sans", sans-serif;
height: 100vh;
transition: background-color var(--trans-dur), color var(--trans-dur);
}
/* Tab Bar Styles */
.tab-bar {
background-color: var(--tab-bar-bg);
border-radius: 2em;
box-shadow: 0 0 0.75em hsla(var(--hue), 10%, 10%, 0.1);
margin: auto;
width: calc(100% - 1.5em);
max-width: 27em;
transition: background-color var(--trans-dur), box-shadow var(--trans-dur);
}
.tab-bar__tabs {
display: flex;
justify-content: space-between;
list-style: none;
}
.tab-bar__tab {
text-align: center;
width: 100%;
}
.tab-bar__tab-icon,
.tab-bar__tab-name {
display: block;
pointer-events: none;
transition: opacity var(--trans-dur) var(--trans-timing), transform var(--trans-dur) var(--trans-timing);
}
.tab-bar__tab-icon {
margin: auto;
overflow: visible;
width: 1.5em;
height: auto;
}
.tab-bar__tab-icon circle,
.tab-bar__tab-icon path,
.tab-bar__tab-icon polygon,
.tab-bar__tab-icon rect {
transition: fill calc(var(--trans-dur) / 2) var(--trans-timing), opacity calc(var(--trans-dur) / 2) var(--trans-timing), stroke calc(var(--trans-dur) / 2) var(--trans-timing), transform var(--trans-dur) var(--trans-timing);
}
.tab-bar__tab-icon--home .tab-bar__tab-icon-1 {
transform-origin: 12px 24px;
}
.tab-bar__tab-icon--videos .tab-bar__tab-icon-3 {
fill: var(--tab-bar-bg);
stroke: var(--tab-bar-bg);
}
.tab-bar__tab-icon--books .tab-bar__tab-icon-2,
.tab-bar__tab-icon--books .tab-bar__tab-icon-3 {
transform-origin: 12px 21px;
}
.tab-bar__tab-name {
font-size: 0.75em;
font-weight: 500;
line-height: 1;
top: calc(100% - 0.5rem);
opacity: 0;
overflow: hidden;
position: absolute;
text-overflow: ellipsis;
white-space: nowrap;
width: 100%;
}
.tab-bar__tab-link {
color: var(--fg);
display: flex;
position: relative;
text-decoration: none;
width: 100%;
height: 5.5em;
transition: color calc(var(--trans-dur) / 2);
-webkit-tap-highlight-color: transparent;
}
.tab-bar__tab-link:hover,
.tab-bar__tab-link:focus-visible {
color: var(--focus);
}
.tab-bar__tab-link[aria-current="page"] {
color: var(--focus);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon {
transform: translateY(-50%);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon circle,
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon path,
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon polygon,
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon rect {
fill: var(--focus);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-name {
opacity: 1;
transform: translateY(-200%);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon--home .tab-bar__tab-icon-1 {
animation: home-bounce calc(var(--trans-dur) * 2.5) cubic-bezier(0.65, 0, 0.35, 1);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon--videos .tab-bar__tab-icon-1 {
animation: video-move-1 calc(var(--trans-dur) * 2.5) cubic-bezier(0.65, 0, 0.35, 1);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon--videos .tab-bar__tab-icon-2 {
animation: video-move-2 calc(var(--trans-dur) * 2.5) cubic-bezier(0.65, 0, 0.35, 1);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon--videos .tab-bar__tab-icon-3 {
animation: video-fade-slide calc(var(--trans-dur) * 2.5) cubic-bezier(0.65, 0, 0.35, 1);
opacity: 1;
fill: var(--tab-bar-bg);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon--books .tab-bar__tab-icon-1 {
animation: books-move calc(var(--trans-dur) * 2.5) cubic-bezier(0.65, 0, 0.35, 1);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon--books .tab-bar__tab-icon-2 {
animation: books-scale-left calc(var(--trans-dur) * 2.5) cubic-bezier(0.65, 0, 0.35, 1);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon--books .tab-bar__tab-icon-3 {
animation: books-scale-right calc(var(--trans-dur) * 2.5) cubic-bezier(0.65, 0, 0.35, 1);
}
.tab-bar__tab-link[aria-current="page"] .tab-bar__tab-icon--profile .tab-bar__tab-icon-1 {
animation: profile-head-bob calc(var(--trans-dur) * 2.5) cubic-bezier(0.65, 0, 0.35, 1);
}
/* Dark Theme */
@media (prefers-color-scheme: dark) {
:root {
--bg: hsl(var(--hue), 10%, 30%);
--fg: hsl(var(--hue), 10%, 90%);
--focus: hsl(var(--hue), 90%, 60%);
--focus-t: hsla(var(--hue), 90%, 60%, 0);
--tab-bar-bg: hsl(var(--hue), 10%, 10%);
}
.tab-bar {
box-shadow: 0 0 0.75em hsla(var(--hue), 10%, 10%, 0.3);
}
}
/* Reduced Motion */
@media (prefers-reduced-motion) {
.tab-bar__tab .tab-bar__tab-icon {
animation: none;
}
}
/* Keyframes */
@keyframes home-bounce {
from, to {
transform: scale(1, 1) translateY(0);
}
20% {
transform: scale(1.5, 0.75) translateY(0);
}
40% {
transform: scale(0.8, 1.2) translateY(-4px);
}
60% {
transform: scale(1.1, 0.9) translateY(0);
}
80% {
transform: scale(0.95, 1.05) translateY(0);
}
}
@keyframes video-move-1 {
from, to {
transform: translate(0, 0);
}
20%, 80% {
transform: translate(0, 4px);
}
}
@keyframes video-move-2 {
from, to {
transform: translate(1px, 9px);
}
20%, 80% {
transform: translate(1px, 5px);
}
}
@keyframes video-fade-slide {
from {
animation-timing-function: steps(1, end);
opacity: 0;
transform: translate(0, 0);
}
40% {
animation-timing-function: cubic-bezier(0.33, 1, 0.68, 1);
opacity: 1;
stroke: hsla(0, 0%, 0%, 0);
transform: translate(-4px, 0);
}
60%, to {
opacity: 1;
stroke: var(--tab-bar-bg);
transform: translate(0, 0);
}
}
@keyframes books-move {
from, 60%, to {
transform: translateY(0);
}
20% {
transform: translateY(-1px);
}
40% {
transform: translateY(0.5px);
}
}
@keyframes books-scale-left {
from, to {
transform: skewY(0);
}
20% {
transform: skewY(-16deg);
}
40% {
transform: skewY(12deg);
}
60% {
transform: skewY(-8deg);
}
80% {
transform: skewY(4deg);
}
}
@keyframes books-scale-right {
from, to {
transform: skewY(0);
}
20% {
transform: skewY(16deg);
}
40% {
transform: skewY(-12deg);
}
60% {
transform: skewY(8deg);
}
80% {
transform: skewY(-4deg);
}
}
@keyframes profile-head-bob {
from, to {
transform: translateX(0);
}
20% {
transform: translateX(4px);
}
40% {
transform: translateX(-3px);
}
60% {
transform: translateX(2px);
}
80% {
transform: translateX(-1px);
}
}
window.addEventListener("DOMContentLoaded", () => {
const tabbar = new TabBar("nav");
});
class TabBar {
/** Tab bar element */
el;
/**
* @param {string} el CSS selector for the tab bar
*/
constructor(el) {
this.el = document.querySelector(el);
if (this.el) {
this.el.setAttribute("data-pristine", "true");
this.el.addEventListener("click", this.switchTab.bind(this));
}
}
/**
* Make the clicked tab active.
* @param {Event} e Click event
*/
switchTab(e) {
// Allow animations, which were prevented on load
if (this.el) {
this.el.removeAttribute("data-pristine");
}
// Ensure the event target is a link
const target = e.target.closest('a');
if (target && target.tagName === 'A') {
const href = target.getAttribute("href");
if (href) {
// Remove the state from the current page
const currentPage = this.el?.querySelector(`[aria-current="page"]`);
if (currentPage) {
currentPage.removeAttribute("aria-current");
}
// Apply the state to the new page
target.setAttribute("aria-current", "page");
}
}
}
}