mirror of
https://github.com/Shik3i/KoalaSync.git
synced 2026-09-03 22:45:21 +00:00
fix(website): smooth anchor scrolling, language-switch scroll position, lang select UX
- Replace JS scrollIntoView handler with CSS scroll-behavior:smooth for native anchor navigation. Fixes #hash-based scroll position surviving language switches since the hash stays in the URL. - Auto-update URL hash via IntersectionObserver as user scrolls through sections, so manual scrolling also sets the anchor for language switch. - Preserve window.location.hash across all language-switch navigations. - Fix Chrome double-arrow on language select by limiting appearance:base-select to ::picker(select) only, keeping the custom SVG chevron. - Make chevron absolutely positioned over the select with pointer-events:none so clicks anywhere on the container open the dropdown. - Remove max-width constraint on language select so longer names fit. - Add scroll-margin-top to section[id] and header[id] so anchored sections clear the fixed navbar (100px offset). - Add header[id] to scroll-margin-top for keyboard skip-link accessibility. Thanks to Taradal from Reddit
This commit is contained in:
+14
-17
@@ -34,6 +34,17 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
revealElements.forEach(el => revealObserver.observe(el));
|
revealElements.forEach(el => revealObserver.observe(el));
|
||||||
|
|
||||||
|
// Auto-update URL hash as user scrolls through sections
|
||||||
|
// (preserves position across language switches)
|
||||||
|
const sectionObserver = new IntersectionObserver((entries) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
history.replaceState(null, null, '#' + entry.target.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, { threshold: 0.3 });
|
||||||
|
document.querySelectorAll('section[id], header[id]').forEach(el => sectionObserver.observe(el));
|
||||||
|
|
||||||
// Navbar scroll effect
|
// Navbar scroll effect
|
||||||
const nav = document.querySelector('nav');
|
const nav = document.querySelector('nav');
|
||||||
window.addEventListener('scroll', () => {
|
window.addEventListener('scroll', () => {
|
||||||
@@ -46,20 +57,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Smooth scroll for anchors
|
|
||||||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
|
||||||
anchor.addEventListener('click', function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const target = document.querySelector(this.getAttribute('href'));
|
|
||||||
if (target) {
|
|
||||||
target.scrollIntoView({
|
|
||||||
behavior: 'smooth',
|
|
||||||
block: 'start'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Invite Detection & Bridge
|
// Invite Detection & Bridge
|
||||||
const checkInvite = () => {
|
const checkInvite = () => {
|
||||||
const isJoinPage = window.location.pathname.includes('join');
|
const isJoinPage = window.location.pathname.includes('join');
|
||||||
@@ -508,7 +505,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
target = hasHtml ? 'imprint.html' : 'imprint';
|
target = hasHtml ? 'imprint.html' : 'imprint';
|
||||||
if (path.includes('/de/')) target = hasHtml ? '../imprint.html' : '../imprint';
|
if (path.includes('/de/')) target = hasHtml ? '../imprint.html' : '../imprint';
|
||||||
}
|
}
|
||||||
window.location.href = target;
|
window.location.href = target + window.location.hash;
|
||||||
return;
|
return;
|
||||||
} else if (isLegalPrivacy) {
|
} else if (isLegalPrivacy) {
|
||||||
let target;
|
let target;
|
||||||
@@ -520,7 +517,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
target = hasHtml ? 'privacy.html' : 'privacy';
|
target = hasHtml ? 'privacy.html' : 'privacy';
|
||||||
if (path.includes('/de/')) target = hasHtml ? '../privacy.html' : '../privacy';
|
if (path.includes('/de/')) target = hasHtml ? '../privacy.html' : '../privacy';
|
||||||
}
|
}
|
||||||
window.location.href = target;
|
window.location.href = target + window.location.hash;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -549,7 +546,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.location.href = targetPath;
|
window.location.href = targetPath + window.location.hash;
|
||||||
} else {
|
} else {
|
||||||
// Dynamic page: Toggle classes and update elements dynamically without navigating away
|
// Dynamic page: Toggle classes and update elements dynamically without navigating away
|
||||||
const html = document.documentElement;
|
const html = document.documentElement;
|
||||||
|
|||||||
+13
-10
@@ -61,6 +61,10 @@
|
|||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
scroll-behavior: smooth;
|
||||||
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Twemoji Country Flags', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
font-family: 'Twemoji Country Flags', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
background-color: var(--bg);
|
background-color: var(--bg);
|
||||||
@@ -2007,12 +2011,14 @@ html:not(.lang-de) [lang="de"] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lang-select-container .chevron-icon {
|
.lang-select-container .chevron-icon {
|
||||||
|
position: absolute;
|
||||||
|
right: 14px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
width: 12px;
|
width: 12px;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
flex-shrink: 0;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
margin-left: -2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lang-select-container:hover .chevron-icon {
|
.lang-select-container:hover .chevron-icon {
|
||||||
@@ -2031,21 +2037,16 @@ html:not(.lang-de) [lang="de"] {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: currentColor;
|
color: currentColor;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 0 12px 0 0;
|
padding: 0 28px 0 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: auto;
|
width: auto;
|
||||||
max-width: 150px;
|
min-width: 110px;
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@supports (appearance: base-select) {
|
@supports (appearance: base-select) {
|
||||||
.lang-dropdown,
|
|
||||||
.lang-dropdown::picker(select) {
|
.lang-dropdown::picker(select) {
|
||||||
appearance: base-select;
|
appearance: base-select;
|
||||||
}
|
|
||||||
.lang-dropdown::picker(select) {
|
|
||||||
background-color: #0f172a;
|
background-color: #0f172a;
|
||||||
border: 1px solid #334155;
|
border: 1px solid #334155;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -2748,7 +2749,9 @@ html:not(.lang-de) [lang="de"] {
|
|||||||
}
|
}
|
||||||
.feature-card,
|
.feature-card,
|
||||||
.use-case-card,
|
.use-case-card,
|
||||||
.compat-logo {
|
.compat-logo,
|
||||||
|
section[id],
|
||||||
|
header[id] {
|
||||||
scroll-margin-top: 100px;
|
scroll-margin-top: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user