Files
kvitt/docs/ux/tokens.md
T
jacobh2 b72ac5bb9e 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>
2026-10-18 08:27:51 +02:00

224 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.