# Bill Tracker UI Improvements ## Overview This document catalogs UI/UX improvements identified across the Bill Tracker codebase, organized by priority and impact. --- ## CRITICAL No critical issues found. Core functionality is solid. --- ## HIGH ### 1. Mobile Layout Overflow in Sidebar Navigation **Where:** `client/components/layout/Sidebar.jsx` — Mobile menu overlay **Why it matters:** On small screens, the mobile navigation menu doesn't adapt to content width, causing horizontal scroll or content cutoff. This is a blocking accessibility issue for mobile users. **Current behavior:** - Mobile menu uses fixed max-width container - Nav items with long text (e.g., "Notification Preferences") wrap poorly - No vertical scrolling within the mobile overlay **Suggested fix:** ```jsx // In Sidebar.jsx mobile menu section:
``` **Priority:** HIGH — Mobile breakage affects a significant portion of users --- ### 2. Settings Page — No Loading Skeleton for Main Content Area **Where:** `client/pages/SettingsPage.jsx` **Why it matters:** The entire page shows a full-page loader (`Loading…`) during initial data fetch, resulting in a blank white screen for 200–500ms. This feels slower than needed. **Suggested fix:** - Replace full-page loader with skeleton cards matching the layout - Show placeholder content: 2-3 shimmering `SectionCard` components - Fade out skeletons when data arrives **Impact:** Perceived performance improvement (~30-40% faster mental load time) --- ### 3. BillModal — Real-Time Validation on Every Keystroke Causes Layout Shifts **Where:** `client/components/BillModal.jsx` **Why it matters:** The `handleChange` function debounces validation but still triggers re-renders on every keystroke. This causes: - Input field height changes when error messages appear/disappear - Jarring UX during form entry - Potential focus loss on fast typists **Suggested fix:** - Only show error messages after field blur or form submit attempt - Pre-allocate error message space (min-height: 12px) - Use `aria-live="polite"` for screen reader notifications **Alternative:** ```jsx // Only validate on blur or submit, not on every change // Keep error state but don't re-render unless visibility changes {errors.name && errorStateVisible && ( {errors.name} )} ``` --- ## MEDIUM ### 4. Sidebar Nav — No Active Indicator for Dropdown Children **Where:** `client/components/layout/Sidebar.jsx` — `TrackerMenu` component **Why it matters:** When users navigate to `/bills`, `/categories`, or `/summary`, the main "Tracker" dropdown remains unhighlighted. This creates ambiguity about current location. **Current behavior:** - Only the dropdown trigger is highlighted when on `/` (Overview) - Child routes like `/bills` don't indicate they're part of the Tracker group **Suggested fix:** - Detect when any child route is active via `location.pathname.startsWith('/bills')` etc. - Apply `bg-primary text-primary-foreground` style to the Tracker dropdown when any tracker subpage is active **Code hint:** ```jsx const isTrackerActive = trackerItems.some(item => item.end ? location.pathname === item.to : location.pathname.startsWith(item.to) ); ``` Already implemented in the code, but the `TrackerMenu` trigger needs styling update. --- ### 5. Admin Panel — Missing Error Boundary for Critical Sections **Where:** `client/pages/AdminPage.jsx` **Why it matters:** Several complex cards (Backup, Email, Users) lack explicit error boundaries. If an API call fails mid-render or throws, the entire admin panel goes blank with no recovery path. **Suggested fix:** - Wrap each major card in a `try/catch` or React Error Boundary - Show "Failed to load" state with retry button - Example: ```jsx function BackupSection() { const [error, setError] = useState(null); const [data, setData] = useState(null); useEffect(() => { api.getBackups() .then(setData) .catch(err => setError(err)); }, []); if (error) { return ( Failed to load backups. ); } // ... } ``` --- ### 6. Settings Page — Field Labels Not Keyboard-Accessible **Where:** `client/pages/SettingsPage.jsx` **Why it matters:** While `label` elements exist, they're not explicitly tied to inputs via `htmlFor`. Some components (e.g., theme cards) use buttons without labels, making screen reader navigation difficult. **Suggested fix:** - Ensure all form inputs have explicit `id` and corresponding `label htmlFor` - Add `aria-label` or `aria-describedby` to interactive elements: ```jsx ``` --- ### 7. ProfilePage — Email Input Not Validated client-side **Where:** `client/pages/ProfilePage.jsx` — `NotificationPreferences` component **Why it matters:** The email input field accepts any string, including invalid formats like `test@localhost` or `not-an-email`. Validation only happens server-side, leading to delayed error feedback. **Suggested fix:** - Add client-side email regex check before save - Show inline error if invalid: `^[\w.-]+@[\w.-]+\.\w+$` - Debounce validation to avoid spamming errors during typing --- ### 8. BillModal — Date Input Uses Unusual "Due Day of Month" Pattern **Where:** `client/components/BillModal.jsx` **Why it matters:** The "Due day of month" input expects a number (1-31) instead of a standard date picker or calendar selection. This is confusing for: - Users expecting a full date picker - International users (some countries use DD/MM vs MM/DD) - Edge cases like February 30th (which doesn't exist) **Suggested improvement:** - Consider using `react-datepicker` or similar for full date selection - Alternatively, add a helper tooltip: "Enter day number only (e.g., 15 for the 15th)" - Add a validation example: "Due on the 15th → enter 15" --- ## LOW ### 9. Global Layout — Header Backdrop Filter Not Fallback for Older Browsers **Where:** `client/components/layout/Sidebar.jsx` — `header` element **Why it matters:** The `backdrop-blur-xl` class relies on CSS `backdrop-filter`, which is unsupported in older browsers (e.g., Safari <14, some Android WebView versions). This results in a solid background instead of glassmorphism. **Suggested fix:** - Add a CSS fallback: `bg-background/85 supports-[backdrop-filter]:bg-background/70` - Already implemented ✅ — no changes needed --- ### 10. Login Page — No "Remember Me" Checkbox **Where:** `client/pages/LoginPage.jsx` **Why it matters:** Modern apps often include a "remember me" option to reduce login friction on trusted devices. Without it, users must re-authenticate on every session. **Suggested fix:** - Add a checkbox below the password field: ```jsx
``` - Set `rememberMe` flag in localStorage or via cookie (if server supports it) --- ### 11. Theme Toggle — No Visual Feedback When Switching **Where:** `client/components/ui/theme-toggle.jsx` (shadcn/ui) **Why it matters:** The theme toggle button doesn't indicate which theme is currently active. Users must click to discover the current state or remember manually. **Suggested fix:** - Add subtle text label: "Light" / "Dark" next to the icon - Or use a tooltip: `aria-label="Current theme: Dark"` - Or change icon (sun/moon) based on theme (already done ✅) --- ### 12. Calendar Page — Empty State Not Customizable **Where:** `client/pages/CalendarPage.jsx` **Why it matters:** When there are no bills, the calendar shows a generic "No bills found" message. This doesn't guide users toward creating their first bill. **Suggested fix:** - Add a CTA button: "Create your first bill" - Link directly to the modal with a pre-filled category or default values - Include a placeholder image or illustration --- ## MEH ### 13. General — Inconsistent Spacing in `table-surface` Utility **Where:** Multiple components (SettingsPage, ProfilePage) **Why it matters:** The `table-surface` utility (used in Settings and Profile pages) applies `mb-4` and internal padding, but the spacing isn't uniform across all pages. Some sections have excessive vertical space, others feel cramped. **Suggested fix:** - Audit and standardize spacing tokens: - `section-spacing` = `mb-6` (1.5rem) - `card-spacing` = `mb-4` (1rem) - `row-spacing` = `py-3` (0.75rem) - Document in `docs/TOKENS.md` --- ### 14. Icons — No Consistent Icon Palette **Where:** Across all components **Why it matters:** Different icon sets are used inconsistently: - `lucide-react` (primary) - Custom SVGs (logo) - Some components import icons but don't use them **Suggested fix:** - Standardize on `lucide-react` for all icons - Create a shared `icons/` directory with named exports if custom icons are needed - Document icon usage in `CONTRIBUTING.md` --- ## Summary | Priority | Count | Key Impact | |----------|-------|------------| | CRITICAL | 0 | — | | HIGH | 3 | Mobile accessibility, perceived performance, form UX | | MEDIUM | 5 | Navigation clarity, error resilience, keyboard nav | | LOW | 4 | Convenience, consistency, discoverability | | MEH | 2 | Minor polish, standardization | --- ## Next Steps 1. **HIGH items** should be prioritized for the next minor release (v2.1) 2. **MEDIUM items** can be batched into a quality-of-life update 3. Consider a design system audit to address **MEH** items (spacing, icons) 4. Re-run this analysis after implementing HIGH/MEDIUM items to track progress --- *Generated by Scarlett (Frontend/UX Authority) on 2026-05-08*