@klodd/ds 3.3.1 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/SKILL.md +56 -0
  2. package/bin/klodd-ds.js +25 -0
  3. package/bin/precache.js +43 -0
  4. package/bin/sync.js +33 -0
  5. package/bin/verify.js +66 -0
  6. package/css/00-primitives.css +13 -3
  7. package/css/components/dropdown.css +1 -1
  8. package/css/components/setting-row.css +1 -1
  9. package/css/components/tab-bar.css +1 -1
  10. package/css/components/tooltip.css +1 -1
  11. package/css/utilities.css +101 -67
  12. package/package.json +12 -3
  13. package/references/01-tokens.md +182 -0
  14. package/references/02-components.md +260 -0
  15. package/references/03-quality-bar.md +238 -0
  16. package/references/04-locked-decisions/0001-three-layer-token-architecture.md +41 -0
  17. package/references/04-locked-decisions/0002-bem-naming.md +37 -0
  18. package/references/04-locked-decisions/0003-radix-colors-oklch.md +39 -0
  19. package/references/04-locked-decisions/0004-pixel-numeric-spacing.md +31 -0
  20. package/references/04-locked-decisions/0005-data-app-theming.md +42 -0
  21. package/references/04-locked-decisions/0006-mauve-neutral.md +40 -0
  22. package/references/04-locked-decisions/0007-font-weight-400-500.md +46 -0
  23. package/references/04-locked-decisions/0008-npm-package-distribution.md +40 -0
  24. package/references/05-open-decisions/0001-tx-row-to-list-row-migration-CLOSED.md +80 -0
  25. package/references/05-open-decisions/0002-parallel-classnames-cleanup-CLOSED.md +75 -0
  26. package/references/05-open-decisions/0003-js-duplicates-migration-CLOSED.md +79 -0
  27. package/references/05-open-decisions/0004-halsomentorn-legacy-css.md +51 -0
  28. package/references/05-open-decisions/0005-cat-class-location-CLOSED.md +37 -0
  29. package/references/05-open-decisions/0006-bar-kind-template-migration-CLOSED.md +75 -0
  30. package/references/05-open-decisions/0007-ekonom-base-import-gap-CLOSED.md +111 -0
@@ -0,0 +1,182 @@
1
+ # Token-arkitektur
2
+
3
+ ## Tre lager med strikt inkapsling
4
+
5
+ ```
6
+ 00-primitives.css raa varden (Radix OKLCH-ramper, exakta pixelvarden)
7
+ 10-semantic.css komponenter laser HARIFRAN
8
+ apps/<app>.css per-app accent + surface-overrides (5-15 rader)
9
+ ```
10
+
11
+ **Komponenter refererar ALDRIG primitives direkt.** De laser bara semantic
12
+ tokens. Apps/-filerna ar enda platsen som mappar primitives till semantic.
13
+
14
+ ### Varfor tre lager
15
+
16
+ - **Primitives ar "implementation detail".** Att byta Radix-skala (eller
17
+ hela fargmodellen) ska inte krava komponent-omskrivning.
18
+ - **Semantic ar bryggan.** Komponentens regel `background: var(--surface-raised)`
19
+ fungerar i alla apps utan andring. Apps/-filen avgor vad `--surface-raised`
20
+ faktiskt blir per-app.
21
+ - **Apps/-overrides racker.** En ny app introduceras genom 5-15 rader CSS,
22
+ inte via komponent-fork.
23
+
24
+
25
+ ## Radix Colors via OKLCH
26
+
27
+ Officiella Radix-skalor (Mauve, Blue, Plum, Violet, Green, Amber, Red)
28
+ i dark-mode-varden, hamtade rakt fran radix-ui.com/colors.
29
+
30
+ ### Varfor
31
+
32
+ - **Pre-kalibrerade.** Perceptuellt jamn ljushet steg-for-steg. Hand-tunade
33
+ OKLCH skulle introducera subtila WCAG-kontrast-buggar.
34
+ - **WCAG-verifierade.** Steg 11 har garanterad 4.5:1 mot steg 1-2 (text
35
+ pa app-bakgrund). Steg 12 har 7:1 (high-contrast text).
36
+ - **OKLCH-bakad.** Modern color-mix() i komponenter ger perceptuellt
37
+ korrekt opacity utan handkalibrering.
38
+ - **Dokumenterad rollkarta.** Steg 1-2 = app-bg, 3 = vila, 4 = hover,
39
+ 5 = active, 6 = subtil border, 7 = interaktiv border, 8 = stark border,
40
+ 9 = solid bakgrund (knappar/badges), 10 = solid hover, 11 = low-contrast
41
+ text, 12 = high-contrast text.
42
+
43
+ ### Mauve som neutral
44
+
45
+ `--gray-1` till `--gray-12` mappas till **Radix Mauve Dark**, inte ren gray.
46
+
47
+ **Varfor:** Mauve ar Radix officiella rekommendation som komplement till
48
+ Blue och Plum. Subtle lila-tonad ger varmare bottenton som matchar bagge
49
+ app-accenterna utan att klinga med nagon.
50
+
51
+ **Trade-off:** Mauve passar inte for en framtida gron- eller orange-accent-
52
+ app. Inte planerat - om det blir aktuellt valjer vi neutral utifran den
53
+ appens accent vid den punkten.
54
+
55
+
56
+ ## Pixel-numerisk spacing/typografi/radius
57
+
58
+ ```
59
+ --space-N dar N = exakt antal pixlar (--space-12 = 12px)
60
+ --fs-N dar N = exakt antal pixlar (--fs-14 = 14px)
61
+ --radius-N dar N = exakt antal pixlar (--radius-12 = 12px)
62
+ ```
63
+
64
+ ### Varfor
65
+
66
+ - **LLM-vanligt.** En CC-session kan resonera om spacing utan att memorera
67
+ en mappnings-tabell (md = 12, lg = 16 etc.).
68
+ - **Designerns Figma-varde matchar token-namnet 1:1.** Mental aritmetik
69
+ elimineras.
70
+ - **Ingen ordinal-explosion.** Inga --space-md vs --space-md-2 vs --space-lg
71
+ vs --space-lg-tight.
72
+
73
+ **Trade-off:** branschstandard ar ordinala namn (Tailwind, Material,
74
+ Bootstrap). Designer som kommer fran de ekosystemen far anpassa sig.
75
+
76
+
77
+ ## Semantic-token-referens
78
+
79
+ ### Surfaces (NAV Aksel-lagermodell - ljusare = hogre i z-stacken)
80
+
81
+ | Token | Anvand for | INTE for |
82
+ |---|---|---|
83
+ | `--surface-page` | `<html>`, `<body>`, page-background | Kort, paneler, modaler |
84
+ | `--surface-default` | Sektionsbakgrunder, tab-innehall | Sidans yttersta bg eller upphojda kort |
85
+ | `--surface-raised` | Kort, paneler, sidopaneler | Modaler/popovers |
86
+ | `--surface-overlay` | Dialog-bg, tooltip, context-menu, sheets | Inbaddade kort |
87
+ | `--surface-hover` | `:hover`-bg pa knappar, rader, kort | Statiska ytor |
88
+ | `--surface-active` | `:active`-bg, `.nav-pod.active` | Hover |
89
+ | `--surface-sunken` | Input-bg, code-block | Interaktiva ytor |
90
+
91
+ ### Text
92
+
93
+ | Token | Anvand for | INTE for |
94
+ |---|---|---|
95
+ | `--text-default` | Rubriker, knapptext, viktig metadata | Hjalptext, datum, placeholder |
96
+ | `--text-subtle` | Metadata, datum, kompletterande info | Rubriker eller primar info |
97
+ | `--text-muted` | Placeholder, hint, micro-labels | Lasbar info med hog prioritet |
98
+ | `--text-disabled` | Disabled-state pa knappar/inputs | Aktiva element |
99
+ | `--text-on-accent` | Text inuti `--accent-9`-bg | Text pa neutral bg |
100
+ | `--text-on-status` | Text inuti `--bg-success/warning/danger` | Text pa neutral bg |
101
+
102
+ ### Borders
103
+
104
+ | Token | Anvand for | INTE for |
105
+ |---|---|---|
106
+ | `--border-subtle` | Tunna dividers, table-row-borders | Interaktiva element |
107
+ | `--border-default` | Kortkanter, input-borders, btn-borders | Fokus-states |
108
+ | `--border-strong` | Hover-state pa inputs, emphasis | Vila-state |
109
+ | `--border-focus` | `:focus-visible` outline | Hover eller statiska borders |
110
+
111
+ ### Accent (byts per app via `data-app`)
112
+
113
+ | Token | Anvand for | INTE for |
114
+ |---|---|---|
115
+ | `--accent-soft` | Selected-state-bg, highlight-rader | Knappar |
116
+ | `--accent-moderate` | Hover-state pa `--accent-soft`-ytor | Vila-bg |
117
+ | `--accent-9` | Solid button-bg, badges, accent-bar | Subtila bg |
118
+ | `--accent-10` | `:hover` pa `--accent-9` | Vila-state |
119
+ | `--accent-text` | Lankar, accentfargade etiketter | Text pa --accent-9-bg |
120
+ | `--accent-a1..a12` | Pre-kalibrerade alpha-tints (Radix-stil) | Solid bg |
121
+
122
+ ### Status
123
+
124
+ | Token | Anvand for | INTE for |
125
+ |---|---|---|
126
+ | `--positive` | Gain-belopp, success-chip-text | Aktion-knappar |
127
+ | `--negative` | Loss-belopp, error-text | Destruktiva aktioner |
128
+ | `--warning` | Warning-banners, score-pill-medium | Errors |
129
+ | `--accent-danger` | Delete-knappar, destructive bekraftelser | Faktiskt negativt belopp |
130
+ | `--bg-success/warning/danger` | Solid status-bg med vit text | Tonad bg (anvand --positive-dim etc) |
131
+
132
+
133
+ ## Alpha-varianter
134
+
135
+ ### Officiella Radix-alpha (v3.0.0+)
136
+
137
+ ```
138
+ --accent-a1 --accent-a2 --accent-a3
139
+ --accent-a6 --accent-a8
140
+ --accent-a10 --accent-a12
141
+ ```
142
+
143
+ Pre-kalibrerade transparency-skalor som overrides per app. Stegs-mappning
144
+ samma som solid-skalan: a1-2 = subtilest, a3 = vila-tint, a6 = subtil
145
+ border-emphasis, a8 = stark border, a10 = solid hover (alpha), a12 = text.
146
+
147
+ ### color-mix() for ad-hoc-alpha
148
+
149
+ Om en specific komponent behover en alpha-niva som inte finns i
150
+ Radix-stegen, anvand `color-mix(in oklch, var(--accent-9) 25%, transparent)`.
151
+
152
+ **Varfor color-mix() istallet for hardkodad rgba():**
153
+ - Perceptuellt korrekt over olika fargscalor (oklch interpolerar
154
+ i ljushet, inte rgb-kanaler)
155
+ - Foljer accent-byte automatiskt - en color-mix mot --accent-9 ger Blue
156
+ i Jubb och Plum i Ekonom utan komponent-andring
157
+ - Modern browser-stod (Chrome 111+, Safari 16.4+, Firefox 113+) som
158
+ redan kravs av Radix-ramparna
159
+
160
+
161
+ ## Component tokens (DRY-mekanism)
162
+
163
+ ```
164
+ --bottom-nav-height-semantic
165
+ --bottom-nav-clearance
166
+ --touch-min: 44px
167
+ --card-radius
168
+ --btn-radius
169
+ --input-radius
170
+ ```
171
+
172
+ Kombination som anvands i 3+ komponenter blir token. Forhindrar drift
173
+ mellan komponenter. Komponenter far referera dessa eftersom de ar
174
+ avledda fran semantic.
175
+
176
+
177
+ ## Light-mode (forberedd, ej produktion-verifierad)
178
+
179
+ `[data-theme="light"]` i 10-semantic.css overrider surface + text +
180
+ borders. Apps fortsatter att anvanda `data-app` for accent-byte oavsett
181
+ tema. Status: forberedd, inte aktivt produktion-verifierad - se
182
+ `05-open-decisions/` om aktualiseras.
@@ -0,0 +1,260 @@
1
+ # Komponentkatalog
2
+
3
+ 33 komponenter i `@klodd/ds@3.x`. Alla refererar bara semantic tokens -
4
+ inga primitives. BEM-konvention (block__element--modifier).
5
+
6
+ ## Grundkomponenter
7
+
8
+ ### button (`button.css`)
9
+ - **Klasser:** `.btn`, `.btn--primary/-secondary/-ghost/-danger`, `.btn--sm/-lg/-icon/-circle`, `.btn--loading`
10
+ - **Anvand:** alla aktioner med tap-target >=44px
11
+ - **INTE:** Cirkel-link i topbar (anvand `.btn--icon.btn--circle`)
12
+ - **Tokens:** `--surface-default/-hover/-active`, `--text-default/-on-accent`, `--border-default/-focus`, `--accent-9/-10`, `--bg-danger`, `--touch-min`
13
+
14
+ ### input (`input.css`)
15
+ - **Klasser:** `.input`, `.textarea`, `.select`, `.input-group__label/__hint/__error`, `.input-icon`
16
+ - **Anvand:** form-falt med 17px font-size (anti iOS auto-zoom)
17
+ - **INTE:** Inline-edit-pattern (anvand `.inline-edit__input`)
18
+ - **Tokens:** `--surface-sunken/-default`, `--accent-9`, `--accent-a3` (focus-ring), `--bg-danger` (error-state)
19
+
20
+ ### badge (`badge.css`)
21
+ - **Klasser:** `.badge`, `.badge--success/-warning/-danger/-accent`, `.score-pill`, `.score-pill--strong/-medium/-low`
22
+ - **Anvand:** Status-pills (statiska)
23
+ - **INTE:** Klickbara chips (anvand `.chip`)
24
+ - **Tokens:** `--positive/-dim/-border`, `--warning/-dim/-border`, `--bg-danger`, `--accent-text/-a2/-a6`
25
+
26
+ ### card (`card.css`)
27
+ - **Klasser:** `.card`, `.card--interactive/-flush`, `.card-header/-body/-footer/-divider`
28
+ - **Anvand:** Standardcontainer for paneler. `--interactive` ger hover/press-feedback
29
+ - **INTE:** Settings-hub-kort (anvand `.hub-card`)
30
+ - **Tokens:** `--surface-raised/-overlay/-hover`, `--border-subtle/-default/-focus`
31
+
32
+ ### nav (`nav.css`)
33
+ - **Klasser:** `.bottom-nav`, `.bottom-nav-item.active`, `.topbar`, `.topbar-back/-title/-action`, `.tab-bar`, `.tab.active`
34
+ - **Anvand:** Mobile bottom-nav (fixed), in-flow topbar, segmented tabs
35
+ - **INTE:** Generisk navigations-meny (anvand `.dropdown`)
36
+ - **Tokens:** `--surface-default`, `--text-muted/-default`, `--accent-text` (active), `--border-subtle/-focus`, `--touch-min`, `--safe-bottom`
37
+
38
+ ### feedback (`feedback.css`)
39
+ - **Klasser:** `.toast`, `.toast--success/-error/-warning`, `.empty-state`, `.skeleton`, `.skeleton--text/-circle/-card`, `.spinner`
40
+ - **Anvand:** Transient feedback + loading-states
41
+ - **INTE:** Inline error-meddelanden (anvand `.input-group__error`)
42
+ - **Tokens:** `--surface-overlay`, `--bg-success/-warning/-danger`, `--text-on-status`, `--shadow-float`. Respekterar `prefers-reduced-motion`.
43
+
44
+ ### overlay (`overlay.css`)
45
+ - **Klasser:** `.dialog`, `.sheet`, `.dialog-backdrop` (med cursor: pointer), `.dialog-header/-body/-footer`, `.sheet-handle/-body/-divider`
46
+ - **Anvand:** Native `<dialog>` (centrerad modal) eller `<dialog class="sheet">` (bottom-attached)
47
+ - **INTE:** Tooltip eller dropdown (egna komponenter)
48
+ - **Tokens:** `--surface-raised/-overlay`, `--border-subtle`, `--shadow-float`, `--z-overlay`, `--safe-bottom`
49
+
50
+ ### icon (`icon.css`)
51
+ - **Klasser:** `.icon`, `.icon--xs/-sm/-md/-lg/-xl`, `.icon-custom`
52
+ - **Anvand:** Lucide via CDN (cdn.jsdelivr.net) ELLER inline-SVG via `.icon-custom`
53
+ - **INTE:** Inline `style="width:Xpx"` pa ikoner (anvand size-modifier)
54
+ - **Tokens:** `currentColor` - parent-element styr fargen
55
+
56
+ ### hero-roll (`hero-roll.css`)
57
+ - **Klasser:** `.hero-amount[data-animate-roll]`, `.hero-digit-track`
58
+ - **Anvand:** Display-siffer-animation pa hero-vyer (kodlas-rullning)
59
+ - **INTE:** Vanliga numeriska varden (anvand `.hero__amount` utan data-animate-roll)
60
+ - **Tokens:** `--text-default`, `--lh-tight`, `--ls-tightest`, `--fs-80`. **Undantag:** `font-weight: 600` (display-undantaget fran 400/500-policyn).
61
+
62
+ ### divider (`divider.css`)
63
+ - **Klasser:** `.divider`, `.divider--vertical/-strong`
64
+ - **Anvand:** Horisontell eller vertikal separator
65
+ - **INTE:** Visuell separator inom card (anvand `.card-divider`)
66
+ - **Tokens:** `--border-subtle/-default`
67
+
68
+ ### progress (`progress.css`)
69
+ - **Klasser:** `.progress`, `.progress-bar`, `.progress-bar--success/-warning/-danger`
70
+ - **Anvand:** Linjar progress med data-bar-width-pattern (CSP-safe via bar-styles.js)
71
+ - **INTE:** Progress med text-overlay (eget komposit-pattern)
72
+ - **Tokens:** `--surface-raised`, `--accent-9`, `--bg-success/-warning/-danger`. Respekterar `prefers-reduced-motion`.
73
+
74
+ ### tooltip (`tooltip.css`)
75
+ - **Klasser:** `.tooltip-wrapper`, `.tooltip`
76
+ - **Anvand:** Pure-CSS tooltip via `:hover/:focus-within` pa wrappern
77
+ - **INTE:** Long-content tooltips (anvand `.dialog`)
78
+ - **Tokens:** `--surface-overlay`, `--text-default`, `--border-default`, `--z-tooltip`
79
+
80
+ ### dropdown (`dropdown.css`)
81
+ - **Klasser:** `.dropdown.is-open`, `.dropdown-menu`, `.dropdown-item`, `.dropdown-item--danger`, `.dropdown-divider`
82
+ - **Anvand:** Kontextuell meny (kebab/more-actions). JS togglar `.is-open`
83
+ - **INTE:** Permanent visible navigation (anvand `.nav` eller `.tab-bar`)
84
+ - **Tokens:** `--surface-overlay/-hover/-active`, `--text-default`, `--border-default/-subtle/-focus`, `--bg-danger` (item--danger), `--z-dropdown`
85
+
86
+
87
+ ## Layout-komponenter (v3.0.0+)
88
+
89
+ ### banner (`banner.css`)
90
+ - **Klasser:** `.banner`, `.banner--hero/-accent/-positive/-negative/-warning`, `.banner__title/__sub/__content`
91
+ - **Anvand:** Bred informations-rad i flode (top-of-page eller mellan sektioner)
92
+ - **INTE:** Toast-notifikation (anvand `.toast`)
93
+
94
+ ### panel (`panel.css`)
95
+ - **Klasser:** `.panel`, `.panel--info/-info-warning/-danger/-attention`, `.panel__title/__title-row/__title-meta/__title-link/__step-row/__step-badge`
96
+ - **Anvand:** Sektion med ramad styling och titel
97
+ - **INTE:** Klickbart kort (anvand `.card--interactive`)
98
+
99
+ ### hub-card (`hub-card.css`)
100
+ - **Klasser:** `.hub-card`, `.hub-card__icon/__text/__title/__subtitle/__chev`, `.hub-list`, `.hub-category`
101
+ - **Anvand:** Settings-hub navigation (kort-listor som lankar till sub-sidor)
102
+ - **INTE:** Generic listrader (anvand `.list-row`)
103
+
104
+ ### stat (`stat.css`)
105
+ - **Klasser:** `.stat-grid`, `.stat-grid--single`, `.stat-card`, `.stat-card__value/__label/__unit/__sub`, `.stat-card__value--positive/-negative/-accent/-warning`
106
+ - **Anvand:** Sma kort med stor siffra + label (read-only metric-display)
107
+ - **INTE:** Editerbart varde (anvand `.setting-row`)
108
+
109
+ ### form (`form.css`)
110
+ - **Klasser:** `.form-card`, `.form-section`, `.form-section__title`, `.form-group`, `.form-group__label`, `.form-hint`, `.form-row`, `.form-row--inline`
111
+ - **Anvand:** Wrapper-strukturen for formularet
112
+ - **INTE:** Inputs sjalva (egen `.input`)
113
+
114
+ ### setting-row (`setting-row.css`)
115
+ - **Klasser:** `.setting-list`, `.setting-row`, `.setting-row--static/-danger`, `.setting-row__text/__label/__sub/__value/__amount/__chevron/__avatar/__pill`, `.setting-toggle/__track/__thumb`
116
+ - **Anvand:** Tap-to-edit-rad som triggar bottom-sheet
117
+ - **INTE:** Klickbart kort med rich content (anvand `.hub-card`)
118
+
119
+ ### collapsible (`collapsible.css`)
120
+ - **Klasser:** `.collapsible-card`, `.collapsible__header/__header-text/__chev/__body`, `[data-expanded]`
121
+ - **Anvand:** Panel som expanderar pa klick. JS togglar `data-expanded`
122
+ - **INTE:** Modal-content (anvand `.dialog` eller `.sheet`)
123
+
124
+ ### hbar (`hbar.css`)
125
+ - **Klasser:** `.hbar`, `.hbar__item/__track/__fill/__label/__value`, `.hbar__fill--positive/-warning/-negative`
126
+ - **Anvand:** Generisk horisontell bar med label + value + track + fill
127
+ - **INTE:** Status-progress (anvand `.progress`)
128
+
129
+ ### split-bar (`split-bar.css`)
130
+ - **Klasser:** `.split-bar`, `.split-bar__segment`, `.split-bar__segment--primary/-secondary/-positive/-negative/-warning`, `.split-bar__label/__labels`
131
+ - **Anvand:** Flersegment-bar for cost-split, debt-vs-equity m.fl.
132
+ - **INTE:** Single-bar-progress (anvand `.hbar`)
133
+
134
+ ### hero (`hero.css`)
135
+ - **Klasser:** `.hero`, `.hero__heading/__amount/__amount--card/__amount--fluid/__amount-row/__label/__label--muted/__meta/__actions`
136
+ - **Anvand:** Stora display-rubriker (80px-skala default, 40px card)
137
+ - **INTE:** Vanlig `<h1>` (anvand `.heading-1`)
138
+
139
+ ### chip (`chip.css`)
140
+ - **Klasser:** `.chip`, `.chip--accent/-positive/-negative/-warning/-faint`, `.chip-list`, `.chip-list__item/__delete/__add`, `.brand-pill`, `.month-pill`, `.score-pill`, `.install-chip`
141
+ - **Anvand:** Kort-format pills med dot-prefix (status, kategori, count)
142
+ - **INTE:** Form-inputs (anvand `.input`)
143
+
144
+ ### avatar (`avatar.css`)
145
+ - **Klasser:** `.avatar`, `.avatar--sm/-lg`
146
+ - **Anvand:** Cirkular initial-avatar med accent-gradient
147
+ - **INTE:** Foto-avatar (overlapp via custom regel)
148
+
149
+ ### list-row (`list-row.css`)
150
+ - **Klasser:** `.list`, `.list-row`, `.list-row--excluded/-pending/-clickable`, `.list-row__icon/__body/__desc/__meta/__date/__amount`, `.list-row__amount--positive/-negative`
151
+ - **Anvand:** Generisk listrad med ikon + body + amount (transaktioner, audit-log, jobs)
152
+ - **INTE:** Settings-rad (anvand `.setting-row`)
153
+
154
+ ### table (`table.css`)
155
+ - **Klasser:** `.table`, `.table .num`
156
+ - **Anvand:** Tabell med tabular-nums-stod
157
+ - **INTE:** Layout-grid (anvand CSS Grid utility)
158
+
159
+ ### auth (`auth.css`)
160
+ - **Klasser:** `.auth-container`, `.auth-container__title/__subtitle`
161
+ - **Anvand:** Centrerad layout for inloggning + onboarding
162
+ - **INTE:** Settings-vyer
163
+
164
+ ### swipe-stack (`swipe-stack.css`)
165
+ - **Klasser:** `.swipe-stack`, `.swipe-card`, `.swipe-card--back/-gone`, `.swipe-card__header/__title/__body/__footer`, `.swipe-decision-overlay`, `.swipe-decision-overlay--save/-dismiss/-visible`, `.swipe-actions`, `.swipe-action-btn`, `.swipe-action-btn--save/-dismiss`, `.swipe-meta`, `.swipe-meta-sep`, `.swipe-progress`, `.swipe-empty`, `.swipe-hint`
166
+ - **Anvand:** Tinder-stil swipe-stack med drag-physics
167
+ - **INTE:** Card-galleri (anvand `.card-grid`)
168
+
169
+ ### inline-edit (`inline-edit.css`)
170
+ - **Klasser:** `.inline-edit`, `.inline-edit__text/__btn/__form/__input`
171
+ - **Anvand:** Inline-edit av profil-namn, etiketter (pencil-knapp togglar form)
172
+ - **INTE:** Form-fields (anvand `.form-group`)
173
+ - **Special:** `.inline-edit__form[hidden]` har explicit `display: none` (paritet med globalt `[hidden]`-stod i base/pwa.css)
174
+
175
+ ### upload-spinner (`upload-spinner.css`)
176
+ - **Klasser:** `.upload-spinner-overlay`, `.upload-spinner-overlay.is-visible`, `.upload-spinner`, `.upload-spinner-overlay__label/__hint`
177
+ - **Anvand:** Viewport-overlay for langa async-operationer (OCR, AI-anrop)
178
+ - **INTE:** Inline-spinner (anvand `.spinner`)
179
+
180
+ ### tab-bar (`tab-bar.css`)
181
+ - **Klasser:** `.tab-bar`, `.tab-bar__item.is-active`
182
+ - **Anvand:** Inline tab-grupp for format-val (CSV/Avi etc.)
183
+ - **INTE:** Bottom-nav (anvand `.bottom-nav`)
184
+
185
+ ### colored-row (`colored-row.css`) - v3.1.0
186
+ - **Klasser:** `.colored-row`, `.colored-row--sm/-lg`
187
+ - **Anvand:** Kategoriserade rader, fargkodade listor, statusrader -
188
+ med vansterkant fran `--row-accent`-token (default: `--border-subtle`)
189
+ - **INTE:** Rader utan fargkodning (anvand `.list-row` eller plain `.tx-row`)
190
+ - **Tokens:** `--row-accent` (sattes av app-bindning), `--border-subtle` (default)
191
+ - **Pattern:** Se "Kategori-monstret" nedan
192
+
193
+ ### colored-bar (`colored-bar.css`) - v3.1.0
194
+ - **Klasser:** `.colored-bar`, `.colored-bar--sm/-md/-lg`, `.colored-bar--vertical`
195
+ - **Anvand:** Budget-staplar, kategori-staplar, progress med semantisk
196
+ farg fran `--bar-accent`-token (default: `--accent-9`)
197
+ - **INTE:** Generisk progress utan kategori (anvand `.progress`)
198
+ - **Tokens:** `--bar-accent` (sattes av app-bindning), `--accent-9` (default)
199
+ - **Pattern:** Se "Kategori-monstret" nedan
200
+
201
+
202
+ ## Kategori-monstret
203
+
204
+ Paketet ager komponenten, appen ager kategorin. Token-definition och
205
+ token-bindning ar appens ansvar - paketet vet ingenting om kategorier.
206
+
207
+ ### I paketet (@klodd/ds)
208
+
209
+ ```css
210
+ .colored-row {
211
+ border-left: 3px solid var(--row-accent, var(--border-subtle));
212
+ }
213
+ .colored-bar {
214
+ background: var(--bar-accent, var(--accent-9));
215
+ }
216
+ ```
217
+
218
+ ### I app-domain-filen (t.ex. ds/ekonom-domain.css)
219
+
220
+ Definiera tokens + bindningar:
221
+
222
+ ```css
223
+ /* Token-definition - VARDEN som ar app-specifika */
224
+ :root {
225
+ --my-category: #A78BFA;
226
+ }
227
+
228
+ /* Token-bindning - kopplar app-tokens till paketets komponenter */
229
+ .my-cat-row { --row-accent: var(--my-category); }
230
+ .my-cat-bar { --bar-accent: var(--my-category); }
231
+ ```
232
+
233
+ ### I template
234
+
235
+ ```html
236
+ <div class="colored-row my-cat-row">...</div>
237
+ <div class="colored-bar my-cat-bar"></div>
238
+ ```
239
+
240
+ ### Resultat
241
+
242
+ - Paketets `.colored-row` ar generic och ovetande om kategorier
243
+ - App:s `.my-cat-row` sattes som wrapper som overrider `--row-accent`
244
+ - Element-baserade `style="--row-accent: ..."` kan ocksa anvandas men
245
+ app-domain-CSS-bindningar ar att foredra (data-attribute-konvention
246
+ + lasbar template)
247
+
248
+ Jubb, Ekonom, framtida appar foljer samma monster med sina egna tokens.
249
+
250
+
251
+ ## Anti-patterns
252
+
253
+ - **`color: #FFF` i komponent-CSS.** Anvand `var(--text-on-accent)` eller `oklch(0.98 0 0)` som primitiv om absolut nodvandig.
254
+ - **`background: rgba(...)`** istallet for `color-mix(in oklch, var(--accent-9) 20%, transparent)` eller en `--accent-aN`-token.
255
+ - **`padding: 13px`** istallet for `--space-12` eller `--space-14` (narmaste pixel-numerisk).
256
+ - **`font-size: 16px`** utan token. Inputs ska ha `--fs-17` (= 17px) for anti-iOS-zoom.
257
+ - **Inline `style="width: 50%"` pa progress-bar.** Anvand `data-bar-width="50%"` + `bar-styles.js` (CSP-safe).
258
+ - **`var(--gray-9)` i komponent-CSS.** Komponenter laser bara semantic.
259
+ - **Egen .button-style.** Anvand `.btn` med modifier istallet.
260
+ - **`font-weight: 700`.** Anvand `--fw-medium` (500). Display-siffror ar dokumenterat undantag.
@@ -0,0 +1,238 @@
1
+ # Kvalitetskrav (icke-forhandlingsbara)
2
+
3
+ Lista over absoluta krav som inte ska brytas. Varje krav har konkret
4
+ "hur man foljer" + rationale.
5
+
6
+ ## 1. BEM-konvention pa alla CSS-klasser
7
+
8
+ ```
9
+ .block
10
+ .block__element
11
+ .block--modifier
12
+ .block__element--modifier
13
+ ```
14
+
15
+ **Hur man foljer:**
16
+ - Block: substantiv (`.card`, `.banner`, `.hub-card`)
17
+ - Element: barn-del med `__` (`.card__title`, `.banner__content`)
18
+ - Modifier: variant med `--` (`.btn--primary`, `.banner--warning`)
19
+ - INGA hyphen-flat-namn (`.btn-primary` ar fel - ska vara `.btn--primary`)
20
+
21
+ **Varfor:**
22
+ - Specificity-balans: alla klasser ar 0,1,0 (en klass) - ingen
23
+ cascade-overraskning
24
+ - Lasbart agentvanlig: en CC-session kan resonera om struktur utan
25
+ att lasa CSS:en
26
+ - Etablerad branschstandard - manga developer-erfarenheter
27
+ - Forhindrar "magic specificity" via descendant-selektorer
28
+
29
+
30
+ ## 2. WCAG AA minimum pa alla interaktiva komponenter
31
+
32
+ **Konkreta krav:**
33
+ - **Text-kontrast 4.5:1** mot bakgrund (Radix steg 11 mot steg 1-2 ar
34
+ pre-verifierad)
35
+ - **Tap-target 44x44px** minimum (`--touch-min` = 44px)
36
+ - **Synlig fokus-ring** pa alla interaktiva element via `:focus-visible`
37
+ (`outline: 2px solid var(--border-focus); outline-offset: 2px`)
38
+ - **`prefers-reduced-motion`** respekteras pa alla animationer
39
+ - **`prefers-contrast: more`** stegrar borders och muted-text 1-2 steg
40
+ (implementerat i 10-semantic.css)
41
+ - **`hidden`-attribut respekteras** via global `[hidden] { display: none
42
+ !important }` i base/pwa.css
43
+ - **ARIA-roles** pa alla interaktiva non-button-element (.dropdown-item
44
+ ska ha role="menuitem", .swipe-card ska ha role="article")
45
+
46
+ **Varfor:**
47
+ - Lagkrav i EU (Tillgangs-direktivet, EAA fran 2025)
48
+ - Bredde tillganglighet ar PWA-default - inte feature i Klodd-apparna
49
+ som anvander mobilen i bil/buss/svaga ljusforhallanden
50
+ - Radix Colors ar pre-WCAG-kalibrerade - att bryta mot dem kraver
51
+ medvetet val + ny dokumentation
52
+
53
+
54
+ ## 3. Inga hardkodade farg-, spacing- eller fs-varden i komponent-CSS
55
+
56
+ **Hur man foljer:**
57
+ - ALDRIG `color: #FF0000` - anvand `var(--negative)` eller `var(--red-9)`
58
+ - ALDRIG `padding: 13px` - anvand `var(--space-12)` eller `var(--space-14)`
59
+ - ALDRIG `font-size: 16px` direkt - anvand `var(--fs-17)` for inputs
60
+ eller anvand klasser som `.text-base`
61
+ - ALDRIG `border-radius: 10px` - anvand `var(--radius-10)`
62
+ - ALDRIG `rgba(0, 0, 0, 0.4)` - anvand `color-mix(in oklch, ...)` eller
63
+ semantic alpha-token
64
+
65
+ **Varfor:**
66
+ - Single source of truth: andring i 00-primitives.css landar omedelbart
67
+ i alla komponenter
68
+ - App-byte fungerar: hardkodad #0090ff i en komponent skulle ge fel farg
69
+ for Ekonom (som har Plum-accent)
70
+ - Token-search ar viktigare an pixel-precision: designern andrar `--fs-14`
71
+ fran 14px till 15px i en sprint, alla komponenter foljer med
72
+
73
+
74
+ ## 4. Pixel-numerisk spacing/typografi/radius
75
+
76
+ **Hur man foljer:**
77
+ - `--space-N` dar N = exakt antal pixlar
78
+ - Tillgangliga steg: 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32,
79
+ 40, 48, 56, 64, 80
80
+ - Vanliga val: 4-8 (tight), 10-14 (komponent-internt), 16-20 (kort-padding),
81
+ 24-32 (sektioner), 40+ (hero-spacing)
82
+ - Behover du `--space-15`? Fragstall det forst - oftast funkar 14 eller 16.
83
+ Lagga till nya steg kraver token-uppdatering + dokumentation
84
+
85
+ **Varfor:**
86
+ - LLM-vanligt: `--space-12 = 12px` ar trivialt att resonera om
87
+ - Designerns Figma-varde matchar token-namnet 1:1 utan mappning
88
+ - Forhindrar ordinal-explosion (md/lg/xl/2xl/3xl-stelhet)
89
+
90
+
91
+ ## 5. Font-weight 400 eller 500 - inga undantag utan dokumentation
92
+
93
+ **Hur man foljer:**
94
+ - `--fw-regular: 400` for vanlig text
95
+ - `--fw-medium: 500` for emphasis (knapptext, etiketter, rubriker)
96
+ - INGA 600 eller 700 utom dokumenterade undantag
97
+ - Display-siffror (`.hero-amount`) har `font-weight: 600` - dokumenterat
98
+ i `components/hero-roll.css` med kommentar
99
+
100
+ **Varfor:**
101
+ - Mer val skapar inkonsistens: en designer valjer 600 nagon gang, en
102
+ annan 700, snart finns alla varianter och inget mover hierarki
103
+ - Tva-steg ger tillrackligt med visual hierarchy: medium ar emphasis,
104
+ regular ar default
105
+ - Display-undantaget ar OK eftersom 80px-display behover visual tyngd
106
+ som UI-text inte gor
107
+
108
+ **Trade-off:**
109
+ - Bold-text-effekt ar narvarande pa display-skala. Vid framtida
110
+ feature-arbete dar bold-text behovs (banners-hero?), maste den
111
+ motiveras + dokumenteras.
112
+
113
+
114
+ ## 6. color-mix() istallet for hardkodade alpha-varden
115
+
116
+ **Hur man foljer:**
117
+ ```css
118
+ /* Korrekt */
119
+ background: color-mix(in oklch, var(--accent-9) 20%, transparent);
120
+
121
+ /* Eller anvand semantic alpha-token */
122
+ background: var(--accent-a3);
123
+
124
+ /* Felaktigt */
125
+ background: rgba(0, 144, 255, 0.2); /* hardkodad rgb */
126
+ background: rgba(var(--accent-rgb), 0.2); /* token-baserad rgb */
127
+ ```
128
+
129
+ **Varfor:**
130
+ - color-mix(in oklch, ...) ar perceptuellt korrekt - rgb-alpha ger
131
+ olika visuell intensitet beroende pa fargen
132
+ - Foljer accent-byte automatiskt: en color-mix mot --accent-9 ger Blue
133
+ i Jubb och Plum i Ekonom utan komponent-andring
134
+ - Modern browser-stod (Chrome 111+, Safari 16.4+, Firefox 113+) som
135
+ redan kravs av Radix-OKLCH-ramparna - ingen polyfill behovs
136
+
137
+
138
+ ## 7. Per-app-overrides bara i apps/<app>.css - aldrig i komponent-CSS
139
+
140
+ **Hur man foljer:**
141
+ - Ny app introduceras genom 5-15 rader CSS i `apps/foo.css`
142
+ - Komponenter forandras INTE for ny app
143
+ - Per-app-overrides ar bara accent-tokens (+ ev. surface-tokens om appen
144
+ vill avvika fran neutral-default)
145
+
146
+ **Varfor:**
147
+ - Komponenten ar source of truth - en regel galler i alla apps
148
+ - Driften mellan apparna minimeras
149
+ - Ny app ar trivial att introducera - INTE en re-fork av komponenter
150
+ - App-byten i samma DOM (om relevant) blir trivialt - bara `data-app`-flip
151
+
152
+
153
+ ## 8. Komponent-states: alla 4 states pa interaktiva komponenter
154
+
155
+ **Hur man foljer:**
156
+ Varje interaktiv komponent SKA ha:
157
+ - `:hover` - bakgrund eller fargandring (anvand `--surface-hover` eller `--accent-10`)
158
+ - `:active` - press-feedback (anvand `--surface-active` plus `transform: scale(0.97)`)
159
+ - `:focus-visible` - tangentbordsfokusring (`outline: 2px solid var(--border-focus); outline-offset: 2px`)
160
+ - `:disabled` eller `[disabled]` - opacity 0.5 + cursor: not-allowed + pointer-events: none
161
+ - Loading-state om relevant (sa knapp inte kan dubbel-klickas)
162
+
163
+ **Varfor:**
164
+ - Tangentbordsanvandare ser inte hover - de behover focus-visible
165
+ - Touch-anvandare maste fa visuell feedback vid press
166
+ - Disabled-state ska var visuellt och funktionellt blockerande
167
+ - Konsekvens utover komponentbiblioteket - en knapp i Jubb beter sig
168
+ identiskt som en knapp i Ekonom
169
+
170
+
171
+ ## 9. CSP-saker JS-driven styling
172
+
173
+ **Hur man foljer:**
174
+ - Inline `style="width: 50%"` blockeras av CSP (style-src utan unsafe-inline)
175
+ - Anvand `data-bar-width="50%"` + `bar-styles.js` som applicerar via
176
+ JS-runtime (`element.style.width = el.dataset.barWidth`)
177
+ - JS-runtime-styling ar tillaten av CSP - bara HTML-parser-styling ar
178
+ blockerad
179
+
180
+ **Varfor:**
181
+ - Mozilla Observatory A+ kraver inga `unsafe-inline` i style-src
182
+ - Inline-styles ar svara att audit:a (kommandot `git grep style=` borde
183
+ hitta noll matches i template-filer)
184
+
185
+
186
+ ## 10. En enda skugga tillaten (DNB Eufemia "arlig materialitet")
187
+
188
+ **Hur man foljer:**
189
+ - ENDAST `var(--shadow-card)` i hela systemet
190
+ - Aldrig `--shadow-resting/-raised/-floating` eller liknande hierarkisk
191
+ skugg-skala
192
+ - Aldrig hardkodade `box-shadow: 0 X Y rgba(...)`
193
+ - Hierarki uppnas via surface-tokens (`--surface-page` < `-default` <
194
+ `-raised` < `-overlay`), INTE via olika skugg-djupa
195
+
196
+ **Varfor:**
197
+ - Dark-mode-first design: skuggor syns inte tydligt mot mork bakgrund -
198
+ ljushet-hierarki ar mer effektiv
199
+ - DNB Eufemias "arliga materialitet": skuggor som bara existerar nar
200
+ fysiskt motiverat (kort som svarar mot bakgrund). Inga fake gradient-
201
+ skuggor som imiterar djup som inte finns.
202
+ - Forhindrar skugg-explosion: en designer valjer 0.5 nagon gang, en
203
+ annan 0.7, snart finns alla varianter och visuellt sprak ar splittrat
204
+
205
+
206
+ ## 11. Inter font + Linear-stack
207
+
208
+ **Hur man foljer:**
209
+ - Anvand `var(--font-sans)` overallt - ALDRIG specifika font-family-
210
+ stackar i komponenter
211
+ - Default-stack ar `Inter, SF Pro Display, -apple-system, system-ui, ...`
212
+ (Linear-konventionen)
213
+ - Inter laddas via Google Fonts i `base/typography.css` om enheten
214
+ saknar den lokalt
215
+ - Numeriska data: `font-variant-numeric: tabular-nums` for kolumn-
216
+ justering (siffror packar identiskt)
217
+
218
+ **Varfor:**
219
+ - Inter ar designad for UI-text och fungerar konsekvent over alla
220
+ storlekar
221
+ - SF Pro Display ar Apples designspraket (synkad UX-feeling pa iOS)
222
+ - system-ui som universell fallback for nischade plattformar
223
+ - Linear, Vercel och liknande modern SaaS anvander samma stack
224
+
225
+
226
+ ## 12. Audit-aliaser halls levande tills hela migration genomford
227
+
228
+ **Hur man foljer:**
229
+ - Gamla token-namn (--text-primary, --bg-base, --accent-dim) finns kvar
230
+ i 10-semantic.css's "LEGACY ALIASES"-block
231
+ - Komponent-CSS som migrerats anvander nya semantic-namn
232
+ (--text-default, --surface-page, --accent-a3)
233
+ - Tar INTE bort legacy-aliaser forrans alla call-sites migrerats
234
+
235
+ **Varfor:**
236
+ - Migrations-bro: app-CSS som inte uppdaterats fortsatter funka
237
+ - Big-bang-migrationer ar riskabla - per-template-migration ar saker
238
+ - Open punkter dokumenterade i `05-open-decisions/`