feat: add 10 UX improvements from interface review
CI / Lint (push) Failing after 10s
CI / Test (push) Has been skipped
CI / Build & Push (push) Has been skipped

- Stale data banner after 3 consecutive fetch failures, auto-clears on recovery
- Date navigation with left/right arrows (Yesterday/Today/Tomorrow labels),
  fetches from NHL API for non-today dates, disables auto-refresh on history
- Empty state message when no games are scheduled
- Series detail page auto-refreshes every 30s when a game is live
- Notification permission deferred until a playoff OT actually occurs
- Scroll position saved/restored when navigating to/from series detail
- Team records rendered with better contrast and tabular nums
- Active bracket round highlighted with gold heading + underline,
  completed rounds dimmed more aggressively, mobile accordion auto-opens
  current round
- Browser tab title shows live game count (e.g. "NHL Scoreboard (3 Live)")
- Service worker update shows a dismissable toast instead of force-reloading

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-23 20:22:03 -04:00
parent 58b27ddd20
commit 2da60e27ae
8 changed files with 313 additions and 39 deletions
+8 -8
View File
@@ -21,31 +21,31 @@
{# Desktop: 7-column grid (East R1 | R2 | CF | Cup | CF | R2 | R1 West) #}
<section class="bracket-grid" aria-label="Full playoff bracket">
<div class="bracket-col bracket-col-r1 bracket-col-east">
<h2 class="bracket-col-heading">First Round</h2>
<h2 class="bracket-col-heading {% if bracket.current_round == 1 %}bracket-col-active{% endif %}">First Round</h2>
{% for m in bracket.east_r1 %}{% include "_bracket_matchup.html" %}{% endfor %}
</div>
<div class="bracket-col bracket-col-r2 bracket-col-east">
<h2 class="bracket-col-heading">Second Round</h2>
<h2 class="bracket-col-heading {% if bracket.current_round == 2 %}bracket-col-active{% endif %}">Second Round</h2>
{% for m in bracket.east_r2 %}{% include "_bracket_matchup.html" %}{% endfor %}
</div>
<div class="bracket-col bracket-col-cf bracket-col-east">
<h2 class="bracket-col-heading">East Final</h2>
<h2 class="bracket-col-heading {% if bracket.current_round == 3 %}bracket-col-active{% endif %}">East Final</h2>
{% for m in bracket.east_cf %}{% include "_bracket_matchup.html" %}{% endfor %}
</div>
<div class="bracket-col bracket-col-cup">
<h2 class="bracket-col-heading bracket-cup-heading">Cup Final</h2>
<h2 class="bracket-col-heading bracket-cup-heading {% if bracket.current_round == 4 %}bracket-col-active{% endif %}">Cup Final</h2>
{% for m in bracket.cup %}{% include "_bracket_matchup.html" %}{% endfor %}
</div>
<div class="bracket-col bracket-col-cf bracket-col-west">
<h2 class="bracket-col-heading">West Final</h2>
<h2 class="bracket-col-heading {% if bracket.current_round == 3 %}bracket-col-active{% endif %}">West Final</h2>
{% for m in bracket.west_cf %}{% include "_bracket_matchup.html" %}{% endfor %}
</div>
<div class="bracket-col bracket-col-r2 bracket-col-west">
<h2 class="bracket-col-heading">Second Round</h2>
<h2 class="bracket-col-heading {% if bracket.current_round == 2 %}bracket-col-active{% endif %}">Second Round</h2>
{% for m in bracket.west_r2 %}{% include "_bracket_matchup.html" %}{% endfor %}
</div>
<div class="bracket-col bracket-col-r1 bracket-col-west">
<h2 class="bracket-col-heading">First Round</h2>
<h2 class="bracket-col-heading {% if bracket.current_round == 1 %}bracket-col-active{% endif %}">First Round</h2>
{% for m in bracket.west_r1 %}{% include "_bracket_matchup.html" %}{% endfor %}
</div>
</section>
@@ -53,7 +53,7 @@
{# Mobile: round-by-round accordion, round 1 open by default #}
<section class="bracket-accordion" aria-label="Playoff bracket by round">
{% for rnd in bracket.rounds %}
<details class="bracket-round" {% if loop.first %}open{% endif %}>
<details class="bracket-round" {% if rnd.round_num == bracket.current_round or (bracket.current_round is none and loop.first) %}open{% endif %}>
<summary class="bracket-round-summary">{{ rnd.label }}</summary>
<div class="bracket-round-body">
{% if rnd.get('east') %}
+10
View File
@@ -15,6 +15,11 @@
<body>
<header>
<span class="header-title">NHL Scoreboard</span>
<nav class="date-nav" aria-label="Date navigation">
<button id="date-prev" class="date-btn" aria-label="Previous day">&larr;</button>
<span id="date-label" class="date-label"></span>
<button id="date-next" class="date-btn" aria-label="Next day">&rarr;</button>
</nav>
</header>
<section id="playoff-banner" class="playoff-banner hidden" aria-hidden="true">
<a class="banner-main" href="/bracket" aria-label="View the playoff bracket">
@@ -45,7 +50,12 @@
</div>
</a>
</section>
<div id="stale-banner" class="stale-banner hidden">Connection lost &mdash; scores may be outdated</div>
<main>
<div id="empty-state" class="empty-state hidden">
<p class="empty-state-heading">No games scheduled today</p>
<p class="empty-state-sub">Check back tomorrow</p>
</div>
<section id="pinned-section" class="section pinned-section hidden">
<h2 class="section-heading section-heading-gold">Spotlight &middot; Game 7</h2>
<div id="pinned-games-section" class="games-grid"></div>
+1
View File
@@ -7,6 +7,7 @@
<link rel="manifest" href="/manifest.json">
<link rel="icon" type="image/png" href="{{ static_v('icon-32x32.png') }}">
<link rel="stylesheet" type="text/css" href="{{ static_v('styles.css') }}">
{% if series.has_live %}<meta http-equiv="refresh" content="30">{% endif %}
</head>
<body class="playoff-mode series-mode">
<header class="series-header">