11 KiB
Phase 17: Chat & Planning - Context
Gathered: 2026-05-17 Status: Ready for planning
## Phase BoundaryRestyle the discussion view and planning page using design system components to make them visually consistent with the Phases 15–16 restyled surfaces. Phase 17 delivers:
- Discussion view: constrained chat bubbles with own-vs-others alignment (own right, others left), brand-tinted backgrounds, new CSS classes in app.css using design tokens,
.ui-cardsurface for message container - Planning page:
.overview-section/.overview-section-headingheader layout, day-grouped events with date separators,DateLabelremoved from individual event rows
Does NOT include: avatars or profile pictures, message reactions or threading, planning sorting/filtering controls, new routes or data models, schema changes.
<spec_lock>
Requirements (locked via SPEC.md)
6 requirements are locked. See 17-SPEC.md for full requirements, boundaries, and acceptance criteria.
Downstream agents MUST read 17-SPEC.md before planning or implementing. Requirements are not duplicated here.
In scope (from SPEC.md):
DiscussionMessageView— addIsOwn boolfieldDiscussionMessagesFromRows— addcurrentUserID uuid.UUIDparameter; setIsOwnby comparing to each message's authorDiscussionMessageFromRow— handleisOwnappropriately for SSE vs. HTMX pathsDiscussionTabFragment/DiscussionMessageRow— restyle as right/left-aligned chat bubbles; message container adopts.ui-cardsurfacePlanningPage/PlanningEventListItem— adopt.overview-sectionheading; addPlanningShowDaySeparatorhelper; add day-separator element; removeDateLabelfrom event row body- All call sites for changed function signatures updated (handlers, test files where necessary to compile)
- Browser walkthrough confirming visual consistency on both pages
Out of scope (from SPEC.md):
- Avatars or profile pictures in message rows
- Message reactions or reply threading
- Planning page sorting or filtering controls
- New database schema, migrations, or sqlc queries
- New routes or HTTP handlers
</spec_lock>
## Implementation DecisionsChat Bubble Visual Design
- D-M01: Own messages render as constrained bubbles — max-width ~70%, right-aligned (use
margin-left: auto). Others render at full width, left-aligned. Each message row is a flex container; own uses.message-ownclass, others use.message-other. - D-M02: Bubble backgrounds use design tokens: own messages use
var(--color-brand-primary)at low opacity (tint — e.g.,rgba(128, 78, 236, 0.1)or a CSS custom property layered on--color-brand-primary). Others usevar(--color-surface-default)(white). Border: subtlevar(--color-border-subtle). - D-M03: Author email + timestamp appear ABOVE the bubble text, mirrored to match bubble alignment. Own: author + time right-aligned. Others: author + time left-aligned.
- D-M04: New CSS classes in
backend/internal/web/ui/app.cssusingvar(--...)tokens only — no Tailwind inline, no hardcoded hex values. Classes:.message-row,.message-own,.message-other,.message-bubble,.message-meta.
Discussion Data Model
- D-D01:
DiscussionMessageViewgainsIsOwn bool. Set viaDiscussionMessagesFromRows(rows, currentUserID)— the handler already hasuser.IDfromloadOwnedTablo. - D-D02:
loadDiscussionTabDatareceives the current user ID and passes it toDiscussionMessagesFromRows. TheTabloDiscussionTabHandleralready hasuser— threaduser.IDthrough. - D-D03: For the HTMX POST response (posting user):
message.IsOwn = true. ForrenderDiscussionMessageHTML(SSE broadcast to other viewers):IsOwn = false. SinceDiscussionMessageViewis a value type in Go, setIsOwn = falsebefore SSE publish andIsOwn = truebefore HTMX render — two sequential mutations on the same local copy work cleanly. - D-D04:
DiscussionMessageFromRowno longer needs anisOwn boolparameter — use the struct field mutation approach in the handler instead. The function returns a view withIsOwn: falseby default; the handler sets.IsOwn = truefor the HTMX path.
Discussion Container
- D-C01: The
#discussion-messagescontainer div changes fromclass="rounded border border-slate-200 bg-white"to use the.ui-cardCSS class. The composer form remains outside the card (below it), as currently — no structural change needed.
Planning Page
- D-P01:
PlanningPageheading section wraps in<section class="overview-section">+<div class="overview-section-heading">with<h1>Planning</h1>and the date range label below — matching the.overview-sectionpattern from tablos.templ. - D-P02: Add
PlanningShowDaySeparator(events []PlanningEventRow, index int) boolhelper inplanning_forms.go— comparesevents[index].DateLabel != events[index-1].DateLabel(string comparison, returns true for index 0 or label change). - D-P03: Day separator element mirrors the discussion day separator style — a centered strip with the date label (e.g.,
<div class="bg-slate-50 px-4 py-2 text-center text-sm text-slate-500">). - D-P04:
DateLabelfield is removed fromPlanningEventListItemrow body — it's now rendered in the separator. Event row shows: TimeRange → Title → TabloTitle + Location (existing right column).
Claude's Discretion
- Exact
max-widthvalue for own message bubbles (recommend 70%) - Border-radius on bubbles (recommend
0.75remfor all corners, or asymmetric: own0.75rem 0.75rem 0.25rem 0.75rem) - Exact opacity/alpha for the brand-primary bubble tint (recommend 10–15% — subtle, readable)
- Divider between messages: keep
divide-y divide-slate-100inside the card or drop it now that bubbles have their own visual weight (recommend dropping the divider) - Empty state for planning when no events: adopt
@ui.EmptyState(...)to match Phase 16 files section (consistent with established pattern) - Navigation controls in planning heading: keep using
.ui-buttoninline utility classes (no change needed)
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Locked Requirements
.planning/phases/17-chat-planning/17-SPEC.md— Locked requirements (6), boundaries, acceptance criteria — MUST read before planning
Product Scope
.planning/ROADMAP.md— Phase 17 goal, success criteria (4 criteria), requirements CHAT-UI-01, PLAN-UI-01.planning/REQUIREMENTS.md— CHAT-UI-01 (card/surface design, own-vs-others), PLAN-UI-01 (overview-section layout).planning/PROJECT.md— v3.0 scope: visual polish only; no new data models or routes
Current Backend (files being changed)
backend/templates/discussion.templ—DiscussionTabFragment,DiscussionMessageRow,DiscussionEmptyState,DiscussionDaySeparator,DiscussionComposer— all being restyledbackend/templates/discussion_forms.go—DiscussionMessageView(addIsOwn bool),DiscussionMessagesFromRows(addcurrentUserID uuid.UUID),DiscussionMessageFromRow(returnsIsOwn: false; handler setsIsOwn: truefor HTMX path)backend/templates/planning.templ—PlanningPage,PlanningEventListItem— adopt overview-section and day separatorsbackend/templates/planning_forms.go— addPlanningShowDaySeparatorhelperbackend/internal/web/handlers_discussion.go—loadDiscussionTabData,DiscussionMessageCreateHandler— threaduser.IDthrough; setIsOwnfor HTMX vs. SSE paths
Design System (Phase 13 — already in backend)
backend/internal/web/ui/app.css— receives new.message-row,.message-own,.message-other,.message-bubble,.message-metaCSS classes using design tokensbackend/internal/web/ui/base.css— design tokens; all new CSS usesvar(--...)tokens. Key tokens:--color-brand-primary,--color-surface-default,--color-border-subtle,--color-text-muted,--color-surface-muted-inversebackend/internal/web/ui/card.templ+backend/internal/web/ui/card.css—.ui-cardclass to apply to discussion message containerbackend/internal/web/ui/empty_state.templ—@ui.EmptyState(...)for planning empty state (Claude's discretion)
Established Patterns (Phase 15–16)
.planning/phases/16-tablo-detail/16-CONTEXT.md— overview-section heading pattern, design token constraints, all-CSS-in-app.css rule.planning/phases/15-dashboard-tablos/15-CONTEXT.md— AppLayout integration, sidebar tablos list pattern
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
backend/internal/web/ui/card.css.ui-card: Apply to#discussion-messagescontainer — swapclass="rounded border border-slate-200 bg-white"withclass="ui-card"backend/templates/discussion.templDiscussionDaySeparator: Day separator pattern already exists for discussion — use the same visual approach for planning day separatorsbackend/internal/web/ui/empty_state.templ@ui.EmptyState(...): Ready for planning empty state to replace the current raw div
Established Patterns
- All new CSS in
app.cssusesvar(--...)tokens only — no hardcoded hex, no Tailwind inline in CSS files (Phases 13–16 convention) - Value-type Go structs:
DiscussionMessageViewis a struct (not pointer) — handler can mutate.IsOwnbetween SSE publish and HTMX render without affecting the published copy loadOwnedTabloreturnstablo, user, ok—user.IDis already available in all discussion handlers without additional queries
Integration Points
backend/internal/web/handlers_discussion.goloadDiscussionTabData: needsuserID uuid.UUIDas a new parameter; one caller (TabloDiscussionTabHandler) passesuser.IDbackend/internal/web/handlers_discussion.goDiscussionMessageCreateHandler: setsmessage.IsOwn = falsebefore SSE publish;message.IsOwn = truebefore HTMX render (line ~141)backend/templates/tablos.templTabloDetailPage: discussion tab passesdiscussion DiscussionTabDataunchanged — no signature change needed at this level- All non-discussion handlers that pass empty
templates.DiscussionTabData{}are unaffected —IsOwndefaults tofalsewhich is correct for empty data
</code_context>
## Specific Ideas- Message bubble CSS:
.message-own .message-bubble { margin-left: auto; max-width: 70%; background: rgba(128, 78, 236, 0.10); border: 1px solid var(--color-border-subtle); border-radius: 0.75rem 0.75rem 0.25rem 0.75rem; }— asymmetric radius gives the classic chat feel for own messages; others getborder-radius: 0.25rem 0.75rem 0.75rem 0.75rem .message-metafor own:text-align: right; display: flex; justify-content: flex-end; gap: 0.5rem; margin-bottom: 0.25rem- Planning day separator: reuse the
DiscussionDaySeparatorvisual pattern — same gray-tinted strip (bg-slate-50 px-4 py-2 text-center text-sm text-slate-500) for visual consistency between the two restyled surfaces
- Hover-to-reveal timestamp (compact mode) — not in v3.0 requirements
- Message edit or delete — not in v3.0 requirements
- Sidebar collapse toggle (JS) — deferred since Phase 15; still deferred
- Mobile-responsive kanban — still deferred (RESP-01 future)
Phase: 17-chat-planning Context gathered: 2026-05-17