From 884feb982d8ee6b63c60015d333a1ef6a7e79de7 Mon Sep 17 00:00:00 2001 From: KoalaDev <6156589+Shik3i@users.noreply.github.com> Date: Tue, 16 Jun 2026 06:05:42 +0200 Subject: [PATCH] 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 --- website/app.js | 31 ++++++++++++++----------------- website/style.css | 23 +++++++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/website/app.js b/website/app.js index 745fd85..329095b 100644 --- a/website/app.js +++ b/website/app.js @@ -34,6 +34,17 @@ document.addEventListener('DOMContentLoaded', () => { 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 const nav = document.querySelector('nav'); 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 const checkInvite = () => { const isJoinPage = window.location.pathname.includes('join'); @@ -508,7 +505,7 @@ document.addEventListener('DOMContentLoaded', () => { 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; } else if (isLegalPrivacy) { let target; @@ -520,7 +517,7 @@ document.addEventListener('DOMContentLoaded', () => { 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; } @@ -549,7 +546,7 @@ document.addEventListener('DOMContentLoaded', () => { } } - window.location.href = targetPath; + window.location.href = targetPath + window.location.hash; } else { // Dynamic page: Toggle classes and update elements dynamically without navigating away const html = document.documentElement; diff --git a/website/style.css b/website/style.css index c30a87e..3cd2aa3 100644 --- a/website/style.css +++ b/website/style.css @@ -61,6 +61,10 @@ border-radius: 6px; } +html { + scroll-behavior: smooth; +} + body { 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); @@ -2007,12 +2011,14 @@ html:not(.lang-de) [lang="de"] { } .lang-select-container .chevron-icon { + position: absolute; + right: 14px; + top: 50%; + transform: translateY(-50%); width: 12px; height: 12px; - flex-shrink: 0; pointer-events: none; opacity: 0.6; - margin-left: -2px; } .lang-select-container:hover .chevron-icon { @@ -2031,21 +2037,16 @@ html:not(.lang-de) [lang="de"] { font-weight: 600; color: currentColor; cursor: pointer; - padding: 0 12px 0 0; + padding: 0 28px 0 0; margin: 0; width: auto; - max-width: 150px; - text-overflow: ellipsis; - white-space: nowrap; + min-width: 110px; overflow: hidden; } @supports (appearance: base-select) { - .lang-dropdown, .lang-dropdown::picker(select) { appearance: base-select; - } - .lang-dropdown::picker(select) { background-color: #0f172a; border: 1px solid #334155; border-radius: 8px; @@ -2748,7 +2749,9 @@ html:not(.lang-de) [lang="de"] { } .feature-card, .use-case-card, -.compat-logo { +.compat-logo, +section[id], +header[id] { scroll-margin-top: 100px; }