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>
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 (handlesdragenter/dragleave/drop, exposesisOverDropZonereactive ref, correctdataTypesfiltering, Safari limitations documented)onKeyStroke— keyboard shortcut registration without manualaddEventListener/removeEventListenerlifecycle managementuseSortable(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.metaKeyinside the handler - Automatic cleanup when the component is unmounted (it wraps
addEventListener/removeEventListenerwithonUnmounted)
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:
- Node.js ≥ 20.0.0 required — Docker Compose service should already use Node 20+ (verify
FROM node:20-alpineor equivalent in any Dockerfile that runs the frontend build). - 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