fix(web): drop back button from movie and series pages (#229)

The rail, browser back, and Esc already cover navigation — the back
button was a third way to do what two other things already do, and it
cost the page its first line.

- Remove #movie-back and #series-back buttons and their event listeners
- Focus lands on the title element (tabindex=-1) instead of the removed
  button on open
- TV releases back button is untouched
- Hero banner now meets the rail with zero top padding on movie/series
- Esc and parent-route behaviour unchanged
- Deep link fallback for TV releases uses #nav-library instead of the
  removed #series-back
This commit is contained in:
Miguel Palhas
2026-08-25 10:40:37 +01:00
parent 8c3e1c4a92
commit 815a072ef0
3 changed files with 15 additions and 19 deletions
+7 -11
View File
@@ -393,14 +393,14 @@ function main() {
must<HTMLElement>("#library"),
{ kind: "library" },
);
// no origin click to restore focus to on a deep link — the series
// view's back control is the closest stand-in
// no origin click to restore focus to on a deep link — the library
// rail button is the closest stand-in
tvDeck.open({
title,
sub,
seriesId,
target,
origin: must<HTMLButtonElement>("#series-back"),
origin: must<HTMLElement>("#nav-library"),
returnTo: must<HTMLElement>("#series"),
parentRoute: { kind: "series", seriesId },
});
@@ -1294,7 +1294,6 @@ function externalLink(label: string, href: string): HTMLAnchorElement {
function movieMain(views: HideableView[]): MovieView {
const view = must<HTMLElement>("#movie");
const deckEl = must<HTMLElement>("#deck");
const back = must<HTMLButtonElement>("#movie-back");
const statusEl = must<HTMLElement>("#movie-status");
const hero = must<HTMLElement>("#movie-hero");
const poster = must<HTMLImageElement>("#movie-poster");
@@ -1906,7 +1905,8 @@ function movieMain(views: HideableView[]): MovieView {
filesSection.hidden = true;
diskRows.replaceChildren();
sweep.disabled = false;
back.focus();
titleEl.setAttribute("tabindex", "-1");
titleEl.focus();
await load();
}
@@ -1929,8 +1929,6 @@ function movieMain(views: HideableView[]): MovieView {
target?.focus();
}
back.addEventListener("click", close);
// capture + stopImmediatePropagation: one Escape steps back one layer —
// the library and search decks also listen for Escape on this window
window.addEventListener(
@@ -3125,7 +3123,6 @@ const SERIES_REFRESH_WAIT_MS = 30_000;
function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView {
const view = must<HTMLElement>("#series");
const deckEl = must<HTMLElement>("#deck");
const back = must<HTMLButtonElement>("#series-back");
const hero = must<HTMLElement>("#series-hero");
const poster = must<HTMLImageElement>("#series-poster");
const titleEl = must<HTMLElement>("#series-title");
@@ -3793,7 +3790,8 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView {
view.hidden = false;
clearRemove();
clearRichDetail();
back.focus();
titleEl.setAttribute("tabindex", "-1");
titleEl.focus();
await load();
}
@@ -3814,8 +3812,6 @@ function seriesMain(tvDeck: TvReleasesView, views: HideableView[]): SeriesView {
origin?.focus();
}
back.addEventListener("click", close);
// capture, like every other layer: the tv deck's listener is registered
// first, so one Esc steps back one layer
window.addEventListener(
+6
View File
@@ -528,6 +528,12 @@ body {
flex: 1;
}
/* movie and series: the hero banner meets the rail — no top padding (#229) */
.deck#movie,
.deck#series {
padding-top: 0;
}
.deck-status {
margin: 0 0 var(--space-4);
color: var(--ink-muted);