7.9 KiB
phase, slug, status, shadcn_initialized, preset, created
| phase | slug | status | shadcn_initialized | preset | created |
|---|---|---|---|---|---|
| 32 | setup-sharing-system | approved | false | none | 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
- Unselected:
- 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:
selectelement styled withpx-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"
- Expiration dropdown:
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-1showing/s/{token-prefix}... - Expiration badge:
text-xs text-gray-400showing "Expires {date}" or "No expiration" - Copy button:
p-1.5 text-gray-400 hover:text-gray-600 roundedwithLucideIcon name="copy" size={16}- After copy: icon changes to
checkwithtext-green-500for 2 seconds
- After copy: icon changes to
- Revoke button:
p-1.5 text-gray-400 hover:text-red-500 roundedwithLucideIcon name="x" size={16}
- URL display:
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}intext-amber-500 - Text: "Switching to private will deactivate all share links. They can be reactivated by switching back." (
text-sm text-amber-700)
- Icon:
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}intext-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
- 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: approved 2026-04-13