Mega Navigation (Directional Hover)
A full-featured mega nav with morphing dropdowns on desktop and a slide-over panel system on mobile. On desktop, hovering a toggle opens a panel; switching between toggles slides content in the direction of travel with a height-morphing container. On mobile (≤991px), the menu opens as a full-screen overlay with staggered nav items and drill-down panels.
navigationmega navdropdowngsapmobileresponsiveanimation
Setup — External Scripts
External Scripts
html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>Code
HTML
html
<nav data-menu-open="false" data-menu-wrap="" class="mega-nav">
<div class="mega-nav__bar">
<div class="mega-nav__container">
<div class="mega-nav__bar-start">
<a data-menu-logo="" href="#" class="mega-nav__bar-logo w-inline-block">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 92 20" fill="none">
<path d="M32.3282 19.9998C35.7836 19.9998 38.8319 18.2812 40.6512 15.6618C41.6963 18.3292 44.3996 19.9998 47.9789 19.9998C50.5576 19.9998 52.7327 19.1845 54.1302 17.8438L53.9287 19.5858H58.2951L59.3401 10.5658L61.7665 19.5858H66.1369L68.5633 10.5658L69.6077 19.5858H73.974L73.5064 15.5478C75.3156 18.2312 78.4024 19.9998 81.9075 19.9998C87.4813 19.9998 91.9999 15.5292 91.9999 10.0145C91.9999 4.49982 87.48 0.0291549 81.9062 0.0291549C77.5817 0.0291549 73.8939 2.72115 72.4573 6.50249L71.7518 0.413822H66.9266L63.9504 11.4785L60.9741 0.413822H56.1489L55.4711 6.26382C55.3889 4.56182 54.6827 3.01982 53.4645 1.90315C52.1067 0.658488 50.2099 0.000488281 47.9783 0.000488281C45.8874 0.000488281 44.0856 0.589822 42.7677 1.70515C41.796 2.52782 41.1599 3.58982 40.914 4.76782C39.1358 1.92382 35.9567 0.0291549 32.3282 0.0291549C26.7544 0.0291549 22.2358 4.49982 22.2358 10.0145C22.2358 15.5292 26.7544 19.9998 32.3282 19.9998ZM81.9062 4.52249C84.9721 4.52249 87.4571 6.98115 87.4571 10.0145C87.4571 13.0478 84.9721 15.5065 81.9062 15.5065C78.8403 15.5065 76.3553 13.0478 76.3553 10.0145C76.3553 6.98115 78.8403 4.52249 81.9062 4.52249ZM47.9789 4.18582C49.8737 4.18582 51.0367 5.05382 51.0893 6.50782L51.1054 6.94982H55.3923L54.9502 10.7685C54.7306 10.4185 54.4698 10.0952 54.1659 9.80116C53.166 8.83582 51.6997 8.17849 49.807 7.84782L47.2553 7.39582C45.5647 7.09449 45.2183 6.57182 45.2183 5.87982C45.2183 5.70649 45.2958 4.18515 47.9789 4.18515V4.18582ZM46.1468 11.5932L49.1534 12.1512C51.0947 12.5198 51.3561 13.2992 51.3561 14.0132C51.3561 15.1405 50.0617 15.8405 47.9776 15.8405C45.5027 15.8405 44.5674 14.4592 44.5182 13.2765L44.5 12.8372H42.0083C42.2744 11.9418 42.42 10.9952 42.42 10.0145C42.42 9.96782 42.4173 9.92115 42.4166 9.87449C43.3128 10.7165 44.564 11.3038 46.1462 11.5932H46.1468ZM32.3282 4.52249C35.3941 4.52249 37.8791 6.98115 37.8791 10.0145C37.8791 13.0478 35.3941 15.5065 32.3282 15.5065C29.2624 15.5065 26.7774 13.0478 26.7774 10.0145C26.7774 6.98115 29.2624 4.52249 32.3282 4.52249Z" fill="#151313"></path>
<path d="M13.6751 8.238L18.1479 3.81267L16.3609 2.04467L11.8881 6.47C11.6974 6.65933 11.3706 6.52533 11.3706 6.258V0H8.84382V7.55C8.84382 8.21267 8.30073 8.75 7.63095 8.75H0V11.25H6.3251C6.5953 11.25 6.73074 11.5733 6.53937 11.762L2.06726 16.1873L3.85422 17.9553L8.32701 13.53C8.51769 13.3413 8.84449 13.4747 8.84449 13.742V20H11.3713V12.45C11.3713 11.7873 11.9144 11.25 12.5842 11.25H20.2151V8.75H13.89C13.6198 8.75 13.4844 8.42667 13.6757 8.238H13.6751Z" fill="#6840FF"></path>
</svg>
</a>
<div data-nav-list="" data-mobile-nav="" class="mega-nav__bar-inner">
<ul class="mega-nav__bar-list">
<li data-nav-list-item="">
<button data-dropdown-toggle="products" aria-expanded="false" aria-haspopup="true" class="mega-nav__bar-link is--dropdown">
<span class="mega-nav__bar-link-label">Products</span>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 20 20" fill="none" class="mega-nav__bar-link-icon is--dropdown">
<path d="M6.6665 8.3335L9.99984 11.6668L13.3332 8.3335" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</button>
</li>
<li data-nav-list-item="">
<button data-dropdown-toggle="solutions" aria-expanded="false" aria-haspopup="true" class="mega-nav__bar-link is--dropdown">
<span class="mega-nav__bar-link-label">Solutions</span>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 20 20" fill="none" class="mega-nav__bar-link-icon is--dropdown">
<path d="M6.6665 8.3335L9.99984 11.6668L13.3332 8.3335" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</button>
</li>
<li data-nav-list-item="">
<button data-dropdown-toggle="company" aria-expanded="false" aria-haspopup="true" class="mega-nav__bar-link is--dropdown">
<span class="mega-nav__bar-link-label">Company</span>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 20 20" fill="none" class="mega-nav__bar-link-icon is--dropdown">
<path d="M6.6665 8.3335L9.99984 11.6668L13.3332 8.3335" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</button>
</li>
<li data-nav-list-item="">
<a href="#" class="mega-nav__bar-link w-inline-block"><span class="mega-nav__bar-link-label">Pricing</span></a>
</li>
</ul>
<ul data-nav-list-item="" class="mega-nav__bar-list is--actions">
<li class="mega-nav__bar-action">
<a href="#" class="mega-nav__bar-cta is--secondary w-inline-block"><span class="mega-nav__bar-link-label">Log in</span></a>
</li>
<li class="mega-nav__bar-action">
<a href="#" class="mega-nav__bar-cta w-inline-block">
<span class="mega-nav__bar-link-label">Get Started</span>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 20 20" fill="none" class="mega-nav__bar-link-icon">
<path d="M8.3335 13.3335L11.6668 10.0002L8.3335 6.66683" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</a>
</li>
</ul>
</div>
<div class="mega-nav__bar-end">
<button data-burger-toggle="" aria-label="toggle menu" aria-expanded="false" class="mega-nav__burger">
<span data-burger-line="top" class="mega-nav__burger-line"></span>
<span data-burger-line="mid" class="mega-nav__burger-line"></span>
<span data-burger-line="bot" class="mega-nav__burger-line"></span>
</button>
</div>
<div data-mobile-back="" class="mega-nav__back">
<button aria-label="back to menu" class="mega-nav__bar-link is--back">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 20 20" fill="none" class="mega-nav__bar-link-icon">
<path d="M11.6665 6.6665L8.33317 9.99984L11.6665 13.3332" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
<span class="mega-nav__bar-link-label">Back</span>
</button>
</div>
</div>
</div>
</div>
<div data-dropdown-wrapper="" class="mega-nav__dropdown-wrapper">
<div data-dropdown-container="" class="mega-nav__dropdown-container">
<div data-dropdown-bg="" class="mega-nav__dropdown-bg"></div>
<div data-panel-state="" data-nav-content="products" role="region" aria-label="products menu" class="mega-nav__dropdown-panel">
<div class="mega-nav__dropdown-inner">
<div data-menu-fade="" class="mega-nav__panel-col"><span data-menu-fade="" class="mega-nav__panel-label">Platform</span>
<ul class="mega-nav__panel-list">
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Overview</span><span class="mega-nav__panel-link-desc">See the full platform</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Analytics</span><span class="mega-nav__panel-link-desc">Track and measure</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Integrations</span><span class="mega-nav__panel-link-desc">Connect your tools</span></a></li>
</ul>
</div>
<div data-menu-fade="" class="mega-nav__panel-col"><span data-menu-fade="" class="mega-nav__panel-label">Features</span>
<ul class="mega-nav__panel-list">
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Automation</span><span class="mega-nav__panel-link-desc">Streamline workflows</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Reporting</span><span class="mega-nav__panel-link-desc">Generate insights</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">AI</span><span class="mega-nav__panel-link-desc">Build custom integrations</span></a></li>
</ul>
</div>
<div data-menu-fade="" class="mega-nav__panel-col is--colored"><span data-menu-fade="" class="mega-nav__panel-label">Infrastructure</span>
<ul class="mega-nav__panel-list">
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Cloud</span><span class="mega-nav__panel-link-desc">Managed hosting</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Security</span><span class="mega-nav__panel-link-desc">Enterprise grade</span></a></li>
</ul>
</div>
</div>
</div>
<div data-panel-state="" data-nav-content="solutions" role="region" aria-label="solutions menu" class="mega-nav__dropdown-panel">
<div class="mega-nav__dropdown-inner">
<div data-menu-fade="" class="mega-nav__panel-col"><span data-menu-fade="" class="mega-nav__panel-label">By use case</span>
<ul class="mega-nav__panel-list">
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">SaaS</span><span class="mega-nav__panel-link-desc">Sell online at scale</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">E-commerce</span><span class="mega-nav__panel-link-desc">Subscription businesses</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Marketplaces</span><span class="mega-nav__panel-link-desc">Multi-vendor platforms</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Platforms</span><span class="mega-nav__panel-link-desc">Built on top of Osmo</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Creator economy</span><span class="mega-nav__panel-link-desc">Monetize your audience</span></a></li>
</ul>
</div>
<div data-menu-fade="" class="mega-nav__panel-col"><span data-menu-fade="" class="mega-nav__panel-label">By industries</span>
<ul class="mega-nav__panel-list">
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Healthcare</span><span class="mega-nav__panel-link-desc">HIPAA compliant</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Finance</span><span class="mega-nav__panel-link-desc">Secure services</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Education</span><span class="mega-nav__panel-link-desc">Learning tools</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Retail</span><span class="mega-nav__panel-link-desc">Omnichannel commerce</span></a></li>
</ul>
</div>
<div data-menu-fade="" class="mega-nav__panel-col"><span data-menu-fade="" class="mega-nav__panel-label">By size</span>
<ul class="mega-nav__panel-list">
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Startups</span><span class="mega-nav__panel-link-desc">Launch faster</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Enterprise</span><span class="mega-nav__panel-link-desc">Scale with confidence</span></a></li>
</ul>
</div>
<div class="mega-nav__panel-col is--colored"><span data-menu-fade="" class="mega-nav__panel-label">Quick links</span>
<ul class="mega-nav__panel-list">
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Customer stories</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Partners</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Professional services</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Migrations</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Compare plans</span></a></li>
</ul>
</div>
</div>
</div>
<div data-panel-state="" data-nav-content="company" role="region" aria-label="company menu" class="mega-nav__dropdown-panel">
<div class="mega-nav__dropdown-inner">
<div data-menu-fade="" class="mega-nav__panel-col"><span data-menu-fade="" class="mega-nav__panel-label">company</span>
<ul class="mega-nav__panel-list">
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">About us</span><span class="mega-nav__panel-link-desc">Our mission and team</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Careers</span><span class="mega-nav__panel-link-desc">Join the team</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Blog</span><span class="mega-nav__panel-link-desc">News and updates</span></a></li>
</ul>
</div>
<div data-menu-fade="" class="mega-nav__panel-col"><span data-menu-fade="" class="mega-nav__panel-label">quick links</span>
<ul class="mega-nav__panel-list">
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Documentation</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Help center</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Contact</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Status</span></a></li>
<li data-menu-fade=""><a href="#" class="mega-nav__panel-link w-inline-block"><span class="mega-nav__panel-link-text">Legal</span></a></li>
</ul>
</div>
<div data-menu-fade="" class="mega-nav__panel-col is--colored has--card">
<div class="mega-nav__card">
<div class="mega-nav__card-visual"><img src="https://cdn.prod.website-files.com/69b13f28b9d6dbc2dfbf1cb1/69b15819d9e2e5fd40ddf716_osmo-mega-nav-card.avif" loading="lazy" alt="" class="mega-nav__card-img"></div>
<div class="mega-nav__card-content">
<div class="mega-nav__card-text">
<span class="mega-nav__panel-link-text">Sign up for the '26 conf</span>
<span class="mega-nav__panel-link-desc">Tickets on sale now</span>
</div>
<a href="#" class="mega-nav__card-cta w-inline-block">
<span class="mega-nav__card-cta-label">Learn more</span>
<svg xmlns="http://www.w3.org/2000/svg" width="100%" viewbox="0 0 20 20" fill="none" class="mega-nav__card-cta-icon">
<path d="M8.3335 13.3335L11.6668 10.0002L8.3335 6.66683" stroke="currentColor" stroke-width="1.25" stroke-linecap="round" stroke-linejoin="round"></path>
</svg>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div data-menu-backdrop="" class="mega-nav__backdrop"></div>
</nav>CSS
css
[data-menu-wrap] {
--nav-height: 4em;
}
.mega-nav {
z-index: 100;
position: fixed;
top: 1.25em;
left: 1.25em;
right: 1.25em;
}
.mega-nav__bar {
z-index: 3;
background-color: #fff;
border-bottom: 1px solid #0000001a;
border-radius: .25em;
width: 100%;
max-width: 80em;
margin-left: auto;
margin-right: auto;
position: relative;
}
.mega-nav__container {
height: var(--nav-height);
justify-content: space-between;
align-items: center;
padding: 1em 1.5em;
display: flex;
}
.mega-nav__bar-start {
justify-content: flex-start;
align-items: center;
width: 100%;
display: flex;
position: relative;
}
.mega-nav__bar-logo {
flex: none;
width: 5.75em;
margin-right: 3em;
display: flex;
}
.mega-nav__bar-list {
grid-column-gap: .75em;
grid-row-gap: .75em;
justify-content: flex-start;
align-items: center;
margin-bottom: 0;
padding-left: 0;
list-style: none;
display: flex;
}
.mega-nav__bar-list.is--actions {
margin-left: auto;
}
.mega-nav__bar-link {
color: #201d1d;
background-color: #0000;
border-radius: .25em;
justify-content: flex-start;
align-items: center;
padding: .375em .625em;
text-decoration: none;
display: flex;
}
.mega-nav__bar-link-label {
font-size: .9375em;
font-weight: 500;
line-height: 1.2;
}
.mega-nav__bar-link-icon {
width: 1.25em;
}
.mega-nav__bar-end {
grid-column-gap: .75em;
grid-row-gap: .75em;
justify-content: flex-end;
align-items: center;
display: none;
}
.mega-nav__bar-cta {
color: #f2f2f2;
background-color: #6840ff;
border-radius: .25em;
justify-content: center;
align-items: center;
padding: .375em .625em;
text-decoration: none;
display: flex;
}
.mega-nav__bar-cta.is--secondary {
color: #6840ff;
background-color: #0000;
border: 1px solid #6840ff;
}
.mega-nav__bar-inner {
grid-column-gap: .75em;
grid-row-gap: .75em;
justify-content: flex-start;
align-items: center;
width: 100%;
margin-bottom: 0;
padding-left: 0;
list-style: none;
display: flex;
}
.mega-nav__bar-inner.is--actions {
margin-left: auto;
}
.mega-nav__burger {
grid-column-gap: .2em;
grid-row-gap: .2em;
background-color: #e4e0f5;
border-radius: .25em;
flex-flow: column;
justify-content: center;
align-items: center;
width: 2.5em;
height: 2.5em;
padding: 0;
display: flex;
}
.mega-nav__burger-line {
z-index: 1;
background-color: #6840ff;
border-radius: .125em;
flex: none;
width: 1.25em;
height: .125em;
padding: 0;
display: block;
position: relative;
}
.mega-nav__back {
z-index: 2;
opacity: 0;
visibility: hidden;
position: absolute;
left: -.625em;
}
.mega-nav__backdrop {
z-index: 0;
opacity: 0;
pointer-events: none;
visibility: hidden;
background-color: #00000040;
position: fixed;
inset: 0%;
}
.mega-nav__dropdown-wrapper {
z-index: 2;
pointer-events: none;
width: 100%;
max-width: 80rem;
margin-left: auto;
margin-right: auto;
position: absolute;
top: calc(100% - .25em);
left: 0;
right: 0;
}
.mega-nav__dropdown-container {
position: relative;
overflow: hidden;
}
.mega-nav__dropdown-bg {
will-change: transform;
background-color: #fff;
border-bottom-right-radius: .25em;
border-bottom-left-radius: .25em;
position: absolute;
inset: 0;
}
.mega-nav__dropdown-panel {
opacity: 0;
pointer-events: none;
visibility: hidden;
position: absolute;
inset: 0 0 auto;
overflow: hidden;
}
.mega-nav__dropdown-inner {
display: flex;
}
.mega-nav__panel-col {
grid-column-gap: 1.25em;
grid-row-gap: 1.25em;
border-right: 1px solid #0000001a;
flex-flow: column;
flex: 1;
justify-content: flex-start;
align-items: flex-start;
padding: 2.5em 1.5em;
display: flex;
}
.mega-nav__panel-col:last-of-type {
border: none;
}
.mega-nav__panel-col.is--colored {
background-color: #f7f5ff;
}
.mega-nav__panel-col.is--colored.has--card {
padding-top: 1.5em;
padding-bottom: 1.5em;
}
.mega-nav__panel-label {
text-transform: uppercase;
padding-left: 1em;
font-family: Haffer Mono, Arial, sans-serif;
font-size: .75em;
line-height: 1;
}
.mega-nav__panel-list {
grid-column-gap: 0px;
grid-row-gap: 0px;
flex-flow: column;
justify-content: flex-start;
align-items: stretch;
width: 100%;
margin-bottom: 0;
padding: 0;
list-style: none;
display: flex;
}
.mega-nav__panel-link {
color: #201d1d;
border-radius: .25em;
flex-flow: column;
justify-content: flex-start;
align-items: flex-start;
padding: .625em .75em;
text-decoration: none;
display: flex;
}
.mega-nav__panel-link-text {
font-weight: 500;
}
.mega-nav__panel-link-desc {
opacity: .6;
font-size: .875em;
font-weight: 400;
}
.mega-nav__card {
background-color: #fff;
border-radius: .5em;
flex-flow: column;
width: 100%;
display: flex;
overflow: hidden;
}
.mega-nav__card-img {
object-fit: cover;
width: 100%;
height: 100%;
}
.mega-nav__card-visual {
width: 100%;
height: 16.25em;
position: relative;
}
.mega-nav__card-content {
grid-column-gap: 1.25em;
grid-row-gap: 1.25em;
flex-flow: column;
justify-content: flex-start;
align-items: flex-start;
padding: 1.5em;
display: flex;
}
.mega-nav__card-text {
grid-column-gap: .125em;
grid-row-gap: .125em;
flex-flow: column;
justify-content: flex-start;
align-items: flex-start;
display: flex;
}
.mega-nav__card-cta {
color: #f2f2f2;
background-color: #6840ff;
border-radius: .25em;
justify-content: center;
align-items: center;
padding: .375em .625em;
text-decoration: none;
display: flex;
}
.mega-nav__card-cta.is--secondary {
color: #6840ff;
background-color: #0000;
border: 1px solid #6840ff;
}
.mega-nav__card-cta-label {
font-size: .9375em;
font-weight: 500;
line-height: 1.2;
}
.mega-nav__card-cta-icon {
width: 1.25em;
}
@media screen and (max-width: 991px) {
.mega-nav {
top: 0;
left: 0;
right: 0;
}
.mega-nav__bar-start {
justify-content: space-between;
align-items: center;
}
.mega-nav__bar-list {
grid-column-gap: 0em;
grid-row-gap: 0em;
flex-flow: column;
justify-content: flex-start;
align-items: stretch;
width: 100%;
}
.mega-nav__bar-list.is--actions {
grid-column-gap: 1em;
grid-row-gap: 1em;
flex-flow: row;
justify-content: space-between;
align-items: stretch;
}
.mega-nav__bar-link {
border-bottom: 1px solid #0000001a;
border-radius: 0;
width: 100%;
padding: .75em 0;
font-size: 1.25em;
}
.mega-nav__bar-link.is--dropdown {
justify-content: space-between;
align-items: center;
}
.mega-nav__bar-link.is--back {
border-bottom-style: none;
font-size: 1em;
}
.mega-nav__bar-link-label {
font-size: 1.25em;
}
.mega-nav__bar-link-icon {
width: 1.5em;
}
.mega-nav__bar-link-icon.is--dropdown {
transform: rotate(-90deg);
}
.mega-nav__bar-end {
display: flex;
}
.mega-nav__bar-cta {
padding: 1em .75em 1em 1em;
}
.mega-nav__bar-inner {
opacity: 0;
bottom: 0%;
left: 0%;
right: 0%;
top: var(--nav-height);
visibility: hidden;
background-color: #fff;
flex-flow: column;
justify-content: space-between;
align-items: center;
padding: 2em 1.5em;
position: fixed;
overflow: auto;
}
.mega-nav__backdrop {
display: none;
}
.mega-nav__dropdown-wrapper {
z-index: 4;
bottom: 0;
top: var(--nav-height);
position: fixed;
}
.mega-nav__dropdown-container {
height: 100%;
overflow: auto;
}
.mega-nav__dropdown-bg {
display: none;
}
.mega-nav__dropdown-panel {
background-color: #fff;
bottom: 0;
overflow: auto;
}
.mega-nav__dropdown-inner {
flex-flow: column;
}
.mega-nav__panel-col {
border-bottom: 1px solid #0000001a;
border-right-style: none;
padding-top: 1.5em;
padding-bottom: 1.5em;
}
.mega-nav__panel-label {
width: 100%;
}
.mega-nav__bar-action {
flex: 1;
}
.mega-nav__card-cta-icon.is--dropdown {
transform: rotate(-90deg);
}
}
@media screen and (max-width: 479px) {
.mega-nav__bar-link-label {
font-size: 1em;
}
.mega-nav__bar-link-icon {
width: 1.375em;
}
.mega-nav__panel-col.is--colored.has--card {
padding: 0;
}
.mega-nav__card {
border-bottom: 1px solid #0000001a;
border-radius: 0;
}
.mega-nav__card-content {
padding: 1em 1em 1.5em;
}
.mega-nav__card-cta-label {
font-size: 1em;
}
.mega-nav__card-cta-icon {
width: 1.375em;
}
}JavaScript
javascript
function initMegaNavDirectionalHover() {
const DUR = {
bgMorph: 0.4,
contentIn: 0.3,
contentOut: 0.2,
stagger: 0.25,
backdropIn: 0.3,
backdropOut: 0.2,
openScale: 0.35,
closeScale: 0.25,
};
const HOVER_ENTER = 120;
const HOVER_LEAVE = 150;
const menuWrap = document.querySelector("[data-menu-wrap]");
const navList = document.querySelector("[data-nav-list]");
const dropWrapper = document.querySelector("[data-dropdown-wrapper]");
const dropContainer = document.querySelector("[data-dropdown-container]");
const dropBg = document.querySelector("[data-dropdown-bg]");
const backdrop = document.querySelector("[data-menu-backdrop]");
const toggles = [...document.querySelectorAll("[data-dropdown-toggle]")];
const panels = [...document.querySelectorAll("[data-nav-content]")];
const burger = document.querySelector("[data-burger-toggle]");
const backBtn = document.querySelector("[data-mobile-back]");
const logo = document.querySelector("[data-menu-logo]");
const [lineTop, lineMid, lineBot] = ["top", "mid", "bot"].map(
(id) => document.querySelector(`[data-burger-line='${id}']`)
);
const state = {
isOpen: false,
activePanel: null,
activePanelIndex: -1,
isMobile: window.innerWidth <= 991,
mobileMenuOpen: false,
mobilePanelActive: null,
hoverTimer: null,
leaveTimer: null,
tl: null,
mobileTl: null,
mobilePanelTl: null,
};
const getPanel = (name) => document.querySelector(`[data-nav-content="${name}"]`);
const getToggle = (name) => document.querySelector(`[data-dropdown-toggle="${name}"]`);
const getFade = (el) => el.querySelectorAll("[data-menu-fade]");
const getNavItems = () => navList.querySelectorAll("[data-nav-list-item]");
const getIndex = (name) => toggles.indexOf(getToggle(name));
const stagger = (n) => (n <= 1 ? 0 : { amount: DUR.stagger });
function clearTimers() {
clearTimeout(state.hoverTimer);
clearTimeout(state.leaveTimer);
state.hoverTimer = state.leaveTimer = null;
}
function killTl(key) {
if (state[key]) { state[key].kill(); state[key] = null; }
}
function killDropdown() {
killTl("tl");
gsap.killTweensOf(dropContainer);
gsap.killTweensOf(backdrop);
panels.forEach((p) => { gsap.killTweensOf(p); gsap.killTweensOf(getFade(p)); });
}
function killMobile() {
killTl("mobileTl");
gsap.killTweensOf([navList, lineTop, lineMid, lineBot]);
}
function killMobilePanel() {
killTl("mobilePanelTl");
gsap.killTweensOf(getNavItems());
gsap.killTweensOf([backBtn, logo]);
panels.forEach((p) => { gsap.killTweensOf(p); gsap.killTweensOf(getFade(p)); });
}
function resetToggles() {
toggles.forEach((t) => t.setAttribute("aria-expanded", "false"));
}
function resetDesktop() {
panels.forEach((p) => {
gsap.set(p, { visibility: "hidden", opacity: 0, pointerEvents: "none", xPercent: 0 });
gsap.set(getFade(p), { autoAlpha: 0, x: 0, y: 0 });
});
gsap.set(dropContainer, { height: 0 });
gsap.set(backdrop, { autoAlpha: 0 });
menuWrap.setAttribute("data-menu-open", "false");
resetToggles();
}
function setupMobile() {
panels.forEach((p) => {
gsap.set(p, { autoAlpha: 0, xPercent: 0, visibility: "visible", pointerEvents: "none" });
gsap.set(getFade(p), { xPercent: 20, autoAlpha: 0 });
});
gsap.set(getNavItems(), { xPercent: 0, y: 0, autoAlpha: 1 });
gsap.set(navList, { autoAlpha: 0, x: 0 });
gsap.set(backBtn, { autoAlpha: 0 });
gsap.set(logo, { autoAlpha: 1 });
gsap.set(dropContainer, { clearProps: "height" });
gsap.set(backdrop, { autoAlpha: 0 });
}
function measurePanel(name) {
const el = getPanel(name);
if (!el) return 0;
const s = el.style;
const prev = [s.visibility, s.opacity, s.pointerEvents];
Object.assign(s, { visibility: "visible", opacity: "0", pointerEvents: "none" });
const h = el.getBoundingClientRect().height;
[s.visibility, s.opacity, s.pointerEvents] = prev;
return h;
}
function openDropdown(panelName) {
if (state.isOpen && state.activePanel === panelName) return;
if (state.isOpen) return switchPanel(state.activePanel, panelName);
const height = measurePanel(panelName);
if (!height) return;
killDropdown();
resetDesktop();
const el = getPanel(panelName);
const fade = getFade(el);
const toggle = getToggle(panelName);
state.isOpen = true;
state.activePanel = panelName;
state.activePanelIndex = getIndex(panelName);
menuWrap.setAttribute("data-menu-open", "true");
if (toggle) toggle.setAttribute("aria-expanded", "true");
gsap.set(dropContainer, { height: 0 });
const tl = gsap.timeline();
state.tl = tl;
tl.to(backdrop, { autoAlpha: 1, duration: DUR.backdropIn, ease: "power2.out" }, 0);
tl.to(dropContainer, { height, duration: DUR.openScale, ease: "power3.out" }, 0);
tl.set(el, { visibility: "visible", opacity: 1, pointerEvents: "auto" }, 0.05);
if (fade.length) {
tl.fromTo(fade,
{ autoAlpha: 0, y: 8 },
{ autoAlpha: 1, y: 0, duration: DUR.contentIn, stagger: stagger(fade.length), ease: "power3.out" },
0.1
);
}
}
function closeDropdown() {
if (!state.isOpen) return;
const el = getPanel(state.activePanel);
const fade = el ? getFade(el) : [];
killDropdown();
const tl = gsap.timeline({
onComplete() {
state.isOpen = false;
state.activePanel = null;
state.activePanelIndex = -1;
state.tl = null;
resetDesktop();
},
});
state.tl = tl;
if (fade.length) tl.to(fade, { autoAlpha: 0, y: -4, duration: DUR.contentOut * 0.7, ease: "power2.in" }, 0);
tl.to(dropContainer, { height: 0, duration: DUR.closeScale, ease: "power2.in" }, 0.05);
tl.to(backdrop, { autoAlpha: 0, duration: DUR.backdropOut, ease: "power2.out" }, 0);
if (el) tl.set(el, { visibility: "hidden", opacity: 0, pointerEvents: "none" });
}
function switchPanel(fromName, toName) {
const dir = getIndex(toName) > getIndex(fromName) ? 1 : -1;
const fromEl = getPanel(fromName), toEl = getPanel(toName);
if (!fromEl || !toEl) return;
const fromFade = getFade(fromEl), toFade = getFade(toEl);
const toHeight = measurePanel(toName);
if (!toHeight) return;
killDropdown();
panels.forEach((p) => {
gsap.set(p, { visibility: "hidden", opacity: 0, pointerEvents: "none", xPercent: 0 });
gsap.set(getFade(p), { autoAlpha: 0, x: 0, y: 0 });
});
gsap.set(fromEl, { visibility: "visible", opacity: 1, pointerEvents: "auto", x: 0 });
if (fromFade.length) gsap.set(fromFade, { autoAlpha: 1, x: 0, y: 0 });
gsap.set(backdrop, { autoAlpha: 1 });
const toToggle = getToggle(toName);
state.activePanel = toName;
state.activePanelIndex = getIndex(toName);
resetToggles();
if (toToggle) toToggle.setAttribute("aria-expanded", "true");
const xOut = dir * -30, xIn = dir * 30;
const tl = gsap.timeline();
state.tl = tl;
if (fromFade.length) tl.to(fromFade, { autoAlpha: 0, x: xOut, duration: DUR.contentOut, ease: "power2.in" }, 0);
tl.set(fromEl, { visibility: "hidden", opacity: 0, pointerEvents: "none", xPercent: 0 }, DUR.contentOut);
if (fromFade.length) tl.set(fromFade, { x: 0 }, DUR.contentOut);
tl.to(dropContainer, { height: toHeight, duration: DUR.bgMorph, ease: "power3.out" }, 0.05);
tl.set(toEl, { visibility: "visible", opacity: 1, pointerEvents: "auto", xPercent: 0 }, DUR.contentOut * 0.5);
if (toFade.length) {
tl.fromTo(toFade,
{ autoAlpha: 0, x: xIn },
{ autoAlpha: 1, x: 0, duration: DUR.contentIn, stagger: stagger(toFade.length), ease: "power3.out" },
DUR.contentOut * 0.6
);
}
}
function handleToggleEnter(e) {
if (state.isMobile) return;
const name = e.currentTarget.getAttribute("data-dropdown-toggle");
if (!name) return;
clearTimeout(state.leaveTimer); state.leaveTimer = null;
clearTimeout(state.hoverTimer);
state.hoverTimer = setTimeout(() => openDropdown(name), state.isOpen ? 0 : HOVER_ENTER);
}
function handleToggleLeave() {
if (state.isMobile) return;
clearTimeout(state.hoverTimer); state.hoverTimer = null;
state.leaveTimer = setTimeout(closeDropdown, HOVER_LEAVE);
}
function handleWrapperEnter() {
if (state.isMobile) return;
clearTimeout(state.leaveTimer); state.leaveTimer = null;
}
function handleWrapperLeave() {
if (state.isMobile) return;
state.leaveTimer = setTimeout(closeDropdown, HOVER_LEAVE);
}
function handleEscape(e) {
if (e.key !== "Escape") return;
if (state.isMobile) {
state.mobilePanelActive ? closeMobilePanel() : state.mobileMenuOpen && closeMobileMenu();
return;
}
if (state.isOpen) {
const t = getToggle(state.activePanel);
closeDropdown();
if (t) t.focus();
}
}
function handleDocClick(e) {
if (state.isMobile || !state.isOpen) return;
if (!e.target.closest("[data-menu-wrap]")) closeDropdown();
}
function focusFirstLink(panelName) {
setTimeout(() => {
const el = getPanel(panelName);
if (!el) return;
const link = el.querySelector("a");
if (!link) return;
gsap.set(link, { visibility: "visible" });
link.focus();
}, 80);
}
function handleKeydownOnToggle(e) {
if (state.isMobile) return;
const name = e.currentTarget.getAttribute("data-dropdown-toggle");
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
if (state.isOpen && state.activePanel === name) closeDropdown();
else { openDropdown(name); focusFirstLink(name); }
return;
}
if (e.key === "ArrowDown") {
e.preventDefault();
if (!state.isOpen || state.activePanel !== name) openDropdown(name);
focusFirstLink(name);
}
if (e.key === "Tab" && !e.shiftKey && state.isOpen && state.activePanel === name) {
e.preventDefault();
const link = getPanel(name)?.querySelector("a");
if (link) link.focus();
}
}
function handleKeydownInPanel(e) {
if (state.isMobile || !state.isOpen) return;
const el = getPanel(state.activePanel);
if (!el) return;
const links = [...el.querySelectorAll("a")];
const idx = links.indexOf(document.activeElement);
if (e.key === "ArrowDown") {
e.preventDefault();
links[(idx + 1) % links.length].focus();
}
if (e.key === "ArrowUp") {
e.preventDefault();
if (idx <= 0) { const t = getToggle(state.activePanel); if (t) t.focus(); }
else links[idx - 1].focus();
}
if (e.key === "Tab" && !e.shiftKey && idx === links.length - 1) {
e.preventDefault();
const curIdx = toggles.indexOf(getToggle(state.activePanel));
const next = curIdx < toggles.length - 1 ? toggles[curIdx + 1] : null;
closeDropdown();
if (next) next.focus();
}
if (e.key === "Tab" && e.shiftKey && idx === 0) {
e.preventDefault();
const t = getToggle(state.activePanel);
if (t) t.focus();
}
}
function animateBurger(toX) {
const tl = gsap.timeline({ defaults: { ease: "power2.inOut" } });
if (toX) {
tl.to(lineTop, { y: "0.3125em", duration: 0.15 }, 0);
tl.to(lineBot, { y: "-0.3125em", duration: 0.15 }, 0);
tl.to(lineMid, { autoAlpha: 0, duration: 0.1 }, 0.1);
tl.to(lineTop, { rotation: 45, duration: 0.2 }, 0.15);
tl.to(lineBot, { rotation: -45, duration: 0.2 }, 0.15);
} else {
tl.to(lineTop, { rotation: 0, duration: 0.2 }, 0);
tl.to(lineBot, { rotation: 0, duration: 0.2 }, 0);
tl.to(lineTop, { y: 0, duration: 0.15 }, 0.15);
tl.to(lineBot, { y: 0, duration: 0.15 }, 0.15);
tl.to(lineMid, { autoAlpha: 1, duration: 0.1 }, 0.15);
}
return tl;
}
function openMobileMenu() {
killMobile();
state.mobileMenuOpen = true;
menuWrap.setAttribute("data-menu-open", "true");
burger.setAttribute("aria-expanded", "true");
document.body.style.overflow = "hidden";
const items = getNavItems();
const tl = gsap.timeline();
state.mobileTl = tl;
tl.add(animateBurger(true), 0);
tl.to(navList, { autoAlpha: 1, duration: 0.3, ease: "power2.out" }, 0);
if (items.length) {
tl.fromTo(items,
{ autoAlpha: 0, y: 12 },
{ autoAlpha: 1, y: 0, duration: 0.3, stagger: 0.04, ease: "power3.out" },
0.15
);
}
}
function closeMobileMenu() {
const hadPanel = state.mobilePanelActive;
const panelEl = hadPanel ? getPanel(hadPanel) : null;
killMobile();
killMobilePanel();
menuWrap.setAttribute("data-menu-open", "false");
state.mobileMenuOpen = false;
state.mobilePanelActive = null;
burger.setAttribute("aria-expanded", "false");
const tl = gsap.timeline({
onComplete() {
document.body.style.overflow = "";
state.mobileTl = null;
setupMobile();
},
});
state.mobileTl = tl;
tl.add(animateBurger(false), 0);
if (hadPanel && panelEl) {
tl.to(panelEl, { autoAlpha: 0, duration: 0.3, ease: "power2.inOut" }, 0.05);
tl.to(backBtn, { autoAlpha: 0, duration: 0.2, ease: "power2.in" }, 0.05);
}
tl.to(navList, { autoAlpha: 0, duration: 0.3, ease: "power2.inOut" }, 0.05);
}
function openMobilePanel(panelName) {
const el = getPanel(panelName);
if (!el) return;
killMobilePanel();
state.mobilePanelActive = panelName;
const navItems = getNavItems();
const panelFade = getFade(el);
const tl = gsap.timeline();
state.mobilePanelTl = tl;
if (navItems.length) {
tl.to(navItems, {
xPercent: -10, autoAlpha: 0,
duration: 0.35, stagger: 0.03, ease: "power2.in",
}, 0);
}
tl.to(logo, { autoAlpha: 0, duration: 0.2, ease: "power2.in" }, 0);
tl.to(backBtn, { autoAlpha: 1, duration: 0.25, ease: "power2.inOut" }, 0.15);
tl.set(el, { autoAlpha: 1, xPercent: 0, pointerEvents: "auto" }, 0.2);
if (panelFade.length) {
tl.fromTo(panelFade,
{ xPercent: 8, autoAlpha: 0 },
{ xPercent: 0, autoAlpha: 1, duration: 0.3, stagger: stagger(panelFade.length), ease: "power3.out" },
0.25
);
}
}
function closeMobilePanel() {
if (!state.mobilePanelActive) return;
const el = getPanel(state.mobilePanelActive);
if (!el) return;
killMobilePanel();
const navItems = getNavItems();
const panelFade = getFade(el);
const tl = gsap.timeline({
onComplete() { state.mobilePanelActive = null; state.mobilePanelTl = null; },
});
state.mobilePanelTl = tl;
if (panelFade.length) {
tl.to(el, {
xPercent: 20, autoAlpha: 0,
duration: 0.3, stagger: 0.02, ease: "power2.in",
}, 0);
}
tl.set(el, { autoAlpha: 0, pointerEvents: "none" }, 0.25);
tl.to(backBtn, { autoAlpha: 0, duration: 0.2, ease: "power2.in" }, 0);
tl.to(logo, { autoAlpha: 1, duration: 0.25, ease: "power2.out" }, 0.15);
if (navItems.length) {
tl.fromTo(navItems,
{ xPercent: -20, autoAlpha: 0 },
{ xPercent: 0, autoAlpha: 1, duration: 0.35, stagger: 0.03, ease: "power3.out" },
0.25
);
}
}
function handleToggleClick(e) {
if (!state.isMobile || !state.mobileMenuOpen) return;
const name = e.currentTarget.getAttribute("data-dropdown-toggle");
if (name) { e.preventDefault(); openMobilePanel(name); }
}
let resizeTimer = null;
let lastWidth = window.innerWidth;
function handleResize() {
const w = window.innerWidth;
if (w === lastWidth) return;
lastWidth = w;
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
const was = state.isMobile;
state.isMobile = window.innerWidth <= 991;
if (was && !state.isMobile) {
killMobile(); killMobilePanel();
gsap.set(navList, { clearProps: "all" });
gsap.set(getNavItems(), { clearProps: "all" });
gsap.set(backBtn, { autoAlpha: 0 });
gsap.set(logo, { clearProps: "all" });
gsap.set([lineTop, lineMid, lineBot], { rotation: 0, y: 0, autoAlpha: 1 });
burger.setAttribute("aria-expanded", "false");
state.mobileMenuOpen = false;
state.mobilePanelActive = null;
document.body.style.overflow = "";
resetDesktop();
}
if (!was && state.isMobile) {
killDropdown();
state.isOpen = false; state.activePanel = null; state.activePanelIndex = -1;
clearTimers();
menuWrap.setAttribute("data-menu-open", "false");
resetToggles();
setupMobile();
}
}, 150);
}
toggles.forEach((btn) => {
btn.addEventListener("mouseenter", handleToggleEnter);
btn.addEventListener("mouseleave", handleToggleLeave);
btn.addEventListener("keydown", handleKeydownOnToggle);
btn.addEventListener("click", handleToggleClick);
});
dropWrapper.addEventListener("mouseenter", handleWrapperEnter);
dropWrapper.addEventListener("mouseleave", handleWrapperLeave);
panels.forEach((p) => p.addEventListener("keydown", handleKeydownInPanel));
backdrop.addEventListener("click", closeDropdown);
document.addEventListener("keydown", handleEscape);
document.addEventListener("click", handleDocClick);
burger.addEventListener("click", () => state.mobileMenuOpen ? closeMobileMenu() : openMobileMenu());
backBtn.addEventListener("click", closeMobilePanel);
window.addEventListener("resize", handleResize);
state.isMobile ? setupMobile() : resetDesktop();
}
document.addEventListener('DOMContentLoaded', function() {
initMegaNavDirectionalHover();
});Attributes
| Name | Type | Default | Description |
|---|---|---|---|
| data-menu-wrap | "" | — | Applied to the root <nav>. Also carries data-menu-open which the script toggles between "false" and "true" whenever the dropdown or mobile menu is active. Used for CSS theming. |
| data-menu-open | "false" | "true" | "false" | Toggled on [data-menu-wrap] by the script to indicate whether any dropdown or mobile menu is currently open. |
| data-menu-logo | "" | — | Applied to the logo element. Fades out on mobile when a slide-over panel opens and is restored when the panel closes. |
| data-mobile-back | "" | — | Applied to the back button wrapper. Appears in place of the logo when a mobile slide-over panel is active and triggers closeMobilePanel on click. |
| data-nav-list | "" | — | Applied to the nav inner container. Faded as a single block on mobile menu open/close. |
| data-nav-list-item | "" | — | Applied to every direct child of [data-nav-list] that should stagger on mobile — dropdown toggles, plain links, and the actions block. |
| data-dropdown-toggle | string | — | Applied to buttons that open a panel. The value must match a corresponding [data-nav-content] panel. Hover opens on desktop; click opens on mobile. |
| data-dropdown-wrapper | "" | — | Applied to the outer dropdown area. Handles hover intent on desktop — mousing into this element cancels the close timer. |
| data-dropdown-container | "" | — | Applied to the inner container that clips content via overflow: hidden. Its height is animated by the script to morph between panel sizes. |
| data-dropdown-bg | "" | — | Applied to the absolute fill element that provides the dropdown background color. |
| data-nav-content | string | — | Applied to each panel. The value must match the corresponding [data-dropdown-toggle]. The script measures panel height dynamically and cross-fades content directionally. |
| data-panel-state | "" | — | Designer-only helper on each panel. Set to "active" in the Webflow designer to force a panel visible for editing. Has no effect on the published site. |
| data-menu-fade | "" | — | Applied to any element inside a panel that should animate individually. All [data-menu-fade] elements stagger in and out as a group. |
| data-menu-backdrop | "" | — | Applied to the backdrop overlay behind the nav. Fades in when a dropdown is open on desktop. |
| data-burger-toggle | "" | — | Applied to the hamburger button. Toggles the mobile menu open/closed. |
| data-burger-line | "top" | "mid" | "bot" | — | Applied to the three <span> elements inside the burger button. The script targets each independently to animate the hamburger → X transition. |
| data-mobile-nav | "" | — | Designer-only helper on [data-nav-list]. Set to "active" in the Webflow designer to reveal the mobile nav list. Has no effect on the published site. |
Notes
- •Requires GSAP loaded via CDN before the script runs.
- •Panel names in [data-dropdown-toggle] and [data-nav-content] must match exactly.
- •The directional slide is derived from the DOM order of the toggle buttons — keep them in the same order as the panels.
- •Panel height is measured live each time a panel opens, so panels with dynamic content or images will measure correctly.
- •The mobile breakpoint is 991px and is re-evaluated on resize with a 150ms debounce.
- •Keyboard navigation is fully supported: Enter/Space toggle, ArrowDown/Up move focus within a panel, Tab exits to the next toggle.
Guide
Durations
Most timing lives in the DUR object and the two hover constants at the top of the script.
const DUR = {
bgMorph: 0.4, // container height morph between panels
contentIn: 0.3, // fade items appearing
contentOut: 0.2, // fade items disappearing
stagger: 0.25, // total stagger spread across all fade items
backdropIn: 0.3, // backdrop fade in
backdropOut: 0.2, // backdrop fade out
openScale: 0.35, // initial container open from 0
closeScale: 0.25, // container close to 0
};
const HOVER_ENTER = 120; // ms delay before opening on hover
const HOVER_LEAVE = 150; // ms before closing after mouse leavesWebflow Designer helpers
Setting [data-panel-state="active"] on a [data-nav-content] element forces it visible for editing in the designer. Setting [data-mobile-nav="active"] on [data-nav-list] reveals the mobile nav list. These attribute values have no effect on the published site.