BillTracker/docs/UI_IMPROVEMENTS.md

10 KiB
Raw Permalink Blame History

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:

// In Sidebar.jsx mobile menu section:
<div className={cn(
  'border-t border-border/60 bg-background/95 px-4 py-3 shadow-lg shadow-foreground/5 lg:hidden',
  'max-h-[70vh] overflow-y-auto', // Add scrollable container
)}>
  <nav className="mx-auto grid max-w-[1500px] gap-1">
    {/* ... */}
  </nav>
</div>

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 200500ms. 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:

// Only validate on blur or submit, not on every change
// Keep error state but don't re-render unless visibility changes
{errors.name && errorStateVisible && (
  <span className="text-[10px] text-red-500 font-medium">{errors.name}</span>
)}

MEDIUM

4. Sidebar Nav — No Active Indicator for Dropdown Children

Where: client/components/layout/Sidebar.jsxTrackerMenu 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:

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:
function BackupSection() {
  const [error, setError] = useState(null);
  const [data, setData] = useState(null);

  useEffect(() => {
    api.getBackups()
      .then(setData)
      .catch(err => setError(err));
  }, []);

  if (error) {
    return (
      <Card>
        <CardContent>Failed to load backups.</CardContent>
        <Button onClick={() => setError(null)}>Retry</Button>
      </Card>
    );
  }
  // ...
}

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:
<button
  type="button"
  onClick={() => onSelect('light')}
  aria-label="Select light theme"
  aria-pressed={currentTheme === 'light'}
>
  <Sun className="h-4 w-4" />
</button>

7. ProfilePage — Email Input Not Validated client-side

Where: client/pages/ProfilePage.jsxNotificationPreferences 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.jsxheader 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:
<div className="flex items-center gap-2">
  <input type="checkbox" id="remember" />
  <label htmlFor="remember" className="text-xs text-muted-foreground">
    Remember me
  </label>
</div>
  • 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