@klodd/ds 3.21.0 → 3.21.2

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.
@@ -98,3 +98,142 @@
98
98
  background: var(--surface-default);
99
99
  border-color: var(--border-subtle);
100
100
  }
101
+
102
+ /* --na: AI har inte bedömt (relevance_score IS NULL). Klickbar för att
103
+ trigga rescore. Dashed border signalerar "bedömning saknas, klicka för
104
+ att försöka". Lyft från Jubb-domain v3.21.1 (2026-05-13).
105
+ Användning: <button class="score-pill score-pill--na" data-action="rescore" data-job-id="X">N/A</button> */
106
+ .score-pill--na {
107
+ color: var(--text-muted);
108
+ background: var(--surface-default);
109
+ border: 1px dashed var(--border-default);
110
+ cursor: pointer;
111
+ font-family: inherit;
112
+ transition: background var(--dur-fast) var(--ease-default),
113
+ color var(--dur-fast) var(--ease-default);
114
+ }
115
+ @media (hover: hover) and (pointer: fine) {
116
+ .score-pill--na:hover {
117
+ background: var(--surface-hover);
118
+ color: var(--text-default);
119
+ }
120
+ }
121
+ .score-pill--na:active { transform: scale(0.96); }
122
+ .score-pill--na:focus-visible {
123
+ outline: 2px solid var(--border-focus);
124
+ outline-offset: 2px;
125
+ }
126
+ .score-pill--na:disabled,
127
+ .score-pill--na[aria-disabled="true"] {
128
+ opacity: 0.5;
129
+ cursor: progress;
130
+ }
131
+ .score-pill--na.is-loading {
132
+ background: var(--surface-active);
133
+ }
134
+
135
+
136
+ /* ================================================================
137
+ .score-circle - cirkulär relevance-score-display (v3.21.2)
138
+ Standardstorlek 36x36 matchar .avatar default. Tabular-nums för
139
+ visuellt-jämn siffer-rendering. Klickbart fall (--na) triggar
140
+ rescore med is-loading spinning animation.
141
+
142
+ Modifiers: --strong (grön), --medium (gul), --low (grå), --na (klickbar)
143
+ States på --na: :hover, :active, :focus-visible, :disabled, .is-loading
144
+
145
+ Användning:
146
+ <span class="score-circle score-circle--strong">8.4</span>
147
+ <button class="score-circle score-circle--na" data-action="rescore"
148
+ data-job-id="X" aria-label="Bedöm jobbet">N/A</button>
149
+ ================================================================ */
150
+ .score-circle {
151
+ display: inline-flex;
152
+ align-items: center;
153
+ justify-content: center;
154
+ flex-shrink: 0;
155
+ width: 36px;
156
+ height: 36px;
157
+ font-size: var(--fs-13);
158
+ font-weight: var(--fw-medium);
159
+ font-variant-numeric: tabular-nums;
160
+ line-height: 1;
161
+ border: 1px solid transparent;
162
+ border-radius: 50%;
163
+ white-space: nowrap;
164
+ position: relative;
165
+ overflow: hidden;
166
+ }
167
+
168
+ .score-circle--strong {
169
+ color: var(--positive);
170
+ background: var(--positive-dim);
171
+ border-color: var(--positive-border);
172
+ }
173
+
174
+ .score-circle--medium {
175
+ color: var(--warning);
176
+ background: var(--warning-dim);
177
+ border-color: var(--warning-border);
178
+ }
179
+
180
+ .score-circle--low {
181
+ color: var(--text-muted);
182
+ background: var(--surface-default);
183
+ border-color: var(--border-subtle);
184
+ }
185
+
186
+ /* --na: klickbar (button-element) som triggar rescore via JS. Dashed
187
+ border signalerar "bedömning saknas, klicka för att försöka". */
188
+ .score-circle--na {
189
+ color: var(--text-muted);
190
+ background: var(--surface-default);
191
+ border: 1px dashed var(--border-default);
192
+ cursor: pointer;
193
+ font-size: var(--fs-11);
194
+ font-family: inherit;
195
+ transition: background var(--dur-fast) var(--ease-default),
196
+ color var(--dur-fast) var(--ease-default);
197
+ }
198
+ @media (hover: hover) and (pointer: fine) {
199
+ .score-circle--na:hover {
200
+ background: var(--surface-hover);
201
+ color: var(--text-default);
202
+ }
203
+ }
204
+ .score-circle--na:active { transform: scale(0.96); }
205
+ .score-circle--na:focus-visible {
206
+ outline: 2px solid var(--border-focus);
207
+ outline-offset: 2px;
208
+ }
209
+ .score-circle--na:disabled,
210
+ .score-circle--na[aria-disabled="true"] {
211
+ cursor: progress;
212
+ }
213
+
214
+ /* is-loading: spinning border-segment medan AI bedömer. Texten döljs
215
+ visuellt (transparent) men ::after-pseudo ritar en roterande ring.
216
+ prefers-reduced-motion slowar ner animationen markant. */
217
+ .score-circle--na.is-loading {
218
+ color: transparent;
219
+ background: var(--surface-default);
220
+ border-color: var(--border-default);
221
+ border-style: solid;
222
+ }
223
+ .score-circle--na.is-loading::after {
224
+ content: '';
225
+ position: absolute;
226
+ inset: 2px;
227
+ border-radius: 50%;
228
+ border: 2px solid transparent;
229
+ border-top-color: var(--accent-9);
230
+ animation: score-circle-spin 0.8s linear infinite;
231
+ }
232
+ @media (prefers-reduced-motion: reduce) {
233
+ .score-circle--na.is-loading::after {
234
+ animation-duration: 2s;
235
+ }
236
+ }
237
+ @keyframes score-circle-spin {
238
+ to { transform: rotate(360deg); }
239
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klodd/ds",
3
- "version": "3.21.0",
3
+ "version": "3.21.2",
4
4
  "description": "Klodd shared design system - tokens, components, JS",
5
5
  "main": "css/index.css",
6
6
  "bin": {
@@ -33,11 +33,12 @@ För varje entry gäller:
33
33
  - **Tokens:** `--surface-sunken/-default`, `--accent-9`, `--accent-a3` (focus-ring), `--bg-danger` (error-state)
34
34
 
35
35
  ### badge (`badge.css`)
36
- - **Blocks:** `.badge`, `.score-pill` (sibling, kanonisk definition)
37
- - **Modifiers:** `.badge--neutral/-success/-warning/-danger/-accent`, `.score-pill--strong/-medium/-low`
38
- - **Anvand:** Status-pills (statiska)
36
+ - **Blocks:** `.badge`, `.score-pill` (legacy rectangular form, behålls för bakåtkompat), `.score-circle` (v3.21.2 - cirkulär form, ersätter score-pill för relevance-displays)
37
+ - **Modifiers:** `.badge--neutral/-success/-warning/-danger/-accent`, `.score-pill--strong/-medium/-low/-na`, `.score-circle--strong/-medium/-low/-na`
38
+ - **States på `.score-pill--na` + `.score-circle--na`:** `.is-loading` (spinning animation), `[aria-disabled="true"]`, `:disabled` (cursor: progress)
39
+ - **Anvand:** Status-pills (statiska) + score-circle--na som klickbar rescore-trigger med spinning loading-animation. Standardstorlek 36x36 matchar .avatar default.
39
40
  - **INTE:** Klickbara chips (anvand `.chip`)
40
- - **Tokens:** `--positive/-dim/-border`, `--warning/-dim/-border`, `--bg-danger`, `--accent-text/-a2/-a6`
41
+ - **Tokens:** `--positive/-dim/-border`, `--warning/-dim/-border`, `--bg-danger`, `--accent-text/-a2/-a6/--accent-9`, `--surface-default/-hover/-active`, `--border-default/-focus/-subtle`, `--dur-fast`, `--ease-default`
41
42
 
42
43
  ### card (`card.css`)
43
44
  - **Blocks:** `.card`
@@ -0,0 +1,147 @@
1
+ # 0016 - "Mina sidor"-doctrine
2
+
3
+ ## Status
4
+ Locked.
5
+
6
+ ## Context
7
+
8
+ Audit 2026-05-13 jämförde Ekonoms `/jag` (sprint 66b-doktrinen) mot
9
+ Jubbs `/min-sida` (just byggd från grund). Båda är settings-hubbar -
10
+ samma roll men olika UI-disciplin.
11
+
12
+ Ekonom hade etablerat ett moget mönster (profilkort med inline-edit,
13
+ logisk sektion-gruppering, "Att åtgärda"-conditional, field_sheet-
14
+ macros för editing, LEAF-topbar). Jubb hade snabbt byggts utan motsvarande
15
+ struktur (profilkort utan namn, blandning av setting-rows och inline-knappar,
16
+ ingen actionable-conditional).
17
+
18
+ Per Calle's princip "alla appar ska ha samma namn och komponentstruktur"
19
+ behöver ett gemensamt locked pattern etableras för "Mina sidor" som
20
+ används av alla nuvarande och framtida Klodd-appar.
21
+
22
+ ## Decision
23
+
24
+ **Locked template-struktur för "Mina sidor"** (kanonisk ordning):
25
+
26
+ ```
27
+ ┌────────────────────────────────────────────┐
28
+ │ Topbar (3-kol grid, LEAF-anpassad) │
29
+ │ col 1: pwa-avatar │
30
+ │ col 2: topbar__logo "Min sida" │
31
+ │ col 3: <span class="avatar"> utan link │ ← LEAF
32
+ ├────────────────────────────────────────────┤
33
+ │ Profilkort (.profile-panel) │
34
+ │ · avatar--lg │
35
+ │ · display-name med inline-edit (om │
36
+ │ multi-user) eller skippa (single-user) │
37
+ │ · email (.text-caption eller .text-meta) │
38
+ ├────────────────────────────────────────────┤
39
+ │ Panel "Att åtgärda" CONDITIONAL │ ← Alerts först
40
+ │ panel--attention │
41
+ │ setting-rows med .panel__pill (count) │
42
+ ├────────────────────────────────────────────┤
43
+ │ Panel "Min [domän]" OPTIONAL │ ← om domän-data finns
44
+ │ setting-rows som öppnar field_sheet │
45
+ ├────────────────────────────────────────────┤
46
+ │ Panel "Mina inställningar" │
47
+ │ setting-rows + toggles + sheets │
48
+ ├────────────────────────────────────────────┤
49
+ │ Panel "Mina vyer" │
50
+ │ setting-rows som länk-rows │
51
+ ├────────────────────────────────────────────┤
52
+ │ Panel "Installera [app]" CONDITIONAL │ ← om !standalone PWA
53
+ │ iOS Safari-instruktioner (iPhone-only) │
54
+ ├────────────────────────────────────────────┤
55
+ │ Logout-row │
56
+ │ .btn--logout (paketet) │
57
+ └────────────────────────────────────────────┘
58
+ ```
59
+
60
+ ### Regler
61
+
62
+ 1. **Topbar är 3-kol-grid, LEAF-anpassad.** Avatar i kolumn 3 finns
63
+ visuellt för layout-konsekvens men är `<span class="avatar">` utan
64
+ `<a>`-wrapper. Inte klickbar (användaren är redan på destinationen).
65
+ Topbar__logo blir korrekt centrerat av grid `auto 1fr auto`.
66
+
67
+ 2. **Topbar-text "Min sida"** (svenska). Kort, idiomatisk, konsekvent
68
+ över alla Klodd-appar.
69
+
70
+ 3. **Profilkort är obligatoriskt.** `.profile-panel` (lyft till paketet
71
+ v3.21.0). Avatar--lg + email som minimum. Display-name med inline-edit
72
+ OBLIGATORISKT för multi-user-appar (Ekonom), OPTIONAL för single-user-
73
+ appar (Jubb). Single-user-app utan tydligt display-name-bruk får skippa.
74
+
75
+ 4. **Sektion-ordning är "alerts first".** "Att åtgärda" placeras FÖRE
76
+ "Min [domän]" och övriga settings så användaren direkt ser pending
77
+ tasks vid sidladdning.
78
+
79
+ 5. **"Min [domän]"-panel är OPTIONAL.** Bara om appen har domän-
80
+ specifika personliga värden separat från preferences (Ekonom:
81
+ inkomst + saldo). Single-purpose-appar (Jubb) slår ihop allt till
82
+ "Mina inställningar".
83
+
84
+ 6. **Alla settings använder setting-row + sheet.** Inga inline-knappar
85
+ för actions som ändrar state. Detta inkluderar:
86
+ - Choice-actions: `.sheet__item-grid` med `.sheet__item--primary`
87
+ - Field-editing: `field_sheet` macro
88
+ - Multi-field-editing: `multi_field_sheet` macro
89
+ - Toggles: `setting-toggle` inom setting-row
90
+
91
+ 7. **Conditional panels visas via JS eller Jinja-conditional.** Inte
92
+ alltid synliga, inte alltid dolda. Aktivering-villkor i ADR/template-
93
+ kommentar.
94
+
95
+ 8. **PWA-install-panel är iPhone-only.** Klodd-appar utvecklas för iOS
96
+ PWA + dev-desktop. Android Chrome-instruktioner inkluderas inte
97
+ (skulle ge falskt-positiva intryck att Android stöds). Detection
98
+ via `window.matchMedia('(display-mode: standalone)').matches`.
99
+
100
+ 9. **Logout är paketet `.btn--logout` i `.logout-row`** (v3.21.0).
101
+
102
+ ### Domain-naming-konvention per app
103
+
104
+ | App | "Min [domän]" eller "Mina inställningar" |
105
+ |---|---|
106
+ | Ekonom | Min ekonomi (inkomst, saldo) + Mina inställningar (toggles, abonnemang) |
107
+ | Jubb | Skip "Min [domän]" → Mina inställningar (Sökläge, Notiser, AI-profil) |
108
+ | Future-app | "Min [domän]" om personliga värden finns separat |
109
+
110
+ ## Konsekvenser
111
+
112
+ **Bra:**
113
+ - Single source of truth för settings-hubbar
114
+ - Future-Margevo + framtida appar får gratis-pattern
115
+ - A11y + iOS-konvention: heading-hierarki + setting-row-rytm
116
+ - Komposit av befintliga paket-komponenter (ingen ny CSS behövs)
117
+
118
+ **Trade-offs:**
119
+ - Single-user-appar tappar ett potentiellt personlighet-element (display-
120
+ name) - acceptabelt eftersom inline-edit av single-user-namn är
121
+ cosmetic, inte funktionellt.
122
+ - "Min sida" är längre än iOS-konvention "Mitt" - men matchar Calle's
123
+ preferens (mer explicit).
124
+
125
+ ## Migration 2026-05-13
126
+
127
+ **Jubb:**
128
+ - /min-sida-template omskriven enligt doktrinen
129
+ - Topbar: lägg avatar i kolumn 3 (`<span class="avatar">` utan link)
130
+ - "Att åtgärda" CONDITIONAL panel: pending triage-jobb med relevance >= 7.0
131
+ - Slå ihop "Sökläge" + "Notiser" + "AI-profil-förslag" till "Mina
132
+ inställningar"-panel
133
+ - "Installera Jubb": bara iOS Safari-instruktioner
134
+
135
+ **Ekonom:**
136
+ - /jag topbar: byt "Mitt" → "Min sida"
137
+ - Topbar: lägg `<span class="avatar">` i kolumn 3 (visuell paritet)
138
+ - Flytta "Att åtgärda" till FÖRE "Min ekonomi" (alerts first)
139
+ - Lägg "Installera Ekonom" CONDITIONAL panel (paritet, iOS-only)
140
+
141
+ ## References
142
+
143
+ - ADR 0011-strikt-bem-elementsyntax.md (besläktad - BEM-disciplin)
144
+ - ADR 0013-visual-coherence-doctrine.md (besläktad - visuell rytm)
145
+ - ADR 0015-panel-title-tag-doctrine.md (besläktad - heading-semantik)
146
+ - 02-components.md profile-panel-entry (v3.21.0)
147
+ - DESIGN-LANGUAGE.md sektion 13 (topbar-layout) + sektion 17 (Mina sidor)
@@ -492,6 +492,44 @@ inte brott - browser-tree accepterar skipping levels).
492
492
  **Etablerad 2026-05-13** efter panel__title-audit över Jubb + Ekonom.
493
493
  Se ADR 0015 (panel-title-tag-doctrine).
494
494
 
495
+ ## 17. "Mina sidor"-doktrin (settings-hub-pattern)
496
+
497
+ **Regel:** alla Klodd-appar har en gemensam "Mina sidor"-route med
498
+ locked template-struktur. Route-URL kan variera per app (`/jag` i
499
+ Ekonom, `/min-sida` i Jubb) men UI-pattern är konsekvent.
500
+
501
+ ### Template-struktur (kanonisk ordning)
502
+
503
+ 1. **Topbar** - 3-kol-grid med LEAF-anpassning: pwa-avatar + "Min sida" + `<span class="avatar">` utan link
504
+ 2. **Profilkort** - `.profile-panel` med avatar--lg + ev. display-name med inline-edit + email
505
+ 3. **"Att åtgärda"** CONDITIONAL - `panel--attention` med setting-rows + `.panel__pill`
506
+ 4. **"Min [domän]"** OPTIONAL - app-specifika personliga värden
507
+ 5. **"Mina inställningar"** - app-agnostiska preferences via setting-rows + sheets
508
+ 6. **"Mina vyer"** - navigation till sub-vyer
509
+ 7. **"Installera [app]"** CONDITIONAL - iPhone-only PWA-instruktioner
510
+ 8. **Logout-row** - `.btn--logout`
511
+
512
+ ### Kritiska regler
513
+
514
+ - Topbar avatar i kolumn 3 är `<span class="avatar">` UTAN `<a>`-wrapper på Min sida (LEAF-pattern - användaren är redan på destinationen, men element kvar för layout-konsekvens)
515
+ - Topbar-text alltid **"Min sida"** över alla appar (svenska, kort)
516
+ - Alla settings använder **setting-row + sheet** - inga inline-knappar för state-changes
517
+ - "Att åtgärda" placeras **FÖRE** "Min [domän]" så alerts visas direkt
518
+ - Display-name **obligatoriskt** för multi-user-appar (Ekonom), **optional** för single-user (Jubb)
519
+ - "Min [domän]" är **OPTIONAL** panel - bara om appen har domän-specifika personliga värden
520
+ - PWA-install-panel **bara iOS Safari**-instruktioner (Klodd-appar är iPhone-targeted)
521
+
522
+ ### Förbjudet
523
+
524
+ - Inline-knapp för push-aktivering (måste vara setting-row)
525
+ - Topbar utan kolumn 3-avatar på Min sida (bryter grid-centrering)
526
+ - Topbar utan LEAF-anpassning (avatar måste vara `<span>`, inte `<a>` på destinationen)
527
+ - Android Chrome-instruktioner i PWA-install-panel (klodd-appar är iPhone-only)
528
+
529
+ **Etablerad 2026-05-13** efter audit-jämförelse mellan Ekonoms `/jag`
530
+ och Jubbs `/min-sida`. Se ADR 0016 (mina-sidor-doctrine) för fullständig
531
+ specifikation + locked rationale.
532
+
495
533
  ## 12. När ändra regler
496
534
 
497
535
  Denna fil är **locked**. Ändringar kräver: