fix(ui): keep Settings tab bar sticky and flush on mobile

Bleed cancels content padding so the bar sits at the scroll top; sticky stays on under 900px/mobile instead of scrolling away; active tab scrolls into view on click/hash.

Refs #396

Thanks: INSOLVE (Honorary); Marco Jakobs (@jacotec); MyNameisStitch (@MyNameisStitch); Redspin (@playerumpknow)
This commit is contained in:
UNITRONIX
2026-08-31 01:39:43 +02:00
parent 3ee0085a87
commit 80659c713b
6 changed files with 46 additions and 22 deletions
+3
View File
@@ -10,6 +10,9 @@
### Added
- **Custom download portal product label (#386):** Agent Generator advanced branding can override or hide the product type line (`download.product_*`) on the public download page without rebuilding installers.
### Fixed
- **Settings tab bar sticky + mobile (#396):** Settings horizontal tabs stay flush under the top of the content scroll area (bleed cancels content padding), keep sticky on narrow/mobile instead of scrolling away, and scroll the active tab into view when switching or opening a hash link. Ships via panel update. Verify: Settings → scroll a long form → tab bar flush at top; ≤900px / mobile → tabs remain visible while scrolling; `/settings#updates` brings Updates into the horizontal track.
### Changed
- _(none yet)_
+3 -3
View File
@@ -468,7 +468,8 @@ body.ux35-page .mobile-bottom-nav {
-webkit-overflow-scrolling: touch;
}
body.app-page.is-mobile-shell .settings-shell-nav,
/* Settings: sticky outer stays; horizontal scroll on the track only */
body.app-page.is-mobile-shell .settings-shell-nav-track,
body.app-page.is-mobile-shell .settings-tabs,
body.app-page.is-mobile-shell .server-mgmt-tabs {
display: flex;
@@ -478,10 +479,9 @@ body.ux35-page .mobile-bottom-nav {
scrollbar-width: none;
gap: var(--space-xs);
padding-bottom: var(--space-xs);
position: static;
}
body.app-page.is-mobile-shell .settings-shell-nav::-webkit-scrollbar,
body.app-page.is-mobile-shell .settings-shell-nav-track::-webkit-scrollbar,
body.app-page.is-mobile-shell .settings-tabs::-webkit-scrollbar,
body.app-page.is-mobile-shell .server-mgmt-tabs::-webkit-scrollbar {
display: none;
+26 -15
View File
@@ -3285,20 +3285,21 @@
}
.settings-shell-nav {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
gap: 4px;
--settings-nav-bleed: 0px;
--settings-nav-bleed-x: 0px;
display: block;
padding: 0;
margin: 0;
border: none;
border-bottom: 1px solid var(--border-primary);
border-radius: 0;
overflow-x: auto;
overscroll-behavior-x: contain;
-webkit-overflow-scrolling: touch;
overflow: visible;
position: sticky;
top: 0;
top: calc(-1 * var(--settings-nav-bleed));
padding-top: var(--settings-nav-bleed);
margin-top: calc(-1 * var(--settings-nav-bleed));
margin-inline: calc(-1 * var(--settings-nav-bleed-x));
padding-inline: var(--settings-nav-bleed-x);
z-index: 2;
/* Keep labels readable while scrolling long forms / branding wallpaper */
backdrop-filter: blur(8px);
@@ -3306,6 +3307,23 @@
background-color: color-mix(in srgb, var(--bg-primary) 88%, transparent);
}
/* Classic layout: cancel .main-content padding so sticky bar is flush */
.app-page:not(.ux35-page) .settings-shell-nav {
--settings-nav-bleed: var(--space-lg);
--settings-nav-bleed-x: var(--space-lg);
}
/* Horizontal scroll lives on the track — not the sticky outer (avoids sticky breakage) */
.settings-shell-nav-track {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
gap: 4px;
overflow-x: auto;
overscroll-behavior-x: contain;
-webkit-overflow-scrolling: touch;
}
.settings-shell-nav .settings-shell-tab,
.settings-shell-nav .settings-tab {
display: flex;
@@ -3768,13 +3786,6 @@
}
@media (max-width: 900px) {
.settings-shell-nav {
position: static;
backdrop-filter: none;
-webkit-backdrop-filter: none;
background-color: transparent;
}
.settings-row {
grid-template-columns: 1fr;
gap: var(--space-sm);
+7 -2
View File
@@ -424,10 +424,11 @@ body.app-page.ux35-page {
margin-bottom: var(--ux35-card-gap);
}
/* Settings shell: horizontal tabs readable over branding wallpaper */
/* Settings shell: horizontal tabs readable over branding wallpaper; bleed cancels content padding */
.ux35-page .settings-shell-nav {
--settings-nav-bleed: var(--ux35-content-padding-lg);
--settings-nav-bleed-x: var(--ux35-content-padding-lg);
position: sticky;
top: 0;
z-index: 2;
background-color: color-mix(in srgb, var(--bg-primary) 92%, transparent);
}
@@ -579,6 +580,10 @@ body.ux35-is-resizing {
padding: var(--ux35-content-padding);
padding-bottom: calc(var(--ux35-content-padding) + 72px);
}
.ux35-page .settings-shell-nav {
--settings-nav-bleed: var(--ux35-content-padding);
--settings-nav-bleed-x: var(--ux35-content-padding);
}
.ux35-sidebar {
width: min(86vw, 280px);
min-width: min(86vw, 280px);
+4 -1
View File
@@ -142,6 +142,9 @@
const target = document.getElementById('tab-' + tab.dataset.tab);
if (target) target.classList.add('active');
window.history.replaceState(null, '', '#' + tab.dataset.tab);
if (typeof tab.scrollIntoView === 'function') {
tab.scrollIntoView({ inline: 'nearest', block: 'nearest' });
}
applySettingsSearchFilter();
onSettingsTabChanged(tab.dataset.tab);
});
@@ -150,7 +153,7 @@
const hash = window.location.hash.replace('#', '');
if (['general', 'branding', 'server', 'backup', 'updates', 'auth', 'advanced', 'email'].includes(hash)) {
const tabName = hash === 'server' ? 'general' : hash;
const tab = document.querySelector(`[data-tab="${tabName}"]`);
const tab = document.querySelector(`.settings-shell-tab[data-tab="${tabName}"], .settings-tab[data-tab="${tabName}"]`);
if (tab) tab.click();
}
}
+3 -1
View File
@@ -11,7 +11,8 @@
</div>
<div class="settings-shell">
<nav class="settings-shell-nav mobile-tabs-scroll" role="tablist" aria-label="${_('settings.title')}">
<nav class="settings-shell-nav" role="tablist" aria-label="${_('settings.title')}">
<div class="settings-shell-nav-track mobile-tabs-scroll">
<button type="button" class="settings-shell-tab settings-tab active" role="tab" aria-selected="true" id="settings-tab-general" data-tab="general" aria-controls="tab-general">
<span class="material-icons" aria-hidden="true">tune</span>
${_('settings.tab_general')}
@@ -40,6 +41,7 @@
<span class="material-icons" aria-hidden="true">code</span>
${_('settings.tab_advanced')}
</button>` : ''}
</div>
</nav>
<div class="settings-shell-main">