Files
Bill Ballou 7940050196 Add mobile floating banners for specialization status and course selection progress
On mobile, the single-column layout makes it easy to lose context when
scrolling between the specializations and course selection panels. This adds
two floating banners that appear via IntersectionObserver:

- Top banner: summarizes specialization statuses (achieved/achievable/missing/unreachable)
- Bottom banner: shows course selection progress (N/12 selected)

Both slide in/out with CSS transitions and scroll to their respective
sections on tap. Only rendered on mobile viewports (max-width: 639px).
2026-02-28 22:27:07 -05:00

2.3 KiB

1. Export STATUS_STYLES

  • 1.1 Export STATUS_STYLES from SpecializationRanking.tsx so it can be imported by the banner component

2. Create MobileStatusBanner component

  • 2.1 Create app/src/components/MobileStatusBanner.tsx with props: statuses: Record<string, SpecStatus>, visible: boolean, onTap: () => void
  • 2.2 Implement status count logic — loop over the statuses record to count each category (achieved, achievable, missing_required, unreachable)
  • 2.3 Render four color-coded badges using STATUS_STYLES colors, each showing "{count} {label}" (display all categories including zero counts)
  • 2.4 Style the banner: position: fixed, top: 0, left: 0, width: 100%, z-index: 1000, white background with bottom border, compact height (~40px)
  • 2.5 Implement slide animation: always mounted, use transform: translateY(-100%) when hidden vs translateY(0) when visible, transition: transform 200ms ease-out
  • 2.6 Attach onClick handler to the banner root element that calls onTap

3. Integrate into App.tsx

  • 3.1 Add a ref to the specializations wrapper <div> in App.tsx (the div containing CreditLegend and SpecializationRanking)
  • 3.2 Add IntersectionObserver logic: observe the specializations ref, set a bannerVisible state to true when the section is not intersecting, false when it is
  • 3.3 Gate the observer and banner on isMobile — only create the observer and render the banner when on mobile breakpoint
  • 3.4 Mount <MobileStatusBanner> at the top of the App return (before <h1>), passing statuses={optimizationResult.statuses}, visible={bannerVisible && isMobile}, and onTap that calls specSectionRef.current.scrollIntoView({ behavior: 'smooth' })
  • 3.5 Clean up the IntersectionObserver in the useEffect return (disconnect on unmount or when isMobile changes)

4. Test

  • 4.1 Verify banner appears on mobile viewport when scrolling past specializations
  • 4.2 Verify banner hides when scrolling back up to specializations
  • 4.3 Verify banner does not render on tablet/desktop viewports
  • 4.4 Verify tapping the banner scrolls back to the specializations section
  • 4.5 Verify status counts update reactively when course selections change while banner is visible
  • 4.6 Verify badge colors match the specialization list status badges