Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
8.1 KiB
UI Improvement Pass — Design Spec
Date: 2026-03-19 Scope: All 6 pages — visual polish + UX usability, equal weight Direction: Darker, cooler blue-grey tone ("monitoring dashboard" feel), lifted nav branding
Goals
- Replace the generic default-Vuetify color palette with a distinctive monitoring-dashboard identity
- Give the nav drawer meaningful visual structure and branding
- Add a WebSocket connection indicator in the app bar
- Improve Dashboard information hierarchy and action bar layout
- Fix dark-mode-breaking hardcoded colour classes across all pages
- Consistent empty states and loading treatment throughout
- Per-page UX improvements (Downloads filter, Credentials visual state, Subscriptions dark mode)
Section 1: Color System (frontend/src/plugins/vuetify.js)
Replace both theme palettes entirely.
Dark theme (primary use case)
dark: {
dark: true,
colors: {
background: '#0F172A', // slate-900 — deep navy
surface: '#1E293B', // slate-800 — card surfaces
primary: '#60A5FA', // blue-400 — cooler accent
secondary: '#94A3B8', // slate-400
error: '#F87171', // red-400
warning: '#FBBF24', // amber-400
success: '#34D399', // emerald-400
info: '#38BDF8', // sky-400
},
}
Light theme
light: {
dark: false,
colors: {
background: '#F1F5F9', // slate-100 — cool grey workspace
surface: '#FFFFFF',
primary: '#2563EB', // blue-600 — deeper, more serious
secondary: '#475569', // slate-600
error: '#EF4444',
warning: '#F59E0B',
success: '#10B981',
info: '#0EA5E9',
},
}
Global component defaults (add to existing defaults)
VCard: { elevation: 0, border: true },
VBtn: { variant: 'flat' },
VChip: { rounded: 'md' },
Removing shadow elevation in favour of a subtle border gives cards a cleaner, more modern feel at both themes. The border adapts to theme colours automatically.
Section 2: Nav Drawer & App Bar (frontend/src/App.vue)
Nav drawer brand header
Replace the plain v-list-item brand block with a styled header:
- Container:
pa-4padding,bg-surfacebackground to visually separate from nav list - Icon:
mdi-download-networkat size 32,color="primary"(icon intentionally changed from currentmdi-image-multiple) - Title:
"GallerySubscriber"infont-weight-bold text-subtitle-1(title intentionally changed from"Gallery Subscriber"— one word, no space) - Subtitle:
"Download Manager"intext-caption text-medium-emphasis - Followed by a
v-divider
Nav list items
Change from density="compact" to density="comfortable". Add rounded="lg" and active-color="primary" to each v-list-item. The built-in Vuetify active state renders a tinted pill highlight on the current route.
App bar
- Change
elevation="1"toelevation="0"withborder="b"— flat bar with bottom border - Add a WebSocket status indicator to the right of the title, before the theme toggle:
- A small icon button showing two states (the store exposes
isConnected: Ref<boolean>only — no "connecting" state is tracked):- Connected:
mdi-circleat size 10,color="success"+v-tooltip"Real-time updates active" - Disconnected:
mdi-circleat size 10,color="error"+v-tooltip"Disconnected — real-time updates paused"
- Connected:
- Reads from
useWebSocketStore().isConnected - Wrapped in a
v-btn variant="text" iconso it is unobtrusive and the tooltip activates on hover
- A small icon button showing two states (the store exposes
Section 3: Dashboard (frontend/src/views/Dashboard.vue)
Stat cards (number-forward layout)
Replace the v-avatar + side-by-side number layout with a vertical number-forward design:
- Large
text-h3 font-weight-boldnumber at top text-caption text-medium-emphasislabel below- A 4px left border accent stripe using inline style
border-left: 4px solid <colour>matching the card's semantic colour (primary, success, error, warning) - Remove the
v-avatarentirely — the border provides the colour signal
Action bar (split into two rows)
Row 1 (actions): The three action buttons unchanged — Check All Sources, Retry All Failed, Reset Stuck.
Row 2 (status): Two groups separated by a v-divider vertical:
- Left: credential status chips (the existing platform chips, unchanged)
- Right: storage stats (existing display, unchanged)
Each row is its own v-card-text. This removes the single-line cramming.
Active downloads list
Running items get a subtle animated left border:
.download-running {
border-left: 3px solid rgb(var(--v-theme-primary));
animation: pulse-border 2s ease-in-out infinite;
}
@keyframes pulse-border {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
Consistent empty states
All three empty panels (active downloads, recent activity, sources needing attention) use the same pattern:
<div class="text-center py-8">
<v-icon size="48" color="secondary" :style="{ opacity: 0.5 }">mdi-[relevant-icon]</v-icon>
<div class="text-body-1 mt-2">[Primary message]</div>
<div class="text-caption text-medium-emphasis mt-1">[Helper text]</div>
</div>
- Active downloads empty: icon
mdi-check-circle-outline, "No active downloads", "All sources are idle" - Recent activity empty: icon
mdi-history, "No recent activity", "Failures and new downloads appear here" - Sources needing attention empty: icon
mdi-check-all, "All sources healthy", "No failures in the last 7 days"
Section 4: Per-Page Fixes
Subscriptions (frontend/src/views/Subscriptions.vue)
- Replace
class="bg-grey-lighten-4"on the expanded inner table withclass="bg-surface". This is the only dark-mode-breaking class in the file. - Add
class="border-t"to the inner table container for visual separation.
Downloads (frontend/src/views/Downloads.vue)
- Add an
exclude_supersededfilter toggle below the existing four filter columns (the current row is already full-width with fourmd="3"columns). Add a newv-rowdirectly below the existing filter row containing a singlev-col cols="12"with av-switchlabelled "Hide superseded failures",density="compact",hide-details, defaulting totrue. When true, addsexclude_superseded: trueto theloadDownloads()params. - The existing four filter columns are unchanged.
Credentials (frontend/src/views/Credentials.vue)
Two visual states for platform cards:
Configured: Card gets border-color set to the success colour via inline style or a CSS class. Existing content unchanged.
Not configured: Card gets a dashed border via :style="{ border: '2px dashed var(--v-border-color)' }" and :border="false" to override the global VCard border: true default. (--v-border-color is a hex value, not an RGB triplet, so it must be used directly without rgb() wrapping.) The v-alert info block is replaced with a concise empty-state message, and the "Add Credentials" button is promoted to color="primary" variant="flat" (currently variant="text"), making the call to action visually prominent.
Settings & Logs
No structural changes — both pages inherit the new palette and card border style from the vuetify.js update automatically.
Files Changed
| File | Change |
|---|---|
frontend/src/plugins/vuetify.js |
Full palette replacement, component defaults update |
frontend/src/App.vue |
Nav branding, nav item density/rounding, app bar border+ws indicator |
frontend/src/views/Dashboard.vue |
Stat card layout, action bar split, running item pulse, empty states |
frontend/src/views/Subscriptions.vue |
Replace bg-grey-lighten-4 with bg-surface |
frontend/src/views/Downloads.vue |
Add exclude_superseded filter toggle |
frontend/src/views/Credentials.vue |
Configured/unconfigured card visual states |
Settings.vue and Logs.vue are not modified — they benefit from the theme update without code changes.
Non-Goals
- No new pages or routes
- No layout restructuring (grid columns, page structure unchanged)
- No new API endpoints
- No component extraction / shared component library
- Settings.vue and Logs.vue: no structural changes