mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-05-14 18:23:52 -04:00
fix(theme): nav-bar layout and sticky behavior in Swage (#8739)
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:**
<img width="950" height="32" alt="Screenshot 2026-04-30 at 18 26 49" src="https://github.com/user-attachments/assets/58635491-3d36-46af-b4ed-45ff8893bf5a" />
**Narrow viewport, before:**
<img width="738" height="72" alt="Screenshot 2026-04-30 at 18 31 02" src="https://github.com/user-attachments/assets/9b66df9e-8677-47f3-8dfe-011c070b0010" />
**Settings and sort buttons drift on scroll (before, any viewport):**
<img width="116" height="83" alt="Screenshot 2026-04-30 at 17 48 52" src="https://github.com/user-attachments/assets/27a5a2b6-3fe7-4ee8-8cfb-4f399937bedc" />
**Article title shifts down on hover (narrow viewport, before):**
<img width="287" height="43" alt="Screenshot 2026-04-30 at 19 31 53" src="https://github.com/user-attachments/assets/642be719-9f4f-4d83-9ba9-3f584fa7684d" />
**Narrow viewport, after:**
<img width="663" height="32" alt="Screenshot 2026-04-30 at 18 26 17" src="https://github.com/user-attachments/assets/9378fc68-9942-4b5e-af21-f10eb87c474b" />
## 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 <polybjorn@users.noreply.github.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user