@klodd/ds 5.5.0 → 5.7.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/SKILL.md +5 -4
- package/css/base/interactions.css +241 -0
- package/css/base/pwa.css +14 -4
- package/css/components/hero.css +2 -2
- package/css/index.css +1 -0
- package/js/sheet.js +96 -0
- package/js/toast.js +31 -0
- package/package.json +3 -2
- package/references/04-locked-decisions/0010-collapsible-block-rename.md +1 -1
- package/references/05-open-decisions/0017-async-progress-pipeline-gaps.md +3 -3
package/SKILL.md
CHANGED
|
@@ -6,10 +6,11 @@ description: Design memory for the @klodd/ds shared design system used by the tw
|
|
|
6
6
|
# @klodd/ds — Design Memory
|
|
7
7
|
|
|
8
8
|
## Status
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- Sprint
|
|
12
|
-
- Sprint
|
|
9
|
+
Designsystem-auditen (2026-05-20, se `audits/00-summary.md`) slutförd -
|
|
10
|
+
alla tre sprintar klara:
|
|
11
|
+
- Sprint 1: dead-code-städning + topbar-bump (5.4.3)
|
|
12
|
+
- Sprint 2: component-token-konsolidering, ADR 0024-backfill, light-theme-removal (5.5.0)
|
|
13
|
+
- Sprint 3: base.css-extraktion till paketets base/interactions.css (5.6.0)
|
|
13
14
|
|
|
14
15
|
## Vad detta är
|
|
15
16
|
Gemensamt designsystem på npm (`@klodd/ds@5.x`). Tre-lagers tokens,
|
|
@@ -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
|
-
|
|
13
|
-
pull-to-refresh, hero-glow) ligger i
|
|
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:
|
|
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(--
|
|
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". */
|
package/css/components/hero.css
CHANGED
|
@@ -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-
|
|
66
|
-
|
|
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
package/js/sheet.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*--------------------------------------------------------------
|
|
2
|
+
sheet.js - bottom-sheet open/close for <dialog class="sheet">.
|
|
3
|
+
|
|
4
|
+
Native <dialog> skoter ESC, focus-trap och inert-bakgrund. Denna
|
|
5
|
+
modul lagger till:
|
|
6
|
+
- openSheet(id) / closeSheet(dlg), exponerade som globals
|
|
7
|
+
window.__sheetOpen / window.__sheetClose
|
|
8
|
+
- click-delegation: [data-sheet]-trigger oppnar sheet, klick pa
|
|
9
|
+
backdrop eller [data-sheet-close] stanger
|
|
10
|
+
- drag-state-reset: nollar inline transform/transition som
|
|
11
|
+
sheet-drag.js kan lamna kvar om en sheet stangs mitt i en drag
|
|
12
|
+
|
|
13
|
+
Laddas fore app-lokal JS som anropar window.__sheetOpen.
|
|
14
|
+
--------------------------------------------------------------*/
|
|
15
|
+
(function () {
|
|
16
|
+
// Defensiv state-reset mot drag-physics-laeckage. sheet-drag.js
|
|
17
|
+
// satter inline style.transform + style.transition under drag och
|
|
18
|
+
// cleanar via transitionend-event. Om dialogen stangs innan
|
|
19
|
+
// transitionen avslutas (ESC, backdrop-klick, programmatic close
|
|
20
|
+
// eller form-submit mid-animation) eldas transitionend ALDRIG och
|
|
21
|
+
// inline-style laecker till nasta showModal() - sheet hamnar dar
|
|
22
|
+
// drag avbrots (ofta utanfor viewport eller halvvags upp). Reset i
|
|
23
|
+
// open + close + close-event-listener ger clean state oavsett hur
|
|
24
|
+
// dialogen stangts.
|
|
25
|
+
function resetDragState(dlg) {
|
|
26
|
+
dlg.style.transform = '';
|
|
27
|
+
dlg.style.transition = '';
|
|
28
|
+
}
|
|
29
|
+
function openSheet(id) {
|
|
30
|
+
const dlg = document.querySelector('dialog.sheet[data-sheet-id="' + id + '"]');
|
|
31
|
+
if (!dlg) return null;
|
|
32
|
+
resetDragState(dlg);
|
|
33
|
+
if (typeof dlg.showModal === 'function' && !dlg.open) {
|
|
34
|
+
dlg.showModal();
|
|
35
|
+
} else {
|
|
36
|
+
// Fallback for browsers utan <dialog>.
|
|
37
|
+
dlg.setAttribute('open', '');
|
|
38
|
+
}
|
|
39
|
+
// Auto-focus + select-all pa input markerad med
|
|
40
|
+
// data-sheet-autofocus. setTimeout 50ms sa showModal-animationen
|
|
41
|
+
// hunnit borja innan focus-grabben (annars hoppar caret).
|
|
42
|
+
const focusEl = dlg.querySelector('[data-sheet-autofocus]');
|
|
43
|
+
if (focusEl) {
|
|
44
|
+
setTimeout(function () {
|
|
45
|
+
try {
|
|
46
|
+
focusEl.focus({ preventScroll: true });
|
|
47
|
+
if (typeof focusEl.select === 'function') focusEl.select();
|
|
48
|
+
} catch (_) { /* ignore */ }
|
|
49
|
+
}, 50);
|
|
50
|
+
}
|
|
51
|
+
return dlg;
|
|
52
|
+
}
|
|
53
|
+
function closeSheet(dlg) {
|
|
54
|
+
if (!dlg) return;
|
|
55
|
+
resetDragState(dlg);
|
|
56
|
+
if (typeof dlg.close === 'function' && dlg.open) {
|
|
57
|
+
dlg.close();
|
|
58
|
+
} else {
|
|
59
|
+
dlg.removeAttribute('open');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Exponera for andra moduler (sheet-drag, optimistic-UI).
|
|
63
|
+
window.__sheetOpen = openSheet;
|
|
64
|
+
window.__sheetClose = closeSheet;
|
|
65
|
+
|
|
66
|
+
// ESC-stangning + native dialog.close() gar inte via closeSheet
|
|
67
|
+
// ovan. Lyssna pa close-event i capture-phase (close-event har
|
|
68
|
+
// bubbles: false per spec, sa capture ar enda sattet att fanga det
|
|
69
|
+
// med delegation pa document-niva). Reset drag-geometri sa nasta
|
|
70
|
+
// open startar med clean inline-style.
|
|
71
|
+
document.addEventListener('close', function (e) {
|
|
72
|
+
if (e.target instanceof HTMLDialogElement && e.target.classList.contains('sheet')) {
|
|
73
|
+
resetDragState(e.target);
|
|
74
|
+
}
|
|
75
|
+
}, true);
|
|
76
|
+
|
|
77
|
+
document.addEventListener('click', function (e) {
|
|
78
|
+
const trigger = e.target.closest('[data-sheet]');
|
|
79
|
+
if (trigger) {
|
|
80
|
+
openSheet(trigger.dataset.sheet);
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Backdrop-click: e.target ar dialog-elementet sjalvt (klick
|
|
85
|
+
// utanfor content-omradet, pa backdrop som <dialog> renderar).
|
|
86
|
+
if (e.target.tagName === 'DIALOG' && e.target.classList.contains('sheet')) {
|
|
87
|
+
closeSheet(e.target);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
// Avbryt-knapp inne i sheet.
|
|
91
|
+
if (e.target.closest('[data-sheet-close]')) {
|
|
92
|
+
const dlg = e.target.closest('dialog.sheet');
|
|
93
|
+
closeSheet(dlg);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
})();
|
package/js/toast.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/*--------------------------------------------------------------
|
|
2
|
+
toast.js - transient feedback-meddelanden.
|
|
3
|
+
|
|
4
|
+
window.showToast(message, type, duration)
|
|
5
|
+
type 'info' (default) | 'success' | 'error' | 'warning'
|
|
6
|
+
duration ms innan auto-removal (default 3000)
|
|
7
|
+
|
|
8
|
+
Skapar ett .toast-element och stackar det i #toast-region. Kraver:
|
|
9
|
+
- ett element med id="toast-region" i DOM (mountpoint)
|
|
10
|
+
- .toast / .toast--out / .toast--{success,error,warning} CSS
|
|
11
|
+
(paketets components/feedback.css + .toast-region/.toast--out
|
|
12
|
+
i app-doman-CSS)
|
|
13
|
+
|
|
14
|
+
Laddas fore app-lokal JS som anropar window.showToast.
|
|
15
|
+
--------------------------------------------------------------*/
|
|
16
|
+
(function () {
|
|
17
|
+
window.showToast = function (message, type, duration) {
|
|
18
|
+
type = type || 'info';
|
|
19
|
+
duration = duration || 3000;
|
|
20
|
+
const region = document.getElementById('toast-region');
|
|
21
|
+
if (!region) return;
|
|
22
|
+
const el = document.createElement('div');
|
|
23
|
+
el.className = 'toast' + (type !== 'info' ? ' toast--' + type : '');
|
|
24
|
+
el.textContent = message;
|
|
25
|
+
region.appendChild(el);
|
|
26
|
+
setTimeout(function () {
|
|
27
|
+
el.classList.add('toast--out');
|
|
28
|
+
el.addEventListener('animationend', function () { el.remove(); }, { once: true });
|
|
29
|
+
}, duration);
|
|
30
|
+
};
|
|
31
|
+
})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@klodd/ds",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.7.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
|
|
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
|
|
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
|
|
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
|
|
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
|