Centered Scaling Navigation Bar
A centered floating pill navigation that expands downward via grid-template-rows to reveal links and a scrolling marquee banner. Links slide up from below with staggered delays. A dark overlay fades in behind the pill when the nav is open. Entirely CSS-driven.
Code
<nav data-navigation-status="not-active" class="navigation">
<div data-navigation-toggle="close" class="navigation__dark-bg"></div>
<div class="centered-nav">
<div class="centered-nav__bg"></div>
<div class="centered-nav__header">
<a href="#" class="centered-nav__logo">
<!-- your logo SVG -->
</a>
<button data-navigation-toggle="toggle" class="centered-nav__toggle">
<div class="centered-nav__toggle-bar"></div>
<div class="centered-nav__toggle-bar"></div>
</button>
</div>
<div class="centered-nav__content">
<div class="centered-nav__inner">
<ul class="centered-nav__ul">
<div data-navigation-item="" class="centered-nav__li">
<a href="#" class="hamburger-nav__a">
<p class="hamburger-nav__p">Home</p>
</a>
</div>
<div data-navigation-item="" class="centered-nav__li">
<a href="#" class="hamburger-nav__a">
<p class="hamburger-nav__p">Portfolio</p>
</a>
</div>
<div data-navigation-item="" class="centered-nav__li">
<a href="#" class="hamburger-nav__a">
<p class="hamburger-nav__p">About us</p>
</a>
</div>
<div data-navigation-item="" class="centered-nav__li">
<a href="#" class="hamburger-nav__a">
<p class="hamburger-nav__p">Our services</p>
</a>
</div>
<div data-navigation-item="" class="centered-nav__li">
<a href="#" class="hamburger-nav__a">
<p class="hamburger-nav__p">Approach</p>
</a>
</div>
</ul>
<div data-navigation-item="" class="centered-nav__banner-w">
<a href="#" class="centered-nav__banner w-inline-block">
<div class="centered-nav__banner-row">
<div data-css-marquee-list="" class="centered-nav__banner-item">
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
</div>
<div data-css-marquee-list="" class="centered-nav__banner-item">
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
<div class="centered-nav__banner-inner"><p class="centered-nav__banner-text">Contact us</p></div>
</div>
</div>
</a>
</div>
</div>
</div>
</div>
</nav>.navigation {
z-index: 500;
pointer-events: none;
position: fixed;
inset: 0;
}
.navigation__dark-bg {
opacity: 0;
pointer-events: auto;
visibility: hidden;
background-color: #000;
position: absolute;
inset: 0;
transition: all 0.7s cubic-bezier(0.5, 0.5, 0, 1);
}
[data-navigation-status="active"] .navigation__dark-bg {
opacity: 0.15;
visibility: visible;
}
.centered-nav {
border-radius: .75em;
flex-flow: column;
justify-content: flex-start;
align-items: stretch;
width: 30em;
display: flex;
position: absolute;
top: 2em;
left: 50%;
transform: translate(-50%);
}
.centered-nav__bg {
background-color: #fff;
border-radius: .75em;
width: 100%;
height: 100%;
position: absolute;
inset: 0%;
}
.centered-nav__header {
z-index: 1;
justify-content: space-between;
align-items: center;
padding: 1.25em 1.5em 1.25em 1.625em;
display: flex;
position: relative;
}
.centered-nav__logo {
pointer-events: auto;
color: #131313;
justify-content: center;
align-items: center;
width: 7.5em;
display: flex;
}
.centered-nav__logo-svg {
width: 100%;
height: 100%;
}
.centered-nav__toggle {
pointer-events: auto;
cursor: pointer;
background-color: #0000;
justify-content: center;
align-items: center;
width: 2.5em;
height: 2.5em;
padding: 0;
display: flex;
position: relative;
}
.centered-nav__toggle .centered-nav__toggle-bar {
background-color: #131313;
width: 1.875em;
height: .125em;
position: absolute;
transition: transform 0.6s cubic-bezier(.65, 0, 0, 1);
transform: translateY(-0.25em) rotate(0.001deg);
}
.centered-nav__toggle:hover .centered-nav__toggle-bar {
transform: translateY(0.25em) rotate(0.001deg);
}
.centered-nav__toggle .centered-nav__toggle-bar:nth-child(2) {
transform: translateY(0.15em) rotate(0.001deg);
}
.centered-nav__toggle:hover .centered-nav__toggle-bar:nth-child(2) {
transform: translateY(-0.15em) rotate(0.001deg);
}
[data-navigation-status="active"] .centered-nav__toggle .centered-nav__toggle-bar {
transform: translateY(0em) rotate(45deg);
}
[data-navigation-status="active"] .centered-nav__toggle .centered-nav__toggle-bar:nth-child(2) {
transform: translateY(0em) rotate(-45deg);
}
.centered-nav__content {
border-bottom-right-radius: .75em;
border-bottom-left-radius: .75em;
grid-template-rows: 0fr;
display: grid;
position: relative;
overflow: hidden;
transition: grid-template-rows 0.6s cubic-bezier(0.625, 0.05, 0, 1);
}
[data-navigation-status="active"] .centered-nav__content {
grid-template-rows: 1fr;
}
.centered-nav__inner {
grid-column-gap: 3em;
grid-row-gap: 3em;
pointer-events: auto;
flex-flow: column;
justify-content: flex-start;
align-items: center;
width: 100%;
height: 10000%;
display: flex;
position: relative;
overflow: hidden;
}
.centered-nav__ul {
flex-flow: column;
justify-content: flex-start;
align-items: stretch;
width: 100%;
margin-top: 0;
margin-bottom: 0;
padding: 0;
display: flex;
position: relative;
}
.centered-nav__li {
margin: 0;
padding: 0;
list-style: none;
overflow: clip;
}
.hamburger-nav__a::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 1px;
background: currentColor;
opacity: 0.2;
transition-delay: inherit;
transform: scaleX(0);
transition: transform 0.6s cubic-bezier(.65, 0, 0, 1);
}
[data-navigation-status="active"] .hamburger-nav__a::after {
transform: scaleX(1);
}
.hamburger-nav__a:hover::after {
opacity: 0.4;
}
.hamburger-nav__a[aria-current]::after {
opacity: 1;
}
.hamburger-nav__p {
transition: transform 0.6s cubic-bezier(.65, 0, 0, 1);
transform: translate(0px, 150%);
transition-delay: inherit;
}
[data-navigation-status="active"] .hamburger-nav__p {
transform: translate(0px, 0%);
}
.centered-nav__banner-w {
justify-content: center;
align-items: center;
width: 100%;
display: flex;
}
.centered-nav__banner {
color: #fff;
background-color: #ff4c24;
max-width: 100%;
padding-top: 1em;
padding-bottom: 1em;
text-decoration: none;
transition: background-color .15s;
overflow: hidden;
}
.centered-nav__banner:hover {
background-color: #ff4c24e6;
}
.centered-nav__banner-row {
justify-content: flex-start;
align-items: center;
display: flex;
}
.centered-nav__banner-item {
flex: none;
justify-content: flex-start;
align-items: center;
display: flex;
}
.centered-nav__banner-inner {
flex: none;
justify-content: flex-start;
align-items: center;
padding-right: 2em;
display: flex;
}
.centered-nav__banner-text {
text-transform: uppercase;
margin-bottom: 0;
font-family: RM Mono, Arial, sans-serif;
font-size: .875em;
font-weight: 400;
line-height: 1;
}
@media screen and (max-width: 767px) {
.centered-nav {
width: auto;
top: 1em;
left: 1em;
right: 1em;
transform: none;
}
}
@keyframes translateX {
to {
transform: translateX(-100%);
}
}
[data-css-marquee-list] {
animation: translateX 20s linear;
animation-iteration-count: infinite;
animation-play-state: paused;
}
[data-navigation-status="active"] [data-css-marquee-list] {
animation-play-state: running;
}
[data-navigation-status="active"] .centered-nav__banner:hover [data-css-marquee-list] {
animation-play-state: paused;
}function initCenteredScalingNavigationBar() {
const navigationInnerItems = document.querySelectorAll("[data-navigation-item]");
// Apply staggered transition delays
navigationInnerItems.forEach((item, index) => {
item.style.transitionDelay = `${index * 0.05}s`;
});
// Toggle Navigation
document.querySelectorAll('[data-navigation-toggle="toggle"]').forEach(toggleBtn => {
toggleBtn.addEventListener('click', () => {
const navStatusEl = document.querySelector('[data-navigation-status]');
if (!navStatusEl) return;
if (navStatusEl.getAttribute('data-navigation-status') === 'not-active') {
navStatusEl.setAttribute('data-navigation-status', 'active');
// If you use Lenis you can 'stop' Lenis here: Example Lenis.stop();
} else {
navStatusEl.setAttribute('data-navigation-status', 'not-active');
// If you use Lenis you can 'start' Lenis here: Example Lenis.start();
}
});
});
// Close Navigation
document.querySelectorAll('[data-navigation-toggle="close"]').forEach(closeBtn => {
closeBtn.addEventListener('click', () => {
const navStatusEl = document.querySelector('[data-navigation-status]');
if (!navStatusEl) return;
navStatusEl.setAttribute('data-navigation-status', 'not-active');
// If you use Lenis you can 'start' Lenis here: Example Lenis.start();
});
});
// ESC closes
document.addEventListener('keydown', e => {
if (e.keyCode === 27) {
const navStatusEl = document.querySelector('[data-navigation-status]');
if (!navStatusEl) return;
if (navStatusEl.getAttribute('data-navigation-status') === 'active') {
navStatusEl.setAttribute('data-navigation-status', 'not-active');
// If you use Lenis you can 'start' Lenis here: Example Lenis.start();
}
}
});
}
document.addEventListener('DOMContentLoaded', function() {
initCenteredScalingNavigationBar();
});Attributes
| Name | Type | Default | Description |
|---|---|---|---|
| data-navigation-status | "active" | "not-active" | "not-active" | Placed on the <nav> element (or <body> for broader scope). Flipping to "active" triggers the pill expansion, link slide-up, and marquee animation. |
| data-navigation-toggle="toggle" | attribute | — | Attach to the hamburger button. Toggles data-navigation-status between active and not-active on click. |
| data-navigation-toggle="close" | attribute | — | Attach to any element that should always close the nav, such as the dark overlay. The dark overlay (.navigation__dark-bg) has this by default. |
| data-navigation-item | attribute | — | Attach to each list item or content block inside the nav. The JS assigns incrementing transition-delay values (0.05s per item) to create the staggered slide-up effect. |
| data-css-marquee-list | attribute | — | Attach to each duplicate marquee row. The CSS animation (translateX -100%) is paused by default and starts running when data-navigation-status is active. Hovering the banner pauses it again. |
Notes
- •No GSAP required — all animation is CSS-driven.
- •The dark background overlay (.navigation__dark-bg) doubles as a close trigger via data-navigation-toggle="close".
- •Pressing Escape closes the nav via the keydown listener.
- •Staggered delays are applied inline by JS — no hard-coded nth-child rules needed in CSS.
- •To integrate Lenis, uncomment Lenis.stop() / Lenis.start() inside the toggle and close handlers.
Guide
Pill expansion
The nav content area uses grid-template-rows: 0fr → 1fr to expand downward without knowing the content height. The inner element has height: 10000% and overflow: hidden so the grid can clip it cleanly.
Staggered link reveal
Each [data-navigation-item] element gets an inline transition-delay set by JS (index × 0.05s). The CSS transitions on .hamburger-nav__p (translateY 150% → 0%) and the ::after underline both inherit this delay via transition-delay: inherit.
CSS marquee banner
Two duplicate .centered-nav__banner-item rows are placed side-by-side. Each animates translateX(-100%) on an infinite loop. The animation is paused when the nav is closed and resumes on open. Hovering the banner pauses playback.
Pill width
The default width is 30em on .centered-nav. On screens ≤767px the pill becomes full-width (left: 1em, right: 1em, transform: none). Adjust these values to taste.