high priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

SummaryShareOverlay renders as a modal bottom sheet using the AccessibleModalSheet component, with drag-to-dismiss and a close button
The overlay contains a preview slot that accepts a child widget (the shareable image card) and renders it with correct aspect ratio and padding using design tokens only (no hardcoded values)
Three share action buttons are present: (1) 'Share' triggering the native share sheet, (2) 'Copy link' triggering clipboard write, (3) 'Save to photos' triggering gallery save — each button uses AppButton or an equivalent design-token-styled component
All spacing, border radius, background colour, and shadow values are sourced from design tokens — no inline style values
Focus is trapped within the bottom sheet when open: tab/arrow key navigation cycles within the sheet and does not reach background content
The sheet dismisses on: (1) close button tap, (2) swipe-down gesture, (3) tap on the scrim behind the sheet
The overlay is responsive: on screens narrower than 360px the action buttons stack vertically; on wider screens they appear in a single row
The overlay compiles without warnings and no RenderFlex overflow errors appear at 320px screen width
Action button callbacks are exposed as constructor parameters (onShare, onCopyLink, onSaveToPhotos) — no business logic is implemented inside the widget itself at this stage

Technical Requirements

frameworks
Flutter
flutter_test
apis
AccessibleModalSheet (accessibility component library)
Design token system (spacing, radii, colors, shadows)
showModalBottomSheet or Navigator.push with AccessibleModalSheet wrapper
data models
SummaryShareOverlayConfig (previewWidget: Widget, shareUrl: String?, onShare: VoidCallback, onCopyLink: VoidCallback, onSaveToPhotos: VoidCallback)
performance requirements
Bottom sheet open animation completes within 350ms
Preview slot renders without blocking the sheet open animation — use a placeholder if the image card is not yet available
security requirements
Copy-to-clipboard must only copy the shareUrl value — no other user data
The share payload must not include raw Supabase row IDs or internal system identifiers in the shared URL
AccessibleModalSheet must enforce focus trapping to prevent background content from being reachable by keyboard/screen reader
ui components
SummaryShareOverlay
AccessibleModalSheet
AppButton (share, copy, save variants)
Preview slot container (design-token-styled)
Scrim layer (provided by AccessibleModalSheet)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Implement SummaryShareOverlay as a StatelessWidget that wraps its content in AccessibleModalSheet. Expose the sheet via a static show() method (SummaryShareOverlay.show(context, config)) that calls showModalBottomSheet with the correct builder. Use LayoutBuilder to switch between row and column layout for action buttons at the 360px breakpoint — avoid MediaQuery in the widget itself to keep it testable. All design token references must use the project's token accessor pattern (e.g.

AppTokens.spacing.md, AppTokens.radius.lg) — zero hardcoded px/colour values. The preview slot should be a simple Container with a design-token-defined aspect ratio (e.g. 4:5 for a portrait card) and a placeholder colour if the child is null. Action button callbacks (onShare, onCopyLink, onSaveToPhotos) are plain VoidCallbacks passed via constructor — the actual platform integrations (share_plus, clipboard, image_gallery_saver) are implemented in a subsequent task.

Document this separation clearly in the widget's dartdoc comment so the next task's implementer knows what to wire up.

Testing Requirements

Widget tests using flutter_test: (1) pump SummaryShareOverlay and assert three action buttons are present with correct labels; (2) tap close button and assert overlay is dismissed (widget no longer in tree); (3) tap 'Share' button and assert onShare callback was called; (4) tap 'Copy link' and assert onCopyLink callback called; (5) tap 'Save to photos' and assert onSaveToPhotos callback called; (6) render at 320px width and assert no overflow errors; (7) render at 428px width and assert buttons in single row (LayoutBuilder test); (8) assert focus is trapped by checking that FocusScope does not allow focus to move outside the sheet. Golden tests for both narrow (320px) and standard (390px) widths.

Component
Summary Share Overlay
ui medium
Epic Risks (2)
medium impact medium prob technical

Simultaneous count-up animations across multiple stat cards and chart draw-in animations on lower-end Android devices may cause frame drops below 60fps, degrading the premium Wrapped experience and making the feature feel unpolished.

Mitigation & Contingency

Mitigation: Stagger animation starts using AnimationController with staggered intervals rather than starting all animations simultaneously. Use RepaintBoundary around each animated widget to isolate rasterisation. Profile on a mid-range Android device (e.g., equivalent to Pixel 4a) during development, not just at QA.

Contingency: If frame rate targets cannot be met on low-end devices, implement a device-capability check at startup and substitute simpler fade-in animations for the count-up and chart draw-in on devices below a CPU performance threshold.

medium impact low prob integration

The activity-type-breakdown-widget must render organisation-specific activity type labels sourced from the terminology system. If the terminology provider is not yet integrated at the time this widget is built, the widget will display hardcoded system labels, which is a regression risk for multi-org support.

Mitigation & Contingency

Mitigation: Accept activity type labels as a typed parameter in the widget constructor rather than reading from the terminology provider directly inside the widget. The BLoC or repository layer resolves labels before passing them to the widget, maintaining clean separation and testability.

Contingency: If terminology resolution is unavailable at widget integration time, display internal activity type keys as a temporary fallback with a localised suffix '(label pending)' visible only in non-production builds so QA can identify unresolved labels.