Skip to content

Spec: consolidate edition chrome — identity row, auto-hiding bottom nav, sticky schedule headers #231

Description

@chiptus

Problem Statement

On mobile, a festival-goer opening an edition's Schedule tab has to scroll past five stacked boxes — hero title block, phase banner, timezone bar, view switcher, and a filters card or toolbar — rendered in four different surface styles before seeing a single set. Chrome eats most of the first screen, the sticky bars slide behind the fixed top bar while scrolling, and the list view's day header un-sticks after about one screen because it lives inside the day's first time-slot group. The amber timezone bar takes a full row to state something that is now true everywhere (times are shown in the festival timezone) and no longer needs flagging.

Solution

A content-first consolidation of the edition chrome, validated through a multi-variant prototype (draft PR #216, winning variant: "autohide"):

  • The hero title block becomes a slim identity row (small logo + festival/edition name), with a pulsing "Live" dot when the festival phase is Live.
  • The boxed phase banner and the timezone bar are removed.
  • The Now / Timeline / List view switcher stays a segmented control at the top, identical on all three schedule views. It is not sticky — it scrolls away with the content; only the day toolbar, the timeline header strip, and the list day header stick.
  • On mobile, the bottom tab navigation auto-hides while scrolling down and returns on scroll-up or near the top, giving content the whole screen.
  • The timeline's date band + hour scale becomes a sticky strip that stays readable while scrolling stages; the list view groups sets per festival day under a day-spanning sticky header that also hosts the Filters trigger.
  • All sticky bars offset correctly below the fixed top bar.

User Stories

  1. As a mobile festival-goer, I want the schedule content to start near the top of the screen, so that I see sets instead of chrome when I open the Schedule tab.
  2. As a festival-goer, I want a slim identity row with the festival logo and edition name, so that I always know which edition I'm looking at without a full hero block.
  3. As a festival-goer during the festival, I want a pulsing "Live" indicator in the identity row, so that I can tell the edition is currently live at a glance.
  4. As a festival-goer, I want set times shown without a timezone warning bar, so that my screen isn't spent on a notice about behavior that is now consistent everywhere (all times render in the festival timezone).
  5. As a festival-goer, I want the Now / Timeline / List switcher present as a segmented control at the top of the schedule (not hidden behind a dropdown or menu; it scrolls away with content rather than sticking), so that I can see all available schedule views at a glance.
  6. As a festival-goer, I want the view switcher to look and sit identically on Now, Timeline, and List, so that the navigation never shifts shape when I change views.
  7. As a mobile festival-goer, I want the bottom tab bar to slide away while I scroll down, so that the schedule gets the full screen height.
  8. As a mobile festival-goer, I want the bottom tab bar to return the moment I scroll up or reach the top, so that switching tabs never requires hunting for hidden navigation.
  9. As a mobile festival-goer, I want the auto-hide behavior to work the same on every edition tab and every screen size, so that the app feels consistent rather than mode-dependent.
  10. As a festival-goer on the timeline view, I want the date band and hour scale to stay pinned while I scroll down through stages, so that I always know what time a set block sits at.
  11. As a festival-goer on the timeline view, I want the pinned hour scale to track my horizontal scrolling, so that the scale always matches the sets under it.
  12. As a festival-goer on the timeline view, I want the sticky header strip to keep a constant opaque background, so that set blocks never show through it while scrolling.
  13. As a festival-goer on the list view, I want sets grouped per festival day, so that a day's schedule reads as one continuous section.
  14. As a festival-goer on the list view, I want the day header to stay stuck for the entire day I'm scrolling through, so that I always know which day the sets belong to (fixing the bug where it un-stuck after one screen).
  15. As a festival-goer on the list view, I want post-midnight sets grouped under the festival's calendar day per the festival timezone, so that day grouping matches the printed program.
  16. As a festival-goer on the list view, I want the Filters trigger inside the sticky day header, so that filtering is always one tap away without a dedicated filters row.
  17. As a festival-goer, I want the filter sheet to keep working exactly as before (stages, days, times, votes), so that the redesign doesn't cost me any filtering ability.
  18. As a festival-goer, I want sticky bars to dock below the fixed top bar instead of sliding behind it, so that no control is ever half-hidden.
  19. As a festival-goer scrolling the timeline, I want the timeline unboxed (no framing panel), so that the schedule uses the full content width.
  20. As a desktop festival-goer, I want the same identity row and consolidated schedule chrome, so that the experience matches mobile (desktop keeps its top tab navigation; nothing hides).
  21. As a group member coordinating with friends, I want voting and group features untouched by this redesign, so that our planning workflow keeps working.
  22. As a festival-goer using a screen reader, I want the Live indicator and the Filters trigger properly labeled, so that the compact chrome remains accessible.
  23. As a Core Team member, I want the admin surfaces unaffected, so that curating the lineup and schedule works as before.
  24. As a developer, I want the prototype scaffolding (variant provider, switcher pill, driver script, prototype-only conditionals) fully removed, so that the codebase carries only the winning design.
  25. As a future maintainer, I want ADR-0002 amended and the glossary's Schedule entry updated, so that the docs match what the app actually does.

Implementation Decisions

Decisions from the grilling session (full iteration history in draft PR #216 and its session log):

  • Winning design: prototype variant "autohide", implemented cleanly as the real design — no variant gates. Draft PR style(schedule): add chrome density prototype variants #216 is never merged; it gets closed with a comment linking the successor PR once open.
  • Identity row replaces the hero on all edition tabs (Vote/Lineup, Schedule, Explore, Info, …) — it lives in the shared edition layout. Contents: small logo (fallback icon), truncating title, and when the festival phase is Live, a pulsing red dot + "Live" text in the right slot. All other phases show nothing for now — non-live copy is an open designer decision (Decide non-live festival phase copy for the edition identity row #229).
  • Tickets/Website buttons are removed from the header; their new home is the Info tab (Move Tickets/Website links into the Info tab #228).
  • The phase banner and the festival-time badge are removed as standalone chrome. Removing the badge is safe because every schedule surface (list, timeline, day-jump) now renders in the festival timezone — the ADR-0002 consequence about a "prominent badge" and its stale note about the timeline being viewer-local must be amended in the same change.
  • Bottom tab navigation auto-hide: applies on all edition tabs and all mobile viewport sizes (no gating by screen size or by view — one behavior, per the "chrome must not morph" principle). Hides on scroll-down past a small threshold, returns on any scroll-up or near the top. The prototype's tuned values — activation threshold ~80px from top, ~4px jitter guard — carry over (from the prototype).
  • View switcher: the current-style segmented Now / Timeline / List control at the top, identical across the three schedule views. It is not sticky — it scrolls off with the content; the sticky elements are the timeline day toolbar, the timeline header strip, and the list day header, all docking below the fixed top bar. A dropdown switcher was explicitly rejected (hides the views); per-view morphing bars were explicitly rejected (breaks the anchor).
  • Sticky timeline header strip: the date band + hour scale is lifted out of the horizontal scroller into a sticky strip below the toolbar, synced to horizontal scroll via a transform (translateX(-scrollLeft), from the prototype). It keeps a constant dark background — no "gain background only when stuck" state.
  • Sticky offsets derive from shared constants (top-bar height, toolbar height) instead of scattered per-component magic numbers. The prototype's empirically derived geometry (margin-collapse–dependent spacing in the timeline) is re-expressed explicitly.
  • List view groups sets per festival day (day boundaries computed in the festival timezone). The sticky day header spans the entire day's section and hosts the Filters trigger; the standalone filters row/card is removed. The old flat list rendering is removed.
  • No schema, API, or data-fetching changes. This is presentation-layer only.
  • Docs updated in the same change: ADR-0002 amendment (above) and the CONTEXT.md Schedule entry gains the Now view ("Presented to users via the Now, Timeline, and List views on the Schedule tab").
  • Repo conventions apply: function declarations, query/mutation hook naming, no barrel exports, minimal comments, react-hook-form for any forms, cn for conditional classes.

Testing Decisions

  • Single seam: the Playwright e2e page level (existing seam, highest available). Tests assert only user-observable behavior — what is visible, stuck, hidden, or tappable — never hook internals or class names. No unit-test seam for the scroll-direction logic; its behavior is fully observable through the page.
  • Existing specs are the regression suite: the schedule filter sheet, timeline day toolbar, timeline scroll/now-indicator/overview, and navigation specs must keep passing. The test ids they rely on (the day toolbar and filter-sheet ids) are contractual and must be preserved.
  • New e2e scenarios: (1) mobile bottom bar hides after scrolling down and returns on scroll-up / near-top; (2) the list view's day header remains stuck across an entire festival day's sets and the next day's header replaces it; (3) the filter sheet opens from the trigger inside the sticky day header.
  • Prior art: the existing tests/e2e schedule specs (local Supabase, seeded data, mobile viewport projects) are the pattern to follow.
  • Type-checking via the project script (pnpm run typecheck) is part of done — it is stricter than bare tsc --noEmit.

Out of Scope

Further Notes

  • The prototype lives on branch claude/frontend-design-layout-br8y1f (draft PR style(schedule): add chrome density prototype variants #216) with the iteration log in the prototype folder's NOTES.md; record the verdict there before deleting the folder, per its own instruction.
  • Deletions the fold-in must include: the prototype folder, the throwaway screenshot driver script at the repo root, every prototype-marked conditional in real components, the festival-time badge component, and the list view's standalone filter container usage.
  • The successor PR must follow the repo's create-pr skill exactly (commitlint title, 1–2 line description, verification section).

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions