Skip to content

Latest commit

 

History

History
357 lines (317 loc) · 22.5 KB

File metadata and controls

357 lines (317 loc) · 22.5 KB

Headway project notes

Updated 2026-09-24. This records the current design decisions and session results; older mockups and QA captures are historical references, not the current specification.

Release validation — 2026-09-24

  • Release scope: upcoming-only departures, official scroll fades, drawer exit timing, simplified footer with refresh feedback, the refined station picker, and smaller line initials on solid discs.
  • Preserved the remote weekly GTFS update (f27b87b) by merging it into the release branch; no new timetable import was run for this UI work.
  • Final merged tree passed all 162 tests across 23 files and the production Vite/TypeScript build. QA fixtures remain excluded from production output.
  • Final mobile preview checks at 320px and 390px confirmed single-line names and stationary search focus with a subtle divider. Solid badges were checked on the departure board. Physical iPhone keyboard, VoiceOver, and fast-flick scrolling checks remain outstanding.
  • Release 2fb1c8d was pushed to develop and main and deployed successfully in Fly Deploy run 35973945241. Production health returned 200; Corona returned 15 upcoming departures across two platforms. The served stylesheet matched the local production build, and both WOFF2 files returned 200 with immutable caching.
  • After the existing PWA installed its update, a second reload loaded the new bundle. Live 390px checks confirmed the new footer, successful refresh toast, dark-mode picker, distinct Churchill search results, and unchanged drawer height on search focus. This documentation-only follow-up does not change the deployed application. Earlier release records below are historical.

Line badge refinement — 2026-09-24

  • Retained solid line-colour discs after trying transparent, coloured outlines. The solid treatment better preserves the transit-signage reference and makes the line colours easier to recognise.
  • Reduced initials by 1px and changed bold (700) to semibold (600): 11px in departure rows and 12px in header filters. Circle dimensions are unchanged. Capital and Valley retain white letters; Metro retains dark letters.
  • Checked the final solid treatment in the local board. The outlined experiment is not part of this release.

Station picker refinement — 2026-09-24

  • Replaced the boxed search and line legend with a full-width “Search stations” row and a thin divider. Focus changes the divider colour rather than drawing an outline or ring. Focusing search no longer expands the drawer; manual dragging between snap points remains available.
  • One unlabelled list shows favourites first, then other stations by distance or alphabetically without location. Each station appears once, including in search results. Filled stars identify favourites. Accessible list names, result counts, clear search, and empty-state feedback remain.
  • Station rows use the existing Geist/Geist Mono pairing, a 56px minimum height, and shared columns for names, distances, and stars. Line badges are omitted at every width to prioritise readable station names; accessible line descriptions remain. Names never wrap: concise display labels and ellipsis handle overflow. All stations share column widths and one scrolling area.
  • Current selection uses a left edge bar, check, and modest semibold name with no permanent background fill. Favourite stars use outline/filled foreground styling and keep separate 44px touch targets. Hover feedback spans the entire row, including gutters and the favourite control; each button stays transparent and retains its own keyboard focus outline.
  • Concise display names include “Bay / Enterprise”, “Kingsway / Royal Alex”, “Health Sciences”, “South Campus”, and “NAIT / Blatchford”. “Churchill” identifies the underground station; “Churchill · Valley” identifies the surface stop. Search still matches full feed names, which remain available as accessible labels and titles. IDs and selection data are unchanged.
  • Distances remain straight-line estimates with no repeated “away” suffix. Without device location, distances remain hidden and non-favourites sort alphabetically.
  • The local picker preview renders the real component with current station data and a fixed example location near Corona. It is development-only and excluded from production and coverage, like the departure typography fixture. Check 320px/390px names, stationary search focus, favourites, selection, and mobile/desktop themes before changing this layout.
  • Validation: all 162 tests and production build passed; browser checks covered 320px/390px layouts, light/dark themes, alias search, 44px favourite targets, independent favourite toggling, and selecting a station. All station names fit untruncated at 390px. At 320px, Kingsway and Millbourne/Woodvale use ellipsis; every row stays on one line at both widths. Physical-device QA remains open.

Footer and refresh feedback — 2026-09-24

  • Removed the scheduled-times/updated-at footer block. The countdown clock runs every second against the loaded schedule; the old fetch timestamp could make valid departures appear stale. The schedule-only explanation remains in About.
  • Headway and a small information icon form one About button at the left, labelled “About Headway” for assistive technology. Its bordered cell and hover and focus feedback reinforce the interaction. Theme and refresh remain at the right, using shared 48px-wide footer button styles.
  • Refresh preserves the visible departure board, spins and disables the refresh button while fetching, and confirms success with a three-second “Departures refreshed.” toast. Failed requests retain the board and existing error feedback without showing a success toast. Duplicate requests are blocked.
  • Added the official shadcn Base UI toast, with square corners and placement above the footer and phone safe area. Verified mobile light/dark appearance, About opening, success feedback, all 160 tests, and the production build.

Scroll fade and drawer follow-up — 2026-09-24

  • Departure viewports now use the official shadcn/tailwind.css utilities: scroll-fade-b scroll-fade-8, replacing the custom bottom mask. The fade is disabled for reduced motion. ScrollArea accepts viewportClassName so the utility applies to the actual scroller. Shadcn is a development dependency; only its generated CSS ships to the browser.
  • The bottom drawer adopts the current shadcn exit safeguards: a 3D transform, an imperceptible opacity transition for exit detection, and an ending-state duration that takes precedence over the zero-duration swipe state. Outside dismissal takes 400 ms; drag dismissal scales with swipe strength.
  • Verified locally: all 156 tests, production build, 390px outside-click and Escape exit transitions, handle drag dismissal, focus return, search expansion, independent departure scrolling, zero fade at the list end and without overflow, and the desktop dialog at 900px. The originally reported abrupt outside-tap close did not reproduce in the in-app browser before the change; physical iPhone/Safari confirmation is still needed. Search expansion was subsequently removed as described in the station picker refinement above.

Product and design direction

The visual reference is Massimo Vignelli's NYC subway graphic design: clear typography, restrained rules, consistent alignment, and coloured line monograms. The primary use is checking departures on a phone before leaving home, often choosing a train in the next 45 minutes rather than simply catching the next one. Keep later departures easy to scan; avoid an oversized next-train countdown.

The user likes the overall result and wants to see it during normal multi-line service before making more substantial visual changes. During this session, Corona showed Metro-only service; the user confirmed an ETS Capital Line weekend closure. That was session context, not a permanent restriction or inferred UI bug.

Accepted UI decisions

  • Typography: Geist for interface text and destinations, Geist Mono for clocks and countdowns. Self-host the Latin upright variable WOFF2 faces from @fontsource-variable/geist and @fontsource-variable/geist-mono through src/fonts.css; the geist/font/* helpers target Next.js, not this Vite app. Two files supply the weight range (52,528 bytes total in the verified build). HTML preloads reference the same Vite-hashed assets as CSS; font-display: swap keeps text visible during loading. Font responses have one-year immutable caching, and the existing PWA precaches both files. No external font requests. Keep natural letter spacing on time/countdown text and shared content-sized clock/countdown columns. The standalone offline fallback retains its system fonts. Build and all 155 tests passed; live desktop/mobile browser checks covered 320px and 390px rows, dark mode, and the station picker. Upcoming clocks use regular weight (400); only the next countdown uses medium weight (500). Physical-device rendering remains unverified. See the typography guide for integration, current hierarchy, performance details, resources, and remaining style cleanup.
  • Two equal, independently scrolling direction panes; a terminus uses one pane. Keep the flex sizing and min-height: 0 constraints that let both panes fit.
  • Compact departure rows, with modest emphasis on the first upcoming train. Clock time sits to the left of countdown; both use monospaced numerals. Upcoming countdowns use 12mins with no space; zero minutes displays Now. There are no redundant “at” and “in” labels.
  • Visible direction headings/rails were removed to reclaim vertical space; accessible headings remain. Direction arrows were subsequently removed. A line badge sits in a fixed column to the left of the destination, keeping destination names aligned across the board.
  • The board abbreviates “NAIT Blatchford Market” to “NAIT / Blatchford”. The full destination remains available as a title; schedule identity is unchanged.
  • Line filter buttons occupy simple rectangular header cells. Both lines are shown by default; selecting one isolates it and toggling it off restores both. There is no separate All button.
  • Footer shows a Headway information button on the left and theme and refresh icon buttons on the right. A strong top rule and thin bottom border sit above the phone's safe-area space. The About surface and station picker follow the same compact, square-edged treatment.
  • Station picker: single-line station names, aligned distances, and separate favourite buttons on the right. Line badges are omitted to give names space. A plain search row with a thin divider replaces the boxed input and legend. Focus subtly changes the divider colour without expanding the drawer. Current selection uses a check and left edge bar. Favourites appear first in one list without duplicate rows or section labels. Other stations sort by distance when location is available, otherwise alphabetically. Distances are not walking-time estimates.
  • No visible Stations heading or dedicated close button in the mobile picker. Retain the drag handle and overlay dismissal, plus accessible labelling. The search-clear control only clears the query.

Schedule behaviour and safeguards

  • Times are scheduled, not live predictions. Show only departures whose scheduled time has not passed. On 2026-09-24, removed the recent-departure window and its smaller, dimmed rows: Headway supports planning before leaving, rather than interpreting whether a train has left the platform. The proposed Due grace period was not adopted. Existing countdowns remain; Now uses sans at the exact scheduled instant, then the row disappears on the next clock update. The next upcoming train receives the usual emphasis. Server queries and the next-service fallback also exclude past departures. This supersedes historical recent-row styling and retention notes below.
  • The normal lookahead is four hours. When the whole station has no upcoming trains in that window, search through the end of the next configured service day and show four hours from the earliest actual departure. This can include later service today; it is not an unconditional jump to tomorrow.
  • Preserve absolute scheduled_at values for clocks, countdowns, ordering, and overlapping GTFS service dates. Calendar exceptions, times beyond 24:00/48:00, and DST are covered in GTFS time-window verification.
  • ui-overhaul-pre-next-service tags 584c44e, the UI checkpoint before fallback work. It retains the old time-query behaviour, so it is not a timing bug fix.

Earlier release verification — historical

  • Release ad11a2b: larger standalone arrows, footer bottom border, and scrolling refinements. Pushed to develop and main; deployed successfully to headway.andy.ws via Fly Deploy run 35508375839.
  • Production health returned 200 and the live CSS contained all three changes. The release build and 13 targeted departure/footer tests passed. The preceding full overhaul release passed all 150 tests; the full suite was not rerun for this CSS-only follow-up.
  • Mobile browser checks covered light/dark appearance, independent pane scrolling, no horizontal overflow, and an unobscured final row at the bottom.
  • The fade now shortens continuously near the end using Base UI overflow values, instead of switching the mask off abruptly. Departure panes use overscroll-behavior: none. These target the reported iPhone flick-at-bottom stutter; the cause and on-device resolution are not yet confirmed.

Earlier local checkpoint verification — historical

  • All 155 tests across 23 files passed with npx vitest run --coverage.enabled=false.
  • SENTRY_UPLOAD=false npm run build passed, including TypeScript compilation.
  • Browser inspection confirmed the muted previous departure and corrected clock/countdown spacing. The preview had retained an old Vite-generated stylesheet (48px countdown column and old unit gap) alongside the new JSX. Reloading alone did not clear it; touching src/globals.css triggered a rebuild and delivered the current 64px column with no unit gap. No additional visual code changes were necessary. If the next session sees mismatched visuals, inspect the delivered styles against source before changing layout.
  • This does not resolve or establish the cause of the deferred fetch error.

Tailwind migration

The recent redesign's component styling now lives in Tailwind utilities in the owning components. src/globals.css is reduced from 829 to 143 lines. No large @apply aliases or additional stylesheet were introduced.

  • Migrated the app shell, departure rows/panes, header/filter controls, footer, About surface, station picker, and loading/error/empty states.
  • Retained theme tokens and document/safe-area base rules, the overflow-driven scroll mask, and reduced-motion policy in CSS. The subsequent Base UI drawer migration removed the Vaul exceptions (see drawer migration). The remaining effects stay together with explanatory comments; forcing them into long arbitrary utilities would make maintenance harder.
  • Added a board toggle variant and used the existing Tailwind-aware cn helper so joined-toggle defaults no longer need global CSS overrides.
  • Preserved the 360px breakpoint, min-h-0 flex constraints, line colours, overnight column widths, typography, and drawer dismissal behaviour.
  • Verified the build and 155 tests. Compared before/after screenshots and computed styles using deterministic API fixtures in Chrome at 360px, 390px, and 900px: board, filters, About, picker/search, dark theme, and overnight service.
  • Physical iPhone flicks, software keyboard, and VoiceOver remain unverified. No deployment was performed as part of this migration.

Deferred local refresh error

The user reported “Failed to fetch departures” after switching away and back in local Codex and Safari previews. Production occurrence is unknown. Local nearby and selected-station API checks succeeded during investigation; the original failure was not reproduced reliably.

A supplied terminal screenshot shows Vite invalidating src/main.tsx because its mountApp export is incompatible with Fast Refresh; it exports both App and mountApp. Test-file edits also triggered page reloads. This is evidence of a development reload issue, not proof of the API error's cause.

The user explicitly deferred the investigation. Speculative focus-refresh handler changes and their tests were removed before this checkpoint. No HMR fix or App/entry-point split was applied. Do not treat this issue as resolved or restart the investigation as part of the styling migration unless asked.

Follow-ups

  • Test fast flicks at the bottom of both panes on a physical iPhone, including Safari and installed PWA usage. Also check the footer's safe-area border.
  • Review the departure hierarchy during normal Capital and Metro service. The initial UI migration and three-row hierarchy shipped in 4e7ddd6 and e71ca2f; the final sizing and alignment refinements are described below.
  • Physical-device VoiceOver and software-keyboard behaviour remain unverified.

Repository and deployment continuity

  • The default working branch is develop. Keep release commits on both develop and main: pushes to main deploy, while the weekly GTFS refresh starts from develop and fast-forwards main before deploying.
  • Branch cleanup removed the completed redesign branch and stale tracking/PR references. design-refresh was retained because it has three unmerged prototype commits. Eight remote Dependabot branches had open PRs at cleanup; recheck their status before removing them.
  • Direct shadcn primitives and the mobile drawer now use Base UI. The shadcn and migration skills are checked into .agents/skills/.
  • Vitest has automatic UI/browser opening disabled. For a local validation build without sourcemap uploads, use SENTRY_UPLOAD=false npm run build.
  • .dockerignore excludes local environment files, agent configuration and Git metadata from remote build context. Preserve those exclusions.

The initial design QA record and station-picker review retain historical evidence; their earlier typography, rails, pills, and close-button descriptions are superseded by the decisions above.

Initial departure hierarchy — 2026-09-20 (superseded sizing)

  • Feature the next three trains per direction with decreasing emphasis, after filtering: 18px bold, 16px semibold, 16px medium; later rows are 14px regular and the recent row is 12px regular. All sizes come from the Tailwind scale.
  • Move line badges before destinations and scale the circle, letter, and fallback icon with the inherited row size. Header filter badges keep their fixed sizing.
  • Clocks and countdowns inherit the row size. Clocks use regular weight and muted-foreground; countdowns remain the primary timing cue.
  • Consolidate row styles in rowVariants, replace repeated custom type sizes and line heights, and let shared grid columns accommodate numerical content.
  • The typography guide describes the current hierarchy and integration; the earlier single-featured-row notes above are historical.

Final departure typography — 2026-09-20

  • Increase the row scale to 20px / 18px / 18px / 16px / 14px for next, second, third, later, and recent departures, with more vertical padding. The first three retain graduated emphasis; smaller destination labels use medium weight.
  • Keep clocks regular (400), one standard Tailwind size step smaller than the countdown. Preserve natural numerical spacing and tabular digits.
  • Centre compact line badges in a fixed 24px column so every destination starts at the same horizontal position. Reset badge tracking, use whole-pixel letter sizes, and trim to capital height where supported; older browsers retain flex centring. The circle still scales with the row.
  • Truncate destination labels to one line with an ellipsis. Keep full text in the DOM and the existing hover title. A 12px column gap separates destinations from clocks, and numerical columns size to their content.
  • Give recent text and clocks 80% foreground colour and badges 70% opacity, distinguishing a past departure from a row under the bottom scroll fade.
  • Typography is the current reference for exact sizes, weights, integration, browser fallback, and verification. Earlier sizing above is historical; countdown urgency colouring remains an unimplemented idea.
  • Release validation: 156 tests passed across 23 files, and the Vite client / TypeScript server production build passed. Local browser checks covered the larger hierarchy, 320px truncation, badge centring, and recent-row contrast beside the dark-mode scroll fade. Physical iPhone testing remains a follow-up.

Uniform mixed-line departures — 2026-09-20

  • Supersedes the graduated sizing above. Mixed Capital/Metro destinations made position-based sizes look inconsistent, so every upcoming row now uses an 18px medium destination, the same compact badge, and a 56px minimum height.
  • Countdown text is 16px; supporting clocks are 14px and regular weight. Only the next countdown in each direction gets medium weight, recalculated after filtering and as trains depart. Recent rows remain 14px with 12px clocks and the existing brighter secondary contrast.
  • Removed the experimental perspective tilt and its wrapper. Past departures stay flat and readable; no departure animation or urgency colouring was added.
  • Kept the fixed-time mixed-line preview at http://localhost:5173/docs/qa/departure-typography/. It renders the real table without changing the device clock. It is excluded from coverage and is not emitted as a production entry point.
  • The typography guide is the current style reference. The release workflow documents branch synchronisation and live verification; AGENTS.md and CLAUDE.md point to both guides.
  • Validation: all 156 tests across 23 files passed, along with the Vite client and TypeScript server production build. The final mixed-line preview was checked at 320px: both panes remain visible and long destinations truncate without colliding with the clocks. Physical iPhone testing remains unverified.