feat: add header elevation effect on scroll for improved UX
This commit is contained in:
parent
3b5370651e
commit
756ce75ad7
2 changed files with 77 additions and 14 deletions
76
dist/css/styles.css
vendored
76
dist/css/styles.css
vendored
|
|
@ -285,10 +285,22 @@ a:hover {
|
||||||
border-bottom: 1px solid var(--border-primary);
|
border-bottom: 1px solid var(--border-primary);
|
||||||
height: var(--header-height);
|
height: var(--header-height);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
transition: background var(--transition-fast), box-shadow var(--transition-fast), border-color var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
th {
|
||||||
[data-theme="dark"] .site-header {
|
background: var(--bg-secondary);
|
||||||
background: rgba(15, 23, 42, .95);
|
color: var(--text-primary);
|
||||||
|
font-weight: var(--font-semibold);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: .05em;
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
border-bottom: 1px solid var(--border-primary);
|
||||||
|
}
|
||||||
|
[data-theme="dark"] .site-header.is-scrolled {
|
||||||
|
background: rgba(15, 23, 42, .92);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Main content */
|
/* Main content */
|
||||||
|
|
@ -440,10 +452,13 @@ a:hover {
|
||||||
transition: all var(--transition-fast);
|
transition: all var(--transition-fast);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.nav-link:hover { color: var(--primary); background: var(--bg-secondary); }
|
.nav-link:hover { color: var(--primary); background: var(--bg-secondary); }
|
||||||
|
|
||||||
.nav-link:hover::before { width: 60%; }
|
.nav-link:hover::before { width: 60%; }
|
||||||
|
|
||||||
|
/* Unified active/current nav link styling */
|
||||||
|
.header-nav .nav-link[aria-current="page"],
|
||||||
.nav-link.active { color: var(--primary); background: var(--bg-secondary); }
|
.nav-link.active { color: var(--primary); background: var(--bg-secondary); }
|
||||||
|
|
||||||
.nav-link:hover:not(.active) {
|
.nav-link:hover:not(.active) {
|
||||||
|
|
@ -455,6 +470,11 @@ a:hover {
|
||||||
width: 90%;
|
width: 90%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Also underline when aria-current is set (scrollspy) */
|
||||||
|
.header-nav .nav-link[aria-current="page"]::before {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
.dark-mode-toggle {
|
.dark-mode-toggle {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
@ -487,7 +507,6 @@ a:hover {
|
||||||
.dark-mode-toggle:hover {
|
.dark-mode-toggle:hover {
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
border-color: var(--primary);
|
border-color: var(--primary);
|
||||||
transform: scale(1.05);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sun-icon,
|
.sun-icon,
|
||||||
|
|
@ -921,7 +940,7 @@ a:hover {
|
||||||
|
|
||||||
/* Button additional states */
|
/* Button additional states */
|
||||||
.btn:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
|
.btn:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
|
||||||
.btn:focus-visible { outline: 2px solid transparent; box-shadow: 0 0 0 3px rgb(99 102 241 / .35); }
|
.btn:focus-visible { outline: 2px solid transparent; box-shadow: var(--shadow-focus); }
|
||||||
.btn[disabled], .btn.is-disabled { opacity: .6; cursor: not-allowed; pointer-events: none; filter: grayscale(.15); }
|
.btn[disabled], .btn.is-disabled { opacity: .6; cursor: not-allowed; pointer-events: none; filter: grayscale(.15); }
|
||||||
.btn.is-loading { position: relative; pointer-events: none; }
|
.btn.is-loading { position: relative; pointer-events: none; }
|
||||||
.btn.is-loading::after {
|
.btn.is-loading::after {
|
||||||
|
|
@ -940,7 +959,7 @@ input[type="email"]:focus-visible,
|
||||||
select:focus-visible,
|
select:focus-visible,
|
||||||
textarea:focus-visible {
|
textarea:focus-visible {
|
||||||
outline: 2px solid transparent;
|
outline: 2px solid transparent;
|
||||||
box-shadow: 0 0 0 3px rgb(99 102 241 / .25);
|
box-shadow: var(--shadow-focus);
|
||||||
border-color: var(--primary);
|
border-color: var(--primary);
|
||||||
}
|
}
|
||||||
input:disabled, select:disabled, textarea:disabled { opacity: .6; cursor: not-allowed; }
|
input:disabled, select:disabled, textarea:disabled { opacity: .6; cursor: not-allowed; }
|
||||||
|
|
@ -1454,11 +1473,15 @@ th {
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 100
|
z-index: 100
|
||||||
}
|
}
|
||||||
|
#file-hosts-table tr:nth-child(even),
|
||||||
tr:hover {
|
.enhanced-table tr:nth-child(even),
|
||||||
background: var(--bg-secondary);
|
.pricing-table tr:nth-child(even),
|
||||||
|
.policies-table tr:nth-child(even) {
|
||||||
|
background-color: color-mix(in srgb, var(--bg-secondary) 60%, transparent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tr:hover { background: color-mix(in srgb, var(--bg-secondary) 80%, transparent); }
|
||||||
|
|
||||||
.pricing-table .service-name {
|
.pricing-table .service-name {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
}
|
}
|
||||||
|
|
@ -1576,7 +1599,7 @@ color: inherit;
|
||||||
|
|
||||||
.clear-icon:hover {
|
.clear-icon:hover {
|
||||||
background: var(--bg-secondary);
|
background: var(--bg-secondary);
|
||||||
color: var(--text-primary);
|
color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Status */
|
/* Status */
|
||||||
|
|
@ -1914,6 +1937,30 @@ select:hover {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Comparison */
|
/* Comparison */
|
||||||
|
.loading-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
display: grid;
|
||||||
|
place-items: center;
|
||||||
|
background: radial-gradient(80% 60% at 50% 40%, rgba(0,0,0,.04), transparent 60%),
|
||||||
|
color-mix(in srgb, var(--bg-primary) 88%, transparent);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
-webkit-backdrop-filter: blur(2px);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity var(--transition-normal);
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
|
.loading-overlay--visible { opacity: 1; }
|
||||||
|
.loading-content { text-align: center; color: var(--text-secondary); }
|
||||||
|
.loading-spinner { position: relative; width: 42px; height: 42px; display: inline-grid; place-items: center; margin-bottom: var(--space-md); }
|
||||||
|
.loading-spinner .spinner-ring {
|
||||||
|
position: absolute; inset: 0; border-radius: 50%;
|
||||||
|
border: 3px solid var(--border-primary); border-top-color: var(--primary);
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
.loading-spinner .spinner-ring:nth-child(2) { inset: 6px; opacity: .7; animation-duration: 1.4s; }
|
||||||
|
.loading-spinner .spinner-ring:nth-child(3) { inset: 12px; opacity: .5; animation-duration: 1.8s; }
|
||||||
|
.loading-text { font-size: var(--text-sm); color: var(--text-secondary); }
|
||||||
.comparison-header {
|
.comparison-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
@ -2078,8 +2125,8 @@ select:hover {
|
||||||
|
|
||||||
.comparison-table th:first-child {
|
.comparison-table th:first-child {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
background: var(--primary);
|
background: var(--bg-secondary);
|
||||||
color: #fff;
|
color: var(--text-primary);
|
||||||
position: sticky;
|
position: sticky;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 101;
|
z-index: 101;
|
||||||
|
|
@ -2398,7 +2445,7 @@ select:hover {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
box-shadow: var(--shadow-lg);
|
box-shadow: var(--shadow-md);
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transform: translateY(20px);
|
transform: translateY(20px);
|
||||||
|
|
@ -2413,7 +2460,8 @@ select:hover {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.back-to-top:hover { background: var(--primary-hover); }
|
.back-to-top:hover { background: var(--primary-hover); box-shadow: var(--shadow-lg); }
|
||||||
|
.back-to-top:focus-visible { outline: 2px solid transparent; box-shadow: var(--shadow-focus); }
|
||||||
|
|
||||||
/* ======= Responsiveness ======= */
|
/* ======= Responsiveness ======= */
|
||||||
@media (max-width:1024px) {
|
@media (max-width:1024px) {
|
||||||
|
|
|
||||||
15
dist/js/app.js
vendored
15
dist/js/app.js
vendored
|
|
@ -941,6 +941,20 @@ function setupNavHighlight() {
|
||||||
sections.forEach(sec => io.observe(sec));
|
sections.forEach(sec => io.observe(sec));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adds/removes a subtle shadow on the fixed header when the page is scrolled
|
||||||
|
function setupHeaderElevate() {
|
||||||
|
const header = document.querySelector('.site-header');
|
||||||
|
if (!header) return;
|
||||||
|
|
||||||
|
const update = () => {
|
||||||
|
if (window.scrollY > 8) header.classList.add('is-scrolled');
|
||||||
|
else header.classList.remove('is-scrolled');
|
||||||
|
};
|
||||||
|
update();
|
||||||
|
const onScroll = Utils.throttle(update, 100);
|
||||||
|
window.addEventListener('scroll', onScroll, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
/* Age verification overlay with focus trap + inert */
|
/* Age verification overlay with focus trap + inert */
|
||||||
function setupAgeVerification() {
|
function setupAgeVerification() {
|
||||||
const overlay = document.getElementById('age-verification-overlay');
|
const overlay = document.getElementById('age-verification-overlay');
|
||||||
|
|
@ -1109,6 +1123,7 @@ document.addEventListener('DOMContentLoaded', async () => {
|
||||||
setupOfflineDetection();
|
setupOfflineDetection();
|
||||||
setupBackToTop();
|
setupBackToTop();
|
||||||
setupNavHighlight();
|
setupNavHighlight();
|
||||||
|
setupHeaderElevate();
|
||||||
setupAgeVerification();
|
setupAgeVerification();
|
||||||
|
|
||||||
performanceMonitor.mark('fully-loaded');
|
performanceMonitor.mark('fully-loaded');
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue