<div class="container">
<nav>
<ul class="tabs">
<li class="tab selected" data-tab="tomato">
<span class="tab-label">Tomato</span>
<div class="underline" data-layout-id="underline"></div>
</li>
<li class="tab" data-tab="lettuce">
<span class="tab-label">Lettuce</span>
</li>
<li class="tab" data-tab="cheese">
<span class="tab-label">Cheese</span>
</li>
</ul>
</nav>
<main class="icon-container">
<div class="icon" data-content="tomato"></div>
</main>
</div>
<script type="module">
import { animate, animateLayout } from "motion"
const tabs = {
tomato: "🍅",
lettuce: "🥬",
cheese: "🧀",
}
let selectedTab = "tomato"
const iconContainer = document.querySelector(".icon-container")
const currentIcon = document.querySelector(".icon")
currentIcon.innerHTML = tabs[selectedTab]
const tabElements = document.querySelectorAll(".tab")
tabElements.forEach((tab) => {
tab.addEventListener("click", async () => {
const newTab = tab.dataset.tab
if (newTab === selectedTab) return
const oldTab = selectedTab
selectedTab = newTab
// Animate the underline moving to the new tab
await animateLayout(
() => {
// Remove underline from old tab
document
.querySelector(`.tab[data-tab="${oldTab}"]`)
.classList.remove("selected")
document
.querySelector(`.tab[data-tab="${oldTab}"] .underline`)
?.remove()
// Add underline to new tab
document
.querySelector(`.tab[data-tab="${newTab}"]`)
.classList.add("selected")
const newTabElement = document.querySelector(
`.tab[data-tab="${newTab}"]`
)
const underline = document.createElement("div")
underline.className = "underline"
underline.setAttribute("data-layout-id", "underline")
newTabElement.appendChild(underline)
},
{ duration: 0.3, ease: [0.4, 0, 0.2, 1] }
)
// Animate content change
const currentIcon = document.querySelector(".icon")
// Exit animation for old content
await animate(
currentIcon,
{ y: -10, opacity: 0 },
{ duration: 0.2 }
)
// Update content
currentIcon.innerHTML = tabs[newTab]
await animate(
currentIcon,
{ y: [10, 0], opacity: [0, 1] },
{ duration: 0.2 }
)
})
})
</script>
<style>
.container {
width: 480px;
height: 360px;
max-width: calc(100% - 40px);
max-height: calc(100% - 40px);
border-radius: 10px;
background: white;
overflow: hidden;
box-shadow: 0 1px 1px hsl(0deg 0% 0% / 0.075),
0 2px 2px hsl(0deg 0% 0% / 0.075), 0 4px 4px hsl(0deg 0% 0% / 0.075),
0 8px 8px hsl(0deg 0% 0% / 0.075),
0 16px 16px hsl(0deg 0% 0% / 0.075);
display: flex;
flex-direction: column;
}
.container nav {
background: #fdfdfd;
padding: 5px 5px 0;
border-radius: 10px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-bottom: 1px solid #eeeeee;
height: 44px;
}
.tabs {
list-style: none;
padding: 0;
margin: 0;
font-weight: 500;
font-size: 14px;
display: flex;
width: 100%;
}
.tab {
border-radius: 5px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
width: 100%;
padding: 10px 15px;
position: relative;
background: white;
cursor: pointer;
height: 24px;
display: flex;
justify-content: space-between;
align-items: center;
flex: 1;
min-width: 0;
user-select: none;
color: var(--black);
}
.tab.selected {
background-color: #eee;
}
.tab-label {
position: relative;
z-index: 1;
}
.underline {
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 2px;
background: var(--hue-1);
}
.icon-container {
display: flex;
justify-content: center;
align-items: center;
flex: 1;
}
.icon {
font-size: 128px;
}
</style>