feat(calendar): recompose the golf calendar on Fairway primitives with a scheduling task board - #1930
Merged
Merged
Conversation
njrini99-code
force-pushed
the
agent/calendar-makeover
branch
from
September 10, 2026 02:02
f362049 to
64377e6
Compare
Rebuild the calendar's look and feel around one material vocabulary (CalendarSurfaces.module.css): a warm ground wash the glass refracts, frosted champagne chrome and docks with token blur, matte ivory paper cards, and an emerald focused plane for selection and the primary action. Home: sticky glass hero (month, nav, week strip, view switcher, one primary action, secondary actions as pills / phone More menu) that agenda rows, the member rail and the grids scroll under; time-gutter event rows with type-tinted icon discs; compact phone month grid with density dots and whole-cell taps; week-strip swipe to move a week; honest conflict count on the home. Find a time: full-screen glass workspace with a finger-smooth selection lens (free follow with no transition while dragging, 200ms settle on release, grab-offset preserved, readout kept in view, lane tap to place, timeline scrolls the band into view), suggestion cards, glowing dock CTA, and an opening slot where everyone is free. Person schedule, class detail, event detail, editor, conflicts, availability, attendance and subscriptions get the same material pass. AppShell publishes --fw-hub-subnav-offset so page chrome can sit under the hub sub-nav without guessing. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHqSV9CHn8zRWuWt57JELZ
…red card edges Phone home now matches the reference screens: month title toggles the compact month grid, ghost prev/next + More menu + glowing primary action, rounded-square selected day in the week strip, portrait member rail with a Team chip first and Compare last, stacked "9:00 / AM" time gutter, and a richer amber attention banner. Event cards lose the colored left rule everywhere (owner ban): the type reads from the tinted icon disc and the card is distinguished by material (specular rim, hairline, layered grounded shadow). Pending attendance rows become an amber wash + ring. "Show N earlier events" is a floating sand chip. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHqSV9CHn8zRWuWt57JELZ
The Fairway Button wraps children in one bare <span>, so every row built from sibling elements inside it (people picker, conflicts, availability, file picker, proposal rows, attendance row, Find-a-time name column) stacked or overflowed on phone. `.buttonRow` makes that span the row and inherits the button's gap, alignment and justification. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHqSV9CHn8zRWuWt57JELZ
Phase B slice for owner review. The calendar stops carrying its own material system on the home screen and composes shared Fairway parts: - Hero: compact title row + explicit view selector (Segmented, new opt-in `quiet` presentation) + prev/next; secondary actions behind More until xl; the week strip only in Day view; no header counters. - People: compact summary (AvatarGroup + status) with a People menu (Everyone / open a schedule) and the existing compare picker; the portrait rail is gone. - Agenda: day heading + one grouped Surface of PressTarget rows with hairline dividers; history is a quiet ghost action with a real count. - Month on phone: CalendarSurface (DayPicker) overview with the selected day's events beneath; the desktop grid keeps a single day target. - Player: "Needs your reply" contextual row instead of a header CTA. - Removed local recipes: .ground, .chipFloat, .avatarChip, .teamChip. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHqSV9CHn8zRWuWt57JELZ
…ure contract Resolves the four source issues from the workspace review and gives Find a time its contextual floating board: - ONE acceptance rule: `acceptProposal` (every schedule verified, every required person free; optional people may be busy) drives the workspace board, its confirm action and the dialog's final recheck. Before, the workspace enabled the action on its own predicate and the dialog rejected the same selection on `allAvailable`. Fixture with an optional busy participant added. - Gesture contract: releasing a drag commits the release position (a pending animation frame is flushed, never dropped); lane taps place the window only when the pointer stayed put (8px), so pans and page scrolls do not. - `SchedulingTaskBoard` (Fairway Elevated): accepted / required busy with names and "Next open time" / unverified with names and Retry / could not verify. The frame reserves its space; nothing floats over the last row. - `ModalShell` gains `presentation="workspace"` (phone: edge-to-edge, keyboard-aware; sm+: wide stage); the scheduling dialog drops its !important geometry overrides. - `.buttonRow` is gone: people, conflict, busy-time, document and attendance rows, the scheduling name column, ruler slots and suggestion cards are `PressTarget` compositions; Button stays for labeled actions. - Week strip marks every day a multi-day event runs (`eventDaySpan`), the same interpretation as the agenda and month overview. - Feature doc updated to the composition contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHqSV9CHn8zRWuWt57JELZ
…lendar surface Phase C, first pass: the detail drawer, person dialog, editor (essentials, people & time, verification, review, stages), class detail, conflicts center/detail, availability, attendance, files, subscriptions and the people picker now compose on Fairway classes instead of CalendarSurfaces recipes. `.paper`/`.row` → bordered bg-surface card with --fw-shadow-card; `.rowIcon` → neutral sunken disc, or a semantic warning/success tone where one was meant; `.float` → bordered surface; `.chrome`/`.dock` → the shell's `fw-glass-chrome`; `.check`/`.selected` → accent-650 fill; `.attention` → warning-bg/ink/ring; `.press`/`.rise`/`.glow` removed (PressTarget and Button already carry the press response; the primary Button is the green treatment). `--row-tint` custom-property plumbing is gone. CalendarSurfaces.module.css keeps only functional vocabulary: colour scope, grid hairlines, interval tints, hatch, pending, reference, lens + motion, inspector, enter. The reduced-motion contract test follows. Also regenerates DOCUMENT_AUTHORITY_INVENTORY.md (knowledge_check fell over the feature doc's new heading count). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHqSV9CHn8zRWuWt57JELZ
…responses - The hero title is now the date-jump: a PressTarget trigger opens a PopoverPanel with the shared CalendarSurface (frameless inside the panel); picking a day selects it and closes the popover. Hero test added. - Agenda is month-scoped: its window is the title's calendar month, prev/next step by month, and the empty state names the period. - Detail drawer per owner review: the Responses block is one centred stat strip (figure over label, hairline dividers) instead of four sunken tiles whose figures read off-centre; neutral row icons are bare glyphs so cream discs no longer wash out on the cream card; the date line uses the sans body face. Semantic warning/success discs keep their fill. - calendar-events.md: retired-recipe list, date-jump, month-scoped agenda, drawer contract. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHqSV9CHn8zRWuWt57JELZ
Owner review of the drawer: "Don't be afraid to use the green as accent." Row icons (location, notes, responses, people) are bare accent-700 glyphs, the header clock is emerald, and the Accepted count in the Responses strip carries the accent when above zero. Warning/success discs are unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HHqSV9CHn8zRWuWt57JELZ
…le first names
- Week view titles its exact Sunday-start range ("Sep 6 – 12, 2026",
cross-month and cross-year forms) and its empty state names the week
instead of "No upcoming events".
- Agenda day headings within the coming week carry a quiet "in N days" cue;
Today / Tomorrow / Yesterday remain the heading itself.
- Find a time: the phone name column is 116px and shows the first name whole
(full name from `sm` and in the accessible label) instead of "Aud…".
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHqSV9CHn8zRWuWt57JELZ
…the pre-land review Four defects a fresh-context review of this branch confirmed, each verified against the code rather than the PR's own summary. CalendarSchedulingDialog: ModalShell's `presentation="workspace"` is full-bleed on phones (top 0, square corners) and delegates safe areas to its child. SchedulingWorkspace honours that in its header and footer; the sibling loading/error branch did not, so the Back button and the `role="alert"` retry sat under the notch. `useScheduleWindow` nulls `snapshot` on every request-key change, so this was not a first-mount state — every date jump landed there, and the error case stayed until a retry succeeded. ModalShell's own comments record the identical incident already biting the `dialog` path once. SchedulingWorkspace: the hour-ruler slots are real start-time controls and their hit area had regressed from main's `min-h-14` (56px) to `h-8` (32px). They now take the kit's 44px floor on a coarse pointer only, so the compact ruler the makeover designed survives for a mouse. Layout-safe: the selection lens spans `gridRow: 3 / span N`, so growing row 2 does not move it. FairwayMonthGrid: below `sm` each cell offered two tab stops, not one. `sr-only` clips a native button visually but leaves it in the tab order and the a11y tree, and `pointer-events-none` does not block keyboard activation — so the grid was 84 focus stops instead of 42, half of them painting no focus ring. `max-sm:hidden` is layout-neutral (`sr-only` is already absolutely positioned) and makes the code match the comment above it. FairwayCalendarHero: Day view announced "Previous/Next day" while navigate() steps `addDays(d, dir * 7)` for both Day and Week by design — the day-strip is the single-day picker and the arrows turn the page. The label now says what the button does; an AT user cannot see the strip to infer it. Covered by a new parameterised test that fails when the old label is restored. Verified: tsc --noEmit clean; eslint --max-warnings=0 clean on all four files; 228 test files / 1760 tests pass — the same baseline as before these changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zGybqwusmeKC2jJhsU9JC
Two multi-touch defects from the pre-land review. A phone is a multi-touch device: a resting thumb, a palm, or a second finger lands on the same band and lanes the gesture is already using, and neither handler cluster asked which pointer it was hearing from. The band and the drag handle share `dragging` (one boolean) and `dragRef` (one record). A second contact re-entered `beginDrag`, overwrote `grabOffsetPx` and captured itself on the same element, after which the band tracked whichever finger moved last — and whichever finger lifted first committed ITS position and set `dragging` false, stranding the finger still down: its moves were then ignored and its own release was a no-op. The gesture now has an explicit owner. A second pointerdown is refused rather than allowed to take over, and move, up and cancel act only for the pointer that started it. The lane-tap path already compared pointer ids; the drag cluster it sits beside did not. The lane rows share one pending-tap record but attach the same three handlers to every row, and the up handler cleared that record BEFORE checking whether it owned it. Two overlapping contacts therefore lost both taps: the first release discarded the second contact's still-valid record on its way to rejecting itself, and the second release then found nothing. The record is now scoped by pointer id — the first contact owns it, and only that pointer's up or cancel consumes it. Also drops a tautological assertion in the workspace test that re-checked the `data-testid` value `getByTestId` had just queried by, so it could not fail. Both fixes are covered by tests proved to fail without them: reverting the drag owner fails only the second-touch test, reverting the tap scoping fails only the stray-contact test. 17 tests pass in the file; tsc --noEmit clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zGybqwusmeKC2jJhsU9JC
…d list details The phone calendar read as a stack of flat strips: six chrome bands before the first event, a view switcher squeezed beside two arrows, a coach people line with ghost-text buttons under it, and a flat list. This gives it the structure and material of a native calendar. Masthead (FairwayCalendarHero) - Large period title with the year quiet beside it; the day short on a phone (full form kept for assistive tech); a settle-in on change. - Full-width Segmented in its shared depth presentation (sunken track, floating thumb). Prev/next only from md up: on a phone the schedule swipes, like every native calendar. - "Today" is a lifted secondary pill carrying today's number in a calendar glyph (decorative, outside the name); More is a lifted disc. - Warm bottom rim + resting whisper so the bar sits ON the list; a 2px progress line along its edge replaces the loading banner. - Publishes --fw-calendar-hero-h so day headings can pin beneath it. Schedule (FairwayCalendar / FairwayAgendaView / FairwayEventCard) - The body swipes horizontally to turn the period (touch/pen, >=56px, <=40px drift) and slides in from the direction of time, derived from the period keys; a view change is a cut; reduced motion disables it. - The coach's phone primary action is a floating "+" above the tab bar; the empty state's own New event and the masthead's labelled button stay md+. - Day headings pin under the masthead; Today/Tomorrow carry their date. - Each day is one raised group card of hairline-divided rows (0.5px on 2x). - Today's group carries a now-line at its sorted position, ticking by the minute after mount, seeded from nowRef for hydration. - Rows show start + meridiem and the end time on every width; touch press tints the row. The range-empty state is compact, never a monolith card. People row (FairwayCalendarMemberRail) - One raised row: the summary IS the People trigger; Compare is a lifted control (icon on a phone, labelled from md), and the People menu also offers "Compare schedules…". Month / strip / skeleton - Phone month is one raised card with sans tabular numerals (not mono); the selected day pill in the strip is lifted; the route skeleton now mirrors the real first paint (masthead, people row, grouped day cards). Tests: masthead naming/glyph/height var/progress line; swipe paging and the floating action; now-line placement; the one-row people entry; plus the coverage tests from the PR review (Segmented quiet, ModalShell workspace, scheduling recheck, relative day cue, agenda window). Feature doc updated. Review scripts live under docs/reviews/calendar-mobile-review (captures are left untracked). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zGybqwusmeKC2jJhsU9JC
Primary and secondary buttons now sit ON the page: a lit top edge plus the resting card whisper at rest, a step up on hover, settled flat on press. Flat controls read as disabled chips beside raised cards and the Segmented switcher's floating thumb. Ghost stays chrome-light by contract; danger keeps its flat whisper. The calendar's local shadow overrides are dropped now that the shared variants carry the depth. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zGybqwusmeKC2jJhsU9JC
…view The push hook's diff --check rejects trailing spaces; finding titles now stand as their own paragraph instead of relying on a two-space line break. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zGybqwusmeKC2jJhsU9JC
A raised circle inside a raised row, same cream, two stacked hairlines and an icon nobody reads. On a phone the row is one control (it opens People, which already offers "Compare schedules…"); from md the labelled Compare button stands beside the row, never inside it. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zGybqwusmeKC2jJhsU9JC
Every finding in it was applied in this branch; as a docs/ file it added 178 markdownlint violations to the repo-wide ratchet, which blocks Review Gate. The review lives in the PR conversation. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011zGybqwusmeKC2jJhsU9JC
njrini99-code
force-pushed
the
agent/calendar-makeover
branch
from
September 10, 2026 02:24
1d57a46 to
d18d77b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The golf calendar recomposed on Fairway's own primitives, plus an interaction-first Find a time workspace. Three passes, oldest first:
fe575fd9c,7a385e9d9) — superseded in most places by the passes below; the owner ban on colored card edges stands.89e4b6051) — Phase B slice for owner review.0ea60406b) — the four source issues from the workspace review, resolved.What is reused (shared kit, unchanged)
PressTargetfor every multi-child row ·Button/IconButtonfor labeled actions only ·Segmented(view selector) ·PopoverPanel(More, People menus) ·Avatar/AvatarGroup·StatusPill·Surface/Elevated·CalendarSurface(DayPicker) for the phone month ·EmptyState·ModalShell· the shell'sfw-glass-chrome.What improved in the shared kit
Segmentedgains an opt-inquietpresentation (no sunken track shadow, flat pill). Default rendering unchanged for the 72 other consumers.ModalShellgainspresentation="workspace"(phone: edge-to-edge full height, keyboard-aware;sm+: a wide stage). Defaultdialogpresentation unchanged; existing ModalShell tests pass.What disappeared
Portrait member rail · header counters · glass hero/dock/float pills in the workspace · every local material recipe except the functional calendar vocabulary (
.ground,.chipFloat,.avatarChip,.teamChip,.buttonRow,.paper,.well,.chrome,.dock,.float,.selected,.glow,.check,.attention,.row,.rowIcon,.press,.rise— consumers now use Fairway tokens directly) · per-dialog!importantgeometry · the workspace's inline status block and glowing dock (replaced by one task board) · the drawer's four sunken Responses tiles.The four workspace issues
.buttonRow > spanremoved; people, conflict, busy-time, document and attendance rows, the scheduling name column, ruler slots and suggestion cards arePressTargetcompositions.ModalShell presentation="workspace";CalendarSchedulingDialogno longer overrides the frame.acceptProposal(every schedule verified, every REQUIRED person free) is the single rule behind the board, the confirm action and the dialog's final recheck. Test fixture with an optional busy participant provesallAvailable=falsewhile acceptance passes andonChoosefires.Task board (
SchedulingTaskBoard, FairwayElevated)accepted → date · time, summary, Use this time · required busy → names + Next open time · unverified → names + Retry · fetch/recheck failure → "Could not verify schedules" + Retry, confirm disabled. The frame reserves its space below the scrolling body.
Compact date exploration and the drawer (Phase C, after the owner checkpoint)
PressTargetopening aPopoverPanelwith the sharedCalendarSurface; picking a day selects it. No ambiguous toggle.Native phone pass (owner review of the phone captures, Fable 5.1)
mdup. The masthead publishes its height so the list's day headings pin beneath it.mdup.mdup a labelled Compare button stands beside the row, never inside it.Shared kit: raised buttons (
Button,IconButton)Primary and secondary now sit on the page at rest (lit top edge plus the resting shadow), lift a step on hover, settle flat on press. Ghost stays chrome-light; danger keeps its flat whisper. The calendar's local shadow overrides were dropped in favour of the shared variants. Covered by
button.test.tsx.Verification
tsc --noEmit: exit 0.src/components/fairway/controls,src/components/fairway/pages/calendar, messages, TeamSwitcher — 77 files, 733 tests pass (new: masthead, now-line, member-row, swipe/FAB, button depth).Not in this PR
Event-to-detail continuity · desktop context rail · wheel picker, resizable panes, Gantt (deferred by brief). Not deployed; production deploy needs explicit authorization. The drawer's People section exposes no buttons on the phone (pre-existing, reported).
🤖 Generated with Claude Code
https://claude.ai/code/session_011zGybqwusmeKC2jJhsU9JC