Animate menu item transitions
by Csaba Hellinger
HTML
<nav class="menu">
<ul>
<li><a href="#home">Home</a></li>
<li><a href="#about">About</a></li>
<li><a href="#services">Services</a></li>
<li><a href="#contact">Contact</a></li>
<li><a href="#faq">FAQ</a></li>
</ul>
<div class="tint"></div>
</nav>
CSS
body {
background: #222;
margin: 0;
font-family: 'Helvetica';
font-weight: 800;
}
/* the menu bar */
.menu {
position: relative;
background: linear-gradient(to bottom, #111, #000);
font-size: 1.5rem;
}
/* the moving “tint” overlay */
.tint {
position: absolute;
top: 0;
bottom: 0;
background: linear-gradient(to right, #fff, #f00 20%, #f00 80%, #fff);
view-transition-name: tint;
mix-blend-mode: multiply;
_opacity:0.5;
}
::view-transition-group(tint) {
animation-duration: 800ms;
animation-timing-function: ease-in-out;
}
/* prevent any Y‑axis drift due to preserving aspect ratio */
::view-transition-old(tint),
::view-transition-new(tint) {
height: 100%;
}
/* lay out the links horizontally */
.menu ul {
display: flex;
margin: 0;
padding: 0;
list-style: none;
}
/* style the links */
.menu a {
display: block;
padding: 1rem 1.5rem;
color: #fff;
text-decoration: none;
position: relative;
}
JavaScript
// helper that falls back if View Transitions unsupported
function animate(fn) {
if (document.startViewTransition) {
document.startViewTransition(fn);
} else {
fn();
}
}
const menu = document.querySelector('.menu');
const tint = menu.querySelector('.tint');
const links = menu.querySelectorAll('a');
function updateTint(el) {
const linkRect = el.getBoundingClientRect();
const menuRect = menu.getBoundingClientRect();
const left = linkRect.left - menuRect.left;
animate(() => {
tint.style.left = `${left}px`;
tint.style.width = `${linkRect.width}px`;
});
// update URL hash without scrolling
history.replaceState(null, '', el.getAttribute('href'));
}
// wire up clicks
links.forEach(link =>
link.addEventListener('click', e => {
e.preventDefault();
updateTint(link);
})
);
// on load, position under the current hash (or default to #home)
window.addEventListener('DOMContentLoaded', () => {
const current = location.hash || '#home';
const startEl = document.querySelector(`.menu a[href="${current}"]`)
|| links[0];
updateTint(startEl);
});