@klodd/ds 3.12.4 → 3.14.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.
- package/css/00-primitives.css +6 -2
- package/css/10-semantic.css +2 -2
- package/css/components/chip.css +4 -110
- package/js/pwa-register.js +27 -93
- package/package.json +1 -1
- package/references/02-components.md +2 -2
package/css/00-primitives.css
CHANGED
|
@@ -424,8 +424,6 @@
|
|
|
424
424
|
--max-w-form: 560px;
|
|
425
425
|
--max-w-bottom-nav: 572px;
|
|
426
426
|
--max-w-nav-desktop: 720px;
|
|
427
|
-
--max-w-install-chip: 580px;
|
|
428
|
-
--max-w-install-chip-desktop: 380px;
|
|
429
427
|
|
|
430
428
|
--content-max-width: 640px;
|
|
431
429
|
}
|
|
@@ -435,6 +433,10 @@
|
|
|
435
433
|
==== PWA / SAFE AREA
|
|
436
434
|
env(safe-area-inset-*) anvands for iOS-notch/home-indicator.
|
|
437
435
|
Touch-min 44px ar Apples HIG-rekommendation.
|
|
436
|
+
--viewport-height defaultar till 100vh, overrideas av JS
|
|
437
|
+
(pwa-register.js syncViewport) via visualViewport.height -
|
|
438
|
+
fungerar som iOS-cold-start-fix (lasningen tvingar WebKit
|
|
439
|
+
att stabilisera viewport-geometri).
|
|
438
440
|
================================================================ */
|
|
439
441
|
:root {
|
|
440
442
|
--safe-top: env(safe-area-inset-top, 0px);
|
|
@@ -442,6 +444,8 @@
|
|
|
442
444
|
--safe-left: env(safe-area-inset-left, 0px);
|
|
443
445
|
--safe-right: env(safe-area-inset-right, 0px);
|
|
444
446
|
|
|
447
|
+
--viewport-height: 100vh;
|
|
448
|
+
|
|
445
449
|
--touch-min: 44px;
|
|
446
450
|
|
|
447
451
|
/* Bottom-nav-tokens lever i 10-semantic.css som calc-baserade
|
package/css/10-semantic.css
CHANGED
|
@@ -226,8 +226,8 @@
|
|
|
226
226
|
--shadow-card: 0 8px 16px color-mix(in oklch, var(--gray-12) 8%, transparent);
|
|
227
227
|
|
|
228
228
|
/* Bakat-kompat: --shadow-float ar nu alias till --shadow-card.
|
|
229
|
-
Befintliga komponenter (
|
|
230
|
-
|
|
229
|
+
Befintliga komponenter (overlay.dialog/sheet, feedback.toast,
|
|
230
|
+
nav.bottom-nav) fortsatter funka utan andring. Migration till
|
|
231
231
|
--shadow-card per komponent kan ske gradvis. */
|
|
232
232
|
--shadow-float: var(--shadow-card);
|
|
233
233
|
|
package/css/components/chip.css
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* ================================================================
|
|
2
2
|
components/chip.css
|
|
3
|
-
Pills- och chips-familjen:
|
|
3
|
+
Pills- och chips-familjen: fyra fristående blocks som delar
|
|
4
4
|
form-språket (rounded-full, kompakt padding, inline-flex).
|
|
5
5
|
|
|
6
6
|
Blocks:
|
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
.chip-list - sibling-wrapper för chips i rad (har __item/__delete/__add)
|
|
9
9
|
.brand-pill - projekt-namn-pill i topbar
|
|
10
10
|
.month-pill - navigations-pill för månadsbyte (Ekonom)
|
|
11
|
-
.install-chip - PWA install-prompt som flyter ovan bottom-nav
|
|
12
11
|
|
|
13
12
|
HTML-relationer:
|
|
14
13
|
.chip-list innehåller .chip-list__item (egna BEM-element, ej .chip)
|
|
@@ -17,8 +16,8 @@
|
|
|
17
16
|
Modifiers:
|
|
18
17
|
.chip --accent/-positive/-negative/-warning/-faint
|
|
19
18
|
|
|
20
|
-
.install-chip
|
|
21
|
-
|
|
19
|
+
v3.13.0: .install-chip (PWA install-prompt) borttagen tillsammans
|
|
20
|
+
med tillhörande JS i pwa-register.js.
|
|
22
21
|
================================================================ */
|
|
23
22
|
.chip {
|
|
24
23
|
display: inline-flex;
|
|
@@ -206,112 +205,7 @@
|
|
|
206
205
|
}
|
|
207
206
|
|
|
208
207
|
|
|
209
|
-
/* ================================================================
|
|
210
|
-
==== INSTALL-CHIP
|
|
211
|
-
PWA install-prompt som flyter ovan bottom-nav.
|
|
212
|
-
================================================================ */
|
|
213
|
-
.install-chip {
|
|
214
|
-
position: fixed;
|
|
215
|
-
bottom: calc(84px + var(--safe-bottom));
|
|
216
|
-
left: var(--space-12);
|
|
217
|
-
right: var(--space-12);
|
|
218
|
-
max-width: 480px;
|
|
219
|
-
margin: 0 auto;
|
|
220
|
-
background: var(--surface-raised);
|
|
221
|
-
border: 1px solid var(--border-subtle);
|
|
222
|
-
border-radius: var(--radius-full);
|
|
223
|
-
padding: var(--space-10) var(--space-10) var(--space-10) var(--space-18);
|
|
224
|
-
display: flex;
|
|
225
|
-
align-items: center;
|
|
226
|
-
gap: var(--space-10);
|
|
227
|
-
font-size: var(--fs-13);
|
|
228
|
-
color: var(--text-default);
|
|
229
|
-
z-index: var(--z-overlay, 60);
|
|
230
|
-
animation: install-chip-up 0.28s var(--ease-out);
|
|
231
|
-
box-shadow: var(--shadow-float);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
.install-chip__text { flex: 1; min-width: 0; }
|
|
235
|
-
|
|
236
|
-
.install-chip__install {
|
|
237
|
-
background: var(--accent-9);
|
|
238
|
-
color: var(--text-on-accent);
|
|
239
|
-
border: none;
|
|
240
|
-
padding: var(--space-8) var(--space-14);
|
|
241
|
-
border-radius: var(--radius-full);
|
|
242
|
-
font-size: var(--fs-12);
|
|
243
|
-
font-weight: var(--fw-medium);
|
|
244
|
-
font-family: inherit;
|
|
245
|
-
cursor: pointer;
|
|
246
|
-
white-space: nowrap;
|
|
247
|
-
transition: background var(--dur-fast) var(--ease-default);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
@media (hover: hover) and (pointer: fine) {
|
|
251
|
-
.install-chip__install:hover { background: var(--accent-10); }
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
.install-chip__install:focus-visible {
|
|
255
|
-
outline: 2px solid var(--border-focus);
|
|
256
|
-
outline-offset: 2px;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
.install-chip__install:active {
|
|
260
|
-
background: var(--surface-active);
|
|
261
|
-
transform: scale(0.97);
|
|
262
|
-
transition: transform 80ms var(--ease-spring-snappy);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
.install-chip__install:disabled,
|
|
266
|
-
.install-chip__install[aria-disabled="true"] {
|
|
267
|
-
opacity: 0.5;
|
|
268
|
-
cursor: not-allowed;
|
|
269
|
-
pointer-events: none;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
.install-chip__dismiss {
|
|
273
|
-
background: transparent;
|
|
274
|
-
border: none;
|
|
275
|
-
color: var(--text-subtle);
|
|
276
|
-
font-size: var(--fs-18);
|
|
277
|
-
line-height: 1;
|
|
278
|
-
padding: var(--space-6) var(--space-10);
|
|
279
|
-
cursor: pointer;
|
|
280
|
-
min-width: 36px;
|
|
281
|
-
transition: color var(--dur-fast) var(--ease-default);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
@media (hover: hover) and (pointer: fine) {
|
|
285
|
-
.install-chip__dismiss:hover { color: var(--text-default); }
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
.install-chip__dismiss:focus-visible {
|
|
289
|
-
outline: 2px solid var(--border-focus);
|
|
290
|
-
outline-offset: 2px;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
.install-chip__dismiss:active {
|
|
294
|
-
background: var(--surface-active);
|
|
295
|
-
transform: scale(0.97);
|
|
296
|
-
transition: transform 80ms var(--ease-spring-snappy);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
.install-chip__dismiss:disabled,
|
|
300
|
-
.install-chip__dismiss[aria-disabled="true"] {
|
|
301
|
-
opacity: 0.5;
|
|
302
|
-
cursor: not-allowed;
|
|
303
|
-
pointer-events: none;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
@keyframes install-chip-up {
|
|
307
|
-
from { transform: translateY(calc(100% + 100px)); opacity: 0; }
|
|
308
|
-
to { transform: translateY(0); opacity: 1; }
|
|
309
|
-
}
|
|
310
|
-
|
|
311
208
|
@media (prefers-reduced-motion: reduce) {
|
|
312
|
-
.install-chip { animation: none; }
|
|
313
209
|
.chip-list__delete:active,
|
|
314
|
-
.chip-list__add:active
|
|
315
|
-
.install-chip__install:active,
|
|
316
|
-
.install-chip__dismiss:active { transform: none; }
|
|
210
|
+
.chip-list__add:active { transform: none; }
|
|
317
211
|
}
|
package/js/pwa-register.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
/*--------------------------------------------------------------
|
|
2
|
-
@klodd/ds - PWA-registrering
|
|
2
|
+
@klodd/ds - PWA-registrering
|
|
3
3
|
|
|
4
4
|
Auto-detekterar app-namn fran <html data-app="X">-attributet.
|
|
5
|
-
Defaults: appName = capitalize(data-app),
|
|
5
|
+
Defaults: appName = capitalize(data-app), swPath = "/sw.js".
|
|
6
6
|
Funkar identiskt for Jubb och Ekonom utan konfiguration.
|
|
7
7
|
|
|
8
|
-
Manuell override: KloddPWA.init({ appName,
|
|
8
|
+
Manuell override: KloddPWA.init({ appName, swPath }) innan load.
|
|
9
9
|
|
|
10
10
|
Funktioner:
|
|
11
11
|
- isNetworkError() - iOS-saker nat-detektering (window.isNetworkError)
|
|
12
12
|
- SW-registrering med updateViaCache: 'none'
|
|
13
|
-
|
|
14
|
-
- iOS
|
|
13
|
+
|
|
14
|
+
Install-prompts (beforeinstallprompt + iOS-hint) borttagna 3.13.0.
|
|
15
|
+
PWA-installation sker numera enbart via browserns inbyggda meny.
|
|
15
16
|
--------------------------------------------------------------*/
|
|
16
17
|
(function (root, doc) {
|
|
17
18
|
'use strict';
|
|
@@ -25,9 +26,7 @@
|
|
|
25
26
|
const cap = dataApp.charAt(0).toUpperCase() + dataApp.slice(1);
|
|
26
27
|
return {
|
|
27
28
|
appName: userCfg.appName || cap,
|
|
28
|
-
appKey: userCfg.appKey || dataApp,
|
|
29
29
|
swPath: userCfg.swPath || '/sw.js',
|
|
30
|
-
minVisits: userCfg.minVisits || 3,
|
|
31
30
|
};
|
|
32
31
|
}
|
|
33
32
|
|
|
@@ -63,91 +62,23 @@
|
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
/*------------------------------------------------------------
|
|
66
|
-
|
|
65
|
+
Viewport-sync (iOS PWA cold-start fix)
|
|
66
|
+
iOS Safari beraknar env(safe-area-inset-bottom) felaktigt vid
|
|
67
|
+
forsta render i PWA-standalone. Att tidigt lasa
|
|
68
|
+
visualViewport.height + skriva till en CSS-custom-property
|
|
69
|
+
tvingar WebKit att stabilisera sin viewport-geometri innan
|
|
70
|
+
layout-pass kor klart.
|
|
71
|
+
|
|
72
|
+
--viewport-height-tokenet ar tillagg for framtida konsumenter
|
|
73
|
+
som behover en JS-driven viewport-hojd. Defaulter till 100vh
|
|
74
|
+
i 00-primitives.css.
|
|
67
75
|
------------------------------------------------------------*/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
maybeShowInstallChip(cfg);
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function maybeShowInstallChip(cfg) {
|
|
79
|
-
if (sessionStorage.getItem(cfg.appKey + '_install_dismissed')) return;
|
|
80
|
-
if (root.matchMedia('(display-mode: standalone)').matches) return;
|
|
81
|
-
const visits = parseInt(localStorage.getItem(cfg.appKey + '_visits') || '0', 10) + 1;
|
|
82
|
-
localStorage.setItem(cfg.appKey + '_visits', String(visits));
|
|
83
|
-
if (visits < cfg.minVisits) return;
|
|
84
|
-
showInstallChip(cfg);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function showInstallChip(cfg) {
|
|
88
|
-
const id = cfg.appKey + '-install-chip';
|
|
89
|
-
if (doc.getElementById(id)) return;
|
|
90
|
-
|
|
91
|
-
const chip = doc.createElement('div');
|
|
92
|
-
chip.className = 'install-chip';
|
|
93
|
-
chip.id = id;
|
|
94
|
-
chip.setAttribute('role', 'dialog');
|
|
95
|
-
chip.setAttribute('aria-label', 'Installera ' + cfg.appName);
|
|
96
|
-
chip.innerHTML =
|
|
97
|
-
'<span>Installera ' + cfg.appName + ' på hemskärmen</span>' +
|
|
98
|
-
'<button type="button" data-install>Installera</button>' +
|
|
99
|
-
'<button type="button" data-dismiss aria-label="Stäng">×</button>';
|
|
100
|
-
doc.body.appendChild(chip);
|
|
101
|
-
|
|
102
|
-
chip.querySelector('[data-install]').addEventListener('click', async function () {
|
|
103
|
-
if (!deferredInstallPrompt) { chip.remove(); return; }
|
|
104
|
-
deferredInstallPrompt.prompt();
|
|
105
|
-
try { await deferredInstallPrompt.userChoice; } catch (e) {}
|
|
106
|
-
deferredInstallPrompt = null;
|
|
107
|
-
chip.remove();
|
|
108
|
-
});
|
|
109
|
-
chip.querySelector('[data-dismiss]').addEventListener('click', function () {
|
|
110
|
-
sessionStorage.setItem(cfg.appKey + '_install_dismissed', '1');
|
|
111
|
-
chip.remove();
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/*------------------------------------------------------------
|
|
116
|
-
iOS install-hint
|
|
117
|
-
Safari har ingen beforeinstallprompt - manuell flow via "Dela" ->
|
|
118
|
-
"Lagg till pa hemskarmen". Visa diskret hint efter N besok.
|
|
119
|
-
------------------------------------------------------------*/
|
|
120
|
-
function setupIOSHint(cfg) {
|
|
121
|
-
root.addEventListener('load', function () {
|
|
122
|
-
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !root.MSStream;
|
|
123
|
-
const inStandalone = root.matchMedia('(display-mode: standalone)').matches
|
|
124
|
-
|| root.navigator.standalone;
|
|
125
|
-
if (!isIOS || inStandalone) return;
|
|
126
|
-
if (sessionStorage.getItem(cfg.appKey + '_ios_install_dismissed')) return;
|
|
127
|
-
const visits = parseInt(localStorage.getItem(cfg.appKey + '_visits') || '0', 10);
|
|
128
|
-
if (visits < cfg.minVisits) return;
|
|
129
|
-
showIOSInstallHint(cfg);
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
function showIOSInstallHint(cfg) {
|
|
134
|
-
const id = cfg.appKey + '-ios-install-hint';
|
|
135
|
-
if (doc.getElementById(id)) return;
|
|
136
|
-
|
|
137
|
-
const hint = doc.createElement('div');
|
|
138
|
-
hint.className = 'install-chip install-chip-ios';
|
|
139
|
-
hint.id = id;
|
|
140
|
-
hint.setAttribute('role', 'dialog');
|
|
141
|
-
hint.setAttribute('aria-label', 'Installera ' + cfg.appName + ' pa iOS');
|
|
142
|
-
hint.innerHTML =
|
|
143
|
-
'<span>Tryck <strong>Dela</strong> → <strong>Lägg till på hemskärmen</strong></span>' +
|
|
144
|
-
'<button type="button" data-dismiss aria-label="Stäng">×</button>';
|
|
145
|
-
doc.body.appendChild(hint);
|
|
146
|
-
|
|
147
|
-
hint.querySelector('[data-dismiss]').addEventListener('click', function () {
|
|
148
|
-
sessionStorage.setItem(cfg.appKey + '_ios_install_dismissed', '1');
|
|
149
|
-
hint.remove();
|
|
150
|
-
});
|
|
76
|
+
function syncViewport() {
|
|
77
|
+
if (!root.visualViewport) return;
|
|
78
|
+
doc.documentElement.style.setProperty(
|
|
79
|
+
'--viewport-height',
|
|
80
|
+
root.visualViewport.height + 'px'
|
|
81
|
+
);
|
|
151
82
|
}
|
|
152
83
|
|
|
153
84
|
/*------------------------------------------------------------
|
|
@@ -160,6 +91,9 @@
|
|
|
160
91
|
root.KloddPWA.config = cfg;
|
|
161
92
|
|
|
162
93
|
registerSW(cfg);
|
|
163
|
-
|
|
164
|
-
|
|
94
|
+
|
|
95
|
+
if (root.visualViewport) {
|
|
96
|
+
root.visualViewport.addEventListener('resize', syncViewport);
|
|
97
|
+
}
|
|
98
|
+
syncViewport();
|
|
165
99
|
})(typeof window !== 'undefined' ? window : globalThis, document);
|
package/package.json
CHANGED
|
@@ -191,8 +191,8 @@ För varje entry gäller:
|
|
|
191
191
|
- **INTE:** Vanlig `<h1>` (anvand `.heading-1`)
|
|
192
192
|
|
|
193
193
|
### chip (`chip.css`)
|
|
194
|
-
- **Blocks:** `.chip`, `.chip-list` (sibling-wrapper for chips i rad), `.brand-pill`, `.month-pill
|
|
195
|
-
- **Element:** `.chip-list__item`, `.chip-list__text`, `.chip-list__form`, `.chip-list__delete`, `.chip-list__add
|
|
194
|
+
- **Blocks:** `.chip`, `.chip-list` (sibling-wrapper for chips i rad), `.brand-pill`, `.month-pill`
|
|
195
|
+
- **Element:** `.chip-list__item`, `.chip-list__text`, `.chip-list__form`, `.chip-list__delete`, `.chip-list__add`
|
|
196
196
|
- **Modifiers:** `.chip--accent/-positive/-negative/-warning/-faint`
|
|
197
197
|
- **Anvand:** Kort-format pills med dot-prefix (status, kategori, count)
|
|
198
198
|
- **INTE:** Form-inputs (anvand `.input`)
|