@klodd/ds 5.5.0 → 5.6.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.
@@ -0,0 +1,241 @@
1
+ /* ================================================================
2
+ base/interactions.css
3
+ Delade interaktions- och beteendelager, lyfta från appernas
4
+ byte-identiska ds/base.css i Sprint 3:
5
+ - Reset + html/body
6
+ - Touch-cleanup (eliminera webb-tells: tap-callout, 300ms-delay)
7
+ - Hero-glow (radial gradient, opt-in via .with-hero-glow)
8
+ - First-load entry animation (hero/stat-cards/bottom-nav)
9
+ - Press-feedback (asymmetri 80ms in / 160ms out, iOS-feel)
10
+ - Pull-to-refresh indicator
11
+ - View transitions (same-document, 160ms snappy)
12
+ Laddas efter base/pwa.css, typography.css, layout.css.
13
+ ================================================================ */
14
+
15
+
16
+ /* ================================================================
17
+ ==== RESET
18
+ ================================================================ */
19
+ *, *::before, *::after {
20
+ box-sizing: border-box;
21
+ margin: 0;
22
+ padding: 0;
23
+ }
24
+
25
+ html {
26
+ /* Sprint A4: Touch-cleanup. Eliminera webb-tells. */
27
+ -webkit-tap-highlight-color: transparent;
28
+ -webkit-text-size-adjust: 100%;
29
+ text-size-adjust: 100%;
30
+ /* Sprint E4: smooth scroll for hash-anchors. Reduced-motion-safe. */
31
+ scroll-behavior: smooth;
32
+ }
33
+
34
+ html, body {
35
+ min-height: 100vh; /* fallback */
36
+ min-height: 100dvh; /* dynamisk viewport, tar hansyn till iOS-toolbar */
37
+ background: var(--surface-page);
38
+ color: var(--text-default);
39
+ font-family: var(--font-sans);
40
+ font-size: var(--fs-14);
41
+ font-weight: var(--fw-regular);
42
+ line-height: var(--lh-base);
43
+ letter-spacing: var(--ls-base);
44
+ -webkit-font-smoothing: antialiased;
45
+ -moz-osx-font-smoothing: grayscale;
46
+ overscroll-behavior: none;
47
+ }
48
+
49
+ /* Sprint G2: reservera utrymme i botten sa sista content inte goms
50
+ bakom fixed bottom-nav. */
51
+ body {
52
+ padding-bottom: var(--bottom-nav-clearance);
53
+ }
54
+
55
+ a {
56
+ color: var(--accent-9);
57
+ text-decoration: none;
58
+ }
59
+ @media (hover: hover) and (pointer: fine) {
60
+ a:hover { text-decoration: underline; }
61
+ }
62
+
63
+ button { font-family: inherit; }
64
+
65
+ /* Tabular numerals dar siffror jamfors visuellt. */
66
+ .num,
67
+ .num-display,
68
+ .num-value,
69
+ .num-inline,
70
+ .stat-card__value,
71
+ .list-row__amount,
72
+ .hero__amount,
73
+ .hero__amount--card {
74
+ font-variant-numeric: tabular-nums;
75
+ }
76
+
77
+
78
+ /* ================================================================
79
+ ==== TOUCH-CLEANUP (Sprint A4)
80
+ Eliminera webb-tells: ingen tap-callout, ingen 300ms-delay,
81
+ ingen text-selektion pa chrome. Text-content forblir markerbart.
82
+ ================================================================ */
83
+ button, a, [role="button"],
84
+ .bottom-nav__item,
85
+ .month-pill,
86
+ .btn, .btn--secondary, .btn--primary,
87
+ .btn--ghost, .btn--danger, .btn--positive, .btn--icon,
88
+ .list-row, .stat-card[onclick] {
89
+ touch-action: manipulation;
90
+ -webkit-touch-callout: none;
91
+ }
92
+
93
+ /* UI-chrome far inte text-selectas (kanns som webb-tell). */
94
+ .bottom-nav, .topbar, .bottom-nav__item, .month-pill,
95
+ .sheet__handle,
96
+ .btn, .btn--secondary, .btn--primary,
97
+ .btn--ghost, .btn--danger, .btn--positive, .btn--icon {
98
+ user-select: none;
99
+ -webkit-user-select: none;
100
+ }
101
+
102
+ /* Text-content forblir markerbart (over no-select-containrar). */
103
+ .list-row .list-row__desc, .list-row .list-row__amount,
104
+ .stat-card__value, .hero__amount, .hero__amount--card,
105
+ .panel p, .panel span,
106
+ .collapsible-body p, .collapsible-body span {
107
+ user-select: text;
108
+ -webkit-user-select: text;
109
+ }
110
+
111
+
112
+ /* ================================================================
113
+ ==== HERO-GLOW (radial bakgrund pa entry-skarmar)
114
+ Opt-in via .with-hero-glow pa <main class="main-content">.
115
+ Visas pa Ekonoms /avstamning och Halsomentorns /dashboard etc.
116
+ ================================================================ */
117
+ .with-hero-glow::before {
118
+ content: '';
119
+ position: absolute;
120
+ top: 0;
121
+ left: 0;
122
+ right: 0;
123
+ height: 420px;
124
+ background: var(--hero-glow);
125
+ pointer-events: none;
126
+ z-index: 0;
127
+ }
128
+ .with-hero-glow > * { position: relative; z-index: 1; }
129
+
130
+
131
+ /* ================================================================
132
+ ==== FIRST-LOAD ENTRY ANIMATION (Sprint D3)
133
+ Hero glider in, stat-cards spring-up sekventiellt, bottom-nav
134
+ hoppar in nerifran. Triggas via data-first-load pa body, vilket
135
+ FastAPI middleware satter vid forsta browser-besoket.
136
+ ================================================================ */
137
+ body[data-first-load="true"] .hero {
138
+ animation: hero-arrive 480ms var(--ease-spring-bounce);
139
+ }
140
+
141
+ body[data-first-load="true"] .stat-card {
142
+ animation: card-arrive 320ms var(--ease-spring-bounce);
143
+ animation-fill-mode: backwards;
144
+ }
145
+ body[data-first-load="true"] .stat-card:nth-child(1) { animation-delay: 200ms; }
146
+ body[data-first-load="true"] .stat-card:nth-child(2) { animation-delay: 280ms; }
147
+ body[data-first-load="true"] .stat-card:nth-child(3) { animation-delay: 360ms; }
148
+ body[data-first-load="true"] .stat-card:nth-child(4) { animation-delay: 440ms; }
149
+
150
+ body[data-first-load="true"] .bottom-nav {
151
+ animation: nav-arrive 400ms var(--ease-spring-snappy);
152
+ animation-delay: 100ms;
153
+ animation-fill-mode: backwards;
154
+ }
155
+
156
+ @keyframes hero-arrive {
157
+ 0% { opacity: 0; transform: translateY(-8px); }
158
+ 100% { opacity: 1; transform: translateY(0); }
159
+ }
160
+ @keyframes card-arrive {
161
+ 0% { opacity: 0; transform: translateY(12px) scale(0.96); }
162
+ 100% { opacity: 1; transform: translateY(0) scale(1); }
163
+ }
164
+ @keyframes nav-arrive {
165
+ 0% { opacity: 0; transform: translateY(60px); }
166
+ 100% { opacity: 1; transform: translateY(0); }
167
+ }
168
+
169
+
170
+ /* ================================================================
171
+ ==== PRESS-FEEDBACK (Sprint A3, native iOS-feel)
172
+ Asymmetri: press in 80ms snappy, release 160ms med spring-bounce.
173
+ color/border/opacity-transitions tappas medvetet for dessa
174
+ interaktiva element. Hover-bg-overrides far press-out-timing
175
+ (kanns smidigt pa desktop ocksa).
176
+ ================================================================ */
177
+ .btn,
178
+ .bottom-nav__item,
179
+ .month-pill a {
180
+ transition:
181
+ transform var(--press-out-duration) var(--press-out-easing),
182
+ background var(--press-out-duration) var(--press-out-easing);
183
+ }
184
+
185
+ .btn:active,
186
+ .bottom-nav__item:active,
187
+ .month-pill a:active {
188
+ transform: scale(0.97);
189
+ transition:
190
+ transform var(--press-in-duration) var(--press-in-easing),
191
+ background var(--press-in-duration) var(--press-in-easing);
192
+ }
193
+
194
+
195
+ /* ================================================================
196
+ ==== PULL-TO-REFRESH (Sprint E1)
197
+ Indikator visas centrerat ovanfor viewport. Pull-progress styr
198
+ transform och opacity (sats inline av pull-to-refresh.js).
199
+ ================================================================ */
200
+ .ptr-indicator {
201
+ position: fixed;
202
+ top: calc(var(--safe-top) + 8px);
203
+ left: 50%;
204
+ transform: translate(-50%, -40px);
205
+ z-index: var(--z-overlay);
206
+ pointer-events: none;
207
+ width: 36px;
208
+ height: 36px;
209
+ border-radius: 50%;
210
+ background: var(--surface-strong);
211
+ display: flex;
212
+ align-items: center;
213
+ justify-content: center;
214
+ opacity: 0;
215
+ }
216
+
217
+ .ptr-spinner {
218
+ width: 18px;
219
+ height: 18px;
220
+ border: 2px solid var(--text-muted);
221
+ border-top-color: var(--accent-9);
222
+ border-radius: 50%;
223
+ }
224
+
225
+ .ptr-indicator--triggered .ptr-spinner {
226
+ animation: ptr-spin 600ms linear infinite;
227
+ }
228
+ @keyframes ptr-spin { to { transform: rotate(360deg); } }
229
+
230
+
231
+ /* ================================================================
232
+ ==== VIEW TRANSITIONS (Sprint B + I)
233
+ Same-document VT via document.startViewTransition i turbo-nav.js.
234
+ Cross-document VT borttagen pga iOS Safari snapshot-bug pa fixed-
235
+ position-element + dvh-instabilitet.
236
+ ================================================================ */
237
+ ::view-transition-old(root),
238
+ ::view-transition-new(root) {
239
+ animation-duration: 160ms;
240
+ animation-timing-function: var(--ease-spring-snappy);
241
+ }
package/css/base/pwa.css CHANGED
@@ -9,8 +9,9 @@
9
9
  - Globala fokusringar (:focus-visible)
10
10
  - Reduced-motion respekt
11
11
 
12
- Mer omfattande Jubb-experience (animations, toasts, view transitions,
13
- pull-to-refresh, hero-glow) ligger i ds/base.css som laddas darefter.
12
+ Det fullare interaktionslagret (animations, view transitions,
13
+ pull-to-refresh, hero-glow, press-feedback) ligger i
14
+ base/interactions.css som laddas darefter.
14
15
  ================================================================ */
15
16
 
16
17
  *, *::before, *::after {
@@ -33,7 +34,7 @@ html {
33
34
  body {
34
35
  margin: 0;
35
36
  min-height: 100dvh; /* dynamic viewport - hanterar mobil-browser chrome */
36
- overscroll-behavior: contain; /* stoppar pull-to-refresh-konflikter */
37
+ overscroll-behavior: none; /* stoppar pull-to-refresh-konflikter */
37
38
  }
38
39
 
39
40
  /* iOS PWA cold-start safe-area-fix. .kds-scroll-buffer ar ett block-
@@ -75,8 +76,17 @@ body {
75
76
 
76
77
  /* Globala fokusringar - alltid synliga vid keyboard-navigation, aldrig vid tap. */
77
78
  :focus-visible {
78
- outline: 2px solid var(--border-focus);
79
+ outline: 2px solid var(--accent-9);
79
80
  outline-offset: 2px;
81
+ border-radius: var(--radius-4);
82
+ }
83
+
84
+ /* <main> undantas fran focus-outline: turbo-nav anropar main.focus()
85
+ for screen-reader-access efter varje swap, och pa iOS Safari triggar
86
+ programmatisk focus :focus-visible - vilket gav en synlig outline. */
87
+ #main:focus,
88
+ #main:focus-visible {
89
+ outline: none;
80
90
  }
81
91
 
82
92
  /* Respektera OS-installningen "Tillganglighet -> Reducera rorelse". */
@@ -62,8 +62,8 @@
62
62
 
63
63
  .hero__amount {
64
64
  /* 2026-05-13: fs-80 → fs-100 (25% storre) for mer display-tyngd.
65
- 2026-05-14 (ADR 0019): font-size konsumerar --hero-amount-fz-token
66
- for app-override-mojlighet. */
65
+ Sprint 2 (2026-05-21): --hero-amount-fz-token raderad, font-size
66
+ läser --fs-100 direkt. */
67
67
  font-size: var(--fs-100);
68
68
  /* Undantag fran 400/500-policy (regel 5): display-siffra, inte
69
69
  UI-text. 600 ger nodvandig visuell tyngd pa hero-storlekar.
package/css/index.css CHANGED
@@ -22,6 +22,7 @@
22
22
  @import './base/pwa.css';
23
23
  @import './base/typography.css';
24
24
  @import './base/layout.css';
25
+ @import './base/interactions.css';
25
26
 
26
27
  /* Komponenter */
27
28
  @import './components/button.css';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@klodd/ds",
3
- "version": "5.5.0",
3
+ "version": "5.6.0",
4
4
  "description": "Klodd shared design system - tokens, components, JS",
5
5
  "main": "css/index.css",
6
6
  "bin": {
@@ -15,7 +15,8 @@
15
15
  "references/"
16
16
  ],
17
17
  "scripts": {
18
- "lint:css": "stylelint 'css/**/*.css'"
18
+ "lint:css": "stylelint 'css/**/*.css'",
19
+ "verify:docs": "node scripts/verify-docs.js"
19
20
  },
20
21
  "engines": {
21
22
  "node": ">=16.7"
@@ -33,7 +33,7 @@ Blocket (.collapsible-card) oförändrat.
33
33
  ~15 ändringar + 1 JS-ändring (app.js rad 202).
34
34
 
35
35
  ## Decision
36
- **Alternativ A.** Elementen är rätt `.collapsible__` speglar det
36
+ **Alternativ A.** Elementen är rätt - `.collapsible__` speglar det
37
37
  semantiska begreppet. Blocket är det som avviker. "-card"-suffixet är
38
38
  en implementationsdetalj från ursprunglig skapelse, inte en semantisk
39
39
  egenskap. JS-kopplingen via data-attribut gör att rename är renare än
@@ -13,7 +13,7 @@ migrationen identifierades tre paket-gaps som krävde workarounds i
13
13
  wrappern eftersom paketets default-beteende är optimerat för ett-shot-
14
14
  flöden (brief-flödet) snarare än kontinuerlig polling (pipeline-flödet).
15
15
 
16
- ### Gap 1 continuousMode
16
+ ### Gap 1 - continuousMode
17
17
 
18
18
  **Behov:** polling fortsätter efter `done`-callback för att detektera
19
19
  nya körningar automatiskt. Pipeline-progress visar status på senaste
@@ -30,7 +30,7 @@ internal cleanup) kan restart-pattern brytas. Plus memory-overhead -
30
30
  varje ny instans skapar nya closures (acceptabel för once-per-N-minute
31
31
  körningar).
32
32
 
33
- ### Gap 2 autoHide: false
33
+ ### Gap 2 - autoHide: false
34
34
 
35
35
  **Behov:** komponenten ska vara hidden tills `data.active === true`.
36
36
  Pipeline kan vara inaktiv vid sidladdning (vanligaste fallet eftersom
@@ -46,7 +46,7 @@ i konstruktor-anrop), så pattern fungerar deterministiskt. Om paketet
46
46
  ändras till async-init (t.ex. wait för DOM-ready) kan en kort flash
47
47
  uppstå mellan start() och första transform.
48
48
 
49
- ### Gap 3 extraRenders
49
+ ### Gap 3 - extraRenders
50
50
 
51
51
  **Behov:** rendera side-elements utanför paketets bar/label/pct-scope.
52
52
  Pipeline har `batch_progress_label` som renderas parallellt med