v0.146.0 — nav polish: styled scrollbars + collapsible sidebar groups
UI-only; no behavioural or backup/restore surface touched. Green: build, vet, tests all pass; template_id_gate, emoji_gate, native_confirm_gate, offbox_rename_gate, mojibake_gate, app_row_dedup_gate all PASS. Scrollbars (style.css): the platform default reads as a bright chunky stripe against the navy and competes with the content it scrolls. Now thin and hairline-coloured — scrollbar-width/scrollbar-color for Firefox AND ::-webkit-scrollbar (8px, thumb --line, hover --text-3, --radius) for WebKit/Blink, because neither alone covers the browsers customers use. The two surfaces that actually scroll take their own panel background as the track (.sidebar -> --bg-2, html -> --bg-0) so the gutter never shows as a lighter channel. Tokens only, no raw hexes. Collapsible nav groups (layout.html + style.css, vanilla JS, no framework): Tárhely, Biztonsági mentés and Megosztás become accordions with a chevron; exactly one open at a time, clicking the open one closes it. Groups without sub-items are untouched plain links. Hungarian labels unchanged. - The header is a real <button>, so keyboard and AT reachability come for free instead of being simulated with tabindex/role on a div. aria-expanded + aria-controls + a :focus-visible outline. - Nothing became unreachable when the header stopped being a link: every group's landing page is ALSO its first sub-item (/storage -> Meghajtók, /backups -> Áttekintés, /sharing -> Hálózati megosztás). Checked before the conversion, not assumed. - Progressive enhancement: the group containing the active page is rendered open SERVER-SIDE (.is-open), so the right group is open before any JS runs and stays open if JS never does. The listener only handles clicks. - No layout jump: collapse animates grid-template-rows 0fr -> 1fr (with min-height:0 + overflow:hidden on the sub-list) rather than max-height, so it animates to the content's REAL height and there is no magic number to drift when a group gains or loses an item — the specific way a max-height accordion rots. The toggle reserves its 3px active border as transparent so becoming active adds no width shift. .18s transitions, and both the collapse and the chevron rotation are disabled under prefers-reduced-motion. Pre-existing and deliberately NOT bundled: docker_run_volume_path_gate.py still fails on internal/appexport/estimate.go:179. That is ROADMAP R-29, unrelated to this change, verified to fail identically on the untouched tree, and R-29 itself says not to bundle it into an unrelated feature commit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nn3VgQk9iwEGgyx6QJ2NvE
This commit is contained in:
@@ -40,6 +40,23 @@
|
||||
if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();}
|
||||
});
|
||||
});
|
||||
/* Collapsible sidebar groups (v0.146.0). Progressive enhancement: the server already
|
||||
renders the group containing the active page with .is-open, so the correct group is
|
||||
open before this runs and stays open if JS never does. Exactly one group open at a
|
||||
time; clicking the open one closes it. */
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null;
|
||||
if(!btn)return;
|
||||
var group=btn.closest('.nav-group');
|
||||
if(!group)return;
|
||||
var willOpen=!group.classList.contains('is-open');
|
||||
document.querySelectorAll('.nav-group.is-open').forEach(function(g){
|
||||
g.classList.remove('is-open');
|
||||
var t=g.querySelector('.nav-group-toggle');
|
||||
if(t)t.setAttribute('aria-expanded','false');
|
||||
});
|
||||
if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
||||
});
|
||||
function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Üzenet</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-2);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);}
|
||||
</script>
|
||||
</head>
|
||||
@@ -53,22 +70,28 @@
|
||||
<ul class="nav-links">
|
||||
<li><a href="/" class="{{if eq .Page "dashboard"}}active{{end}}"><svg class="ico"><use href="#i-layout-grid"/></svg>Vezérlőpult</a></li>
|
||||
<li><a href="/stacks" class="{{if eq .Page "stacks"}}active{{end}}"><svg class="ico"><use href="#i-cloud"/></svg>Alkalmazások</a></li>
|
||||
<li><a href="/storage" class="{{if or (eq .Page "storage") (eq .Page "storage-network")}}active{{end}}"><svg class="ico"><use href="#i-hard-drive"/></svg>Tárhely</a>
|
||||
<ul class="nav-links nav-links-sub nav-links-nested">
|
||||
{{$storageOpen := or (eq .Page "storage") (eq .Page "storage-network")}}
|
||||
<li class="nav-group{{if $storageOpen}} is-open{{end}}">
|
||||
<button type="button" class="nav-group-toggle{{if $storageOpen}} active{{end}}" aria-expanded="{{if $storageOpen}}true{{else}}false{{end}}" aria-controls="nav-group-storage"><svg class="ico"><use href="#i-hard-drive"/></svg>Tárhely<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-storage" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/storage" class="{{if eq .Page "storage"}}active{{end}}">Meghajtók</a></li>
|
||||
<li><a href="/storage/network" class="{{if eq .Page "storage-network"}}active{{end}}">Hálózati tárhely</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/backups" class="{{if or (eq .Page "backups") (eq .Page "backups-remote") (eq .Page "backups-apps") (eq .Page "backups-restore")}}active{{end}}"><svg class="ico"><use href="#i-shield"/></svg>Biztonsági mentés</a>
|
||||
<ul class="nav-links nav-links-sub nav-links-nested">
|
||||
{{$backupsOpen := or (eq .Page "backups") (eq .Page "backups-remote") (eq .Page "backups-apps") (eq .Page "backups-restore")}}
|
||||
<li class="nav-group{{if $backupsOpen}} is-open{{end}}">
|
||||
<button type="button" class="nav-group-toggle{{if $backupsOpen}} active{{end}}" aria-expanded="{{if $backupsOpen}}true{{else}}false{{end}}" aria-controls="nav-group-backups"><svg class="ico"><use href="#i-shield"/></svg>Biztonsági mentés<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-backups" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/backups" class="{{if eq .Page "backups"}}active{{end}}">Áttekintés</a></li>
|
||||
<li><a href="/backups/remote" class="{{if eq .Page "backups-remote"}}active{{end}}">Távoli mentés</a></li>
|
||||
<li><a href="/backups/apps" class="{{if eq .Page "backups-apps"}}active{{end}}">Alkalmazások</a></li>
|
||||
<li><a href="/backups/restore" class="{{if eq .Page "backups-restore"}}active{{end}}">Visszaállítás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/sharing" class="{{if eq .Page "sharing"}}active{{end}}"><svg class="ico"><use href="#i-share"/></svg>Megosztás</a>
|
||||
<ul class="nav-links nav-links-sub nav-links-nested">
|
||||
{{$sharingOpen := eq .Page "sharing"}}
|
||||
<li class="nav-group{{if $sharingOpen}} is-open{{end}}">
|
||||
<button type="button" class="nav-group-toggle{{if $sharingOpen}} active{{end}}" aria-expanded="{{if $sharingOpen}}true{{else}}false{{end}}" aria-controls="nav-group-sharing"><svg class="ico"><use href="#i-share"/></svg>Megosztás<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-sharing" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/sharing" class="{{if eq .Page "sharing"}}active{{end}}">Hálózati megosztás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -3309,6 +3309,98 @@ span.mono, .mono { font-family: var(--font-data); }
|
||||
border-left: 3px solid var(--blue);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
Nav polish (v0.146.0) — scrollbars + collapsible sidebar groups.
|
||||
============================================================================ */
|
||||
|
||||
/* Scrollbars. The platform default is a light, chunky bar that reads as a bright
|
||||
stripe against the navy and competes with the content it is scrolling. Thin +
|
||||
hairline-coloured puts it back in the background where it belongs. Firefox gets
|
||||
the standard two-value form; WebKit/Blink the pseudo-elements. Both are declared
|
||||
because neither alone covers the browsers customers actually use. */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: var(--line) transparent;
|
||||
}
|
||||
*::-webkit-scrollbar { width: 8px; height: 8px; }
|
||||
*::-webkit-scrollbar-track { background: transparent; }
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background: var(--line);
|
||||
border-radius: var(--radius);
|
||||
}
|
||||
*::-webkit-scrollbar-thumb:hover { background: var(--text-3); }
|
||||
/* Track takes the surrounding panel's own background, so the gutter never shows
|
||||
through as a lighter channel on the two surfaces that actually scroll. */
|
||||
.sidebar { scrollbar-color: var(--line) var(--bg-2); }
|
||||
.sidebar::-webkit-scrollbar-track { background: var(--bg-2); }
|
||||
html { scrollbar-color: var(--line) var(--bg-0); }
|
||||
html::-webkit-scrollbar-track { background: var(--bg-0); }
|
||||
|
||||
/* Collapsible nav groups. The row is a real <button> (keyboard + AT reachable for
|
||||
free); every group's own landing page is also its first sub-item, so nothing
|
||||
became unreachable when the header stopped being a link.
|
||||
|
||||
The open/close uses grid-template-rows 0fr -> 1fr rather than max-height: it
|
||||
animates to the content's REAL height, so there is no magic number to drift and
|
||||
no jump when a group's item count changes. */
|
||||
.nav-group {
|
||||
display: grid;
|
||||
grid-template-rows: auto 0fr;
|
||||
transition: grid-template-rows .18s ease;
|
||||
}
|
||||
.nav-group.is-open { grid-template-rows: auto 1fr; }
|
||||
.nav-group > .nav-links-nested {
|
||||
overflow: hidden;
|
||||
min-height: 0; /* required, or the grid row refuses to collapse below content height */
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.nav-group { transition: none; }
|
||||
}
|
||||
|
||||
/* The toggle inherits the nav link's look exactly — it must not read as a form
|
||||
control sitting in a list of links. */
|
||||
.nav-group-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .6rem;
|
||||
width: 100%;
|
||||
padding: .75rem 1.5rem;
|
||||
background: none;
|
||||
border: none;
|
||||
border-left: 3px solid transparent; /* reserved so .active adds no width shift */
|
||||
color: var(--text-2);
|
||||
font-family: inherit;
|
||||
font-size: .95rem;
|
||||
font-weight: 500;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease, background 0.2s ease;
|
||||
}
|
||||
.nav-group-toggle:hover {
|
||||
color: var(--blue-bright);
|
||||
background: var(--blue-dim);
|
||||
}
|
||||
.nav-group-toggle.active {
|
||||
color: var(--blue-bright);
|
||||
background: var(--blue-dim);
|
||||
border-left-color: var(--blue);
|
||||
}
|
||||
.nav-group-toggle:focus-visible {
|
||||
outline: 2px solid var(--blue-bright);
|
||||
outline-offset: -2px;
|
||||
}
|
||||
.nav-chevron {
|
||||
margin-left: auto;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: var(--text-3);
|
||||
transition: transform .18s ease;
|
||||
}
|
||||
.nav-group.is-open .nav-chevron { transform: rotate(180deg); }
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.nav-chevron { transition: none; }
|
||||
}
|
||||
|
||||
/* Unified drive view enrichment slot — breathing room between the agent tag row,
|
||||
the agent action row, and the registry action row below. */
|
||||
.drive-agent-extra {
|
||||
|
||||
Reference in New Issue
Block a user