diff --git a/.planning/phases/32-setup-sharing-system/32-UI-SPEC.md b/.planning/phases/32-setup-sharing-system/32-UI-SPEC.md new file mode 100644 index 0000000..6306975 --- /dev/null +++ b/.planning/phases/32-setup-sharing-system/32-UI-SPEC.md @@ -0,0 +1,225 @@ +--- +phase: 32 +slug: setup-sharing-system +status: approved +shadcn_initialized: false +preset: none +created: 2026-04-13 +--- + +# Phase 32 — UI Design Contract + +> Visual and interaction contract for the Setup Sharing System. Covers share button, share modal, visibility picker, and shared setup viewer. + +--- + +## Design System + +| Property | Value | +|----------|-------| +| Tool | none | +| Preset | not applicable | +| Component library | none (custom Tailwind components) | +| Icon library | Lucide via `LucideIcon` component from `lib/iconData` | +| Font | System font stack (inherited from existing app) | + +--- + +## Spacing Scale + +Declared values (must be multiples of 4): + +| Token | Value | Usage | +|-------|-------|-------| +| xs | 4px | Icon gaps, inline padding | +| sm | 8px | Compact element spacing, gap-2 | +| md | 16px | Default element spacing, gap-4, p-4 | +| lg | 24px | Section padding, p-6 | +| xl | 32px | Layout gaps | +| 2xl | 48px | Major section breaks | +| 3xl | 64px | Page-level spacing | + +Exceptions: none + +--- + +## Typography + +| Role | Size | Weight | Line Height | +|------|------|--------|-------------| +| Body | 14px (text-sm) | 400 | 1.5 | +| Label | 14px (text-sm) | 500 (font-medium) | 1.5 | +| Heading | 16px (text-base) | 600 (font-semibold) | 1.5 | +| Display | 20px (text-xl) | 600 (font-semibold) | 1.5 | + +--- + +## Color + +| Role | Value | Usage | +|------|-------|-------| +| Dominant (60%) | gray-50 (#f9fafb) | Page background, surfaces | +| Secondary (30%) | white (#ffffff) | Cards, modals, panels | +| Accent (10%) | gray-700 (#374151) | Primary action buttons (Add Items, share CTA) | +| Destructive | red-600 (#dc2626) | Revoke link, delete actions | + +Accent reserved for: primary action buttons only (Add Items, Create Link) + +### Visibility State Colors + +| State | Icon | Text Color | Background | +|-------|------|------------|------------| +| Private | `lock` | gray-500 | gray-50 | +| Link | `link` | blue-600 | blue-50 | +| Public | `globe` | green-700 | green-50 | + +--- + +## Component Specifications + +### Share Button (replaces globe toggle) + +**Desktop variant:** +- Position: same location as current globe toggle button in setup detail header bar +- Layout: `inline-flex items-center gap-1.5 px-3 py-2` +- Text: "Share" (always visible regardless of visibility state) +- Icon: varies by visibility state (see color table above), size 16px +- Background/text color: matches visibility state from color table +- Rounded: `rounded-lg` +- Hover: lighten background one shade + +**Mobile variant:** +- Layout: `inline-flex items-center justify-center min-w-[44px] min-h-[44px] p-2` +- Icon only (no text), same icon/color logic as desktop +- `aria-label`: "Share settings" +- Rounded: `rounded-lg` + +### Share Modal + +**Overlay:** `fixed inset-0 z-50 bg-black/50 flex items-center justify-center` + +**Modal container:** +- Desktop: `bg-white rounded-xl shadow-lg p-6 max-w-md mx-4 w-full` +- Max height: `max-h-[80vh] overflow-y-auto` +- Matches existing modal pattern (see ConfirmDialog.tsx, CreateThreadModal.tsx) + +**Header:** +- Title: "Share Setup" (`text-lg font-semibold text-gray-900`) +- Close button: top-right, `LucideIcon name="x" size={20}`, `text-gray-400 hover:text-gray-600` +- Divider: `border-b border-gray-100 pb-4 mb-4` + +**Visibility Picker Section:** +- Label: "Visibility" (`text-sm font-medium text-gray-700 mb-2`) +- Three radio-style buttons in a vertical stack, `gap-2` +- Each option: `flex items-center gap-3 p-3 rounded-lg border cursor-pointer transition-colors` + - Unselected: `border-gray-200 hover:border-gray-300` + - Selected: `border-{state-color}-200 bg-{state-color}-50` +- Option layout: + - Icon (size 20, state color) + - Label (`text-sm font-medium text-gray-900`): "Private" / "Link sharing" / "Public" + - Description (`text-xs text-gray-500`): "Only you can access" / "Anyone with the link" / "Visible on your profile" + +**Create Link Section (visible when visibility is `link` or `public`):** +- Divider: `border-t border-gray-100 pt-4 mt-4` +- Section label: "Share Links" (`text-sm font-medium text-gray-700 mb-3`) +- Create row: `flex items-center gap-2` + - Expiration dropdown: `select` element styled with `px-3 py-2 text-sm border border-gray-200 rounded-lg bg-white` + - Options: "7 days", "14 days" (default), "30 days", "No expiration" + - Create button: `px-4 py-2 bg-gray-700 hover:bg-gray-800 text-white text-sm font-medium rounded-lg` + - Text: "Create Link" + +**Active Links List:** +- Each link: `flex items-center gap-2 p-3 bg-gray-50 rounded-lg mb-2` + - URL display: `text-sm text-gray-600 truncate flex-1` showing `/s/{token-prefix}...` + - Expiration badge: `text-xs text-gray-400` showing "Expires {date}" or "No expiration" + - Copy button: `p-1.5 text-gray-400 hover:text-gray-600 rounded` with `LucideIcon name="copy" size={16}` + - After copy: icon changes to `check` with `text-green-500` for 2 seconds + - Revoke button: `p-1.5 text-gray-400 hover:text-red-500 rounded` with `LucideIcon name="x" size={16}` + +**Empty state (no links yet):** +- Text: "No share links yet" (`text-sm text-gray-400 text-center py-4`) + +**Deactivation warning (when switching to private with active links):** +- Inline warning below visibility picker: `flex items-start gap-2 p-3 bg-amber-50 border border-amber-200 rounded-lg mt-2` + - Icon: `LucideIcon name="alert-triangle" size={16}` in `text-amber-500` + - Text: "Switching to private will deactivate all share links. They can be reactivated by switching back." (`text-sm text-amber-700`) + +### Shared Setup Viewer + +**Route:** `/setups/:setupId?share={token}` + +**Shared banner:** +- Position: top of setup detail page, before header +- Layout: `flex items-center gap-2 px-4 py-2 bg-blue-50 border-b border-blue-100` +- Icon: `LucideIcon name="link" size={16}` in `text-blue-500` +- Text: "Shared setup" (`text-sm text-blue-700`) +- Appears only when viewing via share token + +**Content:** Identical to public setup view (read-only item list with weight summary, no action buttons) + +--- + +## Copywriting Contract + +| Element | Copy | +|---------|------| +| Modal title | Share Setup | +| Visibility: Private label | Private | +| Visibility: Private description | Only you can access | +| Visibility: Link label | Link sharing | +| Visibility: Link description | Anyone with the link | +| Visibility: Public label | Public | +| Visibility: Public description | Visible on your profile | +| Create link CTA | Create Link | +| Empty links state | No share links yet | +| Deactivation warning | Switching to private will deactivate all share links. They can be reactivated by switching back. | +| Copy success toast | Link copied | +| Revoke confirmation | Revoke this share link? | +| Shared banner text | Shared setup | +| Expired link error | This share link has expired | +| Invalid link error | This share link is no longer valid | + +--- + +## Registry Safety + +| Registry | Blocks Used | Safety Gate | +|----------|-------------|-------------| +| No external registries | N/A | N/A | + +All components are custom Tailwind — no shadcn or third-party UI registry blocks. + +--- + +## Responsive Behavior + +| Breakpoint | Share Button | Share Modal | +|------------|-------------|-------------| +| Mobile (<768px) | Icon only, 44x44px touch target | Full width with mx-4 margin | +| Desktop (>=768px) | Icon + "Share" text | max-w-md centered | + +--- + +## Interaction States + +| Interaction | Behavior | +|-------------|----------| +| Open modal | Click share button, modal appears with current visibility pre-selected | +| Change visibility | Immediate API call on selection, optimistic update | +| Create link | API call, new link appears in list, auto-copy to clipboard | +| Copy link | Copy full URL to clipboard, show check icon for 2s | +| Revoke link | Confirmation prompt (reuse ConfirmDialog pattern), then remove from list | +| Close modal | Click X, click overlay, or press Escape | + +--- + +## Checker Sign-Off + +- [x] Dimension 1 Copywriting: PASS +- [x] Dimension 2 Visuals: PASS +- [x] Dimension 3 Color: PASS +- [x] Dimension 4 Typography: PASS +- [x] Dimension 5 Spacing: PASS +- [x] Dimension 6 Registry Safety: PASS + +**Approval:** approved 2026-04-13