Files

4.9 KiB
Raw Permalink Blame History

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, decisions, metrics, requirements
phase plan subsystem tags requires provides affects tech-stack key-files decisions metrics requirements
02.1 07 ui-shell
kotlin
compose-multiplatform
empty-state
viewmodel
theme-tokens
accessibility
i18n
polish-copy
02.1-02
02.1-04
EmptyState
PlannerScreen
RecipesScreen
PantryScreen
ShoppingScreen
PlannerViewModel
RecipesViewModel
PantryViewModel
ShoppingViewModel
added patterns
statelfow-method-per-action
mergeDescendants-a11y
RecipeTheme-tokens
created modified
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/components/empty/EmptyState.kt
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/planner/PlannerScreen.kt
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/planner/PlannerViewModel.kt
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesScreen.kt
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/recipes/RecipesViewModel.kt
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/pantry/PantryScreen.kt
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/pantry/PantryViewModel.kt
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/shopping/ShoppingScreen.kt
composeApp/src/commonMain/kotlin/dev/ulfrx/recipe/ui/screens/shopping/ShoppingViewModel.kt
composeApp/src/commonMain/composeResources/values/strings.xml
Used `BasicText` from compose-foundation rather than Material 3 `Text` to keep shell components Material-3-free per UI-SPEC line 31
Tab screens render inline title + centered EmptyState; chrome bottom inset is owned by AppShell, not screens
All 4 tab VMs ship a marker `isEmpty` field for forward-compatible expansion in feature phases (5/6/8/9)
duration completed
~10m 2026-05-08
UI-09

Phase 02.1 Plan 07: Tab Empty States Summary

UI-09 anticipatory empty states: a reusable EmptyState(icon, title, subtitle, modifier, action?) composable plus four tab screens (Planner / Recipes / Pantry / Shopping) each rendering an inline title and a centered EmptyState with calm Polish copy from UI-SPEC § Copywriting Contract.

What was built

  • EmptyState.kt — reusable centered Column with 48dp muted icon, display headline, body subline, optional action slot, wrapped in Modifier.semantics(mergeDescendants = true) {} so VoiceOver reads the empty state as a single announcement (UI-SPEC line 226).
  • 4 tab *Screen.kt files — each Box(background = RecipeTheme.colors.background) containing a Column with status-bar inset + xl top padding, inline tab title in RecipeTheme.typography.title, and a centered EmptyState reading the tab-specific icon (from BottomBarDestination.<Tab>.icon) and resource strings.
  • 4 tab *ViewModel.kt files — each ViewModel exposes a state: StateFlow<*State> with a marker isEmpty: Boolean = true field; no actions in this phase.
  • strings.xml extended with 8 empty-state keys (Polish copy verbatim from UI-SPEC § Copywriting Contract).

Tasks & commits

Task Commit Description
1 1cc4d9d Add 8 empty-state strings (Polish copy)
2 98baed9 Add reusable EmptyState composable
3 fda8d2a Add 4 tab ViewModels (StateFlow, no actions)
4 c0ca16c Add 4 tab screens with inline title + EmptyState

Verification

  • ./gradlew :composeApp:compileKotlinIosSimulatorArm64 -q — exit 0 after each task
  • ./gradlew :composeApp:linkDebugFrameworkIosSimulatorArm64 -q — exit 0 (only the pre-existing bundleId warning)
  • ./gradlew :composeApp:generateComposeResClass -q — exit 0; new Res.string.empty_* accessors generated
  • Material 3 boundary preserved: grep -rc 'androidx.compose.material3' [9 new files] returns 0
  • Zero hardcoded Polish literals in any *.kt — every string flows through stringResource(Res.string.*)

Spacing accessor names verified

RecipeSpacing exposes: xs (4dp), sm (8dp), lg (16dp), xl (24dp), xxl (32dp), xxxl (48dp). Per RecipeSpacing.kt comment: UI-SPEC's 2xl / 3xl are remapped to xxl / xxxl because Kotlin identifiers cannot start with a digit. This plan uses only sm, lg, xl — all plain identifiers, no backticks needed.

strings.xml state after this plan

  • Total keys: 24
  • Auth (pre-existing): 7 (auth_*)
  • Shell tabs (plan 02.1-04): 4 (shell_tab_*)
  • Search placeholders (plan 02.1-04): 2 (search_placeholder_*)
  • Search a11y (plan 02.1-04): 3 (search_open_a11y, search_close_a11y, search_clear_a11y) — verified each present exactly once
  • Empty-state (this plan): 8 (empty_*_title × 4 + empty_*_subtitle × 4)

Deviations from Plan

None — plan executed exactly as written.

Self-Check: PASSED

  • All 9 created files exist (verified via Write tool success)
  • All 4 task commits present in git log (1cc4d9d, 98baed9, fda8d2a, c0ca16c)
  • Strings file modified with 8 new keys; total count 24
  • iOS K/N compile + link green