# Kvitt: information architecture Status: U1 proposal (2026-10-17). Binding inputs: SPEC §13.2 (navigation model), §13.3 (screens S-1 to S-22), §14.2.4 (UI contract), §6.2 (module ownership). Screen ids are the spec's (`S-1` … `S-22`), not renumbered. ## 1. Bottom navigation **Recommendation: keep the spec's three destinations.** | Destination | sv | en | Icon (Material Symbols) | Start screen | |---|---|---|---|---| | Home | Översikt | Home | `home` (filled when selected) | S-4 | | Analytics | Statistik | Analytics | `bar_chart` | S-11 | | Settings | Inställningar | Settings | `settings` | S-13 | Plus one floating action button, "Lägg till utgift" / "Add expense", on S-4 and S-5 only (SPEC §13.2). Why three and not four: 1. **Home already is the account list.** The second most important question ("who owes whom", §13.1) is answered per account on Home (AC-10.2). A separate "Accounts" destination would split the global balance from its breakdown and add a tap to the most common path (Home → account → expense). 2. **The add flow is the FAB, not a tab.** A tab "Add" would be a navigation item that is really an action; Material 3 discourages that, and the FAB is reachable with the thumb on both phones. 3. **No fourth destination exists in the spec.** The only candidate that would earn the slot is an **Activity** feed (the other person's latest changes across all accounts, i.e. the audit log as a list). It would be useful together with push notifications (US-13), but it is a new feature, out of scope for the UX sprint (§13.5). It is listed as an owner question in the report, not designed. 4. Three items keep Swedish labels whole: "Inställningar" (13 characters) fits at 200 % font scale in a three-item bar but not in a four- or five-item bar of 411 dp. Navigation bar style per direction (Expressive "short navigation bar" 64 dp or classic `NavigationBar` 80 dp) is stated on each direction's review page. ## 2. Screen inventory Kind: **top** = bottom-nav destination; **pushed** = full screen with top app bar and back (§13.2); **flow** = onboarding screen without bottom nav; **dialog** / **banner** as named. | Id | Screen (sv / en) | Kind | Feature module (§6.2) | Reached from | State from (§14.2.4) | |---|---|---|---|---|---| | S-1 | Para ihop / Pair | flow, start when `Unpaired` or `Unrecognised` | `:feature:onboarding` | app start; deep link `kvitt://invite?…` or `https://…/i/` (AC-2.6); S-18 | `SessionRepository` | | S-2 | Din profil (partner step) / Your profile | flow | `:feature:onboarding` | S-1 after a `partner` pairing (AC-2.10) | `SessionRepository`, `ProfileRepository` | | S-3 | Väntar på din partner / Waiting for your partner | top (replaces S-4 content) | `:feature:onboarding` | Home while `partner == null` (AC-5.1) | `SessionRepository`, `InviteRepository` | | S-4 | Översikt / Home | top | `:feature:home` | bottom nav; notification for deletions and `sync_batch` | `HomeRepository` → `HomeState` | | S-5 | Konto (account detail) | pushed | `:feature:account` | S-4 account row | `AccountRepository.detail` → `AccountDetailState` | | S-6 | Nytt konto / Ändra konto | pushed | `:feature:account` | S-4 "Nytt konto"; S-5 overflow "Ändra konto" | `AccountRepository` | | S-7 | Ny utgift / Ändra utgift | pushed, full screen (§13.2) | `:feature:expense` | FAB on S-4 or S-5; S-8 "Ändra"; S-19 editor reuses its fields | `ExpenseRepository.editorOptions`, `preview`, `save` | | S-8 | Utgift (expense detail) | pushed | `:feature:expense` | ledger rows (S-5, S-12, S-16); notification | `ExpenseRepository.detail` → `ExpenseDetailState` | | S-9 | Gör upp / Settle up | pushed | `:feature:settle` | S-4 "Gör upp" (global); S-5 "Gör upp det här kontot" (per account); S-10 "Ändra" | `SettlementRepository.start`, `recompute`, `save` → `SettleState` | | S-10 | Betalning (settlement detail) | pushed | `:feature:settle` | settlement rows; notification | `SettlementRepository.detail` | | S-11 | Statistik / Analytics | top | `:feature:analytics` | bottom nav | `AnalyticsRepository.analytics` → `AnalyticsState` | | S-12 | Statistik, urval (drill-down) | pushed | `:feature:analytics` | tap on a bar or a breakdown row in S-11 (AC-14.8) | `AnalyticsRepository.drillDown` | | S-13 | Inställningar / Settings | top | `:feature:settings` | bottom nav | `ProfileRepository.household`, `SessionRepository` (role) | | S-14 | Profil | pushed | `:feature:settings` | S-13 | `ProfileRepository` | | S-15 | Kategorier (list + editor) | pushed (editor pushed from list) | `:feature:settings` | S-13 | `CategoryRepository` | | S-16 | Synkstatus / Sync status | pushed | `:feature:home` | S-4 sync line; S-13 "Synkstatus" | `SyncRepository.status` | | S-17 | Uppdatering (banner + dialogs) | banner on S-4, dialogs | `:feature:home` | S-4 when `UpdateState != None`; S-22 "Sök efter uppdatering" | `UpdateRepository` | | S-18 | Telefonen har tagits bort / Device removed | flow, start when `Revoked` | `:feature:onboarding` | any 401 `device_revoked` (AC-3.3) | `SessionRepository.acknowledgeRevoked` | | S-19 | Återkommande utgifter (list + editor) | pushed | `:feature:settings` | S-13 | `RecurringRepository` | | S-20 | Enheter / Devices | pushed | `:feature:settings` | S-13 | `DeviceRepository` | | S-21 | Inbjudningar (Bjud in partner / Para ihop en enhet) | pushed | `:feature:settings` | S-13 (admin only); S-3 "Bjud in partner" | `InviteRepository` | | S-22 | Om Kvitt / About | pushed | `:feature:settings` | S-13 | `AboutRepository`, `UpdateRepository.check` | System notifications (SPEC §13.3 table, §14.6) are not screens; they open S-8, S-10 or S-4 (AC-13.6). ## 3. Start destination and session routing The app shell (`:app`) chooses the root from `SessionRepository.session`: | `SessionState` | Root | Notes | |---|---|---| | `Unpaired` | S-1 | Fields filled when opened from an invite link. | | `Paired`, `partner == null` | S-4 showing the S-3 content | Bottom nav visible; Statistik shows its empty state; FAB hidden (AC-5.1). For the member this state cannot occur. | | `Paired`, just paired as `partner` | S-2, then S-4 | AC-2.10. Notification permission is asked after the first full sync (AC-2.9), i.e. on arriving at S-4. | | `Paired` | S-4 | First sync after pairing shows the S-4 loading state. | | `Revoked` | S-18 | Local data already wiped (AC-3.3). | | `Unrecognised` | S-1 with "Para ihop telefonen igen" header | Local data kept (§14.4.8). | Back from a top destination exits the app (Home) or returns to Home (Statistik, Inställningar). Each top destination keeps its own back stack and scroll position when switching tabs. ## 4. Navigation map ``` ┌──────────────── bottom nav ─────────────────┐ S-4 Översikt S-11 Statistik S-13 Inställningar │ ├ sync line → S-16 └ bar/row → S-12 ├ S-14 Profil │ ├ banner → S-17 └ row → S-8 ├ S-15 Kategorier → editor │ ├ Gör upp → S-9 (global) ├ S-19 Återkommande → editor (S-7 fields) │ ├ Nytt konto → S-6 ├ S-20 Enheter │ ├ FAB → S-7 ├ S-21 Inbjudningar (admin) │ └ account → S-5 ├ S-16 Synkstatus │ ├ FAB → S-7 └ S-22 Om Kvitt → S-17 │ ├ Gör upp det här kontot → S-9 (one account) │ ├ overflow: Ändra konto → S-6, Arkivera, Ta bort │ ├ expense row → S-8 → Ändra → S-7 │ └ settlement row → S-10 → Ändra → S-9 Onboarding: S-1 → (partner) S-2 → S-4 ; S-1 → (admin) S-4 with S-3 content → S-21 Anywhere: 401 device_revoked → S-18 → S-1 ``` After saving in S-7 the app returns to where the editor was opened from (S-4 or S-5, or S-8 when editing). After confirming S-9 it returns to S-4 or S-5. ## 5. Which screen owns which data "Owns" = the only place where the data is created or edited. Every other screen only reads it. Offline column per AC-15.2 and §9.9. | Data | Owned (edited) by | Read by | Offline edit | |---|---|---|---| | Session, pairing, device token | S-1, S-18 | `:app` routing | no (pairing needs the server) | | Own display name and picture | S-14 (and S-2 once for the partner) | S-4 hero, S-13, all avatars, timelines | no ("Kräver anslutning") | | Partner's name and picture | partner's own S-14 | everywhere | — | | Accounts (name, default ratio, archived, deleted) | S-6 (create, edit), S-5 overflow (archive, unarchive, delete) | S-4, S-7 picker (active only), S-9 rows and "Lägg till konto" (all), S-11/S-12, S-19 picker | no | | Categories | S-15 | S-7 grid (active only), S-8, ledger rows, S-11/S-12, S-19 | no | | Recurring templates | S-19 | S-8 ("Återkommande: Hyra") | no | | Expenses | S-7 (create, edit), S-8 (delete) | S-4 (counts, balances), S-5, S-11/S-12, S-16 (pending) | **yes** | | Settlements | S-9 (create, edit), S-10 (delete) | S-4/S-5 balances and rows, S-16 (pending) | **yes** | | Audit timeline | server only (never edited) | S-8, S-10 | — | | Balances, proposals, analytics | computed, never stored (§5.5) | S-4, S-5, S-9, S-11, S-12 | always available | | Devices | S-20 (revoke) | — | no | | Invites | S-21 (create, share, cancel), S-3 shortcut | S-3 (expiry of the pending invite) | no | | Sync state, rejections | system; S-16 dismisses rejections | S-4 sync line | dismiss works offline | | App update | S-17 | S-4 banner, S-22 | no | ## 6. Role differences (admin vs member) | Where | Admin (Jacob) | Member (Anna) | |---|---|---| | S-13 | row "Bjud in partner" (before the partner joins) or "Para ihop en enhet" | row "Para ihop en enhet" is **not** shown (AC-2.2: only the admin creates invites). | | S-20 | all devices of both users; revoke any except the current | own devices only; revoke own except the current (AC-3.2) | | S-21 | available | not reachable | | S-3 | shown until the partner joins | never | Everything else is identical for both users (§3.1).