Files
2026-04-06 12:31:10 -06:00

10 KiB
Raw Permalink Blame History

phase, slug, status, shadcn_initialized, preset, created
phase slug status shadcn_initialized preset created
19 template-editor-ui draft false none 2026-04-06

Phase 19 — UI Design Contract

Visual and interaction contract for the template editor UI phase. Generated by gsd-ui-researcher, verified by gsd-ui-checker.


Design System

Property Value
Tool none — manual inline styles + Tailwind utilities
Preset not applicable
Component library none (Radix not installed; components are custom)
Icon library none (text/Unicode symbols used inline, e.g. +, , ×)
Font (sans) Geist Sans via var(--font-sans) / var(--font-geist-sans)
Font (serif/display) Cormorant Garamond via var(--font-serif) — wordmark only

Source: globals.css, PortalNav.tsx, layout.tsx

Note: components.json was not found. The project does not use shadcn. All new components follow the existing inline-style + selective Tailwind pattern established in PreparePanel, ClientsPageClient, and PortalNav.


Spacing Scale

Declared values (multiples of 4 only):

Token Value Usage
xs 4px Icon gaps, dot indicator margin
sm 8px Inline element spacing, button padding vertical
md 16px Panel internal padding, form field gaps
lg 24px Section spacing within panels, list gaps
xl 32px Page-level top margin
2xl 48px Empty-state vertical padding
3xl 64px Nav height (established: PortalNav is 64px)

Exceptions:

  • Role color dot: 8px diameter (not a layout token — cosmetic)
  • Signer/role pill height: 32px (matches PreparePanel signer pill pattern)
  • Right panel (TemplatePanel) width: 280px fixed — matches PreparePanel column width in DocumentPageClient split layout
  • AI Auto-place button and Save button minimum height: 36px (matches existing PreparePanel button height)

Source: PreparePanel.tsx, PortalNav.tsx, layout.tsx (2rem = 32px page padding)


Typography

Role Size Weight Line Height
Body 14px (0.875rem) 400 1.5
Label / caption 12px (0.75rem) 400 1.4
Section heading 14px (0.875rem) 600 1.3
Page heading 24px (1.5rem) 700 1.2

Source: ClientsPageClient.tsx (h1: 1.5rem/700), PreparePanel.tsx (h2: font-semibold/text-gray-900, body: 0.875rem/400), PortalNav.tsx (links: 0.8125rem/500 uppercase)

Weights used: regular (400) and semibold/bold (600/700). No third weight.

Letter-spacing exceptions:

  • Nav links: letter-spacing: 0.08em; text-transform: uppercase — preserved exactly, no change
  • Wordmark: letter-spacing: 0.02em — no change

Color

Role Value Usage
Dominant (60%) #FAF9F7 (cream) Page background, panel backgrounds, modal backgrounds
Secondary (30%) #F9FAFB / #F0EDE8 (cream-mid) Panel interior fill, list row backgrounds, right panel
Accent (10%) #C9A84C (gold) Primary CTA buttons only, active nav underline
Navy #1B2B4B Page headings, nav background, secondary button fill
Destructive #DC2626 (red-600) Delete role action button only

Accent (#C9A84C) reserved for:

  • "Save Template" button (primary CTA)
  • "Add Role" button (secondary CTA within TemplatePanel)
  • Active nav link underline (existing pattern, unchanged)

Navy (#1B2B4B) used for:

  • "AI Auto-place" button fill (matches PreparePanel AI button pattern)
  • Page heading text
  • Wordmark

Semantic colors:

  • Success inline: #059669 (green — "Saved" confirmation inline text, same as PreparePanel "Sent ✓")
  • Error inline: #DC2626 (red — error messages)
  • Muted text: #6B7280 (gray-500 — counts, timestamps, placeholder copy)
  • Border default: #E5E7EB (gray-200)

Source: globals.css, PreparePanel.tsx, ClientsPageClient.tsx, ConfirmDialog.tsx


Component Inventory

Components this phase must produce (from CONTEXT.md D-12/D-13):

1. TemplatePanel (new)

Right panel, 280px wide, sticky within the editor view.

Sections (top to bottom):

  1. Template name — editable inline <input> (14px, border-bottom-only style when focused, gray-200 border at rest). Displays current name from DB.
  2. Roles section — heading "Signers / Roles" (12px uppercase label), list of role pills, "Add role" button.
  3. Role pill — colored dot (8px, background: role.color) + role label string + rename icon (pencil, text "Edit") + remove button (×). Free-form rename via inline edit on click.
  4. Add role — text input + "Add" button. Preset suggestions: "Buyer", "Co-Buyer", "Seller", "Co-Seller" as clickable chips below the input.
  5. AI Auto-place button — full-width, navy fill (#1B2B4B), white text, 36px height. Label: "AI Auto-place Fields". Shows spinner + "Placing…" during loading.
  6. Save button — full-width, gold fill (#C9A84C), white text, 36px height. Label: "Save Template". Shows "Saving…" during loading. Shows inline "Saved" (green) or error message after.

2. TemplatePageClient (new)

State owner. Layout: CSS flexbox row — left: PdfViewerWrapper + FieldPlacer (flex: 1, min-width: 0), right: TemplatePanel (280px, flex-shrink: 0). Gap: 24px. Pattern mirrors DocumentPageClient.

3. Templates list page (new)

  • Page heading: "Templates" (24px/700, navy)
  • Subtitle: "{N} template{s}" (14px/400, gray-500)
  • "New Template" button: top-right, gold fill (primary CTA). Label: "+ New Template"
  • Table/list rows: each row shows template name (14px/600, navy), form name (14px/400, gray-500), field count ("N fields", 12px/400, gray-500), last updated (12px/400, gray-500). Click row → navigate to editor.
  • Row hover: background: #F0EDE8 (cream-mid), cursor pointer.

4. PortalNav update

Add "Templates" link between "Clients" and "Profile" in navLinks array. No visual change — same style as existing links.

5. FieldPlacer update (non-breaking)

Add optional onPersist?: (fields: SignatureFieldData[]) => Promise<void> | void prop. When provided, replaces the 4 internal persistFields(docId, fields) call sites. Existing callers unaffected.


Interaction States

AI Auto-place button

State Visual
Idle Navy fill, "AI Auto-place Fields", full opacity
Loading Navy fill, "Placing…" + spinner (CSS border-radius spin), disabled
Success Button returns to idle; FieldPlacer reloads via key increment
Error Inline error message below button in red-600 text (14px), button returns to idle

Save button

State Visual
Idle Gold fill, "Save Template", full opacity
Loading Gold fill at 0.7 opacity, "Saving…", disabled
Success Inline "Saved" text in green (#059669) below button, fades after 3s
Error Inline error in red below button

Role pill inline rename

  • Click role label → label becomes a text input (same font, border-bottom style)
  • Enter or blur → commits rename, calls PATCH /api/templates/[id] with updated signatureFields role strings
  • Escape → cancels rename, reverts to original label

Remove role

  • Click × on role pill
  • If no fields are assigned to that role: remove immediately
  • If fields are assigned to that role: show ConfirmDialog — "Remove role?" / "Removing 'Buyer' will unassign {N} field(s). This cannot be undone." / Confirm: "Remove Role" (red-600), Cancel: "Cancel"

New Template modal (from list page)

  • Reuses AddDocumentModal pattern: overlay, white rounded card, form fields.
  • Fields: "Template name" (text input), "Select form" (PDF form picker from library).
  • CTA: "Create Template" (gold fill). On success: navigate to /portal/templates/[id].

Layout

Template editor page (/portal/templates/[id])

[PortalNav — 64px sticky top]
[main — max-width 1200px, margin: 0 auto, padding: 32px]
  [page header — flex row]
    [h1 "Edit Template: {name}"] [field count badge "{N} fields placed" — optional, muted]
  [editor body — flex row, gap: 24px]
    [left: PdfViewerWrapper + FieldPlacer — flex:1]
    [right: TemplatePanel — width: 280px, flex-shrink: 0]

Templates list page (/portal/templates)

[PortalNav — 64px sticky top]
[main — max-width 1200px, margin: 0 auto, padding: 32px]
  [page header — flex row space-between]
    [h1 "Templates"] [subtitle "{N} templates"]    [CTA "+ New Template"]
  [list — flex column, gap: 8px]
    [template row × N]

Copywriting Contract

Element Copy
Primary CTA (list page) "+ New Template"
Primary CTA (editor) "Save Template"
Secondary CTA (editor) "AI Auto-place Fields"
Nav link "Templates"
Page heading (list) "Templates"
Page heading (editor) "Edit Template: {name}"
Empty state heading (list) "No templates yet"
Empty state body (list) "Create a template to reuse field placements across documents."
Empty state CTA (list) "+ Create your first template"
Error — AI auto-place "AI placement failed. Check that the form PDF is accessible and try again."
Error — save "Save failed. Please try again."
Loading — AI "Placing…"
Loading — save "Saving…"
Success — save "Saved"
Panel section label — roles "Signers / Roles"
Role add placeholder "Role label (e.g. Buyer)"
Role remove confirm title "Remove role?"
Role remove confirm body "Removing '{role}' will unassign {N} field(s). This cannot be undone."
Role remove confirm CTA "Remove Role"
Role remove cancel "Cancel"
Field count badge "{N} field{s} placed"

Source: CONTEXT.md decisions D-04, D-05, D-12, D-13; modeled on PreparePanel copy patterns.


Registry Safety

Registry Blocks Used Safety Gate
shadcn official none — shadcn not installed not applicable
Third-party none declared not applicable

No third-party registry blocks in scope for this phase.


Checker Sign-Off

  • Dimension 1 Copywriting: PASS
  • Dimension 2 Visuals: PASS
  • Dimension 3 Color: PASS
  • Dimension 4 Typography: PASS
  • Dimension 5 Spacing: PASS
  • Dimension 6 Registry Safety: PASS

Approval: pending