--- phase: 4 slug: folders-sharing-quotas-document-ux status: approved shadcn_initialized: false preset: none created: 2026-05-25 revised: 2026-05-25 reviewed_at: 2026-05-25 --- # Phase 4 — UI Design Contract > Visual and interaction contract for Phase 4: Folders, Sharing, Quotas & Document UX. > Generated by gsd-ui-researcher, verified by gsd-ui-checker. > Source: existing codebase scan + 04-CONTEXT.md decisions. --- ## Design System | Property | Value | |----------|-------| | Tool | none | | Preset | not applicable | | Component library | none (inline SVG icons, Tailwind utility classes only) | | Icon library | Inline SVG (Heroicons stroke style, w-4 h-4 / w-5 h-5) — do not add an icon package | | Font | System font stack (Tailwind default — no custom font loaded) | No design system change for Phase 4. All new components extend the existing Tailwind utility pattern established in Phase 2/3. --- ## Spacing Scale Declared values (multiples of 4 only). Source: existing component scan — `p-4`, `px-6 py-5`, `gap-3`, `px-3 py-2`. | Token | Value | Usage | |-------|-------|-------| | xs | 4px | Icon gaps, topic badge gaps (`gap-1`) | | sm | 8px | Compact element spacing, inline button padding (`py-2`) | | md | 16px | Default element spacing, card padding (`p-4`) | | lg | 24px | Section padding (`px-6 py-5`), page section breaks | | xl | 32px | Page content padding (`p-8`) | | 2xl | 48px | Major empty-state vertical padding (`py-12`) | | 3xl | 64px | Not used in Phase 4 | Exceptions: - Touch targets (icon-only buttons): minimum 44px height enforced via `min-h-[44px]` (established in ConfirmBlock.vue — apply to all icon-only action buttons in this phase). - Breadcrumb segment gap: 8px (`gap-2`) with a separator chevron (SVG, w-3 h-3). - Modal overlay: full-viewport fixed overlay `inset-0`, modal panel `max-w-md w-full` centered with `p-6` internal padding. - Breadcrumb truncation ellipsis button: `px-2 py-1` compact (4px-grid minimum). --- ## Typography Extend existing scale — do not introduce new sizes. Source: DocumentCard.vue, AppSidebar.vue, DocumentView.vue. | Role | Size | Weight | Line Height | Tailwind classes | |------|------|--------|-------------|-----------------| | Page heading | 24px (text-2xl) | 700 (font-bold) | 1.2 | `text-2xl font-bold text-gray-900` | | Section heading | 18px (text-lg) | 600 (font-semibold) | 1.3 | `text-lg font-semibold text-gray-800` | | Body / card label | 14px (text-sm) | 500 (font-medium) | 1.5 | `text-sm font-medium text-gray-900` | | Caption / metadata | 12px (text-xs) | 400 (normal) | 1.4 | `text-xs text-gray-400` | ### Typography Exceptions — Brownfield Baseline The existing Phase 2/3 components (AppSidebar.vue, DocumentCard.vue, DocumentView.vue) use a 4-weight scale: 400 (normal), 500 (medium), 600 (semibold), 700 (bold). Collapsing this to 2 weights would require a mass refactor of all pre-existing components and is out of scope for Phase 4. **Brownfield exception:** The 4-weight scale is an inherited baseline. It is not a new design decision introduced in Phase 4. **Net-new Phase 4 typography tokens** are restricted to exactly 2 weights: - `font-medium` (500) — new UI labels, nav entries, inline text in new components - `font-semibold` (600) — new section headings, modal titles, emphasis labels in new components `font-bold` (700) is already used exclusively for the logo text in AppSidebar.vue. Phase 4 introduces no new elements using `font-bold`. Monospace (extracted text panel): `text-xs font-mono` at `text-gray-600` on `bg-gray-50` — established in DocumentView.vue, reuse for audit log detail cells. Section label / nav category: `text-xs font-semibold text-gray-400 uppercase tracking-wider` — established pattern, reuse for folder section header in sidebar. --- ## Color Source: AppSidebar.vue, QuotaBar.vue, DocumentCard.vue, DocumentView.vue. No new colors introduced. | Role | Tailwind value | Hex (approx) | Usage | |------|---------------|--------------|-------| | Dominant (60%) | `bg-white` | #ffffff | Page background, cards, sidebar background | | Secondary (30%) | `bg-gray-50` / `border-gray-200` | #f9fafb / #e5e7eb | Card hover surface, extracted text block background, modal backdrop inner panel, table row stripes | | Accent (10%) | `indigo-600` / `indigo-50` / `indigo-700` | #4f46e5 | Reserved-for list below | | Destructive | `red-600` / `red-700` | #dc2626 | Destructive action buttons only | | Warning — quota amber | `amber-500` / `amber-600` | #f59e0b | QuotaBar 80–95% (pre-existing, do not change) | | Warning — quota red | `red-500` / `red-600` | #ef4444 | QuotaBar ≥95% (pre-existing, do not change) | **Accent (indigo) reserved for:** - Active nav link background: `bg-indigo-50 text-indigo-700` - Primary action buttons (filled): `bg-indigo-600 hover:bg-indigo-700 text-white` - Document icon background: `bg-indigo-50` with `text-indigo-500` icon - Logo text: `text-indigo-600` - User avatar background: `bg-indigo-100 text-indigo-700` - QuotaBar fill (normal state): `bg-indigo-500` - Link text (back navigation, inline links): `text-indigo-600 hover:underline` - Checkbox / radio checked state: `text-indigo-600` (Tailwind form plugin default) - Share badge (shared indicator on document card): `bg-indigo-50 text-indigo-600` pill Accent is NOT used on: hover states of neutral UI, sort controls, breadcrumb separators, audit log rows, sidebar section labels, permission badges (use gray), or any informational text. **Shared indicator badge** (SHARE-05): small `bg-indigo-50 text-indigo-600 text-xs font-medium px-2 py-1 rounded-full` pill labeled "Shared" — shown inline in DocumentCard below the metadata line when `doc.share_count > 0`. **"Shared with me" virtual folder entry** (D-06): rendered with a distinct inbox-style icon (`bg-purple-50 text-purple-500`) to visually separate it from user-owned folders. Purple is used ONLY for this one sidebar entry to signal "received from others" vs. "my own". No other UI element uses purple. Folder row icons in main content: `bg-gray-100 text-gray-500` (neutral — folders are structural, not accent). --- ## Component Inventory New components required for Phase 4. All follow existing utility-class patterns. Primary focal point: the document/folder list. Secondary: the breadcrumb navigation and search bar. Tertiary: sort controls. ### FolderRow.vue - Used in HomeView main content area to render sub-folders within a folder. - Layout: `flex items-center gap-3 px-4 py-3 bg-white border border-gray-200 rounded-xl hover:border-indigo-300 hover:shadow-sm transition-all cursor-pointer` - Left: folder icon `w-9 h-9 rounded-lg bg-gray-100 flex items-center justify-center` with `text-gray-500` SVG folder icon (w-5 h-5). - Middle: folder name `font-medium text-gray-900 text-sm`, sub-label `text-xs text-gray-400` showing document count (e.g., "3 documents"). - Right: three-dot menu button `text-gray-400 hover:text-gray-600` with dropdown for Rename and Delete actions. ### FolderBreadcrumb.vue - Rendered above document list when inside a folder. - Segments: `flex items-center gap-2 text-sm`. - Each clickable segment: `text-indigo-600 hover:underline font-medium`. - Separator: SVG chevron-right `w-3 h-3 text-gray-400 shrink-0`. - Truncation rule (D-02): when depth > 4, show first segment + `…` button (plain text, `px-2 py-1 text-gray-400 hover:text-gray-600`) + last 2 segments. - Current (non-clickable) final segment: `text-gray-900 font-medium`. ### ShareModal.vue - Triggered by share icon button on DocumentCard. - Modal overlay: `fixed inset-0 bg-black/40 flex items-center justify-center z-50`. - Panel: `bg-white rounded-2xl shadow-xl p-6 max-w-md w-full mx-4`. - Title: `text-lg font-semibold text-gray-900 mb-4` — "Share document". - Handle input row: `flex gap-2` — `` + primary button "Share document" `bg-indigo-600 hover:bg-indigo-700 text-white text-sm px-4 py-2 rounded-lg`. - Separator: `border-t border-gray-100 my-4`. - Recipients list: each row `flex items-center justify-between py-2`. - Left: handle `text-sm text-gray-900` + permission badge `text-xs bg-gray-100 text-gray-600 px-2 py-1 rounded-full font-medium ml-2` (shows "view"). - Right: "Remove access" `text-xs text-red-500 hover:text-red-700 font-medium` button. - Empty state: `text-sm text-gray-400 italic py-2` — "Not shared with anyone yet." - Close button: top-right `absolute top-4 right-4`, `text-gray-400 hover:text-gray-600`, SVG X icon w-5 h-5. ### FolderDeleteModal.vue - Triggered when deleting a non-empty folder (D-03). - Reuses ConfirmBlock.vue pattern but rendered in a modal panel (same overlay style as ShareModal). - Warning icon: `w-10 h-10 bg-red-50 rounded-full flex items-center justify-center` with `text-red-500` exclamation SVG, centered at top of modal. - Heading: `text-lg font-semibold text-gray-900 mt-4 text-center` — "Delete folder?" - Body: `text-sm text-gray-600 text-center mt-2 mb-6` — "This folder contains {N} documents. Deleting it will permanently delete all documents inside. This cannot be undone." - Buttons: `flex gap-3 justify-end`. Dismiss: `text-sm text-gray-600 hover:text-gray-800 px-4 py-2` — "Keep folder". Confirm: `bg-red-600 hover:bg-red-700 text-white text-sm font-semibold px-4 py-2 rounded-lg min-h-[44px]` — "Delete folder and documents". ### DocumentPreviewModal.vue (in-app mode only) - Used when `user.pdf_open_mode === 'in_app'` (D-10). - Overlay: `fixed inset-0 bg-black/60 z-50 flex flex-col`. - Header bar: `bg-white border-b border-gray-200 px-6 py-3 flex items-center justify-between`. - Left: document name `text-sm font-medium text-gray-900 truncate max-w-xs`. - Right: close button `text-gray-400 hover:text-gray-600`, SVG X w-5 h-5. - Content: `flex-1 overflow-hidden` — `