Files
Kordant/tasks/shieldai-unified-restructure/26-error-loading-states.md
2026-05-25 12:23:23 -04:00

5.8 KiB

26. Polish — Error Boundaries, Loading States, Skeletons, and Transitions

meta: id: shieldai-unified-restructure-26 feature: shieldai-unified-restructure priority: P1 depends_on: [shieldai-unified-restructure-24, shieldai-unified-restructure-25] tags: [frontend, polish, ux, accessibility, solidjs]

objective:

  • Add production polish to the web app: error boundaries for graceful failure handling, skeleton screens for loading states, smooth page transitions, and comprehensive accessibility improvements.

deliverables:

  • web/src/components/ui/ErrorBoundary.tsx — Global error boundary:
    • Catches runtime errors in child components
    • Displays friendly error page with ShieldAI branding
    • "Try again" button to reset error boundary
    • "Report issue" button (placeholder for Sentry integration)
    • Logs error details to console (and eventually monitoring)
  • web/src/components/ui/Skeleton.tsx — Skeleton loading components:
    • SkeletonText — animated pulse lines for text placeholders
    • SkeletonCard — card-shaped skeleton with header, body, footer
    • SkeletonAvatar — circular skeleton for profile images
    • SkeletonTable — table row skeletons
    • All use .bg-bg-tertiary with shimmer animation
  • web/src/components/ui/PageTransition.tsx — Page transition wrapper:
    • Fade-in + slight translate-y on route change
    • Respects prefers-reduced-motion
    • Uses SolidJS <Transition> or CSS animations
  • Loading states integrated:
    • Dashboard widgets show skeletons while createResource is loading
    • Service pages show skeleton layouts while data fetches
    • Buttons show spinner inside during mutation loading
    • Forms show disabled state with spinner during submission
  • Accessibility improvements:
    • All images have alt text
    • All interactive elements have focus rings
    • Color contrast meets WCAG AA (4.5:1 for text)
    • ARIA labels on icon-only buttons
    • Skip-to-content link for keyboard navigation
    • Reduced motion support for all animations
  • web/src/components/ui/EmptyState.tsx — Empty state component:
    • Icon, title, description, and optional action button
    • Used when lists have no items (e.g., no watchlist items, no alerts)

steps:

  1. Create web/src/components/ui/ErrorBoundary.tsx:
    • Use SolidJS ErrorBoundary component as base
    • Custom fallback UI with ShieldAI logo, error message, retry button
    • Capture stack trace for debugging
  2. Create web/src/components/ui/Skeleton.tsx:
    • Use Tailwind animate-pulse or custom shimmer animation
    • Each skeleton variant accepts lines, width, height props
    • Use rounded rectangles that mimic content shape
  3. Create web/src/components/ui/PageTransition.tsx:
    • Wrap route content in transition group
    • Apply opacity-0 translate-y-2opacity-100 translate-y-0 on enter
    • Duration: 200ms, easing: ease-out
  4. Integrate skeletons:
    • Dashboard: replace Loading... text with SkeletonCard grids
    • Service pages: add skeleton layouts matching final content shape
    • Tables: use SkeletonTable with 5 rows
  5. Integrate loading states in buttons:
    • Update Button primitive to show spinner when loading prop is true
    • Disable button and reduce opacity during loading
  6. Add accessibility:
    • Audit all pages with axe DevTools
    • Fix any contrast issues (adjust colors in theme if needed)
    • Add aria-label to all icon buttons
    • Add aria-live="polite" regions for toast notifications
    • Ensure all form inputs have associated labels
  7. Create EmptyState component and use it across pages:
    • DarkWatch: "No watchlist items yet" with "Add first item" button
    • VoicePrint: "No voice enrollments" with "Enroll voice" button
    • SpamShield: "No custom rules" with "Create rule" button
    • etc.
  8. Test all improvements across light/dark modes.

steps:

  • Unit: ErrorBoundary catches thrown errors and renders fallback
  • Unit: Skeleton components render with correct dimensions
  • Unit: PageTransition applies enter animation
  • Accessibility: axe DevTools audit passes with 0 critical violations
  • Visual: All loading states look polished and match content layout
  • Visual: Empty states display correctly when lists are empty

acceptance_criteria:

  • ErrorBoundary catches and displays friendly error UI for all routes
  • Skeleton screens match the shape of final content (no layout shift)
  • All buttons show loading state during mutations
  • Page transitions are smooth and respect reduced motion
  • Empty states are informative and provide clear next actions
  • axe DevTools audit shows 0 critical or serious violations
  • All color combinations meet WCAG AA contrast standards
  • Keyboard navigation works for all interactive elements

validation:

  • Throw an error in a component and verify ErrorBoundary catches it
  • Throttle network to 3G and verify skeletons appear during loading
  • Navigate between routes and verify smooth transitions
  • Run axe DevTools on each page and fix any issues
  • Test keyboard-only navigation (Tab, Enter, Escape) on all pages
  • Run cd web && pnpm test for polish-related unit tests

notes:

  • Reference Lendair's skeleton components: ~/code/Lendair/web/src/components/skeletons/
  • The animate-pulse Tailwind class is sufficient for skeletons. For a more polished look, consider a custom shimmer animation with a moving gradient.
  • Error boundaries should NOT catch errors in event handlers or async code — only render errors. Use try/catch for async operations.
  • For monitoring integration, consider adding Sentry in a follow-up task. For now, log errors to console.
  • The EmptyState component should be reusable across all service pages. Keep it generic but allow customization of icon, title, description, and action.
  • Test reduced motion by enabling "Reduce motion" in OS settings and verifying all animations are suppressed.