Problem Statement
The barcode scanner is the app's primary entry point for in-store users , yet the current UX has several friction points that slow down the scan → decision flow:
Torch toggle too small — hard to tap in a dimly-lit store aisle, doesn't meet 44×44px WCAG target
No scan feedback — users don't know if the camera is actively scanning or frozen
No timeout indicator — if a barcode isn't recognized after 10+ seconds, users don't know what to do
Manual EAN input field cramped on mobile — the fallback for when scanning fails
Camera permission recovery — if denied once, users get stuck (fixed partially in PR fix(frontend): scanner — add camera permission recovery and scan feedback (#702) #745 , but needs verification)
No haptic feedback — modern food scanning apps (Yuka, CodeCheck) provide a satisfying vibration on successful scan
Scan result transition — after successful scan, the transition to product detail is abrupt
Scan history — exists but is not prominently accessible during the scanning flow
No "batch scan" mode — users scanning a full grocery cart want to scan multiple products in sequence
Design Vision — Premium Scanner UX
Active Scanning State
┌─────────────────────────────────┐
│ ← Scanner 🔦 │ Large torch toggle (48px)
│ │
│ ╔═══════════════════════════╗ │
│ ║ ║ │
│ ║ [Camera Viewfinder] ║ │
│ ║ ║ │
│ ║ ┌───────────────────┐ ║ │
│ ║ │ ▮▮▮▮ ▮▮▮▮ │ ║ │ Animated scan line
│ ║ └───────────────────┘ ║ │
│ ║ ║ │
│ ╚═══════════════════════════╝ │
│ │
│ 🟢 Scanning... point at barcode │ Status indicator
│ │
│ ┌──────────────────────────┐ │
│ │ 📝 Enter EAN manually │ │ Full-width manual input
│ └──────────────────────────┘ │
│ │
│ 📋 Recent scans (3) ▸ │ Quick access to history
└─────────────────────────────────┘
Successful Scan Transition
┌─────────────────────────────────┐
│ │
│ ╔═══════════════════════════╗ │
│ ║ ✅ Product Found! ║ │ Green overlay
│ ║ ║ │
│ ║ Piątnica Skyr ║ │ Product name preview
│ ║ 🟢 95 — Excellent ║ │ Score preview
│ ║ ║ │
│ ║ [View Details →] ║ │ Tap to navigate
│ ║ [Scan Another] ║ │ Stay in scanner
│ ╚═══════════════════════════╝ │
│ │
│ 📱 Haptic buzz on scan │ Device vibration
└─────────────────────────────────┘
Product Not Found
┌─────────────────────────────────┐
│ │
│ ╔═══════════════════════════╗ │
│ ║ ❌ Product Not Found ║ │ Clear feedback
│ ║ ║ │
│ ║ EAN: 5900512345678 ║ │ Show scanned code
│ ║ ║ │
│ ║ [Submit This Product] ║ │ Community contribution
│ ║ [Try Again] ║ │ Quick retry
│ ╚═══════════════════════════╝ │
│ │
└─────────────────────────────────┘
Implementation Plan
Phase 1 — Scanner UX Polish
Enlarge torch toggle to 48×48px minimum
Add animated scan line in viewfinder
Add scanning status indicator ("Scanning...", "Processing...", "Camera starting...")
Add timeout after 15 seconds: "Having trouble? Try manual entry or adjust lighting"
Phase 2 — Scan Feedback
Haptic vibration on successful scan (navigator.vibrate(200))
Success overlay with product preview (name, score, image)
"Scan Another" vs "View Details" choice
Sound option (optional, off by default)
Phase 3 — Manual Input Enhancement
Full-width EAN input field
Auto-format as user types (add spaces for readability)
EAN checksum validation in real-time (is_valid_ean() client-side)
"Paste from clipboard" button
Phase 4 — Batch Scanning Mode
Toggle "Batch mode" — scans auto-add to a temporary list
Review all scanned products at once
One-tap "Compare all" or "Add to list"
Counter badge showing scan count
Test Requirements
Vitest Tests
Playwright E2E
Accessibility
Verification Checklist
Problem Statement
The barcode scanner is the app's primary entry point for in-store users, yet the current UX has several friction points that slow down the scan → decision flow:
Design Vision — Premium Scanner UX
Active Scanning State
Successful Scan Transition
Product Not Found
Implementation Plan
Phase 1 — Scanner UX Polish
Phase 2 — Scan Feedback
navigator.vibrate(200))Phase 3 — Manual Input Enhancement
is_valid_ean()client-side)Phase 4 — Batch Scanning Mode
Test Requirements
Vitest Tests
Playwright E2E
Accessibility
Verification Checklist
cd frontend && npx tsc --noEmit— 0 errorscd frontend && npx vitest run— all tests passCHANGELOG.mdupdated under[Unreleased]