Video Discover: sticky 'jump to section' nav for the deep rail stack

The page is now a long stack of rail groups (For you / Top 10 / New /
Trending / Mood / Studios / Genres / More). A sticky chip bar lets you
jump straight to any section — Netflix/Disney-style category nav. Chips
stay in lockstep with the groups: async-only groups (foryou/collection/
taste) reveal their chip when filled; pruned groups hide theirs. Lives
inside the shelves host so it auto-hides in the Browse-all grid view.
This commit is contained in:
BoulderBadgeDad 2026-06-23 23:58:53 -07:00
parent a275cbfb7a
commit 24b79dc29e
2 changed files with 54 additions and 4 deletions

View file

@ -543,7 +543,18 @@
}
function renderShelves() {
var host = $('[data-vdsc-shelves]'); if (!host) return;
host.innerHTML = buildSections().map(function (sec) {
var sections = buildSections();
// A sticky "jump to section" bar (Netflix/Disney pattern) — the page is a deep
// stack of rails, so quick navigation makes it far easier to browse. Chips for
// async-only groups (foryou/collection/taste) start hidden, revealed when filled;
// chips for groups that prune away hide in lockstep (see reveal/pruneGroup).
var nav = '<nav class="vdsc-jumpnav" data-vdsc-jumpnav aria-label="Jump to section">' +
sections.map(function (sec) {
var hideCls = sec.rails.length ? '' : ' hidden';
return '<button class="vdsc-jump' + hideCls + '" type="button" data-jump="' +
sec.id + '">' + esc(sec.label) + '</button>';
}).join('') + '</nav>';
host.innerHTML = nav + sections.map(function (sec) {
var rails = sec.rails.map(lazyShelfHtml).join('');
// Groups with no static rails (async-only, e.g. gaps) start hidden — revealed when filled.
var emptyCls = sec.rails.length ? '' : ' vdsc-group--empty';
@ -552,16 +563,35 @@
'<div class="vdsc-group-body" data-group-body="' + sec.id + '">' + rails + '</div>' +
'</section>';
}).join('');
wireJumpNav(host);
observeShelves();
}
// Reveal a group's header once an async loader has put rails in it.
// Smooth-scroll to a group when its jump chip is clicked (delegated, wired once).
function wireJumpNav(host) {
var nav = $('[data-vdsc-jumpnav]', host);
if (!nav || nav.getAttribute('data-wired')) return;
nav.setAttribute('data-wired', '1');
nav.addEventListener('click', function (e) {
var btn = e.target.closest('[data-jump]'); if (!btn) return;
var g = $('[data-group="' + btn.getAttribute('data-jump') + '"]');
if (g) { try { g.scrollIntoView({ behavior: 'smooth', block: 'start' }); } catch (x) { g.scrollIntoView(); } }
});
}
// Reveal a group's header once an async loader has put rails in it (+ its jump chip).
function revealGroup(id) {
var g = $('[data-group="' + id + '"]');
if (g) g.classList.remove('vdsc-group--empty');
var chip = $('[data-jump="' + id + '"]');
if (chip) chip.classList.remove('hidden');
}
// Hide a group whose body has no shelves left (every rail failed / returned nothing).
// Hide a group whose body has no shelves left (every rail failed / returned nothing) + its chip.
function pruneGroup(g) {
if (g && !g.querySelector('.vdsc-shelf')) g.classList.add('vdsc-group--empty');
if (g && !g.querySelector('.vdsc-shelf')) {
g.classList.add('vdsc-group--empty');
var id = g.getAttribute('data-group');
var chip = id && $('[data-jump="' + id + '"]');
if (chip) chip.classList.add('hidden');
}
}
function observeShelves() {
if (state.io) state.io.disconnect();

View file

@ -2303,6 +2303,26 @@ body[data-side="video"] #soulsync-toggle { display: none; }
A group with no rails (async-only, not yet filled, or all rails dropped) is hidden entirely. */
.vdsc-group { margin: 0; }
.vdsc-group--empty { display: none; }
/* ── sticky 'jump to section' bar — quick nav across the deep rail stack ──────── */
.vdsc-jumpnav {
position: sticky; top: 0; z-index: 6;
display: flex; gap: 8px; flex-wrap: nowrap; overflow-x: auto;
margin: 0 0 6px; padding: 11px 2px;
background: linear-gradient(180deg, rgba(10, 11, 16, 0.94) 35%, rgba(10, 11, 16, 0.55) 80%, transparent);
-webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
scrollbar-width: none;
}
.vdsc-jumpnav::-webkit-scrollbar { display: none; }
.vdsc-jump {
flex: 0 0 auto; padding: 7px 15px; border-radius: 999px; cursor: pointer;
border: 1px solid rgba(255, 255, 255, 0.12); background: rgba(255, 255, 255, 0.05);
color: rgba(255, 255, 255, 0.78); font-size: 12.5px; font-weight: 700; white-space: nowrap;
transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}
.vdsc-jump:hover { background: rgba(var(--accent-rgb, 88 101 242), 0.85); border-color: transparent; color: #fff; }
.vdsc-jump.hidden { display: none; }
.vdsc-group-head { font-size: 12.5px; font-weight: 800; letter-spacing: 0.5px; text-transform: uppercase;
color: rgba(255, 255, 255, 0.5); margin: 30px 0 16px; padding-bottom: 9px;
border-bottom: 1px solid rgba(255, 255, 255, 0.07); }