Files
familysync/.planning/milestones/v1.0-phases/02-calendar-display/02-CONTEXT.md
T
Lucas Berger 982438dc10 style(13-03): apply Prettier formatting across repo
Mechanical reformat — no logic changes. 398 files changed, 19125
insertions(+), 16457 deletions(-). Prettier 3.8.4 with .prettierrc
(singleQuote:true, semi:true, tabWidth:2, trailingComma:all,
printWidth:100). Isolated per D-13-08 for reviewability.
2026-06-11 20:35:18 -04:00

8.9 KiB

Phase 2: Calendar Display - Context

Gathered: 2026-06-04 Status: Ready for planning

## Phase Boundary

A unified, color-coded, read-only calendar that aggregates every accessible Fastmail calendar (shared family + each member's personal) into one view, with day / week / month / agenda views, correct rendering of recurring events (server-side expansion), all-day events (no timezone shift), and DST boundaries. Built on the broker confirmed in Phase 1.

Out of scope (other phases): event create/edit/delete write-back (Phase 3), PWA install (Phase 3), shared lists (Phase 4), web push (Phase 5), the tablet/wall-display kiosk mode and a runtime theme-switcher (v2 — see Deferred), per-member show/hide filtering (deferred until >2 members), single-occurrence recurring edits (v1.x).

## Implementation Decisions

Theming architecture (the load-bearing decision)

  • D-01: Build a design-token layer — color, spacing, density, and typography expressed as CSS custom properties + a small theme object — and build the UI exclusively against those tokens. No hard-coded colors/spacing in components.
  • D-02: Ship only the "clean" theme in Phase 2. A future Skylight/tablet "display" theme must be achievable as a token-set swap, not a refactor. Do NOT build the second theme or a runtime theme-switcher UI now (those are v2 kiosk work).
  • D-03: Even the clean theme is tuned to be legible and informational at tablet distance, not ultra-minimal — the v2 north star is a tablet wall-display, so the clean theme must not be so sparse that it can't carry information. (Rationale: user's real end goal is a tablet display; see project-familysync / PROJECT.md Out-of-Scope note on wall display.)

Views & default

  • D-04: Provide all four views: day, week, month, agenda (CAL-03).
  • D-05: Device-adaptive default view: phone → Agenda (lowest friction for the non-technical iPhone member); tablet/desktop → Month (spatial overview, closest to the v2 display). Remember the last-used view per device.

Color & ownership legibility

  • D-06: Per-member color fill using the color already assigned on the user row in Phase 1 (6-color palette already scales as members are added); the shared-family calendar gets one reserved, distinct color. This is the "whose is this" signal and must read at a glance.
  • D-07: Show a small color legend / key (member → color) so ownership is decodable. A per-member show/hide filter is deferred until there are more than two members (same trigger as the future display theme) — a 2-person household doesn't need it yet.

Event detail density

  • D-08: Informational + tap-to-expand. Month = colored bars with the event title (not bare dots); Week/Day = time + title; Agenda = time + title + location. Tapping any event opens a read-only detail popover (title, time, location, description) — this popover is intended to be reused as the edit surface in Phase 3, so build it with that in mind.

Recurrence / time (carried forward — not re-discussed)

  • D-09: Recurring events are expanded server-side (CALDAV:expand / broker emits concrete occurrences for the requested window) — locked in STATE/CLAUDE. The client renders occurrences; it does not run rrule expansion itself for the primary path.
  • D-10: Single local timezone for v1 — all-day events render with no date shift (D-13 split already in the schema). Secondary-timezone display toggle is deferred to v1.x.

Claude's Discretion

  • Week start day (Sunday vs Monday): default Sunday (US locale — the account has a "USA Holidays" calendar); expose as a token/config so it's trivial to flip. Planner/researcher may confirm.
  • Exact rendering library is the researcher's call — but it must support headless/custom styling against the token layer (D-01), all four views, server-expanded occurrences, all-day banners, and good touch UX on iOS. (Candidates to evaluate, not locked: Schedule-X, react-big-calendar, FullCalendar, or a Temporal-based custom grid. Avoid libs that force their own opinionated theme and can't be token-styled.)
  • Skeleton/loading and empty states: build them, polished enough for the "slick" constraint.

Dev-auth bypass (from D-14, project-level)

  • /api/* is OIDC-gated, but live Authelia is deferred (D-14). Plan a documented dev-auth bypass (e.g., an env-flagged middleware that injects a fixed dev user) so Phase 2 UI can be built and tested locally without a live OIDC provider. Must be off by default / impossible in production builds.

<canonical_refs>

Canonical References

Downstream agents MUST read these before planning or implementing.

Project decisions & scope

  • .planning/PROJECT.md — core value, constraints, Key Decisions incl. D-14/D-15; wall-display is v2 (informs D-02/D-03).
  • .planning/ROADMAP.md §"Phase 2: Calendar Display" — goal + success criteria; §Phase 3/4 for scope boundaries.
  • .planning/REQUIREMENTS.md — CAL-02 (unified color-coded view), CAL-03 (day/week/month/agenda), and the recurring-event display portion of CAL-07.

Phase 1 foundation this builds on

  • .planning/phases/01-foundation-broker-spike/01-03-SUMMARY.md — broker API surface (syncCalendar, poller), event cache shape.
  • .planning/phases/01-foundation-broker-spike/01-04-SUMMARY.md — index.ts route wiring, /api/events, /api/me.
  • .planning/phases/01-foundation-broker-spike/CAL-08-DECISION.md — per-member app-password model (how multiple members' calendars aggregate).
  • apps/api/src/routes/events.ts — current /api/events (raw cache dump; will need expansion + parsed fields + color/owner for display).
  • apps/api/src/db/schema.tsusers.color, calendars.userId, calendarEvents (dtstartUtc/dtstartDate/allDay/rawVevent) — the D-13 split the display relies on.
  • CLAUDE.md — locked stack (React 19, Vite, TanStack Query, Zustand, ical.js, rrule), CalDAV/expand guidance, iOS constraints.
  • docs/deployment.md — dev-auth bypass context lives alongside Gate 2 (D-14). </canonical_refs>

<code_context>

Existing Code Insights

Reusable Assets

  • apps/pwa/src/api/client.ts — typed fetch client (fetchMe, credentials: 'include'); extend with a typed fetchEvents(range).
  • apps/pwa/src/components/EventProof.tsx — proof-of-concept that already fetches /api/events and parses a VEVENT; the calendar replaces/absorbs it.
  • apps/pwa/src/App.tsx — member badge (name + color via /api/me); the per-member color source for D-06.
  • apps/pwa/src/main.tsxQueryClientProvider already set up (TanStack Query is the server-state owner).

Established Patterns

  • Server state → TanStack Query; UI-only state (selected date, current view) → Zustand (locked; do not put events in Zustand).
  • Broker is the ONLY Fastmail I/O boundary; /api/events reads the MariaDB cache only (no tsdav in routes) — recurrence expansion belongs server-side near the broker/route, never a direct Fastmail call from the UI.
  • Hono app exported from index.ts without auto-starting (testable); add display-oriented endpoints there.

Integration Points

  • /api/events must evolve from "raw row dump" to a display-ready shape: expanded occurrences within a requested date window, parsed title/time/location, all-day flag, and member color / shared-vs-personal indicator (join calendarEvents → calendars → users.color). This is the main backend work of Phase 2.
  • Dev-auth bypass middleware sits in front of /api/* (see D-14). </code_context>
## Specific Ideas
  • "Clean theme now, switch to Skylight style later" — Skylight = large, glanceable, high-contrast family dashboard. It's the reference for the future display theme; the token layer (D-01) exists to make that swap cheap.
  • The real end goal is a tablet wall-display — legibility and information density are first-class even in v1's clean theme (D-03).
  • Theme/aesthetic reference target: Apple/Fantastical-style clean for v1.
## Deferred Ideas
  • Tablet / wall-display kiosk mode + the actual Skylight "display" theme + runtime theme-switcher UI — v2 (PROJECT.md Out-of-Scope). Phase 2 only guarantees the token architecture makes this a swap, not a rewrite.
  • Per-member show/hide filter — add when membership grows beyond two (D-07).
  • Secondary timezone display toggle — v1.x (already in roadmap deferred items).
  • Single-occurrence / "this and following" recurring edits — v1.x; Phase 3 does create + whole-series only.

Reviewed Todos (not folded)

  • kickoff-new-project.md ("Kick off FamilySync with /gsd:new-project") — matched only on generic keywords (date/requirements/phase); a stale project-bootstrap todo, not Phase 2 scope.

Phase: 2-Calendar Display Context gathered: 2026-06-04