From 28ab78da8609905e5281f37644030308a0c19de0 Mon Sep 17 00:00:00 2001 From: polybjorn Date: Fri, 1 May 2026 06:22:19 +0000 Subject: [PATCH] fix(theme): nav-bar layout and sticky behavior in Swage (#8739) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes a cluster of bugs in the Swage theme. Most are nav-bar related and reproducible in narrow viewports (≤840px); one affects the article list at any viewport. 1. **Horizontal page overflow.** `.nav_menu` had `width: 100%` plus `padding-left: 2rem` with default `box-sizing: content-box`, making it 32px wider than the viewport. The whole page side-scrolled. Fixed with `box-sizing: border-box`. 2. **Article title jumps down on hover.** `.flux:not(.current):hover .item .title { top: auto !important }` overrode the base-theme `top: 0`, dropping the absolutely-positioned title to its in-flow position on hover. The override was added in #4671 (a broad font-size/line-height refactor) and looks like collateral; no other Swage rule depends on it. Removed. 3. **Settings/sort buttons drift on scroll.** Both used `position: fixed` without an explicit `top`, so they resolved to their static document position and drifted with scroll. Compounded by being inside `position: sticky` parents (Firefox bug). Fixed by: - Making `.header` sticky so `.item.configure` (now `position: absolute; top: 0`) anchors to it. - Splitting `#nav_menu_sort .dropdown-menu` from its siblings and making it `position: absolute` (the only one that needs to float when open). 4. **Logo hidden selector typo.** Narrow-view rule used `.item .title` (descendant) but the actual element is `.item.title` (combined class), so the logo was never hidden in narrow view. Fixed. 5. **Logo overlaps nav buttons when sidebar is collapsed.** At any viewport, collapsing the sidebar leaves the absolutely-positioned logo overlapping the now-wider nav. Added `body:has(.aside:not(.visible)) .header .item.title { display: none }` to hide the logo whenever the sidebar isn't expanded. Trade-off: the logo is no longer visible when the sidebar is collapsed at any width. Keeping it visible would require repositioning or resizing the logo itself, which is a redesign rather than a bug fix. 6. **Forced two-row nav at narrow widths even when one row would fit.** Narrow layout used absolute positioning to pin a second row of buttons at `top: 36px`. Replaced with `display: flex; flex-wrap: wrap`, scoped to article-list bodies (`body:is(.normal, .reader, .global)`) so the settings-page custom layout is untouched. ## Screenshots **Wide viewport, before:** Screenshot 2026-04-30 at 18 26 49 **Narrow viewport, before:** Screenshot 2026-04-30 at 18 31 02 **Settings and sort buttons drift on scroll (before, any viewport):** Screenshot 2026-04-30 at 17 48 52 **Article title shifts down on hover (narrow viewport, before):** Screenshot 2026-04-30 at 19 31 53 **Narrow viewport, after:** Screenshot 2026-04-30 at 18 26 17 ## Related - Possibly fixes #6977 (buttons squeezed together at mobile widths in Swage). ## Notes - `swage.rtl.css` regenerated via `npm run rtlcss`. - `npm run stylelint` passes. - All six fixes are small CSS bugs in the Swage theme (most in narrow-viewport nav layout, one in article-list hover); bundled as one PR for that reason. Happy to split into separate PRs if reviewers prefer. --- - box-sizing: border-box on .nav_menu to fix 32px horizontal overflow caused by width: 100% + padding-left: 2rem. - Remove .flux:not(.current):hover .item .title { top: auto !important } override that dropped article titles to their in-flow position on hover (introduced in #4671 as collateral from a wider refactor). - Make .header position: sticky and switch .item.configure (and #nav_menu_sort .dropdown-menu) to position: absolute with explicit top: 0 so the cog and sort menu no longer drift when scrolling. - Fix .item .title -> .item.title selector typo so the logo is actually hidden in narrow view as intended. - Hide the logo when the sidebar is collapsed (body:has(.aside:not(.visible))) to avoid overlap with the now-wider nav. - Replace the two-row absolute-positioned narrow-view nav layout with display: flex; flex-wrap: wrap, scoped to article-list bodies so settings/admin pages keep their custom layout. Regenerated swage.rtl.css via npm run rtlcss. npm run stylelint passes. Co-authored-by: Bjørn A. Andersen --- p/themes/Swage/swage.css | 61 +++++++++++++++++++++++++++++++----- p/themes/Swage/swage.rtl.css | 61 +++++++++++++++++++++++++++++++----- 2 files changed, 106 insertions(+), 16 deletions(-) diff --git a/p/themes/Swage/swage.css b/p/themes/Swage/swage.css index 6c7bdf5f0..5b9833529 100644 --- a/p/themes/Swage/swage.css +++ b/p/themes/Swage/swage.css @@ -749,6 +749,9 @@ main.post .drop-section li.item.feed a:hover .icon { .header { height: auto; + position: sticky; + top: 0; + z-index: 96; /* above .nav_menu (90) and .item.configure (95) */ > .item { padding: 0; @@ -781,7 +784,8 @@ main.post .drop-section li.item.feed a:hover .icon { } .item.configure { - position: fixed; + position: absolute; + top: 0; right: 0; z-index: 95; width: 35px; @@ -957,6 +961,7 @@ main.post .drop-section li.item.feed a:hover .icon { .nav_menu { padding: 0 0 0 2rem; width: 100%; + box-sizing: border-box; font-size: 0; background-color: var(--color-background-nav); text-align: left; @@ -1266,7 +1271,7 @@ main.global { @media (max-width: 840px) { body:not(.formLogin, .register) { .header { - .item .title { + .item.title { display: none; } } @@ -1289,13 +1294,17 @@ main.global { } #nav_menu_sort, - #nav_menu_sort .dropdown-menu, #nav_menu_sort .dropdown, #toggle-order { position: fixed; right: 0; } + #nav_menu_sort .dropdown-menu { + position: absolute; + right: 0; + } + #nav_menu_sort .dropdown-menu, #nav_menu_read_all .dropdown-menu, #dropdown-search-wrapper .dropdown-menu, @@ -1381,7 +1390,6 @@ main.global { .item.configure { padding: 0; - position: fixed; > .icon { margin-top: 5px; @@ -1399,10 +1407,6 @@ main.global { text-align: center; } - .flux:not(.current):hover .item .title { - top: auto !important; - } - .aside { padding: 0; width: 0; @@ -1577,6 +1581,43 @@ main.global { div#nav_menu_views { right: 32px; } + + body:is(.normal, .reader, .global) { + .nav_menu { + display: flex; + height: auto; + flex-wrap: wrap; + align-items: center; + row-gap: 2px; + padding-right: 3rem; /* room for absolute-positioned cog */ + } + + .nav_menu .item.search { + position: static; + top: auto; + width: auto; + flex: 1 1 140px; + min-width: 100px; + margin-left: 0; + } + + .nav_menu form#mark-read-menu, + .nav_menu a#actualize, + .nav_menu div#nav_menu_actions, + .nav_menu div#nav_menu_views, + .nav_menu a#toggle-order, + .nav_menu #nav_menu_sort, + .nav_menu #nav_menu_sort .dropdown { + position: static; + top: auto; + right: auto; + left: auto; + } + + .item.configure { + right: 0; + } + } } @media (max-width: 410px) { @@ -1680,3 +1721,7 @@ button.as-link { min-height: initial; } } + +body:has(.aside:not(.visible)) .header .item.title { + display: none; +} diff --git a/p/themes/Swage/swage.rtl.css b/p/themes/Swage/swage.rtl.css index 01c53a9aa..1919e17a1 100644 --- a/p/themes/Swage/swage.rtl.css +++ b/p/themes/Swage/swage.rtl.css @@ -749,6 +749,9 @@ main.post .drop-section li.item.feed a:hover .icon { .header { height: auto; + position: sticky; + top: 0; + z-index: 96; /* above .nav_menu (90) and .item.configure (95) */ > .item { padding: 0; @@ -781,7 +784,8 @@ main.post .drop-section li.item.feed a:hover .icon { } .item.configure { - position: fixed; + position: absolute; + top: 0; left: 0; z-index: 95; width: 35px; @@ -957,6 +961,7 @@ main.post .drop-section li.item.feed a:hover .icon { .nav_menu { padding: 0 2rem 0 0; width: 100%; + box-sizing: border-box; font-size: 0; background-color: var(--color-background-nav); text-align: right; @@ -1266,7 +1271,7 @@ main.global { @media (max-width: 840px) { body:not(.formLogin, .register) { .header { - .item .title { + .item.title { display: none; } } @@ -1289,13 +1294,17 @@ main.global { } #nav_menu_sort, - #nav_menu_sort .dropdown-menu, #nav_menu_sort .dropdown, #toggle-order { position: fixed; left: 0; } + #nav_menu_sort .dropdown-menu { + position: absolute; + left: 0; + } + #nav_menu_sort .dropdown-menu, #nav_menu_read_all .dropdown-menu, #dropdown-search-wrapper .dropdown-menu, @@ -1381,7 +1390,6 @@ main.global { .item.configure { padding: 0; - position: fixed; > .icon { margin-top: 5px; @@ -1399,10 +1407,6 @@ main.global { text-align: center; } - .flux:not(.current):hover .item .title { - top: auto !important; - } - .aside { padding: 0; width: 0; @@ -1577,6 +1581,43 @@ main.global { div#nav_menu_views { left: 32px; } + + body:is(.normal, .reader, .global) { + .nav_menu { + display: flex; + height: auto; + flex-wrap: wrap; + align-items: center; + row-gap: 2px; + padding-left: 3rem; /* room for absolute-positioned cog */ + } + + .nav_menu .item.search { + position: static; + top: auto; + width: auto; + flex: 1 1 140px; + min-width: 100px; + margin-right: 0; + } + + .nav_menu form#mark-read-menu, + .nav_menu a#actualize, + .nav_menu div#nav_menu_actions, + .nav_menu div#nav_menu_views, + .nav_menu a#toggle-order, + .nav_menu #nav_menu_sort, + .nav_menu #nav_menu_sort .dropdown { + position: static; + top: auto; + left: auto; + right: auto; + } + + .item.configure { + left: 0; + } + } } @media (max-width: 410px) { @@ -1680,3 +1721,7 @@ button.as-link { min-height: initial; } } + +body:has(.aside:not(.visible)) .header .item.title { + display: none; +}