Skip to content

Public UI Design Overhaul #413

@filthyrake

Description

@filthyrake

Summary

The public-facing UI needs a comprehensive design overhaul to transform it from a functional video viewer into an engaging video streaming platform. While the technical foundation is solid (Alpine.js, Tailwind CSS, custom video player), the user experience lacks discovery features, personalization, and the polish expected from modern video platforms.

Current State Assessment

What Works Well

  • Solid custom video player with quality switching, captions, touch gestures, PiP, keyboard shortcuts
  • Clean dark theme with consistent color palette
  • Responsive grid layout (1→2→3→4 columns)
  • Good performance baseline with lazy loading, debounced search
  • DASH/HLS adaptive streaming with Shaka Player + HLS.js fallbacks
  • Well-structured player-controls.js (1,094 lines) - proves modular code is possible

Architecture Issues

  • Inline CSS in watch.html: 698 lines of player CSS embedded in HTML
  • Inline JavaScript: 538 lines in watch.html, similar patterns in other pages
  • Code duplication: Video card markup repeated across index, category, tag pages (lines 76-96 identical)
  • No component abstraction: No reusable Alpine.js components

Discovery & Navigation Gaps

  • No navigation structure: Only "Home" link available
  • No breadcrumbs: Users get lost on category/tag/watch pages
  • Search limited to homepage: Can't search within categories
  • No filtering/sorting: Can't filter by date, duration, or sort results
  • No trending/featured content: Just a flat grid of latest videos
  • Missing footer: No site info, links, or metadata

Missing Platform Features (vs YouTube/Vimeo)

Feature Status
Continue Watching ✅ Implemented (Phase 3)
Watch History ✅ Implemented (localStorage)
Playback Speed Control ✅ Implemented (Phase 4)
Theater Mode ✅ Implemented (Phase 4)
Timeline Thumbnail Preview 📋 Planned (Phase 7)
Video Chapters 📋 Planned (Phase 7)
Share/Embed ✅ Implemented (Phase 5)
Playlists ✅ Implemented (Issue #223)
Related Videos Sidebar ✅ Implemented (Phase 5)
View Counts ✅ Displayed (Phase 3)

Accessibility Issues (WCAG Failures)

Level A Failures (Critical):

  • No skip-to-content link on any page
  • Video player controls missing ARIA labels and states
  • Progress slider lacks role="slider" and ARIA value attributes
  • Quality modal doesn't trap focus or restore focus on close

Level AA Failures (High):

  • Focus indicators insufficient (1px border color change only)
  • Color contrast: text-dark-500 on dark-950 fails 4.5:1 ratio
  • Touch targets 40px on mobile (should be 44px minimum)
  • No screen reader announcements for dynamic content changes

Video Card Issues

  • No watch progress indicator on thumbnails ✅ Fixed (Phase 3)
  • No hover quick actions (add to queue, save) ✅ Fixed (Phase 3 - Watch Later)
  • Missing view counts and engagement metrics ✅ Fixed (Phase 3)
  • No visual distinction between watched/unwatched ✅ Fixed (progress bars show watch status)

Player UX Issues

  • Controls hide too quickly (3s timeout) ✅ Fixed (Phase 4 - now 5s)
  • No current quality indicator when set to "Auto" ✅ Fixed (Phase 4)
  • No playback speed control ✅ Fixed (Phase 4)
  • Volume slider behavior differs desktop vs mobile
  • Time display shrinks to 11px on mobile (hard to read) ✅ Fixed (Phase 4)
  • No theater mode option ✅ Fixed (Phase 4)

Mobile Issues

  • Search bar loses prominence on mobile
  • Category buttons wrap awkwardly (should horizontal scroll)
  • Player controls cramped on small screens
  • No mobile navigation drawer

Proposed Solution

Phase 0: Accessibility Foundation ✅ COMPLETE

Critical fixes before any feature work:

  • Add skip-to-content link to all pages
  • Fix color contrast failures (text-dark-500 → text-dark-400 minimum)
  • Add visible focus indicators (2px outline, 3:1 contrast)
  • Add ARIA labels to all video player controls
  • Fix touch target sizes to 44x44px minimum on mobile
  • Add labels to search inputs (visually hidden)
  • Add role="search" landmark to search region
  • Add screen reader live region for dynamic announcements

Phase 1: Architecture Foundation ✅ COMPLETE (PR #481)

CSS Extraction:

  • Create /static/css/design-tokens.css with CSS custom properties
  • Extract player CSS from watch.html to /static/css/pages/player.css
  • Create /static/css/components/video-card.css
  • Create shared utility classes file (a11y.css, bundle.css)

JS Modularization:

  • Extract watchPage() logic to /static/js/pages/watch.js
  • Create Alpine.data() registration for watchPage component
  • Add watch history utility to utils.js

Template Standardization:

Success Criteria:

  • watch.html inline CSS < 50 lines (achieved: 1 line)
  • watch.html inline JS < 50 lines (achieved: 0 lines)
  • watch.html reduced from 1,402 → 131 lines (90.6% reduction)

Phase 2: Navigation & Structure ✅ COMPLETE (PR #482)

  • Add breadcrumb navigation component
  • Create footer with site info
  • Add mobile navigation drawer
  • Make search globally accessible (header on all pages)
  • Show search result count
  • Add clear search button
  • Improve empty state messaging

Code Review Fixes Applied:

  • Focus management for mobile drawer (trap focus, restore on close)
  • Cached filteredVideos with $watch pattern (performance fix)
  • Slug validation and input length limits (security)
  • Error states with user-friendly messages (reliability)
  • Thumbnail fallback with SVG placeholder
  • ARIA improvements (role=status, aria-controls, focus-visible)

Phase 3: Video Card & Discovery ✅ COMPLETE (PR #483)

Video Cards:

  • Add watch progress bar overlay
  • Display view counts
  • Add hover quick actions (Watch Later)
  • Better thumbnail aspect ratio handling
  • ARIA: aria-label with full context for screen readers

Homepage Redesign:

  • Add hero section with featured video
  • Create "Continue Watching" row
  • Add horizontal scrolling category chips on mobile
  • Implement skeleton loaders (GPU-accelerated)

Browse Experience:

  • Add filter controls (duration filter)
  • Add sort options (date, views, duration, title)
  • Create grid/list view toggle

Code Review Fixes Applied:

  • Bulk endpoint order preservation
  • Positive integer validation for video IDs
  • Retry logic with exponential backoff for Continue Watching
  • Storage write success checking before UI feedback
  • Watch percentage clamping (0-100)
  • Named constants for thresholds (5%, 95%)
  • Composite index migration for view count performance

Phase 4: Player Improvements ✅ COMPLETE (PR #484)

  • Add playback speed control (0.25x - 2x)
  • Implement theater mode toggle (wider player, hide metadata)
  • Increase control visibility timeout (5s when playing)
  • Show current quality when on "Auto" (e.g., "1080p (Auto)")
  • Add keyboard shortcut hints on hover
  • Improve mobile control bar layout
  • Fix time display sizing on mobile
  • Add keyboard shortcuts help dialog (press '?' to show)

Player ARIA Enhancements:

  • Add aria-pressed states to toggle buttons (play, mute, captions, fullscreen)
  • Update aria-label dynamically based on state
  • Add aria-valuetext to sliders with human-readable values

Code Review Fixes Applied:

  • Focus trapping for all modals (quality, speed, shortcuts)
  • Live region for screen reader announcements
  • ARIA roles (role=dialog, aria-label, aria-hidden) on all modals
  • Escape key exits theater mode
  • Memory leak fixes (modal cleanup, ABR event listener cleanup)
  • CSS :has() fallback with body class for older browsers
  • Playback rate validation after setting
  • indexOf guard in speed change methods

Phase 5: Watch Experience ✅ COMPLETE (PR #485)

  • Add related videos sidebar (desktop) / section (mobile)
  • Track watch progress (localStorage with VLogUtils.watchHistory)
  • Implement resume playback (silent auto-resume, 5-95% range)
  • Add share button with copy link modal

API Additions:

  • GET /api/videos/{slug}/related - Smart recommendations (category + tags algorithm)
  • Rate limiting, bounded limit (1-24), SHA256 cache keys

Code Review Fixes Applied:

  • Resume: Validate duration before seeking (NaN guard)
  • Resume: Clear stale data if position exceeds duration
  • Resume: Add 5s timeout for loadedmetadata event
  • localStorage: Try-catch with silent degradation on quota errors
  • Share: Check execCommand return value, specific error messages
  • Related: Guard JSON parsing, retry transient errors
  • Cache: Schema versioning for safe deploys
  • Performance: setInterval vs timeupdate (95% CPU reduction)
  • Accessibility: prefers-reduced-motion for skeleton animations

Phase 6: Accessibility Verification & Polish ✅ COMPLETE (PR #486)

  • Full WCAG 2.1 AA compliance audit (axe DevTools, Lighthouse)
  • Automated accessibility CI pipeline (pa11y-ci + Lighthouse)
  • Fix ARIA listbox pattern in quality/speed modals
  • Convert modal headers to semantic h2 elements
  • Fix touch targets (44px minimum)
  • Add microinteractions and transitions
  • Implement skeleton loaders everywhere
  • Add prefers-reduced-motion support (added in Phase 5)

Code Review Fixes Applied:

  • ARIA: role=option, aria-selected on modal options
  • ARIA: aria-activedescendant on listbox containers
  • ARIA: Listbox keyboard navigation (Arrow keys, Home, End)
  • Remove orientation lock (WCAG 1.3.4 compliance)
  • Remove role="application" from gesture overlay
  • Add quality change screen reader announcements
  • Add null check for quality height in announcements
  • Video card hover microinteractions (scale 1.02)
  • Button tactile feedback (scale 0.95 on active)
  • Staggered skeleton animation delays
  • Continue Watching section skeleton loader
  • Continue Watching error state with retry button
  • Search form consistency (div role=search on all pages)
  • CI: Port conflict fix (8000 → 8888)
  • CI: Puppeteer Chrome path detection for Lighthouse

Phase 7: Advanced Player Features (Timeline Thumbnails & Chapters)

Timeline Thumbnail Preview:

  • Generate thumbnail sprite sheets during transcoding (1 frame every 10 seconds)
  • Store sprite sheet metadata (WebVTT format with timestamps → sprite coordinates)
  • Add thumbnail preview element to player progress bar
  • Show correct thumbnail on hover/scrub based on position
  • Lazy-load sprite sheets for performance

Video Chapters:

  • Add chapters table to database (video_id, start_time, end_time, title)
  • Add chapters API endpoints (CRUD operations)
  • Add chapter management UI in admin panel
  • Display chapter markers on player progress bar
  • Add chapter list/navigation in player UI
  • Support importing chapters from video metadata (if present)

Technical Approach:

  • FFmpeg sprite generation: ffmpeg -i input.mp4 -vf "fps=0.1,scale=160:-1,tile=10x10" sprites.jpg
  • WebVTT thumbnails format is widely supported
  • Shaka Player has native support: addThumbnailsTrack() and addChaptersTrack()
Sub-feature Complexity Dependencies
Timeline Thumbnails High Transcoder changes, storage, player UI
Video Chapters Medium Database migration, admin UI, player UI

API Requirements

New Endpoints Needed:

  • GET /api/videos/:slug/related - Related videos for sidebar (Phase 5)
  • GET /api/videos/trending - Trending/popular videos (or add to existing endpoint)

Phase 7 Endpoints:

  • GET /api/videos/:id/thumbnails - Thumbnail sprite sheet URL and metadata
  • GET /api/videos/:id/chapters - Chapter list for a video
  • POST /api/videos/:id/chapters - Add chapter (admin)
  • PUT /api/videos/:id/chapters/:chapter_id - Update chapter (admin)
  • DELETE /api/videos/:id/chapters/:chapter_id - Delete chapter (admin)

Existing Endpoint Modifications:

  • GET /api/videos - Add sort param (date, views, relevance)
  • GET /api/videos - Add filter params (duration_min, duration_max, uploaded_after)
  • Video responses should include view_count field

Client-Side State Architecture

localStorage Schema:

{
  "vlog:watch-history": {
    "video-id-1": { position: 120, duration: 600, lastWatched: "2024-01-15T..." },
    "video-id-2": { position: 45, duration: 300, lastWatched: "2024-01-14T..." }
  },
  "vlog:preferences": {
    "playbackSpeed": 1.0,
    "autoplay": true,
    "defaultQuality": "auto",
    "theaterMode": false
  }
}

Utility API (add to VLogUtils):

VLogUtils.watchHistory.get(videoId)        // ✅ Implemented
VLogUtils.watchHistory.save(videoId, position, duration)  // ✅ Implemented
VLogUtils.watchHistory.clear(videoId)      // ✅ Implemented
VLogUtils.watchHistory.getAll()            // ✅ Implemented
VLogUtils.preferences.get(key)             // Pending
VLogUtils.preferences.set(key, value)      // Pending

File Structure (Current State after Phase 5)

web/public/
├── static/
│   ├── css/
│   │   ├── design-tokens.css       # ✅ CSS custom properties
│   │   ├── a11y.css                # ✅ Accessibility utilities
│   │   ├── bundle.css              # ✅ Main entry point
│   │   ├── components/
│   │   │   └── video-card.css      # ✅ Video card styles (BEM)
│   │   └── pages/
│   │       └── player.css          # ✅ Player + theater + share modal + watch layout
│   └── js/
│       ├── pages/
│       │   └── watch.js            # ✅ Watch page + resume + related videos
│       ├── utils.js                # ✅ + watchHistory utility
│       └── player-controls.js      # ✅ + speed/theater/shortcuts/share

Design Tokens

:root {
    /* Colors - Semantic Roles */
    --color-bg-primary: #0a0a0b;        /* dark-950 */
    --color-bg-secondary: #18181b;      /* dark-900 */
    --color-bg-elevated: #27272a;       /* dark-800 */
    --color-text-primary: #f4f4f5;      /* dark-100 */
    --color-text-secondary: #a1a1aa;    /* dark-400 */
    --color-text-muted: #71717a;        /* dark-500 - use sparingly, contrast issues */
    --color-accent: #3b82f6;            /* blue-500 */
    --color-accent-hover: #60a5fa;      /* blue-400 */
    
    /* Spacing Scale */
    --space-xs: 4px;
    --space-sm: 8px;
    --space-md: 16px;
    --space-lg: 24px;
    --space-xl: 32px;
    
    /* Transitions */
    --transition-fast: 150ms ease;
    --transition-normal: 300ms ease;
    
    /* Focus */
    --focus-ring: 2px solid var(--color-accent);
    --focus-offset: 2px;
}

Accessibility Testing Checklist

Automated Testing

  • axe DevTools - no violations (Phase 6 CI)
  • Lighthouse accessibility score > 95 (Phase 6 CI)
  • Pa11y CI integration passing (Phase 6)

Manual Testing

  • Tab navigation works on all pages
  • All interactive elements keyboard accessible
  • Focus indicators visible at 3:1 contrast
  • Screen reader announces all controls correctly
  • Screen reader announces dynamic content changes
  • Video player fully operable with keyboard
  • All form inputs have associated labels
  • Color contrast meets AA standards (4.5:1)
  • Touch targets are 44x44px minimum (Phase 6)
  • No keyboard traps
  • Skip link works

Screen Reader Scenarios

  • Navigate homepage with screen reader
  • Search for video with screen reader
  • Play video with screen reader
  • Adjust volume/quality with screen reader
  • Toggle captions with screen reader

Success Metrics

Architecture:

  • watch.html reduced from 1,361 lines to <500 lines (achieved: 131 lines → ~310 lines with related videos)
  • Player CSS extracted (0 inline styles except [x-cloak])
  • Video card component reused (not duplicated) - CSS unified, markup still duplicated

Accessibility:

  • All interactive elements have visible focus states (3:1 contrast)
  • All touch targets are 44px minimum (Phase 6)
  • WCAG 2.1 AA color contrast compliance
  • Lighthouse accessibility score ≥ 95 (Phase 6 CI)

Features:

  • Search available on all listing pages
  • Watch progress persisted and displayed
  • Playback speed control available
  • Breadcrumb navigation on all pages
  • Page loads with skeleton states
  • Related videos shown on watch page
  • Resume playback works
  • Share button with clipboard copy

Benchmark Comparison

Feature Current Target YouTube Vimeo
Featured content
Continue watching
Playback speed
Theater mode
Share buttons
Watch history
Related videos
Resume playback
Keyboard nav
Screen reader
Skip link
Focus indicators
Timeline thumbnails 📋 Phase 7
Video chapters 📋 Phase 7

References

Metadata

Metadata

Assignees

Labels

architectureArchitectural improvementsenhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions