critical priority low complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

ContactCardWidget is a StatelessWidget accepting: contact (Contact), onTap (void Function(String contactId)), and optional organizationLabel (String?) parameters
Contact name is rendered as the primary text using the body-large or headline-small design token typography style
Role badge is color-coded: coordinator role uses a distinct accent color (e.g., design token primary), peer_mentor role uses a secondary accent; the badge displays the human-readable role label from the org-labels system
Organization label is displayed as secondary text below the name using body-small typography token; if organizationLabel is null or empty, the line is omitted (no blank space)
Notes preview is rendered with maxLines: 2 and overflow: TextOverflow.ellipsis; if notes is null or empty, the notes line is omitted entirely
Tapping anywhere on the card triggers onTap(contact.id) — the full card area is tappable, not just the text
All spacing (padding, gap between elements) uses design token spacing constants — no hardcoded pixel values
Border radius uses design token radius constant consistent with other cards in the app
Widget renders correctly at both default and 200% system font scale (WCAG 2.2 AA text resize requirement)
Widget passes flutter_test widget tests for all fixture scenarios (all fields, null notes, long notes, each role type)

Technical Requirements

frameworks
Flutter
Dart
Riverpod
data models
Contact
PeerMentor
performance requirements
Widget must be const-constructible or at minimum not trigger unnecessary rebuilds when used inside a ListView.builder
No async operations inside the widget build method — org label must be resolved before passing to the widget or via a Riverpod watch in the parent
security requirements
Notes preview must be rendered as plain text — no HTML parsing or markdown rendering that could expose XSS-equivalent risks in a Flutter context
Contact ID passed to onTap must be the model's id field, never a display string
ui components
ContactCardWidget
RoleBadge (inline or extracted sub-widget)
AppCard (base card wrapper if exists in shared components)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Place widget in lib/features/contacts/presentation/widgets/contact_card_widget.dart. Extract the role badge into a private _RoleBadgeWidget or reuse a shared RoleBadge widget if it already exists in the codebase. Retrieve design tokens via Theme.of(context).textTheme and a project-specific AppTokens class (or equivalent) — never hardcode colors or sizes. For the org label, accept it as a pre-resolved String parameter rather than reading from a provider inside the widget; this keeps the widget purely presentational and easier to test in isolation.

The parent list widget (built in a later task) is responsible for resolving org labels via Riverpod. Use InkWell with borderRadius matching the card's radius for the tap ripple effect to align with Material Design conventions. Ensure semantic labels are set for accessibility: Semantics(label: '${contact.name}, ${roleLabel}, ${orgLabel}') wrapping the card content for screen reader support (WCAG 2.2 AA, critical for Blindeforbundet users).

Testing Requirements

Widget tests using flutter_test: (1) renders name correctly from mockCoordinatorContact fixture, (2) renders peer_mentor role badge with correct color token, (3) notes preview is absent when notes is null (mockContactNullNotes fixture), (4) long notes are truncated to 2 lines with ellipsis (mockContactLongNotes fixture — assert maxLines behavior via finder), (5) onTap callback is invoked with correct contact ID when card is tapped (use tester.tap()), (6) renders without overflow at 200% textScaleFactor. Run flutter analyze to ensure no lint warnings.

Component
Contact Card Widget
ui low
Epic Risks (2)
medium impact medium prob technical

Design token color values used in role badges, certification status indicators, and availability chips may not meet the WCAG 2.2 AA contrast ratio of 4.5:1 when rendered against card backgrounds, requiring rework after accessibility review and potentially blocking acceptance sign-off.

Mitigation & Contingency

Mitigation: Run the contrast-ratio-validator on every new token combination during widget development. Enforce the CI accessibility lint runner on all PRs touching visualization components, and validate against the contrast-safe-color-palette before finalizing card designs.

Contingency: If contrast failures are found late, adjust token values in the design token theme centrally — since all widgets consume design tokens rather than hardcoded colors, all affected widgets will be corrected by a single token update without per-widget changes.

low impact medium prob dependency

The ContactViewSwitcher is required for Barnekreftforeningen but must not appear for other organizations. If the organization labels provider does not yet expose a reliable feature flag for this widget, it may render universally or be conditionally hidden in an inconsistent way, breaking the role-specific layout contract.

Mitigation & Contingency

Mitigation: Implement view switcher visibility as a constructor parameter on ContactListScreen injected from a provider, defaulting to hidden. Document the integration point for the org labels provider so the flag can be wired without changing the widget's API.

Contingency: If org labels integration is delayed beyond this epic, use a feature flag constant keyed to the Barnekreftforeningen organization ID as a temporary gate, with a tracked issue to replace it with the runtime labels provider before general release.