Files
Krow-workspace/.claude/agent-memory/ui-ux-design/component-patterns.md

5.0 KiB
Raw Blame History

name, description, type
name description type
KROW Staff App Component Patterns Established UI patterns, widget conventions, and design decisions confirmed in the KROW staff app codebase project

Card Pattern (standard surface)

Cards use:

  • UiColors.cardViewBackground (white) background
  • Border.all(color: UiColors.border) outline
  • BorderRadius.circular(UiConstants.radiusBase) = 12dp
  • EdgeInsets.all(UiConstants.space4) = 16dp padding

Do NOT use UiColors.bgSecondary as card background — that is for toggles/headers inside cards.

Section Toggle / Expand-Collapse Header

Used for collapsible sections inside cards:

  • Background: UiColors.bgSecondary
  • Radius: UiConstants.radiusMd (6dp)
  • Height: minimum 48dp (touch target)
  • Label: UiTypography.titleUppercase3m.textSecondary for ALL-CAPS labels
  • Trailing: UiIcons.chevronDown animated 180° via AnimatedRotation, 200ms
  • Ripple: InkWell with borderRadius: UiConstants.radiusMd and splash UiColors.primary.withValues(alpha: 0.06)

Shimmer Loading Pattern

Use UiShimmer wrapper + UiShimmerLine / UiShimmerBox / UiShimmerCircle primitives.

  • Base color: UiColors.muted
  • Highlight: UiColors.background
  • For list content: 3 shimmer rows by default
  • Do NOT use fixed height containers for shimmer — let content flow

Status Badge (read-only, non-interactive)

Custom Container with pill shape:

  • borderRadius: UiConstants.radiusFull
  • padding: EdgeInsets.symmetric(horizontal: space2, vertical: 2)
  • Label style: UiTypography.footnote2b
  • Do NOT use the interactive UiChip widget for read-only display

Status color mapping:

  • ACTIVE: bg=tagActive, fg=textSuccess
  • PENDING: bg=tagPending, fg=textWarning
  • INACTIVE/ENDED: bg=tagFreeze, fg=textSecondary
  • ERROR: bg=tagError, fg=textError

Inline Error Banner (inside card)

NOT a full-page error — a compact container inside the widget:

  • bg: UiColors.tagError
  • radius: UiConstants.radiusMd
  • Icon: UiIcons.error at iconMd (20dp), color: UiColors.destructive
  • Title: body2m.textError
  • Retry link: body3r.primary with TextDecoration.underline

Inline Empty State (inside card)

NOT UiEmptyState widget (that is full-page). Use compact inline version:

  • Icon(UiIcons.clock, size: iconXl=32, color: UiColors.iconDisabled)
  • body2r.textSecondary label
  • EdgeInsets.symmetric(vertical: space6) padding

AnimatedSize for Expand/Collapse

AnimatedSize(
  duration: const Duration(milliseconds: 250),
  curve: Curves.easeInOut,
  child: isExpanded ? content : const SizedBox.shrink(),
)

Benefits Feature Structure

Legacy benefits: apps/mobile/legacy/legacy-staff-app/lib/features/profile/benefits/ V2 domain entity: apps/mobile/packages/domain/lib/src/entities/benefits/benefit.dart V2 history entity: needs creation at packages/domain/lib/src/entities/benefits/benefit_history.dart

Benefit history is lazy-loaded per card (not with the initial overview fetch). History state is cached in BLoC as Map<String, AsyncValue<List<BenefitHistory>>> keyed by benefitId.

Screen Page Pattern (overview pages)

Uses CustomScrollView with SliverList for header + SliverPadding wrapping SliverList.separated for content. Bottom padding on content sliver: EdgeInsets.fromLTRB(16, 16, 16, 120) to clear bottom nav bar.

ShiftDateTimeSection / OrderScheduleSection — Shift Detail Section Pattern

Both widgets live in packages/features/staff/shifts/lib/src/presentation/widgets/:

  • shift_details/shift_date_time_section.dart — single date, clock-in/clock-out boxes
  • order_details/order_schedule_section.dart — date range, 7-day circle row, clock-in/clock-out boxes

Shared conventions (non-negotiable for section consistency):

  • Outer padding: EdgeInsets.all(UiConstants.space5) — 20dp all sides
  • Section title: UiTypography.titleUppercase4b.textSecondary
  • Title → content gap: UiConstants.space2 (8dp)
  • Time boxes: UiColors.bgThird background, UiConstants.radiusBase (12dp) corners, UiConstants.space3 (12dp) all padding
  • Time box label: UiTypography.footnote2b.copyWith(color: UiColors.textSecondary, letterSpacing: 0.5)
  • Time box value: UiTypography.title1m.copyWith(fontWeight: FontWeight.w700).textPrimary
  • Between time boxes: UiConstants.space4 (16dp) gap
  • Date → time boxes gap: UiConstants.space6 (24dp)
  • Time format: DateFormat('h:mm a') — uppercase AM/PM with space

OrderScheduleSection day-of-week circles:

  • 7 circles always shown (MonSun ISO order) regardless of active days
  • Circle size: 32×32dp (fixed, not a token)
  • Active: bg=UiColors.primary, text=UiColors.white, style=footnote2m
  • Inactive: bg=UiColors.bgThird, text=UiColors.textSecondary, style=footnote2m
  • Shape: UiConstants.radiusFull
  • Single-char labels: M T W T F S S
  • Inter-circle gap: UiConstants.space2 (8dp)
  • Accessibility: wrap row with Semantics(label: "Repeats on ..."), mark individual circles with ExcludeSemantics
  • Ordering constant: [DayOfWeek.mon, .tue, .wed, .thu, .fri, .sat, .sun] — do NOT derive from API list order