diff --git a/.planning/phases/17-chat-planning/17-UI-SPEC.md b/.planning/phases/17-chat-planning/17-UI-SPEC.md new file mode 100644 index 0000000..4c4e567 --- /dev/null +++ b/.planning/phases/17-chat-planning/17-UI-SPEC.md @@ -0,0 +1,278 @@ +--- +phase: 17 +slug: chat-planning +status: draft +shadcn_initialized: false +preset: none +created: 2026-05-17 +--- + +# Phase 17 — UI Design Contract + +> Visual and interaction contract for Phase 17: Chat & Planning. +> Generated by gsd-ui-researcher, verified by gsd-ui-checker. + +--- + +## Design System + +| Property | Value | +|----------|-------| +| Tool | none — custom CSS design system via `backend/internal/web/ui/` | +| Preset | not applicable | +| Component library | templ components + design-token CSS classes | +| Icon library | Heroicons (SVG, inline) | +| Font | ui-sans-serif / system-ui stack (declared in `base.css` body rule) | + +Source: `backend/internal/web/ui/base.css`, `backend/internal/web/ui/app.css`, `backend/internal/web/ui/card.css` + +--- + +## Spacing Scale + +All values multiples of 4. Tokens are used in CSS via explicit `px`/`rem` values that map to this scale. + +| Token | Value | Usage | +|-------|-------|-------| +| xs | 4px (0.25rem) | Icon gaps, inline tight padding | +| sm | 8px (0.5rem) | `.message-meta` gap, separator padding | +| md | 16px (1rem) | Default element padding (message bubble horizontal) | +| lg | 24px (1.5rem) | `.overview-section-heading` margin-bottom | +| xl | 32px (2rem) | `dashboard-main` padding | +| 2xl | 48px (3rem) | Major section breaks | +| 3xl | 64px (4rem) | Page-level spacing | + +Exceptions: +- Message bubble vertical padding: 12px (0.75rem) — between sm and md, acceptable for chat density +- `.ui-card-header` / `.ui-card-body` / `.ui-card-footer` padding: 20px 24px (1.25rem 1.5rem) — defined by `card.css`, must not change +- Touch targets for tab nav items: 44px min-height — already declared in `.tab-nav-item` + +--- + +## Typography + +All sizes match existing system. No new sizes are introduced. + +| Role | Size | Weight | Line Height | Token / Class | +|------|------|--------|-------------|---------------| +| Page heading (Planning h1) | 28px (1.75rem) | 600 | 1.25 | Matches `text-[28px] font-semibold` already in planning.templ | +| Section heading (Discussion h2) | 24px (1.5rem) | 600 | 1.25 | Matches `text-2xl font-semibold` already in discussion.templ | +| Body / message text | 16px (1rem) | 400 | 1.5 (leading-6) | `.message-bubble p` — inherits base | +| Label / meta (author, timestamp, date separator) | 14px (0.875rem) | 400 (author: 600) | 1.4 | `.message-meta` | + +Source: `discussion.templ` existing type scale, `planning.templ` existing type scale — phase 17 must not introduce additional sizes. + +--- + +## Color + +All values use design tokens from `backend/internal/web/ui/base.css`. No hardcoded hex in new CSS. + +| Role | Token | Resolved value | Usage | +|------|-------|----------------|-------| +| Dominant (60%) | `--color-surface-default` | `#ffffff` | Page background, `.ui-card` surface, others' message bubbles | +| Secondary (30%) | `--color-surface-subtle` / `--color-surface-muted` | `hsl(0 0% 96.1%)` / `#f3f4f6` | Day separators (`bg-slate-50` equivalent), event list backgrounds, composer border area | +| Accent (10%) | `--color-brand-primary` | `#804eec` | Own message bubble tint background only | +| Destructive | `--color-status-danger-strong` | `#dc2626` | Not applicable in this phase — no destructive actions | + +**Accent reserved for:** Own message bubble background tint only (`rgba(128, 78, 236, 0.10)` — use the resolved value directly in `.message-own .message-bubble`). The brand accent must NOT appear on borders, nav items, or other phase 17 elements. + +**Border tokens:** +- Message bubbles: `--color-border-subtle` (`#d0d5dd`) — 1px solid +- Discussion card container (`.ui-card`): `--color-border-default` (`hsl(0 0% 90.9%)`) +- Day separators: no explicit border — background differentiation only + +**Text tokens:** +- Message body: `--color-text-primary` (`hsl(0 0% 9%)`) +- Meta (author email, timestamp, date separator): `--color-text-muted` (`hsl(0 0% 43.5%)`) +- Author email own message: `--color-text-brand` (`#804eec`) — weight 600 +- Planning event title: `--color-surface-muted-inverse` (`#111827`) +- Planning event time/tablo label: `--color-text-secondary` (`#475467`) + +--- + +## Component Inventory + +### New CSS classes (all go into `backend/internal/web/ui/app.css`) + +#### `.message-row` +```css +.message-row { + padding: 0.75rem 1rem; +} +``` +Flex container for a single message. No divider — drop `divide-y divide-slate-100` from the parent; bubbles carry their own visual weight. + +#### `.message-own` +```css +.message-own { + display: flex; + flex-direction: column; + align-items: flex-end; +} +``` + +#### `.message-other` +```css +.message-other { + display: flex; + flex-direction: column; + align-items: flex-start; +} +``` + +#### `.message-bubble` +```css +.message-bubble { + border: 1px solid var(--color-border-subtle); + border-radius: 0.25rem 0.75rem 0.75rem 0.75rem; + max-width: 70%; + padding: 0.75rem 1rem; + white-space: pre-wrap; + word-break: break-words; +} +``` + +#### `.message-own .message-bubble` +```css +.message-own .message-bubble { + background: rgba(128, 78, 236, 0.10); + border-radius: 0.75rem 0.75rem 0.25rem 0.75rem; +} +``` +Own messages: top-right corner is tight (0.25rem) — classic outgoing bubble cue. + +#### `.message-other .message-bubble` +```css +.message-other .message-bubble { + background: var(--color-surface-default); +} +``` + +#### `.message-meta` +```css +.message-meta { + display: flex; + gap: 0.5rem; + margin-bottom: 0.25rem; + font-size: 0.875rem; + color: var(--color-text-muted); +} +``` + +#### `.message-own .message-meta` +```css +.message-own .message-meta { + justify-content: flex-end; +} +``` + +#### `.message-meta .message-author` +```css +.message-meta .message-author { + font-weight: 600; +} +``` + +### Existing classes applied to new elements + +| Element | Class | Source | +|---------|-------|--------| +| Discussion messages container `#discussion-messages` | `.ui-card` | `backend/internal/web/ui/card.css` | +| Planning page header wrapper | `.overview-section` | `backend/internal/web/ui/app.css` section 13 | +| Planning heading row | `.overview-section-heading` | `backend/internal/web/ui/app.css` section 13 | +| Planning empty state | `@ui.EmptyState(...)` | `backend/internal/web/ui/empty_state.templ` | + +### Planning day separator element + +Use the same visual approach as `DiscussionDaySeparator` for cross-surface consistency: + +```html +