From 1cca57f92d3aa38db7f36098d581d3b2a5026209 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sat, 14 Mar 2026 19:46:00 +0100 Subject: [PATCH 1/8] [BUGFIX] Fix sticky header scroll flicker in Chromium browsers The hardcoded 120px scroll threshold causes a feedback loop in Chromium browsers: at the threshold, adding navbar-transition changes the navbar height (position:sticky), which shifts document content, which changes scrollY, which toggles the class back, creating constant flickering. Replace the hardcoded threshold with getBoundingClientRect().bottom which dynamically adapts to the actual navbar height. Additionally, check classList.contains() before toggling to prevent redundant DOM mutations. Resolves: #1424 Resolves: #1468 --- Resources/Public/JavaScript/Src/bootstrap.stickyheader.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js b/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js index ad5c1b21a..a50539793 100644 --- a/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js +++ b/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js @@ -3,7 +3,13 @@ window.addEventListener('DOMContentLoaded', function () { var stickyheader = document.querySelectorAll(".navbar-fixed-top"); if (stickyheader.length >= 1) { function animateHeader() { - 120 < window.scrollY ? stickyheader[0].classList.add("navbar-transition") : stickyheader[0].classList.remove("navbar-transition"); + if (window.scrollY >= stickyheader[0].getBoundingClientRect().bottom && + !stickyheader[0].classList.contains("navbar-transition")) { + stickyheader[0].classList.add("navbar-transition"); + } else if (window.scrollY < stickyheader[0].getBoundingClientRect().bottom && + stickyheader[0].classList.contains("navbar-transition")) { + stickyheader[0].classList.remove("navbar-transition"); + } } ['scroll', 'resize', 'DOMContentLoaded'].forEach(function (e) { window.addEventListener(e, animateHeader); From e92d3a86cb36a2294d6ad34b01cb6073f860fe50 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sat, 14 Mar 2026 19:46:14 +0100 Subject: [PATCH 2/8] [TASK] Rebuild minified stickyheader.js --- Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js b/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js index 14601a4f8..448c63bca 100644 --- a/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js +++ b/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js @@ -1 +1 @@ -window.addEventListener("DOMContentLoaded",function(){var n=document.querySelectorAll(".navbar-fixed-top");function t(){120=n[0].getBoundingClientRect().bottom&&!n[0].classList.contains("navbar-transition")?n[0].classList.add("navbar-transition"):window.scrollY Date: Sat, 14 Mar 2026 20:09:37 +0100 Subject: [PATCH 3/8] =?UTF-8?q?[REVERT]=20Restore=20original=20stickyheade?= =?UTF-8?q?r.js=20=E2=80=94=20fix=20moves=20to=20CSS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The JS workaround is no longer needed. The root cause (document height instability) is fixed in _transition.scss by compensating with margin-bottom. --- Resources/Public/JavaScript/Src/bootstrap.stickyheader.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js b/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js index a50539793..ad5c1b21a 100644 --- a/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js +++ b/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js @@ -3,13 +3,7 @@ window.addEventListener('DOMContentLoaded', function () { var stickyheader = document.querySelectorAll(".navbar-fixed-top"); if (stickyheader.length >= 1) { function animateHeader() { - if (window.scrollY >= stickyheader[0].getBoundingClientRect().bottom && - !stickyheader[0].classList.contains("navbar-transition")) { - stickyheader[0].classList.add("navbar-transition"); - } else if (window.scrollY < stickyheader[0].getBoundingClientRect().bottom && - stickyheader[0].classList.contains("navbar-transition")) { - stickyheader[0].classList.remove("navbar-transition"); - } + 120 < window.scrollY ? stickyheader[0].classList.add("navbar-transition") : stickyheader[0].classList.remove("navbar-transition"); } ['scroll', 'resize', 'DOMContentLoaded'].forEach(function (e) { window.addEventListener(e, animateHeader); From 71c07701317944b117711457dd5d24f004197631 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sat, 14 Mar 2026 20:09:51 +0100 Subject: [PATCH 4/8] [REVERT] Restore original stickyheader.min.js --- Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js b/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js index 448c63bca..2591d0745 100644 --- a/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js +++ b/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js @@ -1 +1 @@ -window.addEventListener("DOMContentLoaded",function(){var n=document.querySelectorAll(".navbar-fixed-top");if(1<=n.length){function t(){window.scrollY>=n[0].getBoundingClientRect().bottom&&!n[0].classList.contains("navbar-transition")?n[0].classList.add("navbar-transition"):window.scrollY Date: Sat, 14 Mar 2026 20:10:14 +0100 Subject: [PATCH 5/8] [BUGFIX] Fix sticky header scroll flicker via CSS height compensation The navbar uses position:sticky, so height changes affect document flow. When navbar-transition reduces --mainnavigation-nav-height to the xs value (70px), the document shrinks, causing scrollbar jumps and scroll-position oscillation in Chromium browsers. Add compensating margin-bottom computed from $navbar-heights map: the difference between each breakpoint's default height and the transition height (xs). This keeps document height constant while the navbar still visually shrinks. Resolves: #1424 Resolves: #1468 --- .../Scss/components/navbar/_transition.scss | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Resources/Public/Scss/components/navbar/_transition.scss b/Resources/Public/Scss/components/navbar/_transition.scss index 865717862..b32cb95e6 100644 --- a/Resources/Public/Scss/components/navbar/_transition.scss +++ b/Resources/Public/Scss/components/navbar/_transition.scss @@ -11,6 +11,21 @@ } .navbar-transition { --mainnavigation-nav-height: #{map-get($navbar-heights, xs)}; + + // Compensate for navbar height reduction to keep document height stable. + // With position:sticky the navbar is in document flow — shrinking it changes + // the document height, causing scrollbar jumps and scroll-position oscillation + // in Chromium browsers (see #1424, #1468). Adding margin-bottom equal to the + // height difference keeps total document height constant. + @each $breakpoint in map-keys($navbar-heights) { + $default-height: map-get($navbar-heights, $breakpoint); + $transition-height: map-get($navbar-heights, xs); + @if $default-height != $transition-height { + @include media-breakpoint-up($breakpoint) { + margin-bottom: $default-height - $transition-height; + } + } + } } @media (min-width: $grid-float-breakpoint) { .navbar { From a773eaf030c3e00a13dfdc9773f5b1cf6a786ec5 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sat, 14 Mar 2026 20:15:00 +0100 Subject: [PATCH 6/8] [BUGFIX] Restore getBoundingClientRect fix for stickyheader.js Restores the JS fix that was incorrectly reverted. This PR addresses the scroll flicker symptom via dynamic threshold. --- Resources/Public/JavaScript/Src/bootstrap.stickyheader.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js b/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js index ad5c1b21a..a50539793 100644 --- a/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js +++ b/Resources/Public/JavaScript/Src/bootstrap.stickyheader.js @@ -3,7 +3,13 @@ window.addEventListener('DOMContentLoaded', function () { var stickyheader = document.querySelectorAll(".navbar-fixed-top"); if (stickyheader.length >= 1) { function animateHeader() { - 120 < window.scrollY ? stickyheader[0].classList.add("navbar-transition") : stickyheader[0].classList.remove("navbar-transition"); + if (window.scrollY >= stickyheader[0].getBoundingClientRect().bottom && + !stickyheader[0].classList.contains("navbar-transition")) { + stickyheader[0].classList.add("navbar-transition"); + } else if (window.scrollY < stickyheader[0].getBoundingClientRect().bottom && + stickyheader[0].classList.contains("navbar-transition")) { + stickyheader[0].classList.remove("navbar-transition"); + } } ['scroll', 'resize', 'DOMContentLoaded'].forEach(function (e) { window.addEventListener(e, animateHeader); From 945f7277cd67318db6fe764d1d7b7f592b8e9185 Mon Sep 17 00:00:00 2001 From: Sebastian Mendel Date: Sat, 14 Mar 2026 20:15:11 +0100 Subject: [PATCH 7/8] [TASK] Rebuild minified stickyheader.js --- Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js b/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js index 2591d0745..448c63bca 100644 --- a/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js +++ b/Resources/Public/JavaScript/Dist/bootstrap.stickyheader.min.js @@ -1 +1 @@ -window.addEventListener("DOMContentLoaded",function(){var n=document.querySelectorAll(".navbar-fixed-top");function t(){120=n[0].getBoundingClientRect().bottom&&!n[0].classList.contains("navbar-transition")?n[0].classList.add("navbar-transition"):window.scrollY Date: Sat, 14 Mar 2026 20:15:39 +0100 Subject: [PATCH 8/8] =?UTF-8?q?[REVERT]=20Remove=20SCSS=20change=20?= =?UTF-8?q?=E2=80=94=20belongs=20in=20separate=20CSS-only=20PR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scss/components/navbar/_transition.scss | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Resources/Public/Scss/components/navbar/_transition.scss b/Resources/Public/Scss/components/navbar/_transition.scss index b32cb95e6..865717862 100644 --- a/Resources/Public/Scss/components/navbar/_transition.scss +++ b/Resources/Public/Scss/components/navbar/_transition.scss @@ -11,21 +11,6 @@ } .navbar-transition { --mainnavigation-nav-height: #{map-get($navbar-heights, xs)}; - - // Compensate for navbar height reduction to keep document height stable. - // With position:sticky the navbar is in document flow — shrinking it changes - // the document height, causing scrollbar jumps and scroll-position oscillation - // in Chromium browsers (see #1424, #1468). Adding margin-bottom equal to the - // height difference keeps total document height constant. - @each $breakpoint in map-keys($navbar-heights) { - $default-height: map-get($navbar-heights, $breakpoint); - $transition-height: map-get($navbar-heights, xs); - @if $default-height != $transition-height { - @include media-breakpoint-up($breakpoint) { - margin-bottom: $default-height - $transition-height; - } - } - } } @media (min-width: $grid-float-breakpoint) { .navbar {