UX sprint U1: three directions, IA, screens, flows, components, tokens, strings

Three visual directions (Skärgård, Kvitto, Kalas) as self-contained HTML pages with
every SPEC §13.3 screen in light and dark, Swedish, plus PNG exports and the exporter
script. Direction-independent docs: ia.md, screens.md, flows.md, components.md,
tokens.md (values for all three directions), strings.csv (key,en,sv,notes), and the
comparison with a recommendation in directions/README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-17 21:27:51 +02:00
parent 6eaccec7ac
commit b72ac5bb9e
12 changed files with 5216 additions and 0 deletions
+49
View File
@@ -0,0 +1,49 @@
# Kvitt UX (sprint U1)
The UX deliverables for Kvitt v1 (SPEC §13), produced 2026-10-17. Phase 1 (U1) is here: three visual directions plus the direction-independent design documents. After the owner picks a direction, phase 2 (U2) consolidates `tokens.md`, `components.md` and `screens.md` into `design-spec.md` for the chosen direction (SPEC §13.4) and deletes what it supersedes.
## How to read
1. **Look first:** open `directions/1-skargard/index.html`, `directions/2-kvitto/index.html` and `directions/3-kalas/index.html` in a browser (or the PNGs in each `png/` folder). Every screen S-1 to S-22 is there in light and dark, in Swedish, with the same household data, so the same file name compares one screen across directions.
2. **Compare and decide:** `directions/README.md` has the comparison table and the recommendation.
3. **Then the structure,** which holds whichever direction wins:
| File | What it is |
|---|---|
| `ia.md` | Bottom navigation (three destinations, argued), screen inventory S-1…S-22 with modules and state sources, session routing, navigation map, which screen owns which data, admin vs member |
| `screens.md` | Every screen: purpose, content in priority order, empty/loading/offline/error/long-content states, actions; the add-expense flow in detail |
| `flows.md` | F-1 … F-11: first run as admin, join as partner, add expense online and offline, edit the other's expense, global and per-account settle-up, recurring, archive, in-app update, remove a lost phone |
| `components.md` | The `:core:design` component list: anatomy, variants, states, semantics |
| `tokens.md` | Token names and their values in all three directions (colour roles light/dark, semantic and category colours with contrast, charts, type, spacing, shapes, elevation, motion, icons) |
| `strings.csv` | Every UI string, `key,en,sv,notes` (SPEC §13.4 format). Placeholders are Android format arguments (`%1$s`, `%d`); plurals are two rows `<key>:one` and `<key>:other`, which become one `<plurals name="<key>">`. Notification copy (SPEC §13.3, Swedish written here) has the prefix `notif_`. |
| `directions/` | The three directions, their PNGs and `export-png.sh` |
## The three directions
**1 Skärgård** — the Stockholm archipelago on a calm day. Material 3 Expressive in sea blue, granite and sand, with large rounded shapes and generous whitespace. Home is a hero: "Du är skyldig Anna" above a large, narrow Roboto Flex figure and a single "Gör upp" button; accounts are a calm grouped list. The editor puts the amount in display size over an in-app keypad with Expressive key shapes. The most Android-native of the three, and the only one that can follow Material You wallpaper colours.
**2 Kvitto** — a till receipt and a household ledger. Warm paper and ink, one ink-blue accent, every amount in IBM Plex Mono, dashed rules between groups. Home opens with a compact receipt block and then a dense ledger table; account detail reads like a bank statement; an expense is a printed receipt with an "audit trail" below; "Ni är kvitt" is a rubber stamp. The densest and calmest direction; its risk is feeling austere.
**3 Kalas** — a crayfish party. Cream and white cards, a coral primary, very round shapes, and a solid category-colour tile on every expense so you recognise it before reading it. The balance is a seesaw (whoever owes sits lower, and the words always say it); settling up raises bunting. The editor shows the six most used categories as big tiles under the amount. The most personality; its risk is wearing thin over years of daily use.
## Where this follows the spec instead of the U1 brief
The owner's brief for this sprint and SPEC.md differ in places; the spec wins (CLAUDE.md › Source of truth):
| Brief | Spec (followed) |
|---|---|
| Screen ids `S-01 …` | `S-1 … S-22` as in SPEC §13.3 |
| Bottom nav: recommend up to 4 | SPEC §13.2's three destinations kept, with the argument in `ia.md` §1 |
| `docs/ux/proposals/`, 360 dp frames, S26+ at 384 dp | `docs/ux/directions/<n>-<slug>/index.html` at 411 dp (SPEC §13.1/§13.4); both phones are ~411 dp wide, so there is no separate S26+ layout |
| Swedish in one direction, English in the others | Swedish everywhere (§13.4), plus one English Home frame per direction to show English fits |
| `copy.md` table | `strings.csv` in the §13.4 format |
| Category suggested from the description | AC-8.6: last category used in that account on this phone, else Övrigt (a description-based suggestion would be a new rule; open question) |
| `tokens.md` "for the chosen scheme family" | Values for all three directions until the owner picks (§13.4 makes the pick precede the final spec) |
Additions the spec did not state, flagged for U2:
* **S-1 has a "Ditt namn" field:** AC-2.4 sends a display name with the accept call, but §13.3's S-1 lists no name field. A partner therefore types the name on S-1 and confirms it on S-2 (AC-2.10).
* **Category colour hex values** changed for contrast (`tokens.md` §4); adopting them needs a §9.7 spec change after the pick.
* **Avatar colours** for initials (AC-4.5) are defined per direction (`avatar-1`, `avatar-2`).
* **Expense save shows no "Ångra"** (an undo would be an audited create + delete that also notifies the partner).
* **Archived account detail hides the FAB** (an archived account is not offered for new expenses, AC-6.4).
+182
View File
@@ -0,0 +1,182 @@
# Kvitt: components for `:core:design`
Status: U1 proposal (2026-10-17), direction-independent anatomy, states and API shape. The look (colours, shapes, type) comes from the chosen direction's tokens (`tokens.md`). In U2 this list moves into `design-spec.md` with final sizes. Names are Compose function names; every component takes a `Modifier` and gets screenshot tests in light and dark, Swedish and English, at 100 % and 200 % font scale (M6).
Rules for all components:
* Colours and type only from `KvittTheme` (Material 3 `ColorScheme` + `KvittColors` extension + `KvittTypography`); no literal values.
* Strings only from resources (`strings.csv` keys); components that need words take them as resolved `String` parameters or take the model value and resolve inside `:core:design` (shared strings live there, §14.7).
* Touch targets ≥ 48 dp (`minimumInteractiveComponentSize`), TalkBack `contentDescription` / `semantics` as stated per component.
* Material 3 Expressive APIs (`@ExperimentalMaterial3ExpressiveApi`) are used only behind a `:core:design` wrapper so the stable fallback named below can be swapped in one place. The implementing agent confirms each API's status from the official documentation at implementation time (CLAUDE.md › Fast-moving APIs).
## 1. Money and text
### `MoneyText`
Formats and shows an amount in öre via `MoneyFormatter` (§9.8). Never shows a sign.
* Params: `ore: Long`, `style: AmountStyle` (`Hero`, `Large`, `Medium`, `Small`), `tone: AmountTone` (`Neutral`, `Owed`, `Owing`, `Muted`), `maxLines` (default 1; never ellipsizes: overflow wraps to 2 lines or auto-steps down one type size for `Hero`/`Large`).
* Tabular figures always (`fontFeatureSettings = "tnum"`).
* Semantics: spoken form from `MoneyFormatter.spoken(ore)`: Swedish "3 250,00 kronor", English "3,250.00 Swedish kronor" (TalkBack reads "kr" and "SEK" poorly).
### `AmountInput`
Large amount entry for S-7, S-9, S-19. Parses per §9.6 (string → öre, never floating point).
* Params: `value: String`, `onValueChange`, `error: String?`, `size` (`Display`, `Field`).
* States: empty (hint "0,00"), typing, error (supporting text, error colour), disabled.
* Accepts `,` and `.`; displays the locale's separator; inserts no-break-space thousands grouping while typing.
### `AmountKeypad` (only if the chosen direction uses an in-app keypad)
3 × 4 grid: 1–9, decimal separator (locale), 0, backspace (TalkBack "Radera"); keys ≥ 64 dp tall; optional "Klar". Fallback: system keyboard `KeyboardType.Decimal`.
### `EffectLine`
The one-line effect from the viewer's side (§9.2) for an `ExpenseEffect`.
* Variants: `Lent(ore)` "Du lånade ut X" (owed tone) · `Borrowed(ore)` "Du lånade X" (owing tone) · `NotInvolved` "Inte inblandad" · `PaidForYourself` "Betalade för dig själv" (neutral).
### `BalanceWording`
Wording + amount for a `BalanceView` (§9.3).
* Variants: `Global` ("Anna är skyldig dig", "Du är skyldig Anna", "Ni är kvitt") and `Account` (… "Kvitt").
* Semantics: merged into one sentence.
## 2. People
### `PersonAvatar`
* Params: `user: User`, `size` (24, 32, 40, 56, 96 dp), `shape` (direction token; fallback `CircleShape`).
* Picture via Coil keyed by `user.id` + `pictureVersion` (AC-4.6); else initials (first letter of the first two words, uppercase, AC-4.5) on `avatar-1`/`avatar-2` chosen by a stable hash of the user id (the two colours are tokens).
* States: loading (initials shown), picture, initials, uploading (progress ring overlay).
* Semantics: the display name (or decorative when the name is next to it).
### `PersonChip`
Avatar 24 dp + name ("Du" for the viewer). Variants: `Display`, `Selectable` (S-7 payer toggle, S-7 full-split bearer). Selected state per M3 FilterChip.
### `PayerToggle`
Two `PersonChip`s in a connected group; one tap switches. Label "Betalt av". Semantics: "Betalt av: du. Dubbeltryck för att byta till Anna."
## 3. Categories and accounts
### `CategoryIcon`
Material Symbols icon (token from §9.7) on a container in `cat-<color>-container` with the icon in `cat-<color>`. Sizes 24/32/40/56 dp. Shape per direction (fallback circle or rounded square). Decorative when a name is shown next to it; else the category name.
### `CategoryChip`
`CategoryIcon` (24) + name. Variants: `Display`, `Selectable`. Archived variant adds "(arkiverad)".
### `CategoryGrid`
Picker grid in S-7 and S-19: tiles of icon + name, 4 columns at 411 dp (3 at 200 %), selected with outline/check. Only active categories.
### `CategoryIconPicker`, `ColorSwatchPicker`
S-15 editor: 30 icons (6 columns), 12 colour swatches (6 columns), each 48 dp, selected with a check mark (not colour alone). Swatches have names for TalkBack ("Grön", "Orange", …).
### `AccountRow`
Home and pickers: name, `BalanceWording.Account`, amount, secondary "11 utgifter i oktober". Variants: `Home`, `Picker` (no count), `Archived` (badge). 200 %: amount moves under the name.
## 4. Ledger
### `LedgerMonthHeader`
"Oktober · 7 457,90 kr" (household spend); sticky in `LazyColumn`. Year added when not the current year.
### `ExpenseRow`
`CategoryIcon`, description (1 line), date, `MoneyText` amount, `EffectLine`, optional account name (S-12), `SyncPendingMarker`. Heights per direction (56–88 dp). States: normal, pending, pressed. Semantics: "ICA Maxi Lindhagen, 1 247,60 kronor, 16 oktober, du lånade 623,80 kronor, väntar på synk".
### `SettlementRow`
`payments` icon, "Anna betalade dig 800,00 kr", "· 2 konton", date, pending marker.
### `SyncPendingMarker`
Small icon (`cloud_upload` or direction equivalent) in `pending`; label "Väntar på synk".
## 5. Balance and sync
### `BalanceHero`
Home's global balance (S-4) and the account header (S-5, smaller variant).
* Anatomy: both `PersonAvatar`s, `BalanceWording.Global`, `MoneyText(Hero)`, optional action button "Gör upp" / "Gör upp det här kontot".
* States: `Owed` (partner owes you: owed colours, button), `Owing` (you owe: owing colours, button), `Settled` ("Ni är kvitt"/"Kvitt", no amount, no button), `Empty` (no accounts: welcome text), `Loading` (skeleton).
* 200 %: wording and amount each take a full line; avatars may shrink to 40 dp.
* Semantics: one merged sentence + the button.
### `SyncStatusLine`
From `SyncSummary` + connectivity.
* Variants: `Synced` ("Synkad för 3 min sedan"), `Syncing` ("Synkar…"), `Offline` ("Offline"), with suffix "· 2 ändringar väntar på synk" (plural); `Warning` (rejections or clock skew) with icon and warning colour.
* Tap → S-16 (whole line is the 48 dp target). Relative time updates every minute.
### `UpdateBanner`
S-17 states: `Available` (text + "Uppdatera" + "Senare"), `Downloading` (determinate `LinearProgressIndicator` + percent), `Failed` (text + "Försök igen"). Fallback for Expressive wavy progress: standard linear.
### `NeedsConnectionHint`
Inline supporting text / banner "Kräver anslutning" with `cloud_off` icon; used on every online-only control (§9.9). Variant `Banner` for whole forms (S-6, S-14, S-15, S-19).
## 6. Split and settle
### `SplitModeSelector`
Single-select connected buttons (Expressive `ButtonGroup`/connected toggle buttons; fallback `SingleChoiceSegmentedButtonRow`): "Kontots standard 60/40", "Egen fördelning", "Exakta belopp", "Allt på en person". Wraps to 2 rows (Swedish), 4 rows at 200 % (a vertical radio list fallback when the row cannot fit).
### `RatioEditor`
Slider (0–100, step 1, haptic at 50) + two percent `TextField`s summing to 100 (two decimals) + preset chips (50/50, 60/40, 40/60). Used in S-6 and S-7 `ratio`. Semantics: slider "Jacobs andel, 60 procent".
### `ExactSplitEditor`
Two money fields: the typed one editable, the other read-only "beloppet minus …"; error "Andelen är större än beloppet".
### `FullSplitPicker`
Two `PersonChip`s: "Allt på Jacob" / "Allt på Anna".
### `SplitPreview`
"Du 206,00 kr · Anna 206,00 kr" + the effect the row will show. Data from `ExpenseRepository.preview`; hidden when null. Optional bar showing the two shares as proportions (decorative).
### `SplitPicker` (composite)
The S-7 split section: `SplitModeSelector` + the mode's editor + `SplitPreview`; collapsed state is a single chip "Delning: Kontots standard 50/50" which expands it in one tap.
### `DirectionSwitcher`
S-9 "Du betalar Anna" with avatars and an arrow; swap button `swap_horiz` (TalkBack "Byt riktning").
### `AllocationEditor`
S-9 list of `AllocationRow`s + "Lägg till konto" + `RemainingIndicator`.
* `AllocationRow`: account name, debt line ("Du är skyldig 3 084,50 kr"), `AmountInput(Field)`, remove (`close`, "Ta bort Huset från fördelningen"), warning line (`warning` icon + "Det här reglerar mer än som är skyldigt i Japan 2027") when `overSettles`. Variant `Fixed` (per-account settle: no remove).
* `RemainingIndicator`: "Kvar att fördela 0,00 kr" (neutral/positive), "250,00 kr kvar" (warning), "250,00 kr för mycket" (error), always words + colour.
## 7. Timeline
### `TimelineItem`
Anatomy: actor avatar (24 dp; system icon `autorenew` for recurring, `upload_file` for import), sentence, "· 16 okt. 19:40"; vertical connector between items.
* Variants: `Create`, `Update` (one line per `FieldChange`, sentence built from field + old → new, values formatted by `MoneyFormatter`/`DateFormatter`, ids already names), `Delete`, `PendingLocal` ("Din ändring väntar på synk", pending colour, dashed connector), `System` (recurring/import).
* Field names (sv/en) in `strings.csv` (`timeline_field_*`).
### `Timeline`
Column of `TimelineItem`, oldest first, newest last (AC-12.4), with the section title "Historik".
## 8. Charts (`:core:design`, Vico or Canvas fallback per §6.2)
### `ChartCard`
Container: title, optional subtitle (range), the chart, legend, and an invisible-but-spoken summary sentence (`semantics { contentDescription = summary }`). States: loaded, empty ("Inga utgifter i den här perioden"), loading (shimmer; only when the computation takes > 300 ms).
### `StackedBarChart`
Buckets on x (month "maj" or week "v. 41"), kronor on y (whole kronor, 3–5 gridlines, compact "20 tkr" labels in sv, "20k" in en), segments coloured by group (category colour or `chart-cat-n` for accounts, stable order = legend order), 2 dp gap between segments; selected bar highlighted, others dimmed; tap → callback(bucketKey). Each bar is a focusable semantics node ("September, 41 484 kronor"). Horizontal scroll when more than ~8 buckets; newest visible.
### `ChartLegend`
Wrapping row of swatch + label; only groups with data; static (filtering by legend is not in the spec).
### `BreakdownBarList`
Rows: icon/swatch, name, proportional bar, amount, percent (one decimal, AC-14.10). Sorted descending. Tap → callback(groupId). 200 %: bar on its own line.
### `FilterBar`
S-11 filters: range chip with menu (`DropdownMenu` of `RangePreset`s), three binary connected toggles. Wraps to two rows.
## 9. Scaffolding
### `KvittScaffold`
Top app bar (small, 64 dp; back or close), content, optional FAB slot, snackbar host, edge-to-edge insets.
### `KvittNavigationBar`
Three items (ia.md §1). Expressive `ShortNavigationBar` (fallback `NavigationBar`). Selected icon filled. Labels always visible.
### `AddExpenseFab`
Extended FAB "Lägg till utgift" (icon `add`), collapses to icon-only on scroll; Expressive size variants (fallback `ExtendedFloatingActionButton`).
### `EmptyState`
Icon, title, body, optional primary action. Used by S-3, S-4, S-5, S-11, S-16, S-19.
### `ConfirmDialog`
Title, body, confirm (destructive variant in `error`), dismiss.
### `SectionHeader`, `SettingsRow`, `CollapsibleSection`
Settings rows (icon, title, optional trailing value/badge, chevron), section headers ("Konton" + trailing action), "Arkiverade (n)" collapse with rotation of the chevron.
### `InviteCodeDisplay`, `InviteCodeField`
Code in three groups of four, mono/tabular, large; selectable; TalkBack spells characters ("7 K 3 M, Q X 9 D, 2 H P A"). Field variant normalises as typed (§8.3).
### `Skeleton`
Shimmer placeholders for the first-sync loading state only.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+52
View File
@@ -0,0 +1,52 @@
# Kvitt UX directions (U1)
Three visual directions, each one self-contained HTML file with every screen of SPEC §13.3 in light and dark, plus PNG exports. Open `index.html` in a browser: the top of each page is the direction's spec (palette with live contrast ratios, typography, shapes, motion, icons, the balance header in its three states, Expressive components with fallbacks); below are the phone frames (411 × 891 dp, Galaxy S26), light and dark side by side.
| # | Direction | Page | PNGs |
|---|---|---|---|
| 1 | **Skärgård** — calm Material 3 Expressive, big balance hero | `1-skargard/index.html` | `1-skargard/png/` (97) |
| 2 | **Kvitto** — till receipt and ledger, dense and list-first | `2-kvitto/index.html` | `2-kvitto/png/` (99) |
| 3 | **Kalas** — playful cards, strong category colour | `3-kalas/index.html` | `3-kalas/png/` (101) |
All three use the same household data (Jacob and Anna, Vardag / Huset / Japan 2027 / Gotland 2026, ICA, Willys, Systembolaget, SL, Vattenfall, Hyra), the same frame ids and the same Swedish copy, so a screen can be compared across directions by file name (`png/S-4-home-light.png`). One English Home frame (`S-4-home-en`) and two 200 % font-scale frames (`S-4-home-200`, `S-7-editor-200`) per direction. Extra frames beyond the common list: `S-11-analytics-scrolled` (all three; the three charts do not fit one screen), `S-5-account-menu` (Kvitto, Kalas), `S-5-account-archived-menu` (Kalas).
Re-export after a change: `docs/ux/directions/export-png.sh docs/ux/directions/<n>-<slug> [frame-id …]` (needs Google Chrome; fonts load from Google Fonts).
## Comparison
| | 1 Skärgård | 2 Kvitto | 3 Kalas |
|---|---|---|---|
| Character | Archipelago on a calm day: sea blue, granite, sand; generous whitespace; few elements per screen | Till receipt and ledger book: paper, ink, one ink-blue accent, mono amounts, dashed rules | Crayfish party: cream and white cards, coral primary, every expense a solid colour tile |
| Home balance | Hero card, words first, huge narrow figure, cookie-shaped avatars, faint islet drawing | Compact receipt block with "Saldo · alla konton", amount in mono, Gör upp beside it; "Kvitt" rubber stamp when settled | Seesaw: whoever owes sits lower, plank in the debt colour; bunting and yellow card when settled |
| Accounts / ledger | Grouped rounded list; 72 dp rows | Ledger table, 60 dp rows, date column, dotted leaders to month totals — most rows per screen | Colourful cards; account colour on a clover tile matches the account chart |
| Add expense | Amount in display size, in-app keypad with Expressive key shapes, default chips above the keypad | Register-style amount on a till keypad (has "00"), one-tap key/value rows | Big amount, keypad, row of the six most used category tiles, chips; split shown as a two-colour bar |
| Expense detail | Card with facts and a timeline | Printed receipt with zig-zag edges and an "audit trail" printout | Colour-tile header card and timeline |
| Analytics | Calm cards, tonal charts | Chart-first, every number in mono | Colourful stacked bars and tile breakdowns |
| Type | Roboto Flex (OFL) | IBM Plex Sans + Plex Mono (OFL) | Nunito (OFL) |
| Shape | Large radii, MaterialShapes for avatars/FAB | Square, 4–8 dp, receipt edges | Very round, MaterialShapes for tiles and avatars |
| Owed / owing colours | sea-green / coral | green / brick + square marker | green / purple |
| Dynamic colour | Offered (off by default) | No (paper palette is the identity) | No (colours carry meaning) |
| Expressive dependence | Highest (ShortNavigationBar, ButtonGroup, MaterialShapes, LoadingIndicator, wavy progress, expressive motion) — all with stable fallbacks | Lowest (ShortNavigationBar only) | Medium (MaterialShapes, ButtonGroup, ShortNavigationBar, expressive motion) |
| Strengths | Most Android-native; balance readable at a glance; survives dynamic colour; least surprising for both users | Densest; amounts line up perfectly; very calm; the name "Kvitt" and the receipt metaphor reinforce each other; strong dark theme | Expenses recognisable before reading; most personality; strong ten-second add flow with category tiles |
| Risks | Can read as generic Material if the shapes and decoration are dropped; the hero takes ~35 % of Home; the 80 dp FAB covers list content until it collapses | Can feel austere, "accounting software"; mono amounts are wide (wrap at 200 %); custom keypad and receipt shapes | Seesaw may feel gimmicky over time; coral primary is close to error red (errors always carry an icon); less dense; dark yellow containers read olive |
| Compose cost | Medium: keypad, hero Canvas, MaterialShapes, Roboto Flex variations, `KvittColors` | Medium: zig-zag `GenericShape`, dashed dividers, stamp, keypad, Canvas charts, print/roll motion | Medium: `RoundedPolygon.toShape()`, seesaw layout with spring, keypad, stacked bars |
## Recommendation
**Skärgård**, with one borrowing from Kalas: solid category-colour icon tiles in ledger rows (Skärgård's tinted 12 dp containers are the quieter fallback).
Why:
1. **The two jobs that matter most (§13.1) are done best.** The ten-second add flow is equally good in all three, but Skärgård's Home answers "who owes whom" with the least to read: one sentence, one number, one button.
2. **Lowest long-term risk.** It is the closest to stock Material 3, so Compose upgrades, M6's screenshot tests and future screens cost the least; every Expressive part has a stable fallback that still looks right.
3. **Works for both users.** It is neither austere (Kvitto's risk for the spouse) nor novelty-driven (Kalas's seesaw), and it is the only direction that can follow the wallpaper colours if either of you wants that.
4. **Category recognition** is Kalas's real advantage; it transplants cleanly into Skärgård's list rows without the rest of Kalas's playfulness.
Pick Kvitto if density and the receipt character matter more than warmth; pick Kalas if you both want the app to feel fun every day. Mixing is possible: tell me which parts of which direction you like (e.g. "Skärgård, but Kvitto's receipt detail screen").
## Known gaps in the mocks (not design decisions)
* `S-4-home` shows no pending count although the Vardag ledger has one pending row; real Home shows the count whenever `pendingCount > 0` (`screens.md` S-4).
* `S-11-analytics-week` uses a custom range (24 aug.–17 okt., v. 35–42) with invented weekly values; "Senaste 6 mån" by week would show ~26 bars.
* `S-8-expense-deleted` uses a different expense in each direction (no shared data for it).
* Kalas lets the per-account settle-up amount be edited (AC-11.4 says "defaults to"); Skärgård shows the swap control there too (per-account settle-ups go either way, §9.4). Both are allowed by the spec and should become the rule in U2.
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
# Export every phone frame of one direction to <dir>/png/<frame-id>.png at 2x.
# Usage: docs/ux/directions/export-png.sh docs/ux/directions/<n>-<slug> [frame-id ...]
# Needs Google Chrome (macOS path below or CHROME=...). The page must implement ?list=1 and ?frame=<id>.
set -euo pipefail
dir="$(cd "$1" && pwd)"; shift
chrome="${CHROME:-/Applications/Google Chrome.app/Contents/MacOS/Google Chrome}"
page="file://$dir/index.html"
run() { "$chrome" --headless=new --disable-gpu --hide-scrollbars --no-first-run --virtual-time-budget=10000 "$@" 2>/dev/null; }
if [ "$#" -gt 0 ]; then ids=("$@"); else
ids=()
while IFS= read -r line; do ids+=("$line"); done < <(run --dump-dom "$page?list=1" \
| sed -n '/<pre id="frame-list">/,/<\/pre>/p' | sed -e 's/<[^>]*>//g' | grep -v '^[[:space:]]*$')
fi
[ "${#ids[@]}" -gt 0 ] || { echo "no frames listed by $page?list=1" >&2; exit 1; }
mkdir -p "$dir/png"
for id in "${ids[@]}"; do
run --window-size=411,891 --force-device-scale-factor=2 --screenshot="$dir/png/$id.png" "$page?frame=$id"
done
echo "exported ${#ids[@]} frames to $dir/png"
+110
View File
@@ -0,0 +1,110 @@
# Kvitt: flows
Status: U1 proposal (2026-10-17). Step-by-step flows with screen ids (`ia.md`, `screens.md`). Rules referenced by spec section; nothing here redefines them. Data from the shared mock household (Jacob = owner/admin, Anna = partner; today lördag 17 oktober 2026).
Notation: **User** does · *System* does · `S-n` screen · ⟶ navigation.
---
## F-1 First run as admin (US-1, FR-1, FR-2, FR-5)
1. **Jacob** runs `kvitt-server bootstrap` on the Pi (outside the app); it prints the code `7K3M-QX9D-2HPA` and the link `https://icehack.asuscomm.com:8443/i/7K3MQX9D2HPA` (AC-1.1).
2. **Jacob** opens the link on the phone. The landing page (AC-2.5) offers "Ladda ner Kvitt" (first time) and "Öppna i Kvitt".
3. **Jacob** installs the APK (Android asks once to allow installs from the browser; outside the app), then taps "Öppna i Kvitt" ⟶ `S-1` with server and code filled (AC-2.6). Without a link: `S-1` empty, he types the code (server prefilled).
4. **Jacob** types his name in "Ditt namn" and taps "Para ihop". *Progress "Parar ihop…"; pairs with the phone model as device name ("Galaxy S26").* (`S-1` always has "Ditt namn" on a first pairing because AC-2.4 sends the display name in the accept call and the app cannot know the invite kind beforehand; the server ignores it for `device` invites. The field is hidden in the "Para ihop telefonen igen" variant.)
5. *Full sync (AC-2.9)* ⟶ `S-4` loading state "Hämtar era utgifter…" ⟶ the system notification-permission prompt.
6. *Partner not yet joined* ⟶ `S-4` shows the `S-3` content "Väntar på din partner" (AC-5.1).
7. **Jacob** taps "Bjud in partner" ⟶ `S-21`, a `partner` invite is created and shown: code in groups of 4, link, "Gäller till tis 20 okt. 14:02".
8. **Jacob** taps "Dela" ⟶ Android share sheet ⟶ sends the message to Anna (SMS, Signal, …). Back ⟶ `S-3` now shows the expiry.
9. When Anna has joined (F-2), the next sync replaces `S-3` with `S-4` empty state "Skapa ert första konto" ⟶ **Jacob** taps "Nytt konto" ⟶ `S-6` "Vardag", 50/50 ⟶ Spara ⟶ `S-4` with the account and the FAB.
Error branches: `invite_invalid` / network / `rate_limited` inline on `S-1` (screens.md S-1). Lost every phone later: `kvitt-server pair-device` (AC-1.6) then F-1 steps 2–5.
## F-2 Join as spouse (US-2, AC-2.4, AC-2.10)
1. **Anna** receives Jacob's message, taps the link ⟶ landing page ⟶ "Ladda ner Kvitt" ⟶ installs (Android "Install unknown apps" for her browser, once) ⟶ "Öppna i Kvitt" ⟶ `S-1` filled.
2. **Anna** types her name in "Ditt namn" ("Anna") and taps "Para ihop" ⟶ *pairs, creates the member user*.
3. ⟶ `S-2`: "Du går med i Jacobs hushåll", her name prefilled, avatar with initials. **Anna** taps "Lägg till bild" ⟶ photo picker ⟶ picks a photo ⟶ *upload with progress*. Taps "Fortsätt".
4. *Full sync* ⟶ `S-4` loading ⟶ notification permission prompt ⟶ `S-4` empty "Skapa ert första konto" (or Jacob's accounts, if he created any after she joined).
5. On Jacob's phone the next sync (or push-triggered sync) removes `S-3`.
## F-3 Add an expense online (US-3, AC-8.6) — the ten-second flow
1. **Jacob** opens Kvitt ⟶ `S-4`.
2. Taps FAB "Lägg till utgift" ⟶ `S-7` with amount focused, keypad up; defaults: Konto Vardag (last used), Kategori (last used in Vardag, else Övrigt), Datum Idag, Betalt av Du, Delning Kontots standard 50/50.
3. Types `412`.
4. Taps Beskrivning, types "Systembolaget".
5. (optional, one tap each) taps the Kategori chip ⟶ grid ⟶ Nöje; taps Betalt av to switch to Anna; taps Delning to open the split section.
6. Taps "Spara" ⟶ *saved locally, pending* ⟶ back to `S-4`, snackbar "Utgiften sparades"; Vardag's wording/amount and the global hero update immediately (AC-10.4).
7. *2 s later, sync pushes (AC-15.3)*; the sync line returns to "Synkad nyss"; *the server pushes `expense_created` to Anna's phone*, which syncs and shows "Jacob lade till en utgift · Systembolaget · 412,00 kr · Vardag".
## F-4 Add an expense offline, then sync (US-12, FR-15)
1. **Jacob** is on a plane. `S-4` sync line: "Offline · Synkad 14 okt. 08:12".
2. F-3 steps 2–6 exactly. Snackbar "Sparad. Synkas när du är online." The row in `S-5` shows the pending marker; `S-4` sync line "Offline · 1 ändring väntar på synk".
3. He adds more and edits one; the count increases ("3 ändringar väntar på synk"). Account, category and recurring actions show "Kräver anslutning".
4. Landing: network returns ⟶ *sync on foreground or WorkManager* ⟶ sync line "Synkar…" ⟶ "Synkad nyss"; pending markers disappear.
5. **Conflict branch:** Anna edited the same expense meanwhile with a later `updated_at` ⟶ *push answers `stale`* ⟶ the row shows Anna's version; sync line "Synkad nyss · 1 ändring synkades inte" (warning style) ⟶ tap ⟶ `S-16` "Ändringar som inte synkades": "Babette · Din ändring ersattes av en nyare ändring från Anna" ⟶ tap opens the current expense (read-only `S-8`) ⟶ "Ta bort från listan".
6. **Delete branch:** Anna deleted it ⟶ reason `deleted` "Den här togs bort innan din ändring kom fram".
## F-5 Edit an expense the other person created (AC-8.2, AC-12.4)
1. **Jacob** gets a notification "Anna lade till en utgift · ICA Maxi Lindhagen · 1 147,60 kr · Vardag" ⟶ taps ⟶ `S-8` (after sync).
2. Timeline: "Anna skapade den här · 16 okt. 14:02". He sees the receipt says 1 247,60.
3. Taps "Ändra" ⟶ `S-7` "Ändra utgift" with all fields loaded; amount focused.
4. Changes 1 147,60 to 1 247,60 ⟶ "Spara" ⟶ back to `S-8`; timeline gains a last line "Din ändring väntar på synk".
5. *Sync* ⟶ the pending line is replaced by "Du ändrade beloppet från 1 147,60 kr till 1 247,60 kr · 16 okt. 19:40".
6. *Anna's phone* gets "Jacob ändrade en utgift · ICA Maxi Lindhagen · 1 247,60 kr"; her `S-8` shows the same timeline (her view: "Jacob ändrade beloppet …").
## F-6 Global settle-up with adjusted allocation (US-7, AC-11.3, AC-11.5, §9.4)
Start: global "Du är skyldig Anna 3 250,00 kr"; Vardag Anna owes Jacob 1 184,50; Huset Jacob owes 3 084,50; Japan 2027 Jacob owes 1 350,00.
1. **Jacob** taps "Gör upp" on `S-4` ⟶ `S-9`: "Du betalar Anna", amount 3 250,00 kr (default = global debt), proposal (oldest owing first): Huset 3 084,50, Japan 2027 165,50; "Kvar att fördela 0,00 kr"; info "Vardag påverkas inte: där är Anna skyldig dig 1 184,50 kr."
2. They agreed to clear Japan first. **Jacob** edits Japan 2027 to 1 500,00 ⟶ *row warning "Det här reglerar mer än som är skyldigt i Japan 2027"* (1 500 > 1 350); remaining "−" ⟶ "1 334,50 kr för mycket"; Bekräfta disabled.
3. Edits Huset to 1 750,00 ⟶ "Kvar att fördela 0,00 kr"; Bekräfta enabled; Japan warning remains.
4. Sets Anteckning "Swish", Datum Idag. Taps "Bekräfta" ⟶ dialog repeating the warning ⟶ "Bekräfta ändå".
5. *Saved locally* ⟶ back to `S-4`: global −3 250,00 + 3 250,00 = 0 ⟶ "Ni är kvitt"; per account Huset "Du är skyldig Anna 1 334,50 kr", Japan "Anna är skyldig dig 150,00 kr", Vardag unchanged. (This is §9.4's stated consequence; the per-account settle-ups in F-7 clear the rest.)
6. *Sync; Anna gets "Jacob registrerade en betalning · Jacob betalade dig 3 250,00 kr"* (her view).
Variant: "Lägg till konto" ⟶ picker (all non-deleted accounts, archived included) ⟶ adds a row with 0,00 to fill in. Removing a row increases "Kvar att fördela".
## F-7 Per-account settle-up (US-8, AC-11.4)
1. **Jacob** on `S-4` taps Huset ⟶ `S-5` "Du är skyldig Anna 3 084,50 kr" ⟶ "Gör upp det här kontot".
2. ⟶ `S-9` per-account variant: "Du betalar Anna", 3 084,50 kr, one fixed row Huset 3 084,50. No "Lägg till konto".
3. Optional: changes the amount to 2 000,00 ⟶ the single row follows the amount (it is the only allocation) ⟶ Bekräfta.
4. ⟶ back to `S-5`: a settlement row "Du betalade Anna 3 084,50 kr" at the top of October; header "Kvitt".
5. To settle an account where the *other* user owes (Vardag): `S-5` Vardag ⟶ "Gör upp det här kontot" ⟶ direction defaults to "Anna betalar dig" (AC-11.4); Jacob records Anna's Swish.
## F-8 Create a recurring expense (US-10, FR-9)
1. **Jacob** ⟶ Inställningar `S-13` ⟶ "Återkommande utgifter" `S-19` ⟶ "Ny".
2. Editor: amount 12 400,00; description "Hyra"; Konto Huset; Kategori Hem; Betalt av Anna (one tap); Delning Kontots standard 60/40; Dag i månaden 25; Första månad: *defaults to oktober 2026 because the 25th is still ahead (AC-9.8)*; Sista månad empty.
3. Spara (online) ⟶ list row "Hyra · 12 400,00 kr · Huset · Varje månad den 25:e · Nästa: 25 okt.".
4. *On 25 Oct at 06:00 the server creates the expense* ⟶ both phones get "Återkommande utgift tillagd · Hyra · 12 400,00 kr · Huset" (AC-9.6) ⟶ `S-8` shows "Återkommande: Hyra" and "Skapad automatiskt (återkommande) · 25 okt. 06:00".
5. Offline at step 3 ⟶ Spara disabled with "Kräver anslutning".
Pause: list row ⟶ editor ⟶ "Pausad" on ⟶ Spara ⟶ row shows "Pausad". Resume does not backfill (§9.5); the editor shows the hint "Månader som passerat under pausen skapas inte i efterhand."
## F-9 Archive an account (US-9, AC-6.4)
1. **Anna** on `S-4` ⟶ Japan 2027 ⟶ `S-5` ⟶ overflow ⟶ "Arkivera" (online).
2. *Archived* ⟶ back on `S-4`, snackbar "Japan 2027 arkiverades" with "Ångra". The account disappears from the active list and appears under "Arkiverade (2)" (collapsed) with its balance still shown; the global balance is unchanged (AC-10.5).
3. In `S-7` the account picker no longer lists it; `S-9` "Lägg till konto" still does; `S-11` still counts it.
4. Unarchive: Home ⟶ "Arkiverade" ⟶ Japan 2027 ⟶ `S-5` ("Arkiverat" badge) ⟶ "Återställ från arkiv".
5. Delete branch: overflow "Ta bort konto" is disabled with "Kontot kan inte tas bort så länge det har utgifter, betalningar eller återkommande utgifter" unless the account is empty (AC-6.5).
## F-10 In-app update (US-14, FR-16)
1. *Cold start of a release build* ⟶ `GET /apk/latest` ⟶ version code higher ⟶ `S-4` banner "Version 1.2 finns" [Uppdatera] [Senare].
2. **Jacob** taps "Uppdatera". First time: dialog "Tillåt installation" explains that Android will ask once ⟶ "Fortsätt" ⟶ system settings "Installera okända appar" for Kvitt ⟶ he allows and taps back.
3. *Download* ⟶ banner "Laddar ner… 64 %" with progress; the app stays usable.
4. *Size and SHA-256 verified* ⟶ the system installer dialog ⟶ "Uppdatera" (system) ⟶ the app restarts on the new version.
5. Failure ⟶ banner "Nedladdningen misslyckades. Försök igen." ⟶ "Försök igen" (AC-16.4).
6. "Senare" hides the banner until the next cold start. The update is also reachable from `S-22` "Sök efter uppdatering".
## F-11 Remove a lost phone (US-17, FR-3) — supporting flow
1. **Jacob** (admin) ⟶ `S-13` ⟶ "Enheter" `S-20` ⟶ the lost "Galaxy S26+" row ⟶ "Ta bort" ⟶ confirmation ⟶ *revoked*.
2. The lost phone, if it ever connects, gets `device_revoked` ⟶ wipes local data ⟶ `S-18` "Den här telefonen har tagits bort" ⟶ "Para ihop igen" ⟶ `S-1`.
+124
View File
@@ -0,0 +1,124 @@
# 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/<CODE>` (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).
+266
View File
@@ -0,0 +1,266 @@
# Kvitt: screens
Status: U1 proposal (2026-10-17), direction-independent. What each screen contains, in which order, in which states, and what each action does. How it looks is the chosen direction's job (`directions/`); the exact words are in `strings.csv`. Rules (rounding, allocation order, sync, limits) are the spec's and are referenced, never restated as new rules.
Conventions:
* **States** per SPEC §13.3: loaded, empty, loading (first sync only), offline (with pending changes), error (a failed online action), long content (long Swedish names, 7-digit amounts, 200 % font scale).
* "Kräver anslutning" = the action is disabled while offline per §9.9, with that text as a supporting line or tooltip, never a silent disabled control.
* "Kunde inte nå servern" = an online action failed with a network error; nothing changed locally (§9.9). Shown as a snackbar on the screen that triggered it, with "Försök igen" when the action is repeatable.
* Server error codes (§14.3.2) map to inline text where a field is at fault (`name_taken` on the name field) and to a snackbar otherwise. Copy keys in `strings.csv` prefix `error_`.
* Amounts: tabular figures, right-aligned in lists, never ellipsized; at 200 % they move under the description instead. Direction is always words plus colour (§13.1).
* Pending-sync marker: a small `cloud_upload` (or `schedule`) icon with TalkBack label "Väntar på synk" on every row and detail whose `pending == true`.
* Every screen reads local data, so after the first sync there is no loading state except where noted.
---
## S-1 Pairing (Para ihop)
**Purpose:** connect this phone to the household with an invite code (US-1, US-2, AC-2.6, AC-2.7).
Content, in priority order:
1. App name "Kvitt" and the one-line purpose "Delade utgifter för två".
2. Invite code field: 12 characters, displayed and typed in groups of 4 (`7K3M-QX9D-2HPA`); input is uppercased and normalised per §8.3 as the user types (hyphens inserted automatically, `O`→`0`, `I`/`L`→`1`). Trailing "Klistra in" button (reads the clipboard; accepts a full link and extracts the code and server).
3. Explanation: "Koden får du av Jacob: från en länk eller från Inställningar › Bjud in partner." For the admin's first run: "Koden skrivs ut när servern sätts upp."
4. "Ditt namn" field (1–40, §9.6), required on a first pairing: AC-2.4 sends the display name with the accept call and the app cannot know the invite kind beforehand (the server ignores it for `device` invites). Hidden in the "Para ihop telefonen igen" variant. The partner confirms it again on S-2.
5. Server address field, prefilled `https://icehack.asuscomm.com:8443` (release), collapsed under "Serveradress" as a secondary row since it rarely changes; expanded automatically when the link carried a different server.
6. Primary button "Para ihop".
States:
* **Loaded/empty:** code field focused, keyboard open (uppercase letters and digits).
* **From link:** both fields filled, focus on "Para ihop".
* **Loading:** button shows progress "Parar ihop…", fields read-only. After success: full sync runs (S-4 loading state), then the notification permission prompt (AC-2.9).
* **Error** (inline under the code field, button enabled again): `invite_invalid` "Koden fungerar inte. Den kan ha gått ut eller redan använts. Be Jacob om en ny." · network "Kunde inte nå servern. Kontrollera adressen och anslutningen." · `rate_limited` "För många försök. Vänta en stund och försök igen." · `http://` in release (AC-2.8) "Adressen måste börja med https://" on the server field.
* **Unrecognised variant** (§14.4.8): header "Para ihop telefonen igen" and a line "Dina utgifter finns kvar på telefonen."
* **Offline:** the button stays enabled; tapping it gives the network error (pairing never queues).
* **Long content:** single column, scrolls; at 200 % the code field wraps to two lines of groups.
Actions: Para ihop (primary); Klistra in. No FAB, no bottom nav.
## S-2 Partner profile step (Din profil)
**Purpose:** the partner confirms their name and optionally adds a picture before entering the app (AC-2.10).
Content: 1. "Du går med i Jacobs hushåll" with Jacob's avatar and name; 2. own avatar (initials on `avatar-2` until a picture is chosen) with "Lägg till bild" (Android photo picker, AC-4.3); 3. display name field prefilled from the pairing call (1–40 characters, §9.6); 4. "Fortsätt".
States: **uploading picture** (progress on the avatar; Continue waits); **error** `image_invalid` / `unsupported_media_type` / `payload_too_large` as a snackbar "Bilden kunde inte användas" with the reason; network "Kunde inte nå servern" — the user may continue without the picture; **validation** empty name blocks Continue with "Skriv ett namn".
Actions: Fortsätt (primary, saves name if changed); Lägg till bild / Byt bild / Ta bort bild.
## S-3 Waiting for partner (Väntar på din partner)
**Purpose:** the admin's Home until the partner has joined (AC-5.1).
Content: illustration-free empty state on the Home destination: title "Väntar på din partner"; body "Kvitt behöver er båda innan ni kan börja. Bjud in din partner med en länk."; if an invite is pending: "Inbjudan gäller till mån 19 okt. 21:40"; primary "Bjud in partner" (→ S-21, creates a `partner` invite; if one is pending S-21 shows it and offers a new one). Bottom nav visible; no FAB; no balance; no "Nytt konto".
States: **offline** "Bjud in partner" disabled with "Kräver anslutning". **Partner joins** while the screen is open: the next sync replaces it with S-4 (empty state "Skapa ert första konto").
## S-4 Home (Översikt)
**Purpose:** answer "who owes whom" in one glance and start the two most common actions: add an expense and settle up (US-6, AC-10.1, AC-10.2).
Content, in priority order:
1. **Update banner** (only when `UpdateState` is `Available`, `Downloading`, `Failed` or `NeedsInstallPermission`; S-17).
2. **Balance hero:** both avatars; wording per §9.3 from the viewer's side: "Anna är skyldig dig", "Du är skyldig Anna", or "Ni är kvitt"; the amount (`amount-hero`) on its own line; "Gör upp" button when the global balance ≠ 0. TalkBack reads one sentence: "Du är skyldig Anna 3 250,00 kronor".
3. **Sync line**, small, under the hero: "Synkad för 3 min sedan" (relative time per §9.8); appended "· 2 ändringar väntar på synk" when `pendingCount > 0`; "Offline" prefix when offline; "Synkar…" while running; warning style with "· 1 ändring synkades inte" when `rejectedCount > 0` or "Telefonens klocka går fel" when `clockSkew`. Tap → S-16. Never a blocking banner.
4. **Konton** section header with trailing "Nytt konto" (text button; "Kräver anslutning" offline).
5. **Active accounts** in `created_at` order (AC-6.6): name (ellipsize at one line, 2 lines at 200 %), wording ("Anna är skyldig dig", "Du är skyldig Anna", "Kvitt"), amount (omitted when Kvitt), "11 utgifter i oktober" (plural, current month name). Tap → S-5.
6. **"Arkiverade (1)"** collapsed row; expands in place to the archived accounts with an "Arkiverat" badge (AC-6.4).
7. FAB "Lägg till utgift" (extended, collapses to icon-only while scrolling down; TalkBack "Lägg till utgift").
States:
* **Loading** (first sync after pairing only): hero and rows as skeletons, text "Hämtar era utgifter…"; the FAB is hidden until the first sync finishes or fails (then the local, possibly empty, state is shown).
* **Empty** (partner joined, no accounts): no hero amount ("Ni är kvitt" is not meaningful yet: hero shows both avatars and "Välkommen, Anna och Jacob"), empty state "Skapa ert första konto" with one line ("Ett konto samlar utgifter som hör ihop, till exempel Vardag eller Japan 2027.") and the primary button "Nytt konto". No FAB (AC-8.6 needs an account).
* **Settled:** hero "Ni är kvitt", no amount, no "Gör upp".
* **Offline:** everything works; sync line "Offline · 2 ändringar väntar på synk"; "Nytt konto" disabled with "Kräver anslutning".
* **Error:** not applicable (local data); a failed sync shows as the sync line in warning style ("Synk misslyckades · försök igen om en stund"), never a dialog.
* **Long content:** account names up to 60 characters ellipsize in one line (two at 200 %); 7-digit amounts ("1 234 567,89 kr") fit the hero on one line at 100 % and wrap under the wording at 200 %.
Actions: FAB → S-7 (account defaulted per AC-8.6); Gör upp → S-9 global; account row → S-5; Nytt konto → S-6; sync line → S-16; banner → S-17.
## S-5 Account detail (Konto)
**Purpose:** one account's balance and its ledger; settle just this account (AC-10.3, US-8).
Content:
1. Top app bar: account name (ellipsize), overflow menu: "Ändra konto" (→ S-6), "Arkivera" / "Återställ från arkiv", "Ta bort konto" (enabled only when `deletable`; otherwise disabled with the reason "Kontot kan inte tas bort så länge det har utgifter, betalningar eller återkommande utgifter"). All three "Kräver anslutning" offline.
2. Header: default split "50 / 50 · Jacob och Anna" (names in percent order, viewer as "du" is not used here; names keep the line unambiguous); "Arkiverat" badge if archived; balance wording and amount (§9.3, account wording "Kvitt"); "Gör upp det här kontot" when non-zero.
3. Ledger grouped by month, newest first: sticky month header "Oktober · 7 457,90 kr" (household spend of the month, settlements excluded); rows ordered per AC-8.10:
* **Expense row:** category icon on its colour container, description (one line), date ("16 okt."), amount (right), effect line (§9.2: "Du lånade ut 432,17 kr" in owed colour, "Du lånade 623,80 kr" in owing colour, "Inte inblandad" / "Betalade för dig själv" neutral), pending marker. Tap → S-8.
* **Settlement row:** `payments` icon, "Anna betalade dig 800,00 kr" or "Du betalade Anna 800,00 kr", "· 2 konton" when more than one allocation (AC-11.7), date, pending marker. Tap → S-10.
4. FAB "Lägg till utgift" (account preset to this account).
States: **empty** (new account) "Inga utgifter än" + "Lägg till den första utgiften i Vardag." (FAB still there); **archived** badge, "Återställ från arkiv" in the header as a tonal button, FAB still offered? **No**: archived accounts are hidden from the account picker for new expenses (AC-6.4), so the FAB is hidden on an archived account; settling stays available; **offline** rows work, overflow actions disabled; **error** archive/delete failures as snackbar (`account_not_empty` "Kontot har fortfarande utgifter, betalningar eller återkommande utgifter"); **long content** month totals and row amounts at 7 digits stay on one line at 100 %; at 200 % the amount moves to the line under the description.
Actions: FAB → S-7; Gör upp det här kontot → S-9 (one fixed row); row → S-8/S-10; overflow actions; archive asks no confirmation (reversible) and shows a snackbar "Vardag arkiverades" with "Ångra"; delete asks "Ta bort kontot Vardag? Det går inte att ångra." (soft delete on the server, but not restorable from the app).
## S-6 Account editor (Nytt konto / Ändra konto)
**Purpose:** create or edit an account's name and default split (AC-6.1 to AC-6.3). Online only.
Content: 1. Name field (1–60, unique: `name_taken` → "Det finns redan ett konto som heter Vardag"); 2. "Standarddelning": a slider (0–100 % in steps of 1, snapping to 50) plus two percent fields "Jacob %" and "Anna %" that always sum to 100 (typing in one updates the other; two decimals allowed, comma or point); quick presets as chips "50/50", "60/40", "40/60"; 3. preview "Av 100,00 kr: Jacob 60,00 kr, Anna 40,00 kr"; 4. editing only: hint "Ändringen gäller nya utgifter. Befintliga utgifter behåller sin delning." (AC-6.3); 5. "Spara" (top app bar action and/or bottom button, per direction).
States: **offline** all fields disabled, banner "Kräver anslutning. Konton kan bara ändras när telefonen är online." , Spara disabled; **saving** progress on Spara; **error** network snackbar; validation inline.
Actions: Spara; Avbryt (back; asks "Slänga ändringarna?" only if something changed).
## S-7 Expense editor (Ny utgift / Ändra utgift) — the add-expense flow
**Purpose:** record an expense in ten seconds (SPEC §13.1): open app, tap add, type amount, type description, tap save. Full screen, not a sheet (§13.2). Works offline (AC-8.2).
### Layout, top to bottom
1. **Top app bar:** close (`close`, TalkBack "Avbryt"), title "Ny utgift" / "Ändra utgift", trailing "Spara" text button (enabled when valid). Edit mode adds overflow "Ta bort".
2. **Amount** (focus on open): display-size `amount-large`, tabular, with "kr" suffix; input rules §9.6 (digits, one `,` or `.`, max two decimals, no sign, max 10 000 000,00). Empty shows "0,00" as a hint in `on-surface-variant`, not as a value.
3. **Description** field "Beskrivning" (1–100), single line, IME action "Spara" (saves when valid, otherwise moves focus to the first invalid field).
4. **Defaults row**: one chip per field, each showing its current value, each one tap to change:
* **Konto:** "Vardag" (AC-8.6: last used on this phone if still active, else the oldest active). Opens a picker of active accounts (bottom sheet list with balances). Changing the account in mode `default` re-copies the ratio (AC-8.8) and changes the category default only if the user has not picked a category yet.
* **Kategori:** icon + name of the default (AC-8.6: last used in that account on this phone if active, else Övrigt). Opens the category grid (icon + colour + name, active categories, `sort_order`).
* **Datum:** "Idag" (also "Igår", else "16 okt."); opens the Material date picker; range per §9.6.
* **Betalt av:** "Du" or "Anna" with the avatar; **one tap toggles** between the two users (no picker: there are exactly two) (AC-8.3).
* **Delning:** "Kontots standard 50/50" — **one tap opens the split section** in place (below the chips) and scrolls it into view.
5. **Split section** (collapsed by default; expanded when the mode is not `default` or the user tapped Delning):
* Mode chooser (single-select connected buttons / segmented, wraps to two rows in Swedish): "Kontots standard 60/40" · "Egen fördelning" · "Exakta belopp" · "Allt på en person".
* `ratio`: same control as S-6 (slider + two percent fields summing to 100).
* `exact`: two money fields; the user types one, the other is read-only "amount minus typed" (AC-8.7). Typed > amount → inline error "Andelen är större än beloppet" and Save disabled.
* `full`: choose the bearer: "Jacob" / "Anna" (two avatar toggles), independent of the payer.
* **Live preview** for all modes, from `ExpenseRepository.preview` (the domain computes it; the UI never computes shares): "Du 206,00 kr · Anna 206,00 kr", plus the effect line that the row will show ("Du lånade ut 206,00 kr"). Hidden while `preview` returns null.
6. **Anteckningar:** collapsed as "Lägg till anteckning"; expands to a multi-line field (0–1000).
7. **Keypad / keyboard:** while the amount has focus, the numeric input is shown at the bottom (each direction states whether it is an in-app keypad with a guaranteed "," key or the system keyboard with `KeyboardType.Decimal`). Focus moves to the description with "Nästa"/IME or by tapping it; the system text keyboard replaces the keypad.
8. Edit mode only, at the end: a destructive "Ta bort utgift". (Who created the expense is shown on S-8 as the first timeline line; `ExpenseDraft` carries no creator.)
### The ten-second path (binding interaction)
| Step | User does | Screen does |
|---|---|---|
| 1 | Taps FAB on S-4 | S-7 opens with amount focused, keypad up, all defaults set |
| 2 | Types `412` | Amount shows "412", Save still disabled (description missing) |
| 3 | Taps "Beskrivning", types "Systembolaget" | Text keyboard; Save enabled |
| 4 | Taps "Spara" (app bar) or IME "Spara" | Saves locally (`SaveResult.Saved`), closes, snackbar "Utgiften sparades" (no "Ångra": an undo would be a create plus a delete, both audited and pushed to the partner), the new row is visible on return; sync runs after 2 s (AC-15.3) |
The split override is one tap from step 3 (the Delning chip). Payer change is one tap. Nothing else needs touching for the common case.
### States
* **Validation** (inline, from `SaveResult.Invalid` field errors, mapped by token): amount missing "Skriv ett belopp"; amount out of range "Beloppet måste vara mellan 0,01 kr och 10 000 000,00 kr"; description empty "Skriv en beskrivning"; date out of range "Datumet ligger för långt fram"; exact mismatch as above.
* **Offline:** no difference; after saving, the snackbar says "Sparad. Synkas när du är online."
* **No accounts:** the editor is not reachable (no FAB). **Only archived accounts:** same (FAB hidden; AC-6.4).
* **Edit mode for an expense the other user created:** same editor, no warning; the save records the change in the timeline ("Du ändrade …").
* **Discard:** close with changes asks "Slänga utgiften?" (Släng / Fortsätt redigera).
* **Long content:** 7-digit amount scales down one type step rather than wrapping; at 200 % the chips wrap to several rows and the split section scrolls; Spara stays in the app bar so it is always reachable.
## S-8 Expense detail (Utgift)
**Purpose:** everything about one expense and who changed what (US-13, AC-12.4).
Content: 1. amount (large) and description; category chip; 2. facts list: Konto, Datum ("fredag 16 oktober 2026"), Betalt av (avatar + name), Delning (mode label, e.g. "Kontots standard 50/50", "Egen fördelning 70/30", "Exakta belopp", "Allt på Anna") and each user's share ("Du 623,80 kr · Anna 623,80 kr"), the effect line; "Återkommande: Hyra" when `recurringTemplateName != null` (AC-8.11); notes (full text); 3. **Historik** timeline, oldest first, newest last (AC-12.4): actor avatar, sentence, "· 16 okt. 19:40"; create line "Anna skapade den här"; update lines one per changed field ("Du ändrade beloppet från 1 147,60 kr till 1 247,60 kr"; split changes as "Du ändrade delningen från 50/50 till 70/30"); recurring source "Skapad automatiskt (återkommande)"; import source "Importerad från Splitwise"; pending last line "Din ändring väntar på synk" (AC-12.5).
Actions: "Ändra" (→ S-7 edit), "Ta bort" (confirm "Ta bort utgiften? Den försvinner för er båda." → soft delete, returns to the previous screen with snackbar "Utgiften togs bort").
States: **deleted** (opened from a deletion notification or the row was deleted while open): banner "Anna tog bort den här utgiften · 17 okt. 09:12", content muted, no Edit/Delete; **pending** marker next to the amount; **missing** (entity not in the database yet): "Utgiften finns inte på telefonen än" with "Synka nu"; **offline** fully readable, edit and delete allowed.
## S-9 Settle up (Gör upp)
**Purpose:** record a payment between the two users and spread it over accounts (US-7, US-8, AC-11.3 to AC-11.6). Works offline.
Content, in order:
1. **Direction:** "Du betalar Anna" with a swap control "Byt riktning" (TalkBack). Default per AC-11.3 / AC-11.4.
2. **Amount** (large money field), default = absolute global (or account) balance, empty when zero.
3. **Fördelning** list, one row per account from `SettleState.lines`: account name; "Skuld 3 084,50 kr" (the payer's debt in that account, `payerNetOre`, words: "Du är skyldig 3 084,50 kr" / "Anna är skyldig dig 1 184,50 kr" when the payer is owed there); editable allocated amount; per-row remove; over-settle warning row text "Det här reglerar mer än som är skyldigt i Japan 2027" (AC-11.5) in warning colour with `warning` icon.
4. **"Kvar att fördela 0,00 kr"** live indicator; when negative: "250,00 kr för mycket"; colour plus words.
5. **"Lägg till konto"** → picker of all non-deleted accounts not yet listed (active and archived, AC-11.3).
6. Info line when an account is left non-zero because the payer is owed there (§9.4 consequence): "Vardag påverkas inte: där är Anna skyldig dig 1 184,50 kr."
7. Datum (Idag), Anteckning (0–200).
8. **Bekräfta** (primary, enabled only when `canConfirm`), Avbryt.
Behaviour: editing the total amount re-runs the proposal (`recompute` with `allocations = null`) unless the user has edited a row, in which case it keeps the rows and updates "Kvar att fördela" (the user can tap "Föreslå igen" to re-run). Swap direction re-runs the proposal. Confirming with any warned row shows a dialog repeating the warning(s) (one line per warned row, the same text as on the row: "Det här reglerar mer än som är skyldigt i Japan 2027") with "Bekräfta ändå" / "Ändra".
Per-account variant (from S-5): one fixed row for that account (not removable), no "Lägg till konto", no info line.
States: **no accounts** cannot occur (button hidden); **zero balance** amount empty, rows appear after typing; **editing** title "Ändra betalning", proposal computed with this settlement excluded (AC-11.6); **offline** identical; **long content** at 200 % each row stacks name / debt / field vertically.
## S-10 Settlement detail (Betalning)
Content: "Anna betalade dig 800,00 kr" headline with both avatars and an arrow from payer to recipient; Datum ("tisdag 6 oktober 2026"); Anteckning; **Fördelning** per account with amounts; Historik timeline as in S-8 (diff lines "Du ändrade beloppet …", "… ändrade fördelningen"). Actions: Ändra (→ S-9 editing), Ta bort (confirm "Ta bort betalningen? Saldona räknas om för er båda."). States: deleted, pending, missing as S-8.
## S-11 Analytics (Statistik)
**Purpose:** how much the household spends, when, on what and in which account (US-11, FR-14).
Content:
1. **Filters** (sticky under the app bar, wrap on two rows in Swedish):
* Period chip "Senaste 6 mån" → menu with the presets of AC-14.2 (Denna månad, Senaste 3 mån, Senaste 6 mån, Senaste 12 mån, I år, All tid, Anpassad…); Anpassad opens the Material date range picker; the resolved range is shown under the chips ("1 maj – 17 okt. 2026").
* Three binary toggles (connected buttons): Månad / Vecka · Hushållet / Min del · Kategori / Konto.
2. **Totalt** for the range ("178 211,40 kr"), with the measure in words ("Hushållets utgifter" / "Din del").
3. **Utgifter över tid:** stacked bars, one per bucket (all buckets, empty ones as zero, AC-14.7), stacked by group (category colours or account colours), y-axis in kronor (no decimals on the axis), x-axis "maj, jun …" or "v. 35"; legend below (group colour + name, only groups present); tap a bar → S-12 for that bucket; horizontally scrollable when there are more buckets than fit (weeks), newest at the right and scrolled into view.
4. **Per kategori:** horizontal bars sorted descending, each row: icon, name, bar, amount, percent (one decimal, half-up, AC-14.10); tap → S-12.
5. **Per konto:** same form; archived accounts included with "Arkiverat" badge (AC-14.9).
Grouping applies to chart 1 only; charts 2 and 3 are always shown (AC-14.6). Settlements never count (AC-14.6).
Accessibility: each chart has a content description that summarises it in one sentence ("Utgifter över tid, sex månader: högst i september med 41 484 kronor, lägst i maj med 24 710 kronor") and the bars are focusable one by one ("September: 41 484 kronor, Resor största kategori"). Breakdown rows are ordinary list items.
States: **empty** "Inga utgifter i den här perioden" with "Byt period" (charts hidden, filters remain); **loading** none (local); **offline** identical; **long content** category names ellipsize in breakdown rows, amounts never; at 200 % the breakdown row puts the bar on its own line.
No FAB on Statistik.
## S-12 Analytics drill-down
Content: title naming the filter ("Mat & hushåll, oktober 2026", "v. 41, Vardag", "Japan 2027, senaste 6 mån"), the measure ("Hushållet"/"Min del") as subtitle, total, then expense rows exactly as in S-5 (with the account name added to the row's secondary line, since rows come from several accounts), newest first. Tap → S-8. Empty cannot occur (only reachable from a non-zero number); if data changed meanwhile: "Inga utgifter längre".
## S-13 Settings hub (Inställningar)
Content: 1. household card: own avatar, name, "Du · Administratör" / "Du"; partner's avatar and name ("Anna · Partner"); 2. rows with leading icons and chevrons: Profil (`person`), Kategorier (`category`), Återkommande utgifter (`event_repeat`), Enheter (`devices`), Bjud in partner (`person_add`, admin before join) or Para ihop en enhet (`qr_code`/`add_link`, admin after join), Synkstatus (`sync`, with trailing "2 väntar" when pending), Om Kvitt (`info`, trailing "Uppdatering finns" when available). Rows that lead to online-only screens stay enabled; the screens themselves show their offline state.
## S-14 Profile (Profil)
Content: large avatar with "Byt bild" and "Ta bort bild" (when a picture exists); display name field (1–40); Spara. Picture changes apply immediately on success (no Save needed); name needs Spara. States: offline (all disabled, "Kräver anslutning"), uploading (progress over the avatar), error codes as in S-2.
## S-15 Categories (Kategorier)
**List:** active categories in `sort_order`: drag handle (long-press to drag, plus "Flytta upp"/"Flytta ned" TalkBack actions), icon on colour container, name (custom name or localised default), chevron; "Arkiverade (n)" collapsed section; app bar action "Ny kategori". Reorder saves on drop (online; offline: drag handles hidden and a line "Kräver anslutning för att ändra").
**Editor** (pushed): name field (1–40; for a default category, empty name means "use the default name"; "Återställ namn" button shown when a custom name exists, AC-7.4); live preview chip; **icon grid** of the 30 icons of §9.7 (6 per row at 411 dp, 48 dp targets, selected with outline + check); **colour swatches** of the 12 tokens (each 48 dp, selected with check, TalkBack names "Grön", "Orange" …); Spara; overflow "Arkivera" / "Återställ från arkiv"; "Ta bort kategori" only when allowed, otherwise disabled with the reason: default "Standardkategorier kan inte tas bort, bara arkiveras", in use "Kategorin används av utgifter eller återkommande utgifter. Arkivera den i stället."
## S-16 Sync status (Synkstatus)
Content: 1. status headline: "Synkad för 3 min sedan" / "Synkar…" / "Offline"; 2. clock-skew warning card when `clockSkew` ("Telefonens klocka går fel. Rätta den och synka igen.") — shown first; 3. "Väntande ändringar (2)" list of pending ledger rows (as in S-5, with "ny"/"ändrad"/"borttagen" tag), tap → S-8/S-10; 4. "Ändringar som inte synkades" list from `rejections`: summary (description · amount), the reason text per §14.4.6, the time; tap opens the current version (read-only S-8/S-10); "Ta bort från listan" per item; 5. "Synka nu" (primary; disabled with progress while running; offline "Kräver anslutning").
Empty: "Allt är synkat" with the last sync time.
## S-17 Update available (Uppdatering)
* **Banner on S-4** (top of content, dismissible for the session): "Version 1.2 finns" + "Uppdatera" / "Senare".
* **First time only**, before downloading (`NeedsInstallPermission` or not yet granted): dialog "Tillåt installation" — "Android frågar en gång om Kvitt får installera appar. Välj Tillåt från den här källan och gå tillbaka." — buttons "Fortsätt" (opens the system setting) / "Avbryt".
* **Downloading:** banner shows "Laddar ner… 64 %" with a linear progress indicator (determinate); the app stays usable.
* **Verified:** the system installer appears (out of our hands).
* **Failed** (AC-16.4): banner "Nedladdningen misslyckades. Försök igen." + "Försök igen".
* **Offline:** "Uppdatera" disabled with "Kräver anslutning".
## S-18 Device removed (Telefonen har tagits bort)
Full screen, no navigation: icon `phonelink_erase`; title "Den här telefonen har tagits bort"; body "Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats. Be Jacob om en ny kod för att para ihop den igen." (member) / "…Skapa en ny kod på servern med kvitt-server pair-device." (admin, rare); primary "Para ihop igen" → S-1.
## S-19 Recurring expenses (Återkommande utgifter)
**List:** rows: description, amount, account, "Varje månad den 25:e", "Nästa: 25 okt." (`next_occurrence_date`) or "Pausad" badge, payer avatar; app bar "Ny"; empty state "Inga återkommande utgifter" + "Hyra, el och streaming kan läggas in automatiskt varje månad." + "Ny återkommande utgift".
**Editor** (pushed): the S-7 fields without date (amount, description, Konto (active accounts), Kategori, Betalt av, Delning, Anteckningar) plus "Dag i månaden" (1–31 picker; for 29–31 a hint "I kortare månader används sista dagen"), "Första månad" (month picker, default per AC-9.8, back to 12 months), "Sista månad (valfritt)", "Pausad" switch; Spara; overflow "Ta bort" (confirm "Ta bort Hyra? Utgifter som redan skapats finns kvar."). Hint under the fields when editing: "Ändringen gäller kommande utgifter." (AC-9.4). Online only (offline state as S-6).
## S-20 Devices (Enheter)
Rows per AC-3.1: device name, owner avatar and name, "Parad 3 okt.", "Senast sedd nyss", "Kvitt 1.1", "Den här enheten" tag; trailing "Ta bort" (not on the current device). Confirmation dialog: "Ta bort Galaxy S26+? Telefonen loggas ut och dess Kvitt-data raderas nästa gång den ansluter." → "Ta bort" / "Avbryt". States: loading (online fetch: progress), offline ("Kräver anslutning" + nothing listed; this list is not stored locally), error `cannot_revoke_current_device` cannot be triggered from the UI.
## S-21 Invites (Bjud in partner / Para ihop en enhet)
Admin only. Content: actions "Bjud in partner" (only while one user exists, AC-2.1) and "Para ihop en enhet" with a choice "För mig" / "För Anna" (AC-2.2). **Created invite:** the code very large in groups of 4 (`7K3M-QX9D-2HPA`, mono/tabular, selectable), the link, "Gäller till tis 20 okt. 14:02", "Dela" (Android share sheet with a Swedish message: "Gå med i vårt hushåll i Kvitt: <länk> Koden är 7K3M-QX9D-2HPA."), "Kopiera kod". **Pending invites** list: kind ("Partner", "Enhet för Anna"), created, expiry, "Avbryt" (confirm). The code is only visible right after creation (the server stores hashes); later rows say "Koden visas bara när den skapas". States: offline ("Kräver anslutning"), errors `household_full`, network.
## S-22 About (Om Kvitt)
Rows: "Version 1.1 (11)", "Server: https://icehack.asuscomm.com:8443", "Serverversion 1.1.0" (or "Okänd" when `/health` failed), "Öppen källkod-licenser" (→ the standard licences screen), button "Sök efter uppdatering" (→ check; result as S-17 banner or a snackbar "Du har den senaste versionen"; debug builds: hidden, AC-16.5).
---
## Notifications (not screens)
Built locally per AC-13.5 and §13.3 (copy in `strings.csv`, prefix `notif_`). Channel "Aktivitet". Small icon: the app's monochrome icon. Large icon: actor avatar if cached. Tap targets per AC-13.6.
+465
View File
@@ -0,0 +1,465 @@
key,en,sv,notes
common_save,Save,Spara,
common_cancel,Cancel,Avbryt,
common_delete,Delete,Ta bort,
common_edit,Edit,Ändra,
common_close,Close,Stäng,TalkBack label of the close icon
common_back,Back,Tillbaka,TalkBack label of the back icon
common_continue,Continue,Fortsätt,
common_retry,Try again,Försök igen,
common_more_options,More options,Fler alternativ,TalkBack label of the overflow icon
common_you,You,Du,"the viewer as a name, sentence start"
common_you_lower,you,du,the viewer inside a sentence
common_today,Today,Idag,
common_yesterday,Yesterday,Igår,
common_needs_connection,Needs connection,Kräver anslutning,§9.9
common_could_not_reach_server,Could not reach the server,Kunde inte nå servern,§9.9
common_offline,Offline,Offline,
common_pending_sync,Waiting to sync,Väntar på synk,TalkBack label of the pending marker
common_archived,Archived,Arkiverat,badge on an archived account
common_archived_category,archived,arkiverad,suffix on an archived category chip
common_discard_title,Discard changes?,Slänga ändringarna?,
common_discard,Discard,Släng,
common_keep_editing,Keep editing,Fortsätt redigera,
common_undo,Undo,Ångra,
common_amount_spoken,%1$s Swedish kronor,%1$s kronor,TalkBack form of an amount; %1$s = number without currency
common_kr_suffix,SEK,kr,suffix/prefix shown in the amount field
common_ratio,%1$s/%2$s,%1$s/%2$s,"ratio label, percents without % sign, e.g. 60/40"
common_percent,%1$s %%,%1$s %%,"percent with one decimal, e.g. 23,4 %"
common_user_split,%1$s %2$s,%1$s %2$s,"name + amount in a split preview: ""Du 206,00 kr"""
common_separator_dot, · , · ,list separator
common_and,%1$s and %2$s,%1$s och %2$s,
nav_home,Home,Översikt,
nav_analytics,Analytics,Statistik,
nav_settings,Settings,Inställningar,
fab_add_expense,Add expense,Lägg till utgift,FAB on S-4 and S-5
balance_partner_owes_you,%1$s owes you,%1$s är skyldig dig,%1$s partner name; amount shown separately
balance_you_owe_partner,You owe %1$s,Du är skyldig %1$s,
balance_partner_owes_you_amount,%1$s owes you %2$s,%1$s är skyldig dig %2$s,one-line form and TalkBack sentence
balance_you_owe_partner_amount,You owe %1$s %2$s,Du är skyldig %1$s %2$s,
balance_all_settled,All settled up,Ni är kvitt,"global, §9.3"
balance_account_settled,Settled up,Kvitt,"account screens and rows, §9.3"
effect_lent,You lent %1$s,Du lånade ut %1$s,
effect_borrowed,You borrowed %1$s,Du lånade %1$s,
effect_not_involved,Not involved,Inte inblandad,
effect_paid_for_yourself,Paid for yourself,Betalade för dig själv,
settlement_row_partner_paid_you,%1$s paid you %2$s,%1$s betalade dig %2$s,
settlement_row_you_paid_partner,You paid %1$s %2$s,Du betalade %1$s %2$s,
settlement_row_accounts:one,%d account,%d konto,"plural one; shown as ""· 2 konton"" only when > 1"
settlement_row_accounts:other,%d accounts,%d konton,plural other
sync_synced_relative,Synced %1$s,Synkad %1$s,%1$s = relative time below
time_just_now,just now,nyss,
time_minutes_ago,%d min ago,för %d min sedan,
time_hours_ago,%d h ago,för %d h sedan,
sync_syncing,Syncing…,Synkar…,
sync_pending:one,%d change waiting to sync,%d ändring väntar på synk,plural one
sync_pending:other,%d changes waiting to sync,%d ändringar väntar på synk,plural other
sync_rejected:one,%d change was not synced,%d ändring synkades inte,plural one
sync_rejected:other,%d changes were not synced,%d ändringar synkades inte,plural other
sync_failed_line,Sync failed · trying again soon,Synk misslyckades · försöker igen snart,
sync_clock_wrong_short,Your phone's clock is wrong,Telefonens klocka går fel,
pairing_app_name,Kvitt,Kvitt,not translated
pairing_tagline,Shared expenses for two,Delade utgifter för två,
pairing_code_label,Invite code,Inbjudningskod,
pairing_code_paste,Paste,Klistra in,
pairing_name_label,Your name,Ditt namn,
pairing_code_help,You get the code from %1$s: from a link or from Settings › Invite partner.,Koden får du av %1$s: från en länk eller från Inställningar › Bjud in partner.,%1$s admin name when known from the link; else use pairing_code_help_generic
pairing_code_help_generic,"You get the code from a link, or it is printed when the server is set up.","Koden får du från en länk, eller så skrivs den ut när servern sätts upp.",
pairing_server_label,Server address,Serveradress,
pairing_pair,Pair,Para ihop,
pairing_pairing,Pairing…,Parar ihop…,
pairing_again_title,Pair this phone again,Para ihop telefonen igen,§14.4.8
pairing_again_body,Your expenses are still on this phone.,Dina utgifter finns kvar på telefonen.,
pairing_error_invite_invalid,This code does not work. It may have expired or already been used. Ask for a new one.,Koden fungerar inte. Den kan ha gått ut eller redan använts. Be om en ny.,
pairing_error_network,Could not reach the server. Check the address and your connection.,Kunde inte nå servern. Kontrollera adressen och anslutningen.,
pairing_error_rate_limited,Too many attempts. Wait a while and try again.,För många försök. Vänta en stund och försök igen.,
pairing_error_https,The address must start with https://,Adressen måste börja med https://,AC-2.8
pairing_error_code_length,The code has 12 characters,Koden har 12 tecken,
pairing_error_name_required,Enter your name,Skriv ditt namn,
pairing_notification_rationale,Kvitt tells you when %1$s adds or changes something.,Kvitt säger till när %1$s lägger till eller ändrar något.,shown before the system permission prompt (AC-2.9)
profile_step_title,Your profile,Din profil,
profile_step_joining,You are joining %1$s's household,Du går med i %1$ss hushåll,sv genitive: name + s (no apostrophe); names ending in s get no extra s (handled in code: Jonas hushåll)
profile_add_picture,Add picture,Lägg till bild,
profile_change_picture,Change picture,Byt bild,
profile_remove_picture,Remove picture,Ta bort bild,
profile_name_label,Display name,Visningsnamn,
profile_name_required,Enter a name,Skriv ett namn,
profile_picture_failed,The picture could not be used,Bilden kunde inte användas,
profile_picture_too_large,The picture is too large (max 5 MB),Bilden är för stor (högst 5 MB),payload_too_large
profile_picture_unsupported,"Use a JPEG, PNG or WebP picture","Använd en bild i JPEG, PNG eller WebP",unsupported_media_type
profile_picture_invalid,The picture could not be read,Bilden kunde inte läsas,image_invalid
profile_offline_banner,Needs connection. Your profile can only be changed online.,Kräver anslutning. Profilen kan bara ändras när telefonen är online.,
waiting_title,Waiting for your partner,Väntar på din partner,
waiting_body,Kvitt needs both of you before you can start. Invite your partner with a link.,Kvitt behöver er båda innan ni kan börja. Bjud in din partner med en länk.,
waiting_invite_expires,Invite valid until %1$s,Inbjudan gäller till %1$s,"%1$s weekday + date + time, e.g. mån 19 okt. 21:40"
waiting_invite_partner,Invite partner,Bjud in partner,
home_accounts,Accounts,Konton,
home_new_account,New account,Nytt konto,
home_archived_section,Archived (%d),Arkiverade (%d),
home_settle_up,Settle up,Gör upp,
home_account_expenses_month:one,%1$d expense in %2$s,%1$d utgift i %2$s,plural one; %2$s month name (lowercase in sv)
home_account_expenses_month:other,%1$d expenses in %2$s,%1$d utgifter i %2$s,plural other
home_account_no_expenses_month,No expenses in %1$s,Inga utgifter i %1$s,
home_loading,Getting your expenses…,Hämtar era utgifter…,first sync only
home_empty_welcome,"Welcome, %1$s and %2$s","Välkommen, %1$s och %2$s",
home_empty_title,Create your first account,Skapa ert första konto,
home_empty_body,"An account collects expenses that belong together, for example Everyday or Japan 2027.","Ett konto samlar utgifter som hör ihop, till exempel Vardag eller Japan 2027.",
home_expense_saved,Expense saved,Utgiften sparades,
home_expense_saved_offline,Saved. Syncs when you're online.,Sparad. Synkas när du är online.,
home_settlement_saved,Payment saved,Betalningen sparades,
account_default_split,%1$s · %2$s,%1$s · %2$s,"""50 / 50 · Jacob och Anna""; %1$s ratio, %2$s names with common_and"
account_settle_this,Settle this account,Gör upp det här kontot,
account_month_total,%1$s · %2$s,%1$s · %2$s,"month header: ""Oktober · 7 457,90 kr"""
account_edit,Edit account,Ändra konto,
account_archive,Archive,Arkivera,
account_unarchive,Restore from archive,Återställ från arkiv,
account_delete,Delete account,Ta bort konto,
account_delete_blocked,"The account cannot be deleted while it has expenses, payments or recurring expenses","Kontot kan inte tas bort så länge det har utgifter, betalningar eller återkommande utgifter",AC-6.5; also error account_not_empty
account_delete_confirm_title,Delete the account %1$s?,Ta bort kontot %1$s?,
account_delete_confirm_body,This cannot be undone.,Det går inte att ångra.,
account_archived_snackbar,%1$s was archived,%1$s arkiverades,
account_unarchived_snackbar,%1$s was restored,%1$s återställdes,
account_empty_title,No expenses yet,Inga utgifter än,
account_empty_body,Add the first expense in %1$s.,Lägg till den första utgiften i %1$s.,
account_editor_new,New account,Nytt konto,
account_editor_edit,Edit account,Ändra konto,
account_editor_name,Name,Namn,
account_editor_default_split,Default split,Standarddelning,
account_editor_percent_of,%1$s %%,%1$s %%,"label of a percent field: ""Jacob %"""
account_editor_preview,Of %1$s: %2$s,Av %1$s: %2$s,"""Av 100,00 kr: Jacob 60,00 kr, Anna 40,00 kr"""
account_editor_ratio_change_hint,The change applies to new expenses. Existing expenses keep their split.,Ändringen gäller nya utgifter. Befintliga utgifter behåller sin delning.,AC-6.3
account_editor_offline_banner,Needs connection. Accounts can only be changed online.,Kräver anslutning. Konton kan bara ändras när telefonen är online.,
account_editor_name_required,Enter a name,Skriv ett namn,
account_editor_name_taken,There is already an account called %1$s,Det finns redan ett konto som heter %1$s,name_taken
account_editor_ratio_sum,The two parts must add up to 100 %,De två delarna måste bli 100 %,bp_sum_not_10000
expense_editor_new,New expense,Ny utgift,
expense_editor_edit,Edit expense,Ändra utgift,
expense_editor_amount,Amount,Belopp,
expense_editor_amount_hint,"0,00","0,00","hint; en shows 0.00 (built from the formatter, not this string)"
expense_editor_description,Description,Beskrivning,
expense_editor_account,Account,Konto,
expense_editor_category,Category,Kategori,
expense_editor_date,Date,Datum,
expense_editor_paid_by,Paid by,Betalt av,
expense_editor_paid_by_toggle,Paid by %1$s. Double-tap to switch to %2$s.,Betalt av %1$s. Dubbeltryck för att byta till %2$s.,TalkBack
expense_editor_split,Split,Delning,
split_mode_default,Account default %1$s,Kontots standard %1$s,"%1$s ratio, e.g. 60/40"
split_mode_ratio,Custom ratio,Egen fördelning,
split_mode_exact,Exact amounts,Exakta belopp,
split_mode_full,All on one person,Allt på en person,
split_full_on,All on %1$s,Allt på %1$s,
split_exact_rest,%1$s gets the rest,%1$s får resten,read-only field of the other user
split_preview_effect,This expense: %1$s,Den här utgiften: %1$s,%1$s an effect line
expense_editor_notes,Notes,Anteckningar,
expense_editor_add_note,Add note,Lägg till anteckning,
expense_editor_delete,Delete expense,Ta bort utgift,
expense_editor_discard_title,Discard the expense?,Slänga utgiften?,
expense_editor_keypad_delete,Delete,Radera,TalkBack of the keypad backspace
expense_editor_keypad_decimal,Decimal separator,Decimaltecken,TalkBack
expense_editor_next,Next,Nästa,IME action on the amount
expense_editor_pick_account,Choose account,Välj konto,
expense_editor_pick_category,Choose category,Välj kategori,
error_amount_required,Enter an amount,Skriv ett belopp,required on amount
error_amount_out_of_range,The amount must be between %1$s and %2$s,Beloppet måste vara mellan %1$s och %2$s,amount_out_of_range
error_description_required,Enter a description,Skriv en beskrivning,
error_description_too_long,At most %d characters,Högst %d tecken,
error_date_out_of_range,The date is too far ahead or back,Datumet ligger för långt fram eller bak,§9.6
error_exact_share_too_large,The share is larger than the amount,Andelen är större än beloppet,"AC-8.7, exact_sum_mismatch"
error_ratio_invalid,The parts must add up to 100 %,Delarna måste bli 100 %,"bp_out_of_range, bp_sum_not_10000"
error_generic_invalid,This could not be saved. Check the fields.,Det gick inte att spara. Kontrollera fälten.,fallback for other domain tokens
expense_detail_title,Expense,Utgift,
expense_detail_account,Account,Konto,
expense_detail_date,Date,Datum,
expense_detail_paid_by,Paid by,Betalt av,
expense_detail_split,Split,Delning,
expense_detail_notes,Notes,Anteckning,
expense_detail_recurring,Recurring: %1$s,Återkommande: %1$s,AC-8.11
expense_detail_delete_title,Delete the expense?,Ta bort utgiften?,
expense_detail_delete_body,It disappears for both of you.,Den försvinner för er båda.,
expense_detail_deleted_snackbar,Expense deleted,Utgiften togs bort,
expense_detail_deleted_banner,%1$s deleted this expense · %2$s,%1$s tog bort den här utgiften · %2$s,"%1$s actor (Du/Anna), %2$s date+time"
expense_detail_missing,This expense is not on the phone yet,Utgiften finns inte på telefonen än,
sync_now,Sync now,Synka nu,
timeline_title,History,Historik,
timeline_created,%1$s created this,%1$s skapade den här,"%1$s actor: ""Anna"" or common_you"
timeline_deleted,%1$s deleted this,%1$s tog bort den här,
timeline_changed,%1$s changed %2$s from %3$s to %4$s,%1$s ändrade %2$s från %3$s till %4$s,%2$s a timeline_field_* (definite form)
timeline_changed_added,%1$s added %2$s: %3$s,%1$s lade till %2$s: %3$s,old value empty (e.g. notes)
timeline_changed_removed,%1$s removed %2$s,%1$s tog bort %2$s,new value empty
timeline_recurring,Created automatically (recurring),Skapad automatiskt (återkommande),source recurring
timeline_import,Imported from Splitwise,Importerad från Splitwise,source import
timeline_pending,Your change is waiting to sync,Din ändring väntar på synk,AC-12.5
timeline_time,· %1$s,· %1$s,"""· 16 okt. 19:40"""
timeline_field_account,the account,kontot,
timeline_field_category,the category,kategorin,
timeline_field_payer,the payer,betalaren,
timeline_field_amount,the amount,beloppet,
timeline_field_description,the description,beskrivningen,
timeline_field_notes,the notes,anteckningen,
timeline_field_date,the date,datumet,
timeline_field_split_mode,the split,delningen,split_mode and splits are shown as one change when both differ
timeline_field_splits,the shares,andelarna,
timeline_field_from,who paid,vem som betalade,settlement from_user_id
timeline_field_to,who received,vem som fick,settlement to_user_id
timeline_field_note,the note,anteckningen,settlement note
timeline_field_allocations,the allocation,fördelningen,
settle_title,Settle up,Gör upp,
settle_title_edit,Edit payment,Ändra betalning,
settle_you_pay,You pay %1$s,Du betalar %1$s,
settle_partner_pays,%1$s pays you,%1$s betalar dig,
settle_swap,Swap direction,Byt riktning,TalkBack and tooltip
settle_amount,Amount,Belopp,
settle_allocation,Allocation,Fördelning,
settle_row_debt_you_owe,You owe %1$s,Du är skyldig %1$s,payer is the viewer
settle_row_debt_partner_owes,%1$s owes you %2$s,%1$s är skyldig dig %2$s,
settle_row_debt_payer_owes,%1$s owes %2$s,%1$s är skyldig %2$s,payer is the partner
settle_row_settled,Settled up,Kvitt,
settle_row_remove,Remove %1$s from the allocation,Ta bort %1$s från fördelningen,TalkBack
settle_over_settle_warning,This settles more than is owed in %1$s,Det här reglerar mer än som är skyldigt i %1$s,AC-11.5
settle_remaining,Left to allocate %1$s,Kvar att fördela %1$s,
settle_over_allocated,%1$s too much,%1$s för mycket,
settle_add_account,Add account,Lägg till konto,
settle_propose_again,Suggest again,Föreslå igen,
settle_untouched_info,%1$s is not affected: there %2$s.,%1$s påverkas inte: där %2$s.,"%2$s e.g. ""är Anna skyldig dig 1 184,50 kr"" — build with settle_untouched_owes_you"
settle_untouched_owes_you,%1$s owes you %2$s,är %1$s skyldig dig %2$s,"fragment for settle_untouched_info (sv word order after ""där"")"
settle_date,Date,Datum,
settle_note,Note,Anteckning,
settle_confirm,Confirm,Bekräfta,
settle_confirm_dialog_title,Confirm the payment?,Bekräfta betalningen?,
settle_confirm_anyway,Confirm anyway,Bekräfta ändå,
settle_change,Change,Ändra,
error_allocation_sum,The accounts must add up to the amount,Kontona måste bli lika med beloppet,allocation_sum_mismatch
error_allocation_positive,"Each account needs at least 0,01","Varje konto behöver minst 0,01 kr",allocation_not_positive
error_no_allocations,Add at least one account,Lägg till minst ett konto,no_allocations
error_too_many_allocations,At most 50 accounts,Högst 50 konton,too_many_allocations
error_note_too_long,At most %d characters,Högst %d tecken,
settlement_detail_title,Payment,Betalning,
settlement_detail_delete_title,Delete the payment?,Ta bort betalningen?,
settlement_detail_delete_body,Balances are recalculated for both of you.,Saldona räknas om för er båda.,
settlement_detail_deleted_banner,%1$s deleted this payment · %2$s,%1$s tog bort den här betalningen · %2$s,
settlement_detail_deleted_snackbar,Payment deleted,Betalningen togs bort,
analytics_title,Analytics,Statistik,
analytics_period,Period,Period,
range_this_month,This month,Denna månad,
range_last_3_months,Last 3 months,Senaste 3 mån,
range_last_6_months,Last 6 months,Senaste 6 mån,
range_last_12_months,Last 12 months,Senaste 12 mån,
range_this_year,This year,I år,
range_all_time,All time,All tid,
range_custom,Custom…,Anpassad…,
analytics_range_dates,%1$s – %2$s,%1$s – %2$s,
granularity_month,Month,Månad,
granularity_week,Week,Vecka,
measure_household,Household,Hushållet,
measure_my_share,My share,Min del,
group_by_category,Category,Kategori,
group_by_account,Account,Konto,
analytics_total_household,Household spending,Hushållets utgifter,
analytics_total_my_share,Your share,Din del,
analytics_over_time,Spending over time,Utgifter över tid,
analytics_by_category,By category,Per kategori,
analytics_by_account,By account,Per konto,
analytics_week_label,W%1$d,v. %1$d,x-axis week label
analytics_axis_thousands,%1$sk,%1$s tkr,y-axis compact
analytics_empty,No expenses in this period,Inga utgifter i den här perioden,
analytics_change_period,Change period,Byt period,
analytics_summary_over_time,"Spending over time, %1$s: highest in %2$s with %3$s, lowest in %4$s with %5$s.","Utgifter över tid, %1$s: högst i %2$s med %3$s, lägst i %4$s med %5$s.",TalkBack summary of chart 1
analytics_summary_breakdown,%1$s: largest is %2$s with %3$s (%4$s).,%1$s: störst är %2$s med %3$s (%4$s).,TalkBack summary of charts 2 and 3
analytics_bar_spoken,%1$s: %2$s,%1$s: %2$s,TalkBack per bar
drilldown_empty,No expenses any more,Inga utgifter längre,
drilldown_title_category_period,"%1$s, %2$s","%1$s, %2$s","""Mat & hushåll, oktober 2026"""
settings_title,Settings,Inställningar,
settings_role_admin,You · Admin,Du · Administratör,
settings_role_member,You,Du,
settings_partner,%1$s · Partner,%1$s · Partner,
settings_profile,Profile,Profil,
settings_categories,Categories,Kategorier,
settings_recurring,Recurring expenses,Återkommande utgifter,
settings_devices,Devices,Enheter,
settings_invite_partner,Invite partner,Bjud in partner,
settings_pair_device,Pair a device,Para ihop en enhet,
settings_sync_status,Sync status,Synkstatus,
settings_about,About Kvitt,Om Kvitt,
settings_pending_badge,%d waiting,%d väntar,
settings_update_badge,Update available,Uppdatering finns,
categories_title,Categories,Kategorier,
categories_new,New category,Ny kategori,
categories_archived,Archived (%d),Arkiverade (%d),
categories_reorder_offline,Needs connection to change,Kräver anslutning för att ändra,
categories_move_up,Move up,Flytta upp,TalkBack custom action
categories_move_down,Move down,Flytta ned,TalkBack custom action
category_editor_new,New category,Ny kategori,
category_editor_edit,Edit category,Ändra kategori,
category_editor_name,Name,Namn,
category_editor_reset_name,Reset name,Återställ namn,AC-7.4
category_editor_icon,Icon,Ikon,
category_editor_color,Colour,Färg,
category_editor_archive,Archive,Arkivera,
category_editor_unarchive,Restore from archive,Återställ från arkiv,
category_editor_delete,Delete category,Ta bort kategori,
category_delete_blocked_default,"Default categories cannot be deleted, only archived","Standardkategorier kan inte tas bort, bara arkiveras",category_is_default
category_delete_blocked_in_use,The category is used by expenses or recurring expenses. Archive it instead.,Kategorin används av utgifter eller återkommande utgifter. Arkivera den i stället.,category_in_use
category_name_required,Enter a name,Skriv ett namn,
category_default_groceries,Groceries,Mat & hushåll,AC-7.2
category_default_restaurants,Restaurants,Restaurang & café,
category_default_home,Home,Hem,
category_default_utilities,Utilities,Räkningar,
category_default_transport,Transport,Transport,
category_default_travel,Travel,Resor,
category_default_entertainment,Entertainment,Nöje,
category_default_health,Health,Hälsa,
category_default_shopping,Shopping,Shopping,
category_default_other,Other,Övrigt,
color_green,Green,Grön,"TalkBack of colour swatches, §9.7 tokens"
color_orange,Orange,Orange,
color_brown,Brown,Brun,
color_yellow,Yellow,Gul,
color_blue,Blue,Blå,
color_teal,Teal,Blågrön,
color_purple,Purple,Lila,
color_red,Red,Röd,
color_pink,Pink,Rosa,
color_grey,Grey,Grå,
color_indigo,Indigo,Indigo,
color_lime,Lime,Lime,
icon_shopping_cart,Shopping cart,Kundvagn,"TalkBack of icon picker, §9.7 tokens"
icon_restaurant,Restaurant,Restaurang,
icon_local_cafe,Café,Kafé,
icon_home,Home,Hem,
icon_bolt,Electricity,El,
icon_water_drop,Water,Vatten,
icon_wifi,Internet,Internet,
icon_directions_car,Car,Bil,
icon_train,Train,Tåg,
icon_local_gas_station,Fuel,Bränsle,
icon_flight,Flight,Flyg,
icon_hotel,Hotel,Hotell,
icon_luggage,Luggage,Bagage,
icon_movie,Film,Film,
icon_music_note,Music,Musik,
icon_sports_esports,Games,Spel,
icon_fitness_center,Fitness,Träning,
icon_medical_services,Health care,Vård,
icon_medication,Medicine,Medicin,
icon_shopping_bag,Shopping bag,Shoppingkasse,
icon_checkroom,Clothes,Kläder,
icon_pets,Pets,Husdjur,
icon_child_care,Children,Barn,
icon_school,School,Skola,
icon_card_giftcard,Gift,Present,
icon_celebration,Party,Fest,
icon_build,Repairs,Reparationer,
icon_chair,Furniture,Möbler,
icon_subscriptions,Subscriptions,Prenumerationer,
icon_category,Other,Övrigt,
sync_status_title,Sync status,Synkstatus,
sync_status_all_synced,Everything is synced,Allt är synkat,
sync_status_pending_title,Pending changes (%d),Väntande ändringar (%d),
sync_status_tag_new,new,ny,
sync_status_tag_changed,changed,ändrad,
sync_status_tag_deleted,deleted,borttagen,
sync_status_rejected_title,Changes that were not synced,Ändringar som inte synkades,
sync_status_dismiss,Remove from list,Ta bort från listan,
sync_reject_stale,Your change was replaced by a newer change from %1$s,Din ändring ersattes av en nyare ändring från %1$s,§14.4.6
sync_reject_deleted,This was deleted before your change arrived,Den här togs bort innan din ändring kom fram,
sync_reject_validation,This change was not valid and was not saved,Ändringen var inte giltig och sparades inte,
sync_reject_reference,Its account or category no longer exists,Dess konto eller kategori finns inte längre,
sync_reject_clock_skew,Your phone's clock is wrong. Fix it and sync again,Telefonens klocka går fel. Rätta den och synka igen,
sync_status_clock_warning,Your phone's clock is wrong. Fix it and sync again.,Telefonens klocka går fel. Rätta den och synka igen.,§9.10
update_available,Version %1$s available,Version %1$s finns,
update_install,Update,Uppdatera,
update_later,Later,Senare,
update_downloading,Downloading… %1$d %%,Laddar ner… %1$d %%,
update_failed,"Download failed, try again",Nedladdningen misslyckades. Försök igen.,AC-16.4
update_permission_title,Allow installation,Tillåt installation,
update_permission_body,Android asks once whether Kvitt may install apps. Choose Allow from this source and go back.,Android frågar en gång om Kvitt får installera appar. Välj Tillåt från den här källan och gå tillbaka.,AC-16.3; Android setting name differs per locale
update_up_to_date,You have the latest version,Du har den senaste versionen,
update_check,Check for update,Sök efter uppdatering,
removed_title,This phone has been removed,Den här telefonen har tagits bort,
removed_body_member,Access was removed and all Kvitt data on the phone has been deleted. Ask %1$s for a new code to pair it again.,Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats. Be %1$s om en ny kod för att para ihop den igen.,
removed_body_admin,Access was removed and all Kvitt data on the phone has been deleted. Create a new code on the server to pair it again.,Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats. Skapa en ny kod på servern för att para ihop den igen.,
removed_pair_again,Pair again,Para ihop igen,
recurring_title,Recurring expenses,Återkommande utgifter,
recurring_new,New,Ny,
recurring_every_month_on,Every month on day %1$d,Varje månad den %1$d:e,"sv ordinal: 1:a, 2:a, otherwise :e — use recurring_every_month_on_1_2 for 1 and 2"
recurring_every_month_on_1_2,Every month on day %1$d,Varje månad den %1$d:a,"for days 1, 2, 21, 22, 31 (sv ordinal -a)"
recurring_next,Next: %1$s,Nästa: %1$s,
recurring_paused,Paused,Pausad,
recurring_empty_title,No recurring expenses,Inga återkommande utgifter,
recurring_empty_body,"Rent, electricity and streaming can be added automatically every month.","Hyra, el och streaming kan läggas in automatiskt varje månad.",
recurring_empty_action,New recurring expense,Ny återkommande utgift,
recurring_editor_new,New recurring expense,Ny återkommande utgift,
recurring_editor_edit,Edit recurring expense,Ändra återkommande utgift,
recurring_day_of_month,Day of month,Dag i månaden,
recurring_day_clamp_hint,In shorter months the last day is used,I kortare månader används sista dagen,days 29–31
recurring_first_month,First month,Första månad,
recurring_last_month,Last month (optional),Sista månad (valfritt),
recurring_paused_switch,Paused,Pausad,
recurring_resume_hint,Months that passed while paused are not created afterwards.,Månader som passerat under pausen skapas inte i efterhand.,§9.5
recurring_edit_hint,The change applies to upcoming expenses.,Ändringen gäller kommande utgifter.,AC-9.4
recurring_delete_title,Delete %1$s?,Ta bort %1$s?,
recurring_delete_body,Expenses already created stay.,Utgifter som redan skapats finns kvar.,
recurring_offline_banner,Needs connection. Recurring expenses can only be changed online.,Kräver anslutning. Återkommande utgifter kan bara ändras när telefonen är online.,
error_end_before_start,The last month cannot be before the first,Sista månaden kan inte vara före den första,
error_start_too_early,At most 12 months back,Högst 12 månader bakåt,
devices_title,Devices,Enheter,
devices_paired,Paired %1$s,Parad %1$s,
devices_last_seen,Last seen %1$s,Senast sedd %1$s,
devices_app_version,Kvitt %1$s,Kvitt %1$s,
devices_this_device,This device,Den här enheten,AC-3.1
devices_revoke,Remove,Ta bort,
devices_revoke_title,Remove %1$s?,Ta bort %1$s?,
devices_revoke_body,The phone is signed out and its Kvitt data is deleted the next time it connects.,Telefonen loggas ut och dess Kvitt-data raderas nästa gång den ansluter.,
devices_offline,Needs connection to show devices,Kräver anslutning för att visa enheter,
invites_title_partner,Invite partner,Bjud in partner,
invites_title_device,Pair a device,Para ihop en enhet,
invites_for_me,For me,För mig,
invites_for_partner,For %1$s,För %1$s,
invites_valid_until,Valid until %1$s,Gäller till %1$s,
invites_share,Share,Dela,
invites_copy_code,Copy code,Kopiera kod,
invites_code_copied,Code copied,Koden kopierades,
invites_share_message,Join our household in Kvitt: %1$s The code is %2$s.,Gå med i vårt hushåll i Kvitt: %1$s Koden är %2$s.,
invites_pending,Pending invites,Väntande inbjudningar,
invites_kind_partner,Partner,Partner,
invites_kind_device,Device for %1$s,Enhet för %1$s,
invites_code_hidden,The code is only shown when it is created,Koden visas bara när den skapas,
invites_cancel,Cancel invite,Avbryt inbjudan,
invites_cancel_title,Cancel the invite?,Avbryta inbjudan?,
error_household_full,Your partner has already joined,Din partner har redan gått med,household_full
about_title,About Kvitt,Om Kvitt,
about_version,Version %1$s (%2$d),Version %1$s (%2$d),
about_server,Server,Server,
about_server_version,Server version %1$s,Serverversion %1$s,
about_server_version_unknown,Unknown,Okänd,
about_licences,Open-source licences,Öppen källkod-licenser,
error_forbidden,You are not allowed to do this,Du får inte göra det här,forbidden
error_not_found,This no longer exists,Det här finns inte längre,not_found
error_household_incomplete,Your partner needs to join first,Din partner behöver gå med först,household_incomplete
error_account_not_empty,"The account still has expenses, payments or recurring expenses","Kontot har fortfarande utgifter, betalningar eller återkommande utgifter",account_not_empty
error_rate_limited,Too many attempts. Wait a while and try again.,För många försök. Vänta en stund och försök igen.,rate_limited
error_server,Something went wrong on the server. Try again later.,Något gick fel på servern. Försök igen senare.,"internal, unavailable, not_implemented"
error_app_too_old,Update Kvitt to continue,Uppdatera Kvitt för att fortsätta,protocol_unsupported
error_validation,The server did not accept the values,Servern godtog inte värdena,validation_failed without a mapped field
notif_channel_activity,Activity,Aktivitet,§14.6
notif_expense_created_title,%1$s added an expense,%1$s lade till en utgift,
notif_expense_created_body,%1$s · %2$s · %3$s,%1$s · %2$s · %3$s,description · amount · account
notif_expense_updated_title,%1$s changed an expense,%1$s ändrade en utgift,
notif_expense_updated_body,%1$s · %2$s,%1$s · %2$s,description · amount
notif_expense_deleted_title,%1$s deleted an expense,%1$s tog bort en utgift,
notif_expense_deleted_body,%1$s · %2$s,%1$s · %2$s,description · amount
notif_settlement_created_title,%1$s recorded a payment,%1$s registrerade en betalning,
notif_settlement_body,%1$s paid %2$s %3$s,%1$s betalade %2$s %3$s,"payer, recipient (dig/you when the viewer), amount"
notif_settlement_body_to_you,%1$s paid you %2$s,%1$s betalade dig %2$s,recipient is the viewer
notif_settlement_body_from_you,You paid %1$s %2$s,Du betalade %1$s %2$s,payer is the viewer
notif_settlement_updated_title,%1$s changed a payment,%1$s ändrade en betalning,
notif_settlement_deleted_title,%1$s deleted a payment,%1$s tog bort en betalning,
notif_settlement_deleted_body,%1$s,%1$s,amount
notif_recurring_created_title,Recurring expense added,Återkommande utgift tillagd,
notif_recurring_created_body,%1$s · %2$s · %3$s,%1$s · %2$s · %3$s,description · amount · account
notif_sync_batch_title:one,%1$s made %2$d change,%1$s gjorde %2$d ändring,plural one
notif_sync_batch_title:other,%1$s made %2$d changes,%1$s gjorde %2$d ändringar,plural other
notif_sync_batch_body,Open Kvitt to see them,Öppna Kvitt för att se dem,
notif_generic_title_actor,%1$s changed something in Kvitt,%1$s ändrade något i Kvitt,sync failed
notif_generic_title,Kvitt,Kvitt,"sync failed, actor unknown"
notif_generic_body,Open Kvitt to see it,Öppna Kvitt för att se det,
1 key en sv notes
2 common_save Save Spara
3 common_cancel Cancel Avbryt
4 common_delete Delete Ta bort
5 common_edit Edit Ändra
6 common_close Close Stäng TalkBack label of the close icon
7 common_back Back Tillbaka TalkBack label of the back icon
8 common_continue Continue Fortsätt
9 common_retry Try again Försök igen
10 common_more_options More options Fler alternativ TalkBack label of the overflow icon
11 common_you You Du the viewer as a name, sentence start
12 common_you_lower you du the viewer inside a sentence
13 common_today Today Idag
14 common_yesterday Yesterday Igår
15 common_needs_connection Needs connection Kräver anslutning §9.9
16 common_could_not_reach_server Could not reach the server Kunde inte nå servern §9.9
17 common_offline Offline Offline
18 common_pending_sync Waiting to sync Väntar på synk TalkBack label of the pending marker
19 common_archived Archived Arkiverat badge on an archived account
20 common_archived_category archived arkiverad suffix on an archived category chip
21 common_discard_title Discard changes? Slänga ändringarna?
22 common_discard Discard Släng
23 common_keep_editing Keep editing Fortsätt redigera
24 common_undo Undo Ångra
25 common_amount_spoken %1$s Swedish kronor %1$s kronor TalkBack form of an amount; %1$s = number without currency
26 common_kr_suffix SEK kr suffix/prefix shown in the amount field
27 common_ratio %1$s/%2$s %1$s/%2$s ratio label, percents without % sign, e.g. 60/40
28 common_percent %1$s %% %1$s %% percent with one decimal, e.g. 23,4 %
29 common_user_split %1$s %2$s %1$s %2$s name + amount in a split preview: "Du 206,00 kr"
30 common_separator_dot · · list separator
31 common_and %1$s and %2$s %1$s och %2$s
32 nav_home Home Översikt
33 nav_analytics Analytics Statistik
34 nav_settings Settings Inställningar
35 fab_add_expense Add expense Lägg till utgift FAB on S-4 and S-5
36 balance_partner_owes_you %1$s owes you %1$s är skyldig dig %1$s partner name; amount shown separately
37 balance_you_owe_partner You owe %1$s Du är skyldig %1$s
38 balance_partner_owes_you_amount %1$s owes you %2$s %1$s är skyldig dig %2$s one-line form and TalkBack sentence
39 balance_you_owe_partner_amount You owe %1$s %2$s Du är skyldig %1$s %2$s
40 balance_all_settled All settled up Ni är kvitt global, §9.3
41 balance_account_settled Settled up Kvitt account screens and rows, §9.3
42 effect_lent You lent %1$s Du lånade ut %1$s
43 effect_borrowed You borrowed %1$s Du lånade %1$s
44 effect_not_involved Not involved Inte inblandad
45 effect_paid_for_yourself Paid for yourself Betalade för dig själv
46 settlement_row_partner_paid_you %1$s paid you %2$s %1$s betalade dig %2$s
47 settlement_row_you_paid_partner You paid %1$s %2$s Du betalade %1$s %2$s
48 settlement_row_accounts:one %d account %d konto plural one; shown as "· 2 konton" only when > 1
49 settlement_row_accounts:other %d accounts %d konton plural other
50 sync_synced_relative Synced %1$s Synkad %1$s %1$s = relative time below
51 time_just_now just now nyss
52 time_minutes_ago %d min ago för %d min sedan
53 time_hours_ago %d h ago för %d h sedan
54 sync_syncing Syncing… Synkar…
55 sync_pending:one %d change waiting to sync %d ändring väntar på synk plural one
56 sync_pending:other %d changes waiting to sync %d ändringar väntar på synk plural other
57 sync_rejected:one %d change was not synced %d ändring synkades inte plural one
58 sync_rejected:other %d changes were not synced %d ändringar synkades inte plural other
59 sync_failed_line Sync failed · trying again soon Synk misslyckades · försöker igen snart
60 sync_clock_wrong_short Your phone's clock is wrong Telefonens klocka går fel
61 pairing_app_name Kvitt Kvitt not translated
62 pairing_tagline Shared expenses for two Delade utgifter för två
63 pairing_code_label Invite code Inbjudningskod
64 pairing_code_paste Paste Klistra in
65 pairing_name_label Your name Ditt namn
66 pairing_code_help You get the code from %1$s: from a link or from Settings › Invite partner. Koden får du av %1$s: från en länk eller från Inställningar › Bjud in partner. %1$s admin name when known from the link; else use pairing_code_help_generic
67 pairing_code_help_generic You get the code from a link, or it is printed when the server is set up. Koden får du från en länk, eller så skrivs den ut när servern sätts upp.
68 pairing_server_label Server address Serveradress
69 pairing_pair Pair Para ihop
70 pairing_pairing Pairing… Parar ihop…
71 pairing_again_title Pair this phone again Para ihop telefonen igen §14.4.8
72 pairing_again_body Your expenses are still on this phone. Dina utgifter finns kvar på telefonen.
73 pairing_error_invite_invalid This code does not work. It may have expired or already been used. Ask for a new one. Koden fungerar inte. Den kan ha gått ut eller redan använts. Be om en ny.
74 pairing_error_network Could not reach the server. Check the address and your connection. Kunde inte nå servern. Kontrollera adressen och anslutningen.
75 pairing_error_rate_limited Too many attempts. Wait a while and try again. För många försök. Vänta en stund och försök igen.
76 pairing_error_https The address must start with https:// Adressen måste börja med https:// AC-2.8
77 pairing_error_code_length The code has 12 characters Koden har 12 tecken
78 pairing_error_name_required Enter your name Skriv ditt namn
79 pairing_notification_rationale Kvitt tells you when %1$s adds or changes something. Kvitt säger till när %1$s lägger till eller ändrar något. shown before the system permission prompt (AC-2.9)
80 profile_step_title Your profile Din profil
81 profile_step_joining You are joining %1$s's household Du går med i %1$ss hushåll sv genitive: name + s (no apostrophe); names ending in s get no extra s (handled in code: Jonas hushåll)
82 profile_add_picture Add picture Lägg till bild
83 profile_change_picture Change picture Byt bild
84 profile_remove_picture Remove picture Ta bort bild
85 profile_name_label Display name Visningsnamn
86 profile_name_required Enter a name Skriv ett namn
87 profile_picture_failed The picture could not be used Bilden kunde inte användas
88 profile_picture_too_large The picture is too large (max 5 MB) Bilden är för stor (högst 5 MB) payload_too_large
89 profile_picture_unsupported Use a JPEG, PNG or WebP picture Använd en bild i JPEG, PNG eller WebP unsupported_media_type
90 profile_picture_invalid The picture could not be read Bilden kunde inte läsas image_invalid
91 profile_offline_banner Needs connection. Your profile can only be changed online. Kräver anslutning. Profilen kan bara ändras när telefonen är online.
92 waiting_title Waiting for your partner Väntar på din partner
93 waiting_body Kvitt needs both of you before you can start. Invite your partner with a link. Kvitt behöver er båda innan ni kan börja. Bjud in din partner med en länk.
94 waiting_invite_expires Invite valid until %1$s Inbjudan gäller till %1$s %1$s weekday + date + time, e.g. mån 19 okt. 21:40
95 waiting_invite_partner Invite partner Bjud in partner
96 home_accounts Accounts Konton
97 home_new_account New account Nytt konto
98 home_archived_section Archived (%d) Arkiverade (%d)
99 home_settle_up Settle up Gör upp
100 home_account_expenses_month:one %1$d expense in %2$s %1$d utgift i %2$s plural one; %2$s month name (lowercase in sv)
101 home_account_expenses_month:other %1$d expenses in %2$s %1$d utgifter i %2$s plural other
102 home_account_no_expenses_month No expenses in %1$s Inga utgifter i %1$s
103 home_loading Getting your expenses… Hämtar era utgifter… first sync only
104 home_empty_welcome Welcome, %1$s and %2$s Välkommen, %1$s och %2$s
105 home_empty_title Create your first account Skapa ert första konto
106 home_empty_body An account collects expenses that belong together, for example Everyday or Japan 2027. Ett konto samlar utgifter som hör ihop, till exempel Vardag eller Japan 2027.
107 home_expense_saved Expense saved Utgiften sparades
108 home_expense_saved_offline Saved. Syncs when you're online. Sparad. Synkas när du är online.
109 home_settlement_saved Payment saved Betalningen sparades
110 account_default_split %1$s · %2$s %1$s · %2$s "50 / 50 · Jacob och Anna"; %1$s ratio, %2$s names with common_and
111 account_settle_this Settle this account Gör upp det här kontot
112 account_month_total %1$s · %2$s %1$s · %2$s month header: "Oktober · 7 457,90 kr"
113 account_edit Edit account Ändra konto
114 account_archive Archive Arkivera
115 account_unarchive Restore from archive Återställ från arkiv
116 account_delete Delete account Ta bort konto
117 account_delete_blocked The account cannot be deleted while it has expenses, payments or recurring expenses Kontot kan inte tas bort så länge det har utgifter, betalningar eller återkommande utgifter AC-6.5; also error account_not_empty
118 account_delete_confirm_title Delete the account %1$s? Ta bort kontot %1$s?
119 account_delete_confirm_body This cannot be undone. Det går inte att ångra.
120 account_archived_snackbar %1$s was archived %1$s arkiverades
121 account_unarchived_snackbar %1$s was restored %1$s återställdes
122 account_empty_title No expenses yet Inga utgifter än
123 account_empty_body Add the first expense in %1$s. Lägg till den första utgiften i %1$s.
124 account_editor_new New account Nytt konto
125 account_editor_edit Edit account Ändra konto
126 account_editor_name Name Namn
127 account_editor_default_split Default split Standarddelning
128 account_editor_percent_of %1$s %% %1$s %% label of a percent field: "Jacob %"
129 account_editor_preview Of %1$s: %2$s Av %1$s: %2$s "Av 100,00 kr: Jacob 60,00 kr, Anna 40,00 kr"
130 account_editor_ratio_change_hint The change applies to new expenses. Existing expenses keep their split. Ändringen gäller nya utgifter. Befintliga utgifter behåller sin delning. AC-6.3
131 account_editor_offline_banner Needs connection. Accounts can only be changed online. Kräver anslutning. Konton kan bara ändras när telefonen är online.
132 account_editor_name_required Enter a name Skriv ett namn
133 account_editor_name_taken There is already an account called %1$s Det finns redan ett konto som heter %1$s name_taken
134 account_editor_ratio_sum The two parts must add up to 100 % De två delarna måste bli 100 % bp_sum_not_10000
135 expense_editor_new New expense Ny utgift
136 expense_editor_edit Edit expense Ändra utgift
137 expense_editor_amount Amount Belopp
138 expense_editor_amount_hint 0,00 0,00 hint; en shows 0.00 (built from the formatter, not this string)
139 expense_editor_description Description Beskrivning
140 expense_editor_account Account Konto
141 expense_editor_category Category Kategori
142 expense_editor_date Date Datum
143 expense_editor_paid_by Paid by Betalt av
144 expense_editor_paid_by_toggle Paid by %1$s. Double-tap to switch to %2$s. Betalt av %1$s. Dubbeltryck för att byta till %2$s. TalkBack
145 expense_editor_split Split Delning
146 split_mode_default Account default %1$s Kontots standard %1$s %1$s ratio, e.g. 60/40
147 split_mode_ratio Custom ratio Egen fördelning
148 split_mode_exact Exact amounts Exakta belopp
149 split_mode_full All on one person Allt på en person
150 split_full_on All on %1$s Allt på %1$s
151 split_exact_rest %1$s gets the rest %1$s får resten read-only field of the other user
152 split_preview_effect This expense: %1$s Den här utgiften: %1$s %1$s an effect line
153 expense_editor_notes Notes Anteckningar
154 expense_editor_add_note Add note Lägg till anteckning
155 expense_editor_delete Delete expense Ta bort utgift
156 expense_editor_discard_title Discard the expense? Slänga utgiften?
157 expense_editor_keypad_delete Delete Radera TalkBack of the keypad backspace
158 expense_editor_keypad_decimal Decimal separator Decimaltecken TalkBack
159 expense_editor_next Next Nästa IME action on the amount
160 expense_editor_pick_account Choose account Välj konto
161 expense_editor_pick_category Choose category Välj kategori
162 error_amount_required Enter an amount Skriv ett belopp required on amount
163 error_amount_out_of_range The amount must be between %1$s and %2$s Beloppet måste vara mellan %1$s och %2$s amount_out_of_range
164 error_description_required Enter a description Skriv en beskrivning
165 error_description_too_long At most %d characters Högst %d tecken
166 error_date_out_of_range The date is too far ahead or back Datumet ligger för långt fram eller bak §9.6
167 error_exact_share_too_large The share is larger than the amount Andelen är större än beloppet AC-8.7, exact_sum_mismatch
168 error_ratio_invalid The parts must add up to 100 % Delarna måste bli 100 % bp_out_of_range, bp_sum_not_10000
169 error_generic_invalid This could not be saved. Check the fields. Det gick inte att spara. Kontrollera fälten. fallback for other domain tokens
170 expense_detail_title Expense Utgift
171 expense_detail_account Account Konto
172 expense_detail_date Date Datum
173 expense_detail_paid_by Paid by Betalt av
174 expense_detail_split Split Delning
175 expense_detail_notes Notes Anteckning
176 expense_detail_recurring Recurring: %1$s Återkommande: %1$s AC-8.11
177 expense_detail_delete_title Delete the expense? Ta bort utgiften?
178 expense_detail_delete_body It disappears for both of you. Den försvinner för er båda.
179 expense_detail_deleted_snackbar Expense deleted Utgiften togs bort
180 expense_detail_deleted_banner %1$s deleted this expense · %2$s %1$s tog bort den här utgiften · %2$s %1$s actor (Du/Anna), %2$s date+time
181 expense_detail_missing This expense is not on the phone yet Utgiften finns inte på telefonen än
182 sync_now Sync now Synka nu
183 timeline_title History Historik
184 timeline_created %1$s created this %1$s skapade den här %1$s actor: "Anna" or common_you
185 timeline_deleted %1$s deleted this %1$s tog bort den här
186 timeline_changed %1$s changed %2$s from %3$s to %4$s %1$s ändrade %2$s från %3$s till %4$s %2$s a timeline_field_* (definite form)
187 timeline_changed_added %1$s added %2$s: %3$s %1$s lade till %2$s: %3$s old value empty (e.g. notes)
188 timeline_changed_removed %1$s removed %2$s %1$s tog bort %2$s new value empty
189 timeline_recurring Created automatically (recurring) Skapad automatiskt (återkommande) source recurring
190 timeline_import Imported from Splitwise Importerad från Splitwise source import
191 timeline_pending Your change is waiting to sync Din ändring väntar på synk AC-12.5
192 timeline_time · %1$s · %1$s "· 16 okt. 19:40"
193 timeline_field_account the account kontot
194 timeline_field_category the category kategorin
195 timeline_field_payer the payer betalaren
196 timeline_field_amount the amount beloppet
197 timeline_field_description the description beskrivningen
198 timeline_field_notes the notes anteckningen
199 timeline_field_date the date datumet
200 timeline_field_split_mode the split delningen split_mode and splits are shown as one change when both differ
201 timeline_field_splits the shares andelarna
202 timeline_field_from who paid vem som betalade settlement from_user_id
203 timeline_field_to who received vem som fick settlement to_user_id
204 timeline_field_note the note anteckningen settlement note
205 timeline_field_allocations the allocation fördelningen
206 settle_title Settle up Gör upp
207 settle_title_edit Edit payment Ändra betalning
208 settle_you_pay You pay %1$s Du betalar %1$s
209 settle_partner_pays %1$s pays you %1$s betalar dig
210 settle_swap Swap direction Byt riktning TalkBack and tooltip
211 settle_amount Amount Belopp
212 settle_allocation Allocation Fördelning
213 settle_row_debt_you_owe You owe %1$s Du är skyldig %1$s payer is the viewer
214 settle_row_debt_partner_owes %1$s owes you %2$s %1$s är skyldig dig %2$s
215 settle_row_debt_payer_owes %1$s owes %2$s %1$s är skyldig %2$s payer is the partner
216 settle_row_settled Settled up Kvitt
217 settle_row_remove Remove %1$s from the allocation Ta bort %1$s från fördelningen TalkBack
218 settle_over_settle_warning This settles more than is owed in %1$s Det här reglerar mer än som är skyldigt i %1$s AC-11.5
219 settle_remaining Left to allocate %1$s Kvar att fördela %1$s
220 settle_over_allocated %1$s too much %1$s för mycket
221 settle_add_account Add account Lägg till konto
222 settle_propose_again Suggest again Föreslå igen
223 settle_untouched_info %1$s is not affected: there %2$s. %1$s påverkas inte: där %2$s. %2$s e.g. "är Anna skyldig dig 1 184,50 kr" — build with settle_untouched_owes_you
224 settle_untouched_owes_you %1$s owes you %2$s är %1$s skyldig dig %2$s fragment for settle_untouched_info (sv word order after "där")
225 settle_date Date Datum
226 settle_note Note Anteckning
227 settle_confirm Confirm Bekräfta
228 settle_confirm_dialog_title Confirm the payment? Bekräfta betalningen?
229 settle_confirm_anyway Confirm anyway Bekräfta ändå
230 settle_change Change Ändra
231 error_allocation_sum The accounts must add up to the amount Kontona måste bli lika med beloppet allocation_sum_mismatch
232 error_allocation_positive Each account needs at least 0,01 Varje konto behöver minst 0,01 kr allocation_not_positive
233 error_no_allocations Add at least one account Lägg till minst ett konto no_allocations
234 error_too_many_allocations At most 50 accounts Högst 50 konton too_many_allocations
235 error_note_too_long At most %d characters Högst %d tecken
236 settlement_detail_title Payment Betalning
237 settlement_detail_delete_title Delete the payment? Ta bort betalningen?
238 settlement_detail_delete_body Balances are recalculated for both of you. Saldona räknas om för er båda.
239 settlement_detail_deleted_banner %1$s deleted this payment · %2$s %1$s tog bort den här betalningen · %2$s
240 settlement_detail_deleted_snackbar Payment deleted Betalningen togs bort
241 analytics_title Analytics Statistik
242 analytics_period Period Period
243 range_this_month This month Denna månad
244 range_last_3_months Last 3 months Senaste 3 mån
245 range_last_6_months Last 6 months Senaste 6 mån
246 range_last_12_months Last 12 months Senaste 12 mån
247 range_this_year This year I år
248 range_all_time All time All tid
249 range_custom Custom… Anpassad…
250 analytics_range_dates %1$s – %2$s %1$s – %2$s
251 granularity_month Month Månad
252 granularity_week Week Vecka
253 measure_household Household Hushållet
254 measure_my_share My share Min del
255 group_by_category Category Kategori
256 group_by_account Account Konto
257 analytics_total_household Household spending Hushållets utgifter
258 analytics_total_my_share Your share Din del
259 analytics_over_time Spending over time Utgifter över tid
260 analytics_by_category By category Per kategori
261 analytics_by_account By account Per konto
262 analytics_week_label W%1$d v. %1$d x-axis week label
263 analytics_axis_thousands %1$sk %1$s tkr y-axis compact
264 analytics_empty No expenses in this period Inga utgifter i den här perioden
265 analytics_change_period Change period Byt period
266 analytics_summary_over_time Spending over time, %1$s: highest in %2$s with %3$s, lowest in %4$s with %5$s. Utgifter över tid, %1$s: högst i %2$s med %3$s, lägst i %4$s med %5$s. TalkBack summary of chart 1
267 analytics_summary_breakdown %1$s: largest is %2$s with %3$s (%4$s). %1$s: störst är %2$s med %3$s (%4$s). TalkBack summary of charts 2 and 3
268 analytics_bar_spoken %1$s: %2$s %1$s: %2$s TalkBack per bar
269 drilldown_empty No expenses any more Inga utgifter längre
270 drilldown_title_category_period %1$s, %2$s %1$s, %2$s "Mat & hushåll, oktober 2026"
271 settings_title Settings Inställningar
272 settings_role_admin You · Admin Du · Administratör
273 settings_role_member You Du
274 settings_partner %1$s · Partner %1$s · Partner
275 settings_profile Profile Profil
276 settings_categories Categories Kategorier
277 settings_recurring Recurring expenses Återkommande utgifter
278 settings_devices Devices Enheter
279 settings_invite_partner Invite partner Bjud in partner
280 settings_pair_device Pair a device Para ihop en enhet
281 settings_sync_status Sync status Synkstatus
282 settings_about About Kvitt Om Kvitt
283 settings_pending_badge %d waiting %d väntar
284 settings_update_badge Update available Uppdatering finns
285 categories_title Categories Kategorier
286 categories_new New category Ny kategori
287 categories_archived Archived (%d) Arkiverade (%d)
288 categories_reorder_offline Needs connection to change Kräver anslutning för att ändra
289 categories_move_up Move up Flytta upp TalkBack custom action
290 categories_move_down Move down Flytta ned TalkBack custom action
291 category_editor_new New category Ny kategori
292 category_editor_edit Edit category Ändra kategori
293 category_editor_name Name Namn
294 category_editor_reset_name Reset name Återställ namn AC-7.4
295 category_editor_icon Icon Ikon
296 category_editor_color Colour Färg
297 category_editor_archive Archive Arkivera
298 category_editor_unarchive Restore from archive Återställ från arkiv
299 category_editor_delete Delete category Ta bort kategori
300 category_delete_blocked_default Default categories cannot be deleted, only archived Standardkategorier kan inte tas bort, bara arkiveras category_is_default
301 category_delete_blocked_in_use The category is used by expenses or recurring expenses. Archive it instead. Kategorin används av utgifter eller återkommande utgifter. Arkivera den i stället. category_in_use
302 category_name_required Enter a name Skriv ett namn
303 category_default_groceries Groceries Mat & hushåll AC-7.2
304 category_default_restaurants Restaurants Restaurang & café
305 category_default_home Home Hem
306 category_default_utilities Utilities Räkningar
307 category_default_transport Transport Transport
308 category_default_travel Travel Resor
309 category_default_entertainment Entertainment Nöje
310 category_default_health Health Hälsa
311 category_default_shopping Shopping Shopping
312 category_default_other Other Övrigt
313 color_green Green Grön TalkBack of colour swatches, §9.7 tokens
314 color_orange Orange Orange
315 color_brown Brown Brun
316 color_yellow Yellow Gul
317 color_blue Blue Blå
318 color_teal Teal Blågrön
319 color_purple Purple Lila
320 color_red Red Röd
321 color_pink Pink Rosa
322 color_grey Grey Grå
323 color_indigo Indigo Indigo
324 color_lime Lime Lime
325 icon_shopping_cart Shopping cart Kundvagn TalkBack of icon picker, §9.7 tokens
326 icon_restaurant Restaurant Restaurang
327 icon_local_cafe Café Kafé
328 icon_home Home Hem
329 icon_bolt Electricity El
330 icon_water_drop Water Vatten
331 icon_wifi Internet Internet
332 icon_directions_car Car Bil
333 icon_train Train Tåg
334 icon_local_gas_station Fuel Bränsle
335 icon_flight Flight Flyg
336 icon_hotel Hotel Hotell
337 icon_luggage Luggage Bagage
338 icon_movie Film Film
339 icon_music_note Music Musik
340 icon_sports_esports Games Spel
341 icon_fitness_center Fitness Träning
342 icon_medical_services Health care Vård
343 icon_medication Medicine Medicin
344 icon_shopping_bag Shopping bag Shoppingkasse
345 icon_checkroom Clothes Kläder
346 icon_pets Pets Husdjur
347 icon_child_care Children Barn
348 icon_school School Skola
349 icon_card_giftcard Gift Present
350 icon_celebration Party Fest
351 icon_build Repairs Reparationer
352 icon_chair Furniture Möbler
353 icon_subscriptions Subscriptions Prenumerationer
354 icon_category Other Övrigt
355 sync_status_title Sync status Synkstatus
356 sync_status_all_synced Everything is synced Allt är synkat
357 sync_status_pending_title Pending changes (%d) Väntande ändringar (%d)
358 sync_status_tag_new new ny
359 sync_status_tag_changed changed ändrad
360 sync_status_tag_deleted deleted borttagen
361 sync_status_rejected_title Changes that were not synced Ändringar som inte synkades
362 sync_status_dismiss Remove from list Ta bort från listan
363 sync_reject_stale Your change was replaced by a newer change from %1$s Din ändring ersattes av en nyare ändring från %1$s §14.4.6
364 sync_reject_deleted This was deleted before your change arrived Den här togs bort innan din ändring kom fram
365 sync_reject_validation This change was not valid and was not saved Ändringen var inte giltig och sparades inte
366 sync_reject_reference Its account or category no longer exists Dess konto eller kategori finns inte längre
367 sync_reject_clock_skew Your phone's clock is wrong. Fix it and sync again Telefonens klocka går fel. Rätta den och synka igen
368 sync_status_clock_warning Your phone's clock is wrong. Fix it and sync again. Telefonens klocka går fel. Rätta den och synka igen. §9.10
369 update_available Version %1$s available Version %1$s finns
370 update_install Update Uppdatera
371 update_later Later Senare
372 update_downloading Downloading… %1$d %% Laddar ner… %1$d %%
373 update_failed Download failed, try again Nedladdningen misslyckades. Försök igen. AC-16.4
374 update_permission_title Allow installation Tillåt installation
375 update_permission_body Android asks once whether Kvitt may install apps. Choose Allow from this source and go back. Android frågar en gång om Kvitt får installera appar. Välj Tillåt från den här källan och gå tillbaka. AC-16.3; Android setting name differs per locale
376 update_up_to_date You have the latest version Du har den senaste versionen
377 update_check Check for update Sök efter uppdatering
378 removed_title This phone has been removed Den här telefonen har tagits bort
379 removed_body_member Access was removed and all Kvitt data on the phone has been deleted. Ask %1$s for a new code to pair it again. Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats. Be %1$s om en ny kod för att para ihop den igen.
380 removed_body_admin Access was removed and all Kvitt data on the phone has been deleted. Create a new code on the server to pair it again. Åtkomsten togs bort och allt Kvitt-data på telefonen har raderats. Skapa en ny kod på servern för att para ihop den igen.
381 removed_pair_again Pair again Para ihop igen
382 recurring_title Recurring expenses Återkommande utgifter
383 recurring_new New Ny
384 recurring_every_month_on Every month on day %1$d Varje månad den %1$d:e sv ordinal: 1:a, 2:a, otherwise :e — use recurring_every_month_on_1_2 for 1 and 2
385 recurring_every_month_on_1_2 Every month on day %1$d Varje månad den %1$d:a for days 1, 2, 21, 22, 31 (sv ordinal -a)
386 recurring_next Next: %1$s Nästa: %1$s
387 recurring_paused Paused Pausad
388 recurring_empty_title No recurring expenses Inga återkommande utgifter
389 recurring_empty_body Rent, electricity and streaming can be added automatically every month. Hyra, el och streaming kan läggas in automatiskt varje månad.
390 recurring_empty_action New recurring expense Ny återkommande utgift
391 recurring_editor_new New recurring expense Ny återkommande utgift
392 recurring_editor_edit Edit recurring expense Ändra återkommande utgift
393 recurring_day_of_month Day of month Dag i månaden
394 recurring_day_clamp_hint In shorter months the last day is used I kortare månader används sista dagen days 29–31
395 recurring_first_month First month Första månad
396 recurring_last_month Last month (optional) Sista månad (valfritt)
397 recurring_paused_switch Paused Pausad
398 recurring_resume_hint Months that passed while paused are not created afterwards. Månader som passerat under pausen skapas inte i efterhand. §9.5
399 recurring_edit_hint The change applies to upcoming expenses. Ändringen gäller kommande utgifter. AC-9.4
400 recurring_delete_title Delete %1$s? Ta bort %1$s?
401 recurring_delete_body Expenses already created stay. Utgifter som redan skapats finns kvar.
402 recurring_offline_banner Needs connection. Recurring expenses can only be changed online. Kräver anslutning. Återkommande utgifter kan bara ändras när telefonen är online.
403 error_end_before_start The last month cannot be before the first Sista månaden kan inte vara före den första
404 error_start_too_early At most 12 months back Högst 12 månader bakåt
405 devices_title Devices Enheter
406 devices_paired Paired %1$s Parad %1$s
407 devices_last_seen Last seen %1$s Senast sedd %1$s
408 devices_app_version Kvitt %1$s Kvitt %1$s
409 devices_this_device This device Den här enheten AC-3.1
410 devices_revoke Remove Ta bort
411 devices_revoke_title Remove %1$s? Ta bort %1$s?
412 devices_revoke_body The phone is signed out and its Kvitt data is deleted the next time it connects. Telefonen loggas ut och dess Kvitt-data raderas nästa gång den ansluter.
413 devices_offline Needs connection to show devices Kräver anslutning för att visa enheter
414 invites_title_partner Invite partner Bjud in partner
415 invites_title_device Pair a device Para ihop en enhet
416 invites_for_me For me För mig
417 invites_for_partner For %1$s För %1$s
418 invites_valid_until Valid until %1$s Gäller till %1$s
419 invites_share Share Dela
420 invites_copy_code Copy code Kopiera kod
421 invites_code_copied Code copied Koden kopierades
422 invites_share_message Join our household in Kvitt: %1$s The code is %2$s. Gå med i vårt hushåll i Kvitt: %1$s Koden är %2$s.
423 invites_pending Pending invites Väntande inbjudningar
424 invites_kind_partner Partner Partner
425 invites_kind_device Device for %1$s Enhet för %1$s
426 invites_code_hidden The code is only shown when it is created Koden visas bara när den skapas
427 invites_cancel Cancel invite Avbryt inbjudan
428 invites_cancel_title Cancel the invite? Avbryta inbjudan?
429 error_household_full Your partner has already joined Din partner har redan gått med household_full
430 about_title About Kvitt Om Kvitt
431 about_version Version %1$s (%2$d) Version %1$s (%2$d)
432 about_server Server Server
433 about_server_version Server version %1$s Serverversion %1$s
434 about_server_version_unknown Unknown Okänd
435 about_licences Open-source licences Öppen källkod-licenser
436 error_forbidden You are not allowed to do this Du får inte göra det här forbidden
437 error_not_found This no longer exists Det här finns inte längre not_found
438 error_household_incomplete Your partner needs to join first Din partner behöver gå med först household_incomplete
439 error_account_not_empty The account still has expenses, payments or recurring expenses Kontot har fortfarande utgifter, betalningar eller återkommande utgifter account_not_empty
440 error_rate_limited Too many attempts. Wait a while and try again. För många försök. Vänta en stund och försök igen. rate_limited
441 error_server Something went wrong on the server. Try again later. Något gick fel på servern. Försök igen senare. internal, unavailable, not_implemented
442 error_app_too_old Update Kvitt to continue Uppdatera Kvitt för att fortsätta protocol_unsupported
443 error_validation The server did not accept the values Servern godtog inte värdena validation_failed without a mapped field
444 notif_channel_activity Activity Aktivitet §14.6
445 notif_expense_created_title %1$s added an expense %1$s lade till en utgift
446 notif_expense_created_body %1$s · %2$s · %3$s %1$s · %2$s · %3$s description · amount · account
447 notif_expense_updated_title %1$s changed an expense %1$s ändrade en utgift
448 notif_expense_updated_body %1$s · %2$s %1$s · %2$s description · amount
449 notif_expense_deleted_title %1$s deleted an expense %1$s tog bort en utgift
450 notif_expense_deleted_body %1$s · %2$s %1$s · %2$s description · amount
451 notif_settlement_created_title %1$s recorded a payment %1$s registrerade en betalning
452 notif_settlement_body %1$s paid %2$s %3$s %1$s betalade %2$s %3$s payer, recipient (dig/you when the viewer), amount
453 notif_settlement_body_to_you %1$s paid you %2$s %1$s betalade dig %2$s recipient is the viewer
454 notif_settlement_body_from_you You paid %1$s %2$s Du betalade %1$s %2$s payer is the viewer
455 notif_settlement_updated_title %1$s changed a payment %1$s ändrade en betalning
456 notif_settlement_deleted_title %1$s deleted a payment %1$s tog bort en betalning
457 notif_settlement_deleted_body %1$s %1$s amount
458 notif_recurring_created_title Recurring expense added Återkommande utgift tillagd
459 notif_recurring_created_body %1$s · %2$s · %3$s %1$s · %2$s · %3$s description · amount · account
460 notif_sync_batch_title:one %1$s made %2$d change %1$s gjorde %2$d ändring plural one
461 notif_sync_batch_title:other %1$s made %2$d changes %1$s gjorde %2$d ändringar plural other
462 notif_sync_batch_body Open Kvitt to see them Öppna Kvitt för att se dem
463 notif_generic_title_actor %1$s changed something in Kvitt %1$s ändrade något i Kvitt sync failed
464 notif_generic_title Kvitt Kvitt sync failed, actor unknown
465 notif_generic_body Open Kvitt to see it Öppna Kvitt för att se det
+223
View File
@@ -0,0 +1,223 @@
# Kvitt: design tokens
Status: U1 proposal (2026-10-17). One token **set of names** (binding once the owner picks a direction) with **values for all three directions** (`directions/1-skargard`, `2-kvitto`, `3-kalas`), so `:core:design` can be built from whichever is chosen. After the pick, U2 copies the chosen column into `design-spec.md` and deletes the other two here (one source of truth). Each direction's review page recomputes every contrast ratio below in JavaScript and marks failures; the values here were checked there.
Contrast targets (SPEC §13.1): text ≥ 4.5:1 against the surface it sits on; icons and graphics ≥ 3:1; nothing conveyed by colour alone.
## 1. How tokens map to Compose
| Token group | Compose home in `:core:design` | Notes |
|---|---|---|
| Material 3 colour roles (§2) | `lightColorScheme(...)` / `darkColorScheme(...)` | names map 1:1 (`on-primary-container` → `onPrimaryContainer`) |
| Kvitt semantic roles (§3) | `KvittColors` data class in a `CompositionLocal`, light and dark instance | `owed*`, `owing*`, `settled*`, `warning*`, `pending`, `avatar*` |
| Category colours (§4) | `KvittColors.category(ColorToken)` and `.categoryContainer(ColorToken)` | `ColorToken` enum from `:core:model` (§14.2.4) |
| Chart colours (§5) | `KvittColors.chartCategorical[0..7]`, `chartSequential[0..4]`, `chartGrid`, `chartAxisLabel` | |
| Type (§6) | `Typography(...)` + `KvittTypography(amountHero, amountLarge, amountMedium, amountSmall)` | amounts always `fontFeatureSettings = "tnum"` |
| Spacing (§7) | `object KvittSpacing { val s1 = 4.dp … }` | |
| Shapes (§8) | `Shapes(extraSmall…extraLarge)` + `KvittShapes` for the Expressive sizes and custom shapes | |
| Elevation, motion (§9) | `MotionScheme` (Expressive) or durations/easings constants | |
Dynamic colour (Material You): offered only by Skärgård, as an off-by-default option that replaces §2 only; §3–§5 keep their values in every case.
## 2. Material 3 colour roles
`L` = light, `D` = dark.
| Token | Skärgård L | Skärgård D | Kvitto L | Kvitto D | Kalas L | Kalas D |
|---|---|---|---|---|---|---|
| primary | #1A6476 | #8ED0E2 | #23408E | #B3C3FF | #B23A1C | #FFB59F |
| on-primary | #FFFFFF | #00363F | #FFFFFF | #112A6A | #FFFFFF | #5E1604 |
| primary-container | #C2E8F3 | #0E4D5C | #DCE3F6 | #2B4590 | #FFDBD0 | #8A2A10 |
| on-primary-container | #0B4C5C | #C2E8F3 | #14295E | #DDE3FF | #3D0B00 | #FFDBD0 |
| secondary | #4E5F69 | #B6C9D4 | #4E5466 | #C2C6D8 | #4B6A2A | #B2D38A |
| on-secondary | #FFFFFF | #20333B | #FFFFFF | #2B3040 | #FFFFFF | #1F3701 |
| secondary-container | #D3E3EE | #374952 | #DFE2EB | #3B4050 | #D3EFAE | #344F15 |
| on-secondary-container | #374851 | #D3E5F0 | #1B2030 | #DEE1F1 | #112000 | #D3EFAE |
| tertiary | #7B5C24 | #E8C48A | #73552A | #DCC39A | #7A5900 | #F2C14E |
| on-tertiary | #FFFFFF | #422C00 | #FFFFFF | #3E2D10 | #FFFFFF | #402D00 |
| tertiary-container | #F7DFB4 | #5D4410 | #F1E2C8 | #574425 | #FFDF8E | #5A3F00 |
| on-tertiary-container | #5A410E | #FFDEA8 | #2E1F06 | #F7E1BD | #261A00 | #FFDF8E |
| error | #B3261E | #F2B8B5 | #B0261B | #FFB4A8 | #BA1A1A | #FFB4AB |
| on-error | #FFFFFF | #601410 | #FFFFFF | #690004 | #FFFFFF | #690005 |
| error-container | #F9DEDC | #8C1D18 | #F9DCD6 | #8E1E14 | #FFDAD6 | #93000A |
| on-error-container | #8C1D18 | #F9DEDC | #5A0E08 | #FFDAD4 | #410002 | #FFDAD6 |
| surface | #F6FAFC | #0F1417 | #F7F4EC | #1C1A17 | #FFF8F1 | #1D1415 |
| on-surface | #171C1F | #DEE3E6 | #1D1B17 | #ECE6DA | #231918 | #F1DFDB |
| surface-variant | #DBE4E8 | #40484C | #E7E2D6 | #4A463E | #F5DED6 | #53433F |
| on-surface-variant | #40484C | #BFC8CC | #58534A | #C0B9AB | #53433F | #D8C2BB |
| surface-container-lowest | #FFFFFF | #0A0F11 | #FFFDF8 | #161512 | #FFFFFF | #170F10 |
| surface-container-low | #F0F4F7 | #171C1F | #F2EEE5 | #211F1B | #FFF1E8 | #251B1C |
| surface-container | #EAEFF2 | #1B2124 | #EDE9DF | #26241F | #FAEADF | #2A1F20 |
| surface-container-high | #E4E9EC | #252B2E | #E6E1D6 | #302D28 | #F4E4D8 | #352A2A |
| surface-container-highest | #DEE3E6 | #303639 | #E0DBCF | #3B3832 | #EEDED2 | #403434 |
| surface-dim | #D6DBDE | #0F1417 | #D9D4C8 | #1C1A17 | #E6D6CB | #1D1415 |
| surface-bright | #F6FAFC | #353A3D | #FBF9F3 | #42403A | #FFF8F1 | #463A3A |
| inverse-surface | #2C3134 | #DEE3E6 | #32302B | #ECE6DA | #392E2C | #F1DFDB |
| inverse-on-surface | #EDF1F4 | #2C3134 | #F4F0E7 | #32302B | #FFEDE7 | #392E2C |
| inverse-primary | #8ED0E2 | #1A6476 | #B3C3FF | #23408E | #FFB59F | #B23A1C |
| outline | #70787C | #899296 | #7E786C | #8D877A | #85736E | #A08C87 |
| outline-variant | #BFC8CC | #40484C | #D3CDC0 | #48443C | #D8C2BB | #53433F |
| scrim | #000000 (40 %) | #000000 | #000000 (42 %) | #000000 | #000000 (38 %) | #000000 |
Key contrast (text role vs surface, L / D): on-surface 16.4 / 14.3 · 15.6 / 14.0 · 16.3 / 14.0; on-surface-variant 8.9 / 10.9 · 6.9 / 8.9 · 8.9 / 10.6; primary 6.4 / 10.8 · 8.7 / 10.1 · 5.7 / 10.6. `outline` is used for graphics only (3.99–5.84).
Direction-specific extra aliases: Kvitto `paper` (= surface-container-lowest in light, surface-container-high in dark; the receipt surface). Kalas `card` (#FFFFFF / #2A1F20) and `on-cat` (#FFFFFF / #1D1415, icon colour on solid category tiles).
## 3. Kvitt semantic roles
| Token | Use | Skärgård L | D | Kvitto L | D | Kalas L | D |
|---|---|---|---|---|---|---|---|
| owed | text/graphic: partner owes you | #00695C | #7DD8C5 | #2C6A38 | #92CC9A | #1B6B45 | #8BD8A8 |
| on-owed | | #FFFFFF | #00382F | #FFFFFF | #0E3818 | #FFFFFF | #003920 |
| owed-container | | #C2EDE4 | #005047 | #DCEBD8 | #1E3A24 | #C4EFD2 | #0F5233 |
| on-owed-container | | #00443B | #A3F2E0 | #123A1C | #BFE8C4 | #00210F | #A7F5C3 |
| owing | text/graphic: you owe partner | #A33E1C | #FFB59E | #A23B25 | #F2A890 | #7E2F84 | #F2AEF0 |
| on-owing | | #FFFFFF | #5E1700 | #FFFFFF | #4F1708 | #FFFFFF | #4C0F53 |
| owing-container | | #FFDBCF | #7E2C0E | #F6DDD3 | #4B2418 | #F9D7F6 | #66226C |
| on-owing-container | | #792A0B | #FFDBCF | #4D170A | #FFDACD | #33003A | #FFD6F8 |
| settled | "Kvitt" / "Ni är kvitt" | #4E5F69 | #B6C9D4 | #58534A | #C0B9AB | #6E5500 | #F2D45C |
| settled-container | | #DCE5EA | #2C3B43 | #E6E1D6 | #302D28 | #FFE38F | #5A3F00 |
| on-settled-container | | #36464F | #D3E5F0 | #2A2823 | #E5DFD2 | #241A00 | #FFE38F |
| warning | over-settle, sync warnings | #855700 | #F5BD62 | #875400 | #EBC06C | #8A4D00 | #FFB86E |
| on-warning | | #FFFFFF | #452B00 | #FFFFFF | #3E2900 | #FFFFFF | #4A2800 |
| warning-container | | #FFDEAD | #633F00 | #F7E3BF | #4D3708 | #FFDDBA | #6A3B00 |
| on-warning-container | | #633F00 | #FFDEAD | #3B2600 | #FCE3AE | #2C1600 | #FFDDBA |
| pending | sync-pending marker | #7B5C24 | #E8C48A | #875400 | #EBC06C | #00658F | #86CFFF |
| avatar-1 | initials background, user slot 1 | #C2E8F3 | #0E4D5C | #3F5E4C | #A5C9B0 | #9A4A00 | #FFB870 |
| on-avatar-1 | | #0B4C5C | #C2E8F3 | #FFFFFF | #10291A | #FFFFFF | #2E1500 |
| avatar-2 | initials background, user slot 2 | #F2DDB8 | #5D4410 | #6B4C8A | #CDB4E8 | #2C5F9E | #A8C8FF |
| on-avatar-2 | | #5A400E | #F7DFB4 | #FFFFFF | #2F1B45 | #FFFFFF | #002F64 |
All owed/owing text values ≥ 5.6:1 on surface in both themes in all directions. Owed/owing hue pairs are chosen to stay distinct for red-green colour blindness (Skärgård sea-green vs coral, Kvitto green vs brick with a square marker, Kalas green vs purple); the words carry the meaning anyway. Avatar slot: a stable function of the user id (e.g. the last hex digit's parity), so each person keeps one colour on both phones (AC-4.5).
## 4. Category colours (SPEC §9.7 token names)
Use: the icon colour on its container (`cat-<token>` on `cat-<token>-container`), chart segments, Kalas solid tiles (`on-cat` icon on `cat-<token>`). Never text in light themes. Container rule: Skärgård 16 % (L) / 22 % (D) of the colour over surface; Kvitto 16 % / 24 %; Kalas 18 % / 24 %.
Baseline values (used unchanged by Kvitto and Kalas, and by Skärgård in dark):
| Token | Light | Dark | Skärgård light override |
|---|---|---|---|
| green | #2E7D32 | #81C784 | #2A732E |
| orange | #B84A00 | #FFB74D | #A84300 |
| brown | #795548 | #C8B4AC | — |
| yellow | #8A6A00 | #F2D45C | #7D6000 |
| blue | #1565C0 | #90CAF9 | — |
| teal | #00796B | #80CBC4 | #006F62 |
| purple | #7B1FA2 | #D1A0DB | — |
| red | #C62828 | #EF9A9A | — |
| pink | #AD1457 | #F48FB1 | — |
| grey | #5F6B73 | #B0BEC5 | #58646C |
| indigo | #3949AB | #A5AEE0 | — |
| lime | #5B6B00 | #C5D86D | — |
Measured: icon vs its container ≥ 3.7:1 (light) and ≥ 4.8:1 (dark) in every direction; colour vs surface ≥ 4.6:1 (light) and ≥ 7.7:1 (dark). These differ from the provisional hex values in SPEC §9.7 (orange, brown, yellow, purple, grey, indigo, lime in light; brown, yellow, purple, indigo, lime in dark): adopting them is a spec change (decisions entry + spec version bump) that the orchestrator makes after the pick (SPEC §13.4 U2).
Per-direction container hex values are listed on each review page (computed, not hand-typed).
## 5. Chart colours
| Token | Skärgård L | Skärgård D | Kvitto L | Kvitto D | Kalas L | Kalas D |
|---|---|---|---|---|---|---|
| chart-cat-1 | #1A6476 | #8ED0E2 | #23408E | #B3C3FF | #C2462A | #FF9F86 |
| chart-cat-2 | #A8741A | #E8C48A | #B4602A | #F0A673 | #4F7A2B | #A9D37F |
| chart-cat-3 | #5B5FA8 | #B8BCF4 | #2D7A6C | #7DCAB8 | #8C6200 | #F2C14E |
| chart-cat-4 | #4D8559 | #9BD3A3 | #7B4E9E | #CDA8EE | #2D63AE | #9CC2FF |
| chart-cat-5 | #A0526E | #EDA9C1 | #7F7225 | #D6C66B | #8A3F92 | #E2A6EA |
| chart-cat-6 | #2F86A0 | #5FB6D0 | #AE3A5C | #F29CB6 | #00796F | #7FD3C8 |
| chart-cat-7 | #7A6A58 | #CDB9A1 | #4D6A7A | #A6BFCD | #B0306A | #FF9EC3 |
| chart-cat-8 | #66737C | #AEBDC8 | #73552A | #DCC39A | #5B6670 | #B7C3CC |
| chart-seq-1 | #D3ECF3 | #1E3A42 | #D6DDF2 | #2A3352 | #FBD8CC | #4A2A23 |
| chart-seq-2 | #9FD3E2 | #245A68 | #A9B8E3 | #3C4C80 | #F4A68E | #7A3B2C |
| chart-seq-3 | #62AFC5 | #2F7C8F | #7890CF | #5A70B0 | #E3765A | #B5563C |
| chart-seq-4 | #2E869E | #5EADC3 | #4865B1 | #8499DC | #C24A2E | #E8826A |
| chart-seq-5 | #165A6B | #A3DDEC | #23408E | #B3C3FF | #8E2A14 | #FFB59F |
| chart-grid | #DEE3E6 | #303639 | #E3DED2 | #37342E | #EBDACF | #3E3233 |
| chart-axis-label | #40484C | #BFC8CC | = on-surface-variant | = on-surface-variant | #53433F | #D8C2BB |
Rules: categorical colours are assigned to **accounts** in `created_at` order (Vardag 1, Huset 2, Gotland 2026 3, Japan 2027 4, cycling after 8); **categories** always use their own category colour, so a category looks the same in lists and charts. Sequential ramps are for single-series views (a highlighted bucket, "Min del" totals); seq-1 and seq-2 are backgrounds only (< 3:1). Stacked segments are separated by a 2 dp gap in the surface colour so adjacent similar hues stay distinct.
## 6. Typography
| Direction | Typeface(s) | Licence | Amounts |
|---|---|---|---|
| Skärgård | Roboto Flex (variable: wght, wdth, opsz) | OFL 1.1 | Roboto Flex with `tnum`; hero uses wdth 84 |
| Kvitto | IBM Plex Sans (text), IBM Plex Mono (amounts, captions) | OFL 1.1 | Plex Mono (tabular by nature) |
| Kalas | Nunito | OFL 1.1 | Nunito (default figures tabular, verified in renders) |
Type scale (sp / line height / weight):
| Style | Skärgård | Kvitto | Kalas |
|---|---|---|---|
| display-large | 57/64 400 | 57/64 400 (unused) | 57/64 800 |
| display-medium | 45/52 400 | 45/52 400 (unused) | 45/52 800 |
| display-small | 36/44 400 | 36/44 400 (unused) | 36/44 800 |
| headline-large | 32/40 500 | 30/38 500 | 32/40 800 |
| headline-medium | 28/36 500 | 26/34 500 | 28/36 800 |
| headline-small | 24/32 500 | 22/30 500 | 24/32 800 |
| title-large | 22/28 500 | 20/28 500 | 22/28 800 |
| title-medium | 16/24 560 | 16/24 500 | 16/24 700 |
| title-small | 14/20 560 | 14/20 600 | 14/20 800 |
| body-large | 16/24 400 | 16/24 400 | 16/24 500 |
| body-medium | 14/20 400 | 14/20 400 | 14/20 500 |
| body-small | 12/16 400 | 12/16 400 | 12/16 600 |
| label-large | 14/20 560 | 14/20 500 | 14/20 800 |
| label-medium | 12/16 560 | 12/16 500 | 12/16 700 |
| label-small | 11/16 560 | 11/16 500 Mono caps, tracking 0.08 em | 11/16 700 |
| amount-hero | 60/64 720 wdth 84, decimals and kr at 50 % | 44/52 500 Mono | 44/52 900 |
| amount-large | 32/40 650 wdth 92 | 28/36 500 Mono | 28/36 900 |
| amount-medium | 16/24 620 | 15/22 500 Mono | 18/24 800 |
| amount-small | 14/20 520 | 12/16 400 Mono | 15/20 800 |
Font scale: sp follows Android's non-linear scaling (Android 14+, Compose default): text ≥ 24 sp grows less than smaller text, so a 60 sp amount does not double at 200 %. The 200 % frames approximate this. Amounts never ellipsize: they wrap or step down one size.
## 7. Spacing (dp)
All three use one 4 dp scale: `space-1` 4, `space-2` 8, `space-3` 12, `space-4` 16, `space-5` 20, `space-6` 24, `space-8` 32, `space-10` 40, `space-12` 48, `space-16` 64 (Kvitto does not use 40 and 64). Screen gutter 16 in all directions.
| Measure | Skärgård | Kvitto | Kalas |
|---|---|---|---|
| card padding | 20 | 20 (receipt) | 20 |
| home account row | ~72 (card row) | 60 | ~76 (card) |
| ledger row | 72 | 60 | 72 |
| settings row | 56 | 56 | 56 |
| keypad key | 64 | 52 | 52 |
| gap between grouped items | 2 | rule (1 dp dashed) | 12 |
## 8. Shapes (corner radius, dp)
M3 shape tokens: extraSmall 4, small 8, medium 12, large 16, largeIncreased 20, extraLarge 28, extraLargeIncreased 32, extraExtraLarge 48, full.
| Component | Skärgård | Kvitto | Kalas |
|---|---|---|---|
| buttons, chips | full | small 8 | full |
| text fields | large 16 (outlined) | extraSmall 4 | large 16 (filled, no indicator) |
| cards | extraLarge 28 | none (receipt zig-zag edge, 10 dp teeth) | 24–28 |
| balance hero | extraLargeIncreased 32 | top corners 8, zig-zag bottom | extraLarge 28 |
| grouped/segmented lists | outer 20, inner 4 | none, rules | outer 24, inner 8 |
| category icon container | medium 12 | extraSmall 4 | MaterialShapes Cookie4Sided (fallback 30 %) |
| avatars | MaterialShapes Cookie9/12 (fallback circle) | full | MaterialShapes Sunny (fallback circle) |
| FAB | largeIncreased 20 | medium 12 | largeIncreased 20 |
| dialogs | extraLarge 28 | large 16 | extraLarge 28 |
| keypad keys | full, morph to 16 on press | small 8 | full |
## 9. Elevation and motion
| | Skärgård | Kvitto | Kalas |
|---|---|---|---|
| elevation model | tonal (surface-container levels); 2 dp shadow on menus, 3 dp on FAB | almost none: rules and tones; shadow only on menus, FAB, dialogs | tonal + soft warm shadow on light cards (0 1 2, 0 2 8, 10 % brown); no shadows in dark |
| motion scheme | `MotionScheme.expressive()` springs: spatial fast 0.6/800, default 0.8/380, slow 0.8/200; effects 1.0/3800, 1600, 800 | standard easing curves: receipt print reveal 350 ms emphasizedDecelerate, number roll 250 ms per digit, "Kvitt" stamp 200 ms, push shared-axis X 300 ms, tabs fade-through 200 ms | `MotionScheme.expressive()`; seesaw re-tilt spring 0.55/300; amount count-up 400 ms emphasizedDecelerate; bunting drop with 40 ms stagger when settled |
| stable fallback | `MotionScheme.standard()` / tween 200–500 ms emphasized | (already stable) | `MotionScheme.standard()` |
| reduced motion | everything except fades snaps | everything becomes a 150 ms crossfade | springs critically damped |
## 10. Icons
Material Symbols (Apache 2.0), as a bundled variable font or vector drawables generated from it.
| | Skärgård | Kvitto | Kalas |
|---|---|---|---|
| style | Rounded | Sharp | Rounded |
| weight / grade / optical size | 400 / 0 / 24 | 300 / 0 / 20–24 | 500–600 / 0 / 24 |
| fill | 1 for selected nav item and category icons, else 0 | 0, except the selected nav item | 1 for category icons and selected nav item |
Category icons: the 30 tokens of SPEC §9.7. Other icons used by all directions: `home`, `bar_chart`, `settings`, `add`, `close`, `arrow_back`, `more_vert`, `edit`, `delete`, `swap_horiz`, `payments`, `cloud_upload` / `cloud_off` / `cloud_done`, `sync`, `warning`, `info`, `history`, `event_repeat`, `devices`, `person_add`, `content_paste`, `share`, `content_copy`, `drag_indicator`, `archive` / `unarchive`, `backspace`, `system_update`, `phonelink_erase`.