# Technology Stack — DocuVault v0.2: UI Overhaul and Optimization

**Milestone:** v0.2 — UI Overhaul and Optimization
**Researched:** 2026-06-07
**Scope:** New additions only. The v0.1 stack (FastAPI, Vue 3, Pinia, Vue Router 4, Vite, Tailwind CSS, PostgreSQL, MinIO) is unchanged and not re-researched here.

---

## Existing Stack Baseline (Confirmed, Do Not Change)

| Component | Current Version (package.json / requirements.txt) | Notes |
|-----------|--------------------------------------------------|-------|
| Vue | `^3.4.0` | Must bump to `^3.5.0` (see VueUse note below) |
| Vite | `^5.2.0` | Must bump to `^6.0.0` — Vite 8 is latest; skip 8 (too new) |
| Tailwind CSS | `^3.4.0` | Stay on v3; v4 is out but requires config rewrite |
| Pinia | `^2.1.0` | No change needed |
| Vue Router | `^4.3.0` | No change needed |

---

## New Frontend Dependencies

### 1. VueUse — `@vueuse/core` + `@vueuse/integrations`

**Recommended:** `@vueuse/core@^14.3.0`, `@vueuse/integrations@^14.3.0`
**Confidence:** HIGH (verified from Context7 docs and npm registry)

**Why:** VueUse provides three composables this milestone needs, each saving 30-100 lines of custom code that would otherwise require careful browser-compatibility testing:

- `useDropZone` — drop-target zones for drag-and-drop file upload (handles `dragenter`/`dragleave`/`drop`, exposes `isOverDropZone` reactive ref, correct `dataTypes` filtering, Safari limitations documented)
- `onKeyStroke` — keyboard shortcut registration without manual `addEventListener`/`removeEventListener` lifecycle management
- `useSortable` (from `@vueuse/integrations`) — thin wrapper over SortableJS for list/folder reordering, auto-syncs to reactive array

**Bundle impact:** `@vueuse/core` unpacked size is ~870 KB but it is fully tree-shakeable — only imported composables are included in the bundle. Each composable is typically 1-3 KB gzipped. This is the correct way to import:

```js
import { useDropZone, onKeyStroke } from '@vueuse/core'
import { useSortable } from '@vueuse/integrations/useSortable'
```

**Critical version constraint:** VueUse v14.0+ requires `vue@^3.5.0`. The current `package.json` pins `^3.4.0`. Vue 3.5 has no breaking changes for Options API components (confirmed from official Vue blog — purely additive release: reactivity improvements, -56% memory usage). Bump Vue to `^3.5.0` as part of this milestone's first phase.

**`@vueuse/integrations` peer deps:** `useSortable` requires `sortablejs@^1` as a peer dependency. Install `sortablejs` alongside.

```bash
npm install @vueuse/core@^14.3.0 @vueuse/integrations@^14.3.0 sortablejs@^1.15.7
npm install -D @types/sortablejs
```

**Do NOT use VueUse as a global plugin** (`app.use(VueUse)`) — it doesn't have one. Import composables individually, always.

---

### 2. vue-virtual-scroller — `vue-virtual-scroller`

**Recommended:** `vue-virtual-scroller@^3.0.4`
**Confidence:** HIGH (verified from Context7 docs and npm registry; published 2026-05-20, actively maintained)

**Why:** Document lists can reach hundreds or thousands of entries per user. Without virtual scrolling, rendering all `DocumentCard` components simultaneously degrades scroll performance significantly on mobile. `vue-virtual-scroller` is the canonical Vue 3 virtual list library by Guillaume Chau (Akryum, Vue core team contributor). It ships ESM-only, is Vite-native, and requires Vue 3.3+ (compatible with our 3.5 bump).

**Components used:**
- `<RecycleScroller>` — fixed item height, highest performance. Use for the main document grid where item height is deterministic.
- `<DynamicScroller>` — variable item height. Use only if needed (e.g. list view with text previews of different lengths).

**Bundle size:** Unpacked ~461 KB; ESM-only means Rollup tree-shakes unused components. Requires importing its CSS: `import 'vue-virtual-scroller/index.css'`.

**Integration note:** Because `RecycleScroller` pools and reuses DOM nodes, every item component must be fully reactive from props alone — no internal `mounted()` side effects that assume a fresh component per item. Options API components that follow the props-down / events-up pattern (as required by the existing architecture) are fully compatible.

```bash
npm install vue-virtual-scroller@^3.0.4
```

---

### 3. Drag-and-Drop for File Upload — Native HTML5 + VueUse `useDropZone`

**Recommended:** No additional library. Use `useDropZone` from `@vueuse/core` (already added above).
**Confidence:** HIGH

**Why not a dedicated library:** The drag-and-drop requirement for this milestone is drop-zone file upload (files dragged from OS to a browser area). `useDropZone` covers this completely. Dedicated upload libraries like `dropzone-vue` or `vue-file-uploader` are either poorly maintained, opinionated about UI, or duplicate what the existing upload flow already does. The existing backend upload endpoint and progress tracking are already implemented — adding a library that wraps both drop detection and upload would require replacing working code.

**For folder/list reordering** (drag document into a different folder), use `useSortable` from `@vueuse/integrations` (already added above via SortableJS). SortableJS handles this precisely: it is a low-level, pure-DOM drag-sort library with no Vue-specific assumptions.

**Do NOT use `vue-draggable-plus` (0.6.1, last published 2026-01-11):** It wraps SortableJS but adds a Vue component abstraction layer that conflicts with the project's data-provider view pattern. It also adds bundle overhead that `useSortable` avoids. The last publish date of January 2026 is acceptable for a stable library, but there is no reason to prefer it over the VueUse integration that is already in the bundle.

---

### 4. Keyboard Shortcuts — VueUse `onKeyStroke`

**Recommended:** `onKeyStroke` from `@vueuse/core` (already added). No additional library.
**Confidence:** HIGH

**Why no dedicated library:** Libraries like `vue-shortkey` or `hotkeys-js` exist but are unnecessary overhead. `onKeyStroke` from VueUse handles:
- Single key listening: `onKeyStroke('/', handler)` (focus search)
- Modifier combos: check `event.ctrlKey` / `event.metaKey` inside the handler
- Automatic cleanup when the component is unmounted (it wraps `addEventListener`/`removeEventListener` with `onUnmounted`)

For a document manager, typical shortcuts are: `/` (search focus), `Escape` (close modal/panel), `Delete` (delete selected), `Enter` (open/confirm). All fit `onKeyStroke` with no wrapper library.

**Implementation pattern:**
```js
// In setup() or a composable
import { onKeyStroke } from '@vueuse/core'

onKeyStroke('/', (e) => {
  e.preventDefault()
  searchInput.value?.focus()
})
```

---

### 5. Loading Skeletons — Pure Tailwind CSS

**Recommended:** No library. Use `animate-pulse` utility with placeholder shapes.
**Confidence:** HIGH

**Why no library:** Skeleton loaders in this app are simple rectangular placeholders — cards, list rows, and a few text lines. Tailwind's `animate-pulse` combined with `bg-gray-200 dark:bg-gray-700` rounded shapes is sufficient and produces zero additional bundle weight. A skeleton library (`vue3-skeleton`, etc.) would add a dependency for something that is 5 lines of Tailwind HTML per component.

**Recommended pattern for a document card skeleton:**
```html
<div class="animate-pulse rounded-lg border border-gray-200 p-4">
  <div class="h-4 bg-gray-200 rounded w-3/4 mb-2"></div>
  <div class="h-3 bg-gray-200 rounded w-1/2"></div>
</div>
```

For a shimmer effect (more polished than pulse), use a CSS `@keyframes` background-position animation — still zero JS dependency. Only add a library if shimmer is required across 10+ distinct skeleton shapes that need consistent management.

---

### 6. Tailwind CSS Plugins

**Recommended:** `@tailwindcss/forms@^0.5.11`
**Confidence:** HIGH (official Tailwind Labs plugin, 4,500+ GitHub stars, supports both v3 and v4)

**Why `@tailwindcss/forms`:** DocuVault has several form-heavy screens (login, registration, upload dialog, admin user management, cloud credential entry). Browser defaults for `<input>`, `<select>`, `<textarea>`, `<checkbox>`, and `<radio>` are inconsistent across platforms and resist Tailwind utility styling. `@tailwindcss/forms` provides a minimal, accessible reset that makes all form elements style-consistent and fully overridable with utilities. Unpacked size is 55 KB; it is a PostCSS plugin with zero runtime JS.

```bash
npm install -D @tailwindcss/forms@^0.5.11
```

```js
// tailwind.config.js
plugins: [require('@tailwindcss/forms')]
```

**Container queries (`@tailwindcss/container-queries`):** Do NOT add. In Tailwind v3, this would be the `@tailwindcss/container-queries` plugin. However, the v0.2 responsive layout target is standard viewport-based breakpoints (`sm:`, `md:`, `lg:`). Container queries are useful when the same component is rendered in contexts of different widths (sidebar vs. main content), but the DocuVault component architecture uses the data-provider view pattern where layout is controlled by the view, not by individual components. This eliminates the primary use case for container queries in this codebase. If a future phase introduces a component genuinely embedded in variable-width containers, add the plugin then.

**Typography plugin (`@tailwindcss/typography`):** Do NOT add. There is no long-form rich text content in this app — document content is displayed in a preview iframe, not rendered as styled HTML.

---

### 7. Bundle Analysis — `rollup-plugin-visualizer`

**Recommended:** `rollup-plugin-visualizer@^7.0.1` (dev dependency, build-time only)
**Confidence:** MEDIUM-HIGH (standard tool, actively maintained, Vite-native)

**Why:** The bundle size reduction goal requires knowing what is currently in the bundle. `rollup-plugin-visualizer` generates an interactive treemap that shows each module's contribution. Run once at the start of v0.2 to establish baseline, and again after refactoring to verify improvement. It does not affect the production build.

```bash
npm install -D rollup-plugin-visualizer@^7.0.1
```

```js
// vite.config.js — enable only when analyzing, not in every build
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [
    vue(),
    process.env.ANALYZE ? visualizer({ open: true, filename: 'dist/stats.html' }) : null,
  ].filter(Boolean),
})
```

Run with: `ANALYZE=1 npm run build`

---

## Vite Upgrade

**Recommended:** Bump from `^5.2.0` to `^6.4.3` (the stable Vite 6 release; avoid Vite 7/8 beta)
**Confidence:** HIGH (Vite 6 released 2024-11; Vite 7 and 8 are in beta as of 2026-06)

**Why Vite 6 over staying on Vite 5:** Vite 6 is a stable, minimal-breaking-change major release. Breaking changes for this project are:
1. **Node.js ≥ 20.0.0 required** — Docker Compose service should already use Node 20+ (verify `FROM node:20-alpine` or equivalent in any Dockerfile that runs the frontend build).
2. **No other breaking changes apply** to this project (CSS library output file rename only affects library authors, not app builds; SSR CSS change is not applicable).

Vite 6 delivers improved dependency pre-bundling and module runner improvements relevant to the performance goals. The built-in code splitting (`import()` dynamic imports for lazy routes) and tree-shaking require no config changes — they are on by default. The manual chunking config for vendor splitting (Pinia, Vue Router) in `build.rollupOptions.output.manualChunks` can be used if the baseline analysis shows large vendor chunks being repeated across async routes.

**Do NOT upgrade to Vite 7 or 8** — both are in beta as of this research date and are not appropriate for a production milestone.

```bash
npm install -D vite@^6.4.3 @vitejs/plugin-vue@^5.0.0
```

---

## Vue Version Bump

**Required:** `^3.4.0` → `^3.5.0`
**Confidence:** HIGH (no breaking changes for Options API; confirmed from official Vue 3.5 blog post)

This is required by `@vueuse/core@^14.0`. Vue 3.5 is a drop-in upgrade for Options API components — the release is purely additive (reactivity performance improvements, `useTemplateRef()` for Composition API). No component changes are needed.

```bash
npm install vue@^3.5.0
```

---

## Backend — No New Python Packages Needed

**Confidence:** HIGH

FastAPI's `APIRouter` (already used) is the correct and sufficient tool for router decomposition. Splitting large router files into smaller modules uses only `APIRouter(prefix=..., tags=[...])` and `app.include_router()` — both already in the project. No new library is warranted.

**Pattern for large router decomposition:**
```
backend/routers/
  documents/
    __init__.py     # from .upload import router as upload_router; etc.
    upload.py       # APIRouter for POST /documents
    search.py       # APIRouter for GET /documents
    share.py        # APIRouter for share endpoints
  admin/
    __init__.py
    users.py
    audit.py
    settings.py
```

Include in `main.py`:
```python
from routers.documents import router as documents_router
from routers.admin import router as admin_router

app.include_router(documents_router, prefix="/documents", tags=["documents"])
app.include_router(admin_router, prefix="/admin", tags=["admin"])
```

This is a pure refactor — no new dependencies, no behavior change, no test changes except updating import paths.

---

## Full v0.2 Dependency Diff

### `npm install` (production dependencies)

```bash
npm install \
  vue@^3.5.0 \
  @vueuse/core@^14.3.0 \
  @vueuse/integrations@^14.3.0 \
  sortablejs@^1.15.7 \
  vue-virtual-scroller@^3.0.4
```

### `npm install -D` (dev dependencies)

```bash
npm install -D \
  vite@^6.4.3 \
  @vitejs/plugin-vue@^5.0.0 \
  @tailwindcss/forms@^0.5.11 \
  rollup-plugin-visualizer@^7.0.1 \
  @types/sortablejs@^1.15.0
```

### Python backend additions

**None.** All backend work (router decomposition, service extraction) uses FastAPI primitives already present.

---

## Alternatives Considered and Rejected

| Category | Rejected | Reason |
|----------|----------|--------|
| Drag-and-drop file upload | `dropzone-vue`, `vue-file-uploader` | Opinionated UI, poor maintenance, duplicates existing backend upload flow |
| Drag-and-drop sorting | `vue-draggable-plus@0.6.1` | Adds Vue component abstraction over SortableJS that conflicts with data-provider view pattern; VueUse `useSortable` covers the same need with less surface area |
| Virtual scrolling | `@tanstack/vue-virtual` | Lower-level API requiring more integration code than `vue-virtual-scroller`; no meaningful bundle advantage; vue-virtual-scroller is Vue-native |
| Keyboard shortcuts | `hotkeys-js`, `vue-shortkey` | Unnecessary wrapper over `addEventListener`; VueUse `onKeyStroke` is sufficient and already in the bundle |
| Loading skeletons | `vue3-skeleton` | 5-line Tailwind pattern produces identical results; no justification for an additional dependency |
| Tailwind container queries | `@tailwindcss/container-queries` | Viewport breakpoints are sufficient for this app's layout; container queries solve a problem this codebase does not have |
| Tailwind typography | `@tailwindcss/typography` | No prose HTML content in this app |
| Vite 7 / 8 | Latest beta | Both in beta as of research date; not appropriate for a production milestone |

---

## Compatibility Matrix (v0.2 additions)

| Package | Version | Vue Requirement | Vite Compatibility | Tree-Shakeable |
|---------|---------|-----------------|-------------------|----------------|
| `@vueuse/core` | ^14.3.0 | ^3.5.0 | Vite 5+ | Yes — import individually |
| `@vueuse/integrations` | ^14.3.0 | ^3.5.0 | Vite 5+ | Yes |
| `sortablejs` | ^1.15.7 | — (plain JS) | Any | Yes |
| `vue-virtual-scroller` | ^3.0.4 | ^3.3.0 | Vite (ESM) | Partial (register components individually) |
| `@tailwindcss/forms` | ^0.5.11 | — (PostCSS) | Any | N/A (CSS plugin) |
| `rollup-plugin-visualizer` | ^7.0.1 | — (build tool) | Vite 5+ | N/A (dev only) |
| `vue` | ^3.5.0 | — | — | — |
| `vite` | ^6.4.3 | — | — | — |

---

## Sources

- VueUse v14.3.0 docs and peer dependencies: Context7 `/vueuse/vueuse`, verified via npm registry (2026-06-07)
- vue-virtual-scroller v3.0.4: Context7 `/akryum/vue-virtual-scroller`, npm registry (last published 2026-05-20)
- vue-draggable-plus v0.6.1: npm registry (last published 2026-01-11) — rejected
- sortablejs v1.15.7: npm registry (2026-06-07)
- Tailwind CSS container queries: official Tailwind CSS announcement — built into v4 core; plugin for v3 would be `@tailwindcss/container-queries` — rejected as out of scope
- @tailwindcss/forms: official Tailwind Labs GitHub (tailwindlabs/tailwindcss-forms), 4,500+ stars, supports v3 and v4
- rollup-plugin-visualizer: npm registry, v7.0.1 (2026-06-07)
- Vue 3.5 no-breaking-changes confirmation: https://blog.vuejs.org/posts/vue-3-5
- Vite 6 migration guide: https://v6.vite.dev/guide/migration
- FastAPI router decomposition: https://fastapi.tiangolo.com/tutorial/bigger-applications/ (built-in, no new deps)

---
*Stack research for: DocuVault v0.2 — UI Overhaul and Optimization*
*Researched: 2026-06-07*
