# Research: Event List Temporal Grouping **Feature**: 010-event-list-grouping | **Date**: 2026-03-08 ## 1. Week Boundary Calculation **Decision**: Use ISO 8601 week convention (Monday = first day of week). "This Week" spans from tomorrow through Sunday of the current week. **Rationale**: The spec explicitly states "ISO convention where Monday is the first day of the week" (Assumptions section). The browser's `Date.getDay()` returns 0 for Sunday, 1 for Monday — straightforward to compute end-of-week as next Sunday 23:59:59. **Implementation**: Compare event date against: - `startOfToday` and `endOfToday` for "Today" - `startOfTomorrow` and `endOfSunday` for "This Week" - `after endOfSunday` for "Later" - `before startOfToday` for "Past" Edge case (spec scenario 4): On Sunday, "This Week" is empty (tomorrow is already next week Monday), so events for Monday appear under "Later". This falls out naturally from the algorithm. **Alternatives considered**: - Using a date library (date-fns, luxon): Rejected — dependency discipline (Constitution V). Native `Date` + `Intl` is sufficient for this logic. - Locale-dependent week start: Rejected — spec mandates ISO convention explicitly. ## 2. Date Formatting for Subheaders **Decision**: Use `Intl.DateTimeFormat` with `{ weekday: 'short', day: 'numeric', month: 'short' }` to produce labels like "Wed, 12 Mar". **Rationale**: Consistent with existing use of `Intl.RelativeTimeFormat` in `useRelativeTime.ts`. Respects user locale for month/weekday names. No external dependency needed. **Alternatives considered**: - Hardcoded English day/month names: Rejected — the project already uses `Intl` APIs for locale awareness. - Full date format (e.g., "Wednesday, March 12, 2026"): Rejected — too long for mobile cards. ## 3. Time Display on Event Cards **Decision**: Add a `timeDisplayMode` prop to `EventCard.vue` with two modes: - `'clock'`: Shows formatted time (e.g., "18:30") using `Intl.DateTimeFormat` with `{ hour: '2-digit', minute: '2-digit' }` - `'relative'`: Shows relative time (e.g., "3 days ago") using existing `formatRelativeTime()` **Rationale**: Spec requires different time representations per section: clock time for Today/This Week/Later, relative time for Past. A prop-driven approach keeps EventCard stateless regarding section context. **Alternatives considered**: - EventCard determining its own display mode: Rejected — card shouldn't know about sections; parent owns that context. - Passing a pre-formatted string: Viable but less type-safe. A mode enum is clearer. ## 4. Grouping Data Structure **Decision**: The `useEventGrouping` composable returns an array of section objects: ```typescript interface EventSection { key: 'today' | 'thisWeek' | 'later' | 'past' label: string // "Today", "This Week", "Later", "Past" events: GroupedEvent[] } interface DateGroup { date: string // ISO date string (YYYY-MM-DD) for keying label: string // Formatted date label (e.g., "Wed, 12 Mar") events: StoredEvent[] } interface GroupedEvent extends StoredEvent { dateGroup: string // ISO date for sub-grouping } ``` Actually, simpler: the composable returns sections, each containing date groups, each containing events. ```typescript interface EventSection { key: 'today' | 'thisWeek' | 'later' | 'past' label: string dateGroups: DateGroup[] } interface DateGroup { dateKey: string // YYYY-MM-DD label: string // Formatted: "Wed, 12 Mar" events: StoredEvent[] } ``` **Rationale**: Two-level grouping (section → date → events) matches the spec's hierarchy. Empty sections are simply omitted from the returned array (FR-002). The "Today" section still has one DateGroup but the template skips rendering its subheader (FR-005). **Alternatives considered**: - Flat list with section markers: Harder to template, mixes data and presentation. - Map/Record structure: Arrays preserve ordering guarantees (Today → This Week → Later → Past). ## 5. Visual Emphasis for "Today" Section **Decision**: Apply a CSS class `.section--today` to the Today section that uses: - Slightly larger section header (font-weight: 800, font-size: 1.1rem vs 700/1rem for others) - A subtle left border accent using the primary gradient pink (`#F06292`) **Rationale**: Consistent with Electric Dusk design system. Subtle enough not to distract but visually distinct. The existing past-event fade (opacity: 0.6, saturate: 0.5) already handles the other end of the spectrum. **Alternatives considered**: - Background highlight: Could clash with card backgrounds on mobile. - Icon/emoji prefix: Spec doesn't mention icons; keep it typography-driven per design system. ## 6. Accessibility Considerations **Decision**: - Section headers are `

` elements - Date subheaders are `

` elements - The event list container keeps its existing `role="list"` - Each section is a `
` element with `aria-label` matching the section label **Rationale**: Constitution VI requires semantic HTML and ARIA. The heading hierarchy (h2 > h3) provides screen reader navigation landmarks. The `
` element with label allows assistive technology to announce section boundaries. ## 7. Existing Test Updates **Decision**: - Existing `EventList.spec.ts` unit tests will be updated to account for the new grouped structure (sections instead of flat list) - Existing `home-events.spec.ts` E2E tests will be extended with new scenarios for temporal grouping - New `useEventGrouping.spec.ts` tests the pure grouping function in isolation **Rationale**: TDD (Constitution II). The grouping logic is a pure function — ideal for thorough unit testing with various date combinations and edge cases.