Files
kite/.planning/research/STACK.md
T
curo1305andClaude Sonnet 4.6 f7758776ec docs: define milestone v0.2 requirements (36 reqs, 6 categories)
UI overhaul, codebase quality, admin panel rearchitecture, UX
improvements, responsive layout, and performance/stack updates.
Research: STACK.md, FEATURES.md, ARCHITECTURE.md, PITFALLS.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 09:22:40 +02:00

17 KiB

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:

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.

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.

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:

// 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:

<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.

npm install -D @tailwindcss/forms@^0.5.11
// 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.

npm install -D rollup-plugin-visualizer@^7.0.1
// 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.

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.

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:

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)

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)

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