(function() {
document.documentElement.classList.add('menu-loading');
const criticalStyle = document.createElement('style');
criticalStyle.textContent = `
html.menu-loading .bf-expandable-menu {
display: none !important;
visibility: hidden !important;
opacity: 0 !important;
max-height: 0 !important;
overflow: hidden !important;
transition: none !important;
pointer-events: none !important;
}
`;
document.head.appendChild(criticalStyle);
})();
document.addEventListener('DOMContentLoaded', function() {
const style = document.createElement('style');
style.textContent = `
html.menu-loading * .bf-expandable-menu {
transition: none !important;
}
.bf-expandable-menu {
opacity: 0;
max-height: 0;
margin-bottom: 0;
overflow: hidden;
pointer-events: none;
transform-origin: top center;
transform: translateY(-20px);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform, opacity, max-height;
visibility: hidden;
}
.bf-expandable-menu.active {
opacity: 1;
max-height: 520px;
margin-bottom: 16px;
pointer-events: auto;
transform: translateY(0);
visibility: visible;
}
.bf-expandable-menu > * {
opacity: 0;
transform: translateY(-10px);
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
will-change: transform, opacity;
}
.bf-expandable-menu.active > * {
opacity: 1;
transform: translateY(0);
}
[data-menu-trigger].active-trigger {
background-color: rgba(169, 169, 169, 0.2);
border-radius: 6px;
padding: 6px 12px;
transition: all 0.2s ease;
position: relative;
}
[data-menu-trigger].active-trigger:hover {
background-color: rgba(169, 169, 169, 0.25);
}
.bf-expandable-menu.instant-close,
.bf-expandable-menu.instant-close > * {
transition: none !important;
}
.bf-expandable-menu.no-animation,
.bf-expandable-menu.no-animation > * {
transition: none !important;
}
.bf-blur-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to bottom, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0.4));
backdrop-filter: blur(8px);
opacity: 0;
visibility: hidden;
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
z-index: 999;
}
.bf-blur-overlay.active {
opacity: 1;
visibility: visible;
}
.Blur {
position: relative;
z-index: 1000;
}
`;
document.head.appendChild(style);
const overlay = document.createElement('div');
overlay.className = 'bf-blur-overlay';
document.body.appendChild(overlay);
const menuTriggers = document.querySelectorAll('[data-menu-trigger]');
const menus = document.querySelectorAll('.bf-expandable-menu');
menus.forEach(menu => {
menu.style.opacity = '0';
menu.style.visibility = 'hidden';
menu.style.maxHeight = '0';
menu.style.overflow = 'hidden';
menu.style.pointerEvents = 'none';
});
function updateTriggerState(targetId, isActive) {
menuTriggers.forEach(trigger => {
const triggerId = trigger.getAttribute('data-menu-trigger');
if (triggerId === targetId) {
isActive
? trigger.classList.add('active-trigger')
: trigger.classList.remove('active-trigger');
}
});
}
function handleMenuTransition(menu, isClosing, instantClose = false) {
return new Promise((resolve) => {
const menuId = menu.id;
if (instantClose) {
menu.classList.add('instant-close');
menu.classList.remove('active');
menu.style.visibility = 'hidden';
overlay.classList.remove('active');
updateTriggerState(menuId, false);
setTimeout(() => {
menu.classList.remove('instant-close');
resolve();
}, 50);
return;
}
if (isClosing) {
menu.classList.remove('active');
overlay.classList.remove('active');
updateTriggerState(menuId, false);
setTimeout(() => {
menu.style.visibility = 'hidden';
resolve();
}, 400);
} else {
menu.style.visibility = 'visible';
requestAnimationFrame(() => {
menu.classList.add('active');
overlay.classList.add('active');
updateTriggerState(menuId, true);
setTimeout(resolve, 50);
});
}
});
}
async function closeAllMenus(exceptMenuId = null) {
const closingPromises = Array.from(menus).map(menu => {
if (menu.id !== exceptMenuId && menu.classList.contains('active')) {
return handleMenuTransition(menu, true, !!exceptMenuId);
}
return Promise.resolve();
});
await Promise.all(closingPromises);
}
menuTriggers.forEach(trigger => {
trigger.addEventListener('click', async function(e) {
e.preventDefault();
e.stopPropagation();
const targetId = this.getAttribute('data-menu-trigger');
const targetMenu = document.getElementById(targetId);
if (!targetMenu) return;
if (targetMenu.classList.contains('active')) {
await handleMenuTransition(targetMenu, true);
return;
}
await closeAllMenus(targetId);
await handleMenuTransition(targetMenu, false);
});
});
document.addEventListener('click', function(e) {
const isClickInsideMenu = Array.from(menus).some(menu => menu.contains(e.target));
const isClickOnTrigger = Array.from(menuTriggers).some(trigger => trigger.contains(e.target));
if (!isClickInsideMenu && !isClickOnTrigger) {
closeAllMenus();
}
});
window.addEventListener('load', function() {
setTimeout(() => {
document.documentElement.classList.remove('menu-loading');
setTimeout(() => {
menus.forEach(menu => {
menu.removeAttribute('style');
});
}, 100);
}, 50);
});
});
v2.0
Add stunning animated gradient borders to your website with this custom-crafted JavaScript snippet.