feat: implement mobile navigation enhancements with close button and scroll locking
This commit is contained in:
parent
110c15792c
commit
093237aecb
2 changed files with 265 additions and 81 deletions
287
dist/css/styles.css
vendored
287
dist/css/styles.css
vendored
|
|
@ -328,7 +328,7 @@ button:focus-visible {
|
|||
overflow-x: hidden;
|
||||
position: relative;
|
||||
background: var(--bg-primary);
|
||||
transition: background-color var(--transition-normal)
|
||||
margin: var(--space-3xl) 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
|
|
@ -651,6 +651,14 @@ button:focus-visible {
|
|||
transition: width var(--transition-fast);
|
||||
}
|
||||
|
||||
.nav-mobile-header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-close {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--primary);
|
||||
background: var(--bg-secondary);
|
||||
|
|
@ -951,6 +959,23 @@ button:focus-visible {
|
|||
@media (max-width:900px) {
|
||||
.nav-dropdown {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: var(--space-sm) var(--space-md);
|
||||
border-radius: var(--radius-xl);
|
||||
background: rgba(148, 163, 184, 0.12);
|
||||
border: 1px solid var(--border-primary);
|
||||
box-shadow: 0 12px 40px rgba(15, 23, 42, 0.08);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-dropdown + .nav-dropdown {
|
||||
margin-top: var(--space-sm);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-dropdown {
|
||||
background: rgba(30, 41, 59, 0.6);
|
||||
border-color: rgba(71, 85, 105, 0.8);
|
||||
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
.nav-dropdown-trigger {
|
||||
|
|
@ -959,17 +984,27 @@ button:focus-visible {
|
|||
text-align: left;
|
||||
padding: var(--space-lg) var(--space-xl);
|
||||
border-radius: var(--radius-lg);
|
||||
font-size: var(--text-base);
|
||||
font-weight: var(--font-medium);
|
||||
color: var(--text-secondary);
|
||||
min-height: 44px;
|
||||
font-size: clamp(1rem, 2.5vw, 1.05rem);
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
min-height: 48px;
|
||||
background: transparent;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
touch-action: manipulation;
|
||||
transition: background var(--transition-fast), color var(--transition-fast), box-shadow var(--transition-fast);
|
||||
}
|
||||
|
||||
.nav-dropdown-trigger:hover {
|
||||
.nav-dropdown-trigger:hover,
|
||||
.nav-dropdown-trigger:focus-visible {
|
||||
color: var(--primary);
|
||||
background: var(--bg-secondary);
|
||||
background: rgba(99, 102, 241, 0.1);
|
||||
box-shadow: inset 0 0 0 1px rgba(99, 102, 241, 0.18);
|
||||
}
|
||||
|
||||
.nav-dropdown-trigger[aria-expanded="true"] {
|
||||
color: var(--primary);
|
||||
background: rgba(99, 102, 241, 0.12);
|
||||
box-shadow: inset 0 0 0 1px rgba(99, 102, 241, 0.25);
|
||||
}
|
||||
|
||||
.nav-dropdown-menu {
|
||||
|
|
@ -986,44 +1021,52 @@ button:focus-visible {
|
|||
backdrop-filter: none;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
/* Remove problematic animations */
|
||||
transition: none;
|
||||
will-change: auto;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-dropdown-menu {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.nav-dropdown-trigger[aria-expanded="true"] + .nav-dropdown-menu {
|
||||
display: block;
|
||||
padding: var(--space-xs) 0 var(--space-md) var(--space-2xl);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-xs);
|
||||
padding: var(--space-sm) var(--space-lg) var(--space-lg);
|
||||
margin-top: var(--space-xs);
|
||||
margin-left: var(--space-lg);
|
||||
border-left: 2px solid var(--border-primary);
|
||||
background: rgba(99, 102, 241, 0.02);
|
||||
margin-left: 0;
|
||||
border-left: none;
|
||||
background: rgba(99, 102, 241, 0.08);
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-dropdown-trigger[aria-expanded="true"] + .nav-dropdown-menu {
|
||||
background: rgba(99, 102, 241, 0.03);
|
||||
border-left-color: var(--border-secondary);
|
||||
background: rgba(99, 102, 241, 0.16);
|
||||
}
|
||||
|
||||
.nav-dropdown-item {
|
||||
padding: var(--space-md) var(--space-xl);
|
||||
font-size: var(--text-base);
|
||||
min-height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
touch-action: manipulation;
|
||||
transition: transform 0.2s ease, background 0.2s ease;
|
||||
padding: var(--space-md) var(--space-lg);
|
||||
color: var(--text-secondary);
|
||||
border-radius: var(--radius-md);
|
||||
font-size: var(--text-base);
|
||||
font-weight: var(--font-medium);
|
||||
transform: translateX(0);
|
||||
transition: transform var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
|
||||
}
|
||||
|
||||
.nav-dropdown-item + .nav-dropdown-item {
|
||||
margin-top: var(--space-2xs);
|
||||
}
|
||||
|
||||
.nav-dropdown-item:hover,
|
||||
.nav-dropdown-item:focus-visible,
|
||||
.nav-dropdown-item:active {
|
||||
background: rgba(255, 255, 255, 0.65);
|
||||
color: var(--primary);
|
||||
transform: translateX(4px);
|
||||
background: rgba(99, 102, 241, 0.05);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-dropdown-item:hover,
|
||||
[data-theme="dark"] .nav-dropdown-item:focus-visible {
|
||||
background: rgba(15, 23, 42, 0.65);
|
||||
color: var(--primary-light);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1032,12 +1075,14 @@ button:focus-visible {
|
|||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 90;
|
||||
z-index: 120;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
transition: opacity 0.3s ease, visibility 0.3s ease;
|
||||
pointer-events: none;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
.nav-open .nav-overlay {
|
||||
|
|
@ -1048,97 +1093,189 @@ button:focus-visible {
|
|||
|
||||
/* ===== MOBILE NAVIGATION STYLES ===== */
|
||||
@media (max-width: 900px) {
|
||||
/* Show nav toggle button */
|
||||
.nav-toggle {
|
||||
display: inline-flex;
|
||||
width: 48px !important;
|
||||
height: 48px !important;
|
||||
}
|
||||
|
||||
/* Hide nav by default on mobile */
|
||||
.header-nav {
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: var(--header-height);
|
||||
right: 0;
|
||||
width: min(340px, 85%);
|
||||
height: calc(100vh - var(--header-height));
|
||||
background: rgba(255, 255, 255, 0.98);
|
||||
border-left: 1px solid var(--border-primary);
|
||||
box-shadow: -4px 0 20px rgba(0, 0, 0, 0.1);
|
||||
padding: var(--space-2xl) var(--space-xl) var(--space-3xl);
|
||||
inset: 0;
|
||||
padding: var(--space-3xl) var(--space-xl) var(--space-3xl);
|
||||
padding-top: var(--space-3xl);
|
||||
padding-top: calc(var(--space-3xl) + env(safe-area-inset-top));
|
||||
background: linear-gradient(180deg, rgba(248, 250, 255, 0.96) 0%, rgba(255, 255, 255, 0.98) 55%, rgba(255, 255, 255, 0.94) 100%);
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-sm);
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
overscroll-behavior: contain;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
transform: translateX(100%);
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
z-index: 100;
|
||||
transform: translateX(105%);
|
||||
transition: transform 0.32s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
z-index: 140;
|
||||
will-change: transform;
|
||||
backdrop-filter: blur(18px);
|
||||
-webkit-backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.header-nav::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
background: radial-gradient(120% 120% at 50% 0%, rgba(99, 102, 241, 0.08) 0%, rgba(59, 130, 246, 0.04) 45%, rgba(15, 23, 42, 0) 100%);
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .header-nav {
|
||||
background: linear-gradient(180deg, rgba(15, 23, 42, 0.96) 0%, rgba(15, 23, 42, 0.98) 60%, rgba(15, 23, 42, 0.94) 100%);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
[data-theme="dark"] .header-nav::before {
|
||||
background: radial-gradient(120% 120% at 50% 0%, rgba(37, 99, 235, 0.2) 0%, rgba(59, 130, 246, 0.12) 45%, rgba(15, 23, 42, 0) 100%);
|
||||
}
|
||||
|
||||
/* Show nav when open */
|
||||
.nav-open .header-nav {
|
||||
display: flex;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
/* Dark theme mobile nav */
|
||||
[data-theme="dark"] .header-nav {
|
||||
background: rgba(15, 23, 42, 0.98);
|
||||
border-color: rgba(52, 65, 85, 0.6);
|
||||
box-shadow: -4px 0 20px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
/* Prevent body scroll when nav is open */
|
||||
body.nav-open {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Mobile nav links */
|
||||
.nav-open .nav-link {
|
||||
font-size: var(--text-base);
|
||||
padding: var(--space-lg) var(--space-xl);
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
border-radius: var(--radius-lg);
|
||||
min-height: 44px;
|
||||
.nav-mobile-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
touch-action: manipulation;
|
||||
justify-content: space-between;
|
||||
gap: var(--space-lg);
|
||||
padding: 0 0 var(--space-xl);
|
||||
margin-bottom: var(--space-lg);
|
||||
border-bottom: 1px solid rgba(148, 163, 184, 0.4);
|
||||
}
|
||||
|
||||
.nav-open .nav-link::before {
|
||||
[data-theme="dark"] .nav-mobile-header {
|
||||
border-bottom-color: rgba(71, 85, 105, 0.4);
|
||||
}
|
||||
|
||||
.nav-mobile-brand {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2xs);
|
||||
}
|
||||
|
||||
.nav-mobile-title {
|
||||
font-size: clamp(1rem, 2.8vw, 1.15rem);
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.nav-mobile-subtitle {
|
||||
font-size: var(--text-sm);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.nav-close {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: var(--radius-full);
|
||||
border: 1px solid rgba(148, 163, 184, 0.5);
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
color: var(--text-primary);
|
||||
box-shadow: 0 14px 32px rgba(15, 23, 42, 0.12);
|
||||
transition: transform var(--transition-fast), background var(--transition-fast), border-color var(--transition-fast);
|
||||
}
|
||||
|
||||
.nav-close:hover,
|
||||
.nav-close:focus-visible {
|
||||
transform: scale(1.05);
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border-color: rgba(99, 102, 241, 0.55);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-close {
|
||||
background: rgba(30, 41, 59, 0.9);
|
||||
border-color: rgba(71, 85, 105, 0.7);
|
||||
box-shadow: 0 14px 32px rgba(0, 0, 0, 0.4);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-close:hover,
|
||||
[data-theme="dark"] .nav-close:focus-visible {
|
||||
border-color: rgba(147, 197, 253, 0.6);
|
||||
}
|
||||
|
||||
.nav-close svg {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.nav-open a.nav-link {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
font-size: clamp(1.05rem, 3vw, 1.2rem);
|
||||
font-weight: 600;
|
||||
padding: var(--space-lg) var(--space-xl);
|
||||
border-radius: var(--radius-xl);
|
||||
min-height: 52px;
|
||||
background: rgba(99, 102, 241, 0.1);
|
||||
color: var(--text-primary);
|
||||
box-shadow: 0 16px 36px rgba(15, 23, 42, 0.12);
|
||||
margin-bottom: var(--space-sm);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.nav-open a.nav-link:hover,
|
||||
.nav-open a.nav-link:focus-visible {
|
||||
background: rgba(99, 102, 241, 0.18);
|
||||
color: var(--primary);
|
||||
box-shadow: 0 20px 42px rgba(15, 23, 42, 0.16);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-open a.nav-link {
|
||||
background: rgba(59, 130, 246, 0.18);
|
||||
color: var(--text-primary);
|
||||
box-shadow: 0 20px 48px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .nav-open a.nav-link:hover,
|
||||
[data-theme="dark"] .nav-open a.nav-link:focus-visible {
|
||||
background: rgba(59, 130, 246, 0.26);
|
||||
color: var(--primary-light);
|
||||
}
|
||||
|
||||
.nav-open .nav-link::before,
|
||||
.nav-open .nav-link::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-open .nav-link::after {
|
||||
border-radius: var(--radius-lg);
|
||||
}
|
||||
|
||||
/* Dark mode toggle positioning on mobile */
|
||||
.dark-mode-toggle {
|
||||
display: flex;
|
||||
flex: 0 0 auto;
|
||||
width: 40px !important;
|
||||
height: 40px !important;
|
||||
order: 3;
|
||||
align-self: center;
|
||||
z-index: 110;
|
||||
align-self: flex-start;
|
||||
margin-left: auto;
|
||||
z-index: 150;
|
||||
}
|
||||
|
||||
/* Nav toggle sizing */
|
||||
.nav-toggle {
|
||||
width: 40px !important;
|
||||
height: 40px !important;
|
||||
}
|
||||
|
||||
/* Disable transitions before JS is ready */
|
||||
body:not(.js-ready) .header-nav,
|
||||
body:not(.js-ready) .nav-toggle-bar,
|
||||
body:not(.js-ready) .nav-overlay {
|
||||
|
|
@ -1893,7 +2030,7 @@ table button:focus-visible {
|
|||
border-radius: var(--radius-lg);
|
||||
box-shadow: var(--shadow-sm);
|
||||
border: 1px solid var(--border-primary);
|
||||
margin: var(--space-3xl) 0;
|
||||
margin: var(--space-xl) 0;
|
||||
overflow: hidden;
|
||||
width: 100%
|
||||
}
|
||||
|
|
@ -1972,8 +2109,6 @@ th {
|
|||
align-items: center
|
||||
}
|
||||
|
||||
/* Legacy support - removed duplicate enhanced resource styles and newsletter form */
|
||||
|
||||
.social-btn,
|
||||
.github-link {
|
||||
width: 36px;
|
||||
|
|
|
|||
59
dist/index.html
vendored
59
dist/index.html
vendored
|
|
@ -459,6 +459,17 @@
|
|||
</a>
|
||||
<nav id="primary-navigation" class="header-nav" aria-label="Main navigation" aria-hidden="true"
|
||||
role="navigation">
|
||||
<div class="nav-mobile-header">
|
||||
<div class="nav-mobile-brand">
|
||||
<span class="nav-mobile-title">Menu</span>
|
||||
<span class="nav-mobile-subtitle">Jump to a section</span>
|
||||
</div>
|
||||
<button class="nav-close" id="navClose" type="button" aria-label="Close navigation menu">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||
<use href="#icon-close" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<a href="#what-are-debrid-services" class="nav-link">
|
||||
<span class="nav-link-text">Intro</span>
|
||||
</a>
|
||||
|
|
@ -555,8 +566,7 @@
|
|||
Compare Premium <span class="gradient-text">Debrid Services</span>
|
||||
</h1>
|
||||
<p class="hero-description">
|
||||
Compare pricing, features, and 400+ file hosts across 9 premium debrid services in real-time.
|
||||
Make informed decisions with our comprehensive comparison tool.
|
||||
Compare pricing, features, policies & 400+ file hosts across 10 premium debrid services in real-time.
|
||||
</p>
|
||||
<div class="hero-actions">
|
||||
<a href="#debrid-pricing-comparison" class="btn btn-primary">
|
||||
|
|
@ -568,7 +578,7 @@
|
|||
</div>
|
||||
<div class="hero-stats" aria-label="Key stats">
|
||||
<div class="stat-item">
|
||||
<span class="stat-number">9</span>
|
||||
<span class="stat-number">10</span>
|
||||
<span class="stat-label">Services</span>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
|
|
@ -1747,9 +1757,39 @@
|
|||
navToggle: document.getElementById('navToggle'),
|
||||
nav: document.getElementById('primary-navigation'),
|
||||
navOverlay: document.getElementById('navOverlay'),
|
||||
navClose: document.getElementById('navClose'),
|
||||
darkModeToggle: document.getElementById('darkModeToggle'),
|
||||
dropdownTriggers: []
|
||||
};
|
||||
|
||||
let scrollPosition = 0;
|
||||
|
||||
const lockScroll = () => {
|
||||
if (!elements.body) return;
|
||||
scrollPosition = window.scrollY || window.pageYOffset || document.documentElement.scrollTop || 0;
|
||||
elements.body.dataset.navScrollLock = String(scrollPosition);
|
||||
elements.body.style.top = `-${scrollPosition}px`;
|
||||
elements.body.style.position = 'fixed';
|
||||
elements.body.style.width = '100%';
|
||||
elements.body.style.overflow = 'hidden';
|
||||
elements.body.style.touchAction = 'none';
|
||||
elements.body.style.left = '0';
|
||||
elements.body.style.right = '0';
|
||||
};
|
||||
|
||||
const unlockScroll = () => {
|
||||
if (!elements.body) return;
|
||||
const saved = Number(elements.body.dataset.navScrollLock || scrollPosition || 0);
|
||||
elements.body.style.removeProperty('top');
|
||||
elements.body.style.removeProperty('position');
|
||||
elements.body.style.removeProperty('width');
|
||||
elements.body.style.removeProperty('overflow');
|
||||
elements.body.style.removeProperty('touch-action');
|
||||
elements.body.style.removeProperty('left');
|
||||
elements.body.style.removeProperty('right');
|
||||
delete elements.body.dataset.navScrollLock;
|
||||
requestAnimationFrame(() => window.scrollTo(0, saved));
|
||||
};
|
||||
|
||||
// Check if mobile
|
||||
const checkMobile = () => window.innerWidth <= 900;
|
||||
|
|
@ -1769,13 +1809,15 @@
|
|||
|
||||
// Open mobile navigation
|
||||
const openNav = () => {
|
||||
if (!elements.nav || state.isOpen) return;
|
||||
if (!elements.nav || state.isOpen || !state.isMobile) return;
|
||||
|
||||
state.isOpen = true;
|
||||
lockScroll();
|
||||
elements.body.classList.add('nav-open');
|
||||
|
||||
if (elements.navToggle) {
|
||||
elements.navToggle.setAttribute('aria-expanded', 'true');
|
||||
elements.navToggle.setAttribute('aria-label', 'Close navigation menu');
|
||||
}
|
||||
|
||||
if (elements.navOverlay) {
|
||||
|
|
@ -1788,7 +1830,6 @@
|
|||
}
|
||||
};
|
||||
|
||||
// Close mobile navigation
|
||||
const closeNav = () => {
|
||||
if (!elements.nav || !state.isOpen) return;
|
||||
|
||||
|
|
@ -1797,6 +1838,7 @@
|
|||
|
||||
if (elements.navToggle) {
|
||||
elements.navToggle.setAttribute('aria-expanded', 'false');
|
||||
elements.navToggle.setAttribute('aria-label', 'Open navigation menu');
|
||||
}
|
||||
|
||||
if (elements.navOverlay) {
|
||||
|
|
@ -1810,6 +1852,7 @@
|
|||
|
||||
// Close all dropdowns when closing nav
|
||||
closeAllDropdowns();
|
||||
unlockScroll();
|
||||
};
|
||||
|
||||
// Toggle navigation
|
||||
|
|
@ -1883,6 +1926,12 @@
|
|||
toggleNav();
|
||||
}, { passive: false });
|
||||
}
|
||||
|
||||
if (elements.navClose) {
|
||||
elements.navClose.addEventListener('click', () => {
|
||||
closeNav();
|
||||
}, { passive: true });
|
||||
}
|
||||
|
||||
// Overlay click handler
|
||||
if (elements.navOverlay) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue