@aiaiai-pt/design-system 0.3.3 → 0.3.5

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,255 @@
1
+ <!--
2
+ @component FilterBar
3
+
4
+ Horizontal bar of filter chips with active-filter state and clear-all action.
5
+ Accepts a declarative config of filters or renders children directly.
6
+ Consumes --filter-chip-* and --filter-bar-* tokens from components.css.
7
+
8
+ @example Declarative (common case)
9
+ <FilterBar
10
+ filters={[
11
+ { key: 'status', label: 'Status', type: 'toggle', options: [
12
+ { value: 'active', label: 'Active' },
13
+ { value: 'draft', label: 'Draft' },
14
+ { value: 'error', label: 'Error' },
15
+ ]},
16
+ ]}
17
+ bind:value={activeFilters}
18
+ onchange={handleFilterChange}
19
+ />
20
+
21
+ @example Composable (custom rendering)
22
+ <FilterBar bind:value={activeFilters} onchange={handleFilterChange}>
23
+ {#snippet children(toggle, isActive)}
24
+ <FilterBar.Chip active={isActive('status', 'active')} onclick={() => toggle('status', 'active')}>
25
+ Active
26
+ </FilterBar.Chip>
27
+ {/snippet}
28
+ </FilterBar>
29
+ -->
30
+ <script module>
31
+ /**
32
+ * @typedef {{ value: string, label: string, icon?: import('svelte').Component }} FilterOption
33
+ * @typedef {{ key: string, label: string, type: 'toggle' | 'select' | 'multi-select', options?: FilterOption[], defaultValue?: any }} FilterDef
34
+ */
35
+ </script>
36
+
37
+ <script>
38
+ let {
39
+ /** @type {FilterDef[]} Declarative filter definitions */
40
+ filters = [],
41
+ /** @type {Record<string, any>} Active filter values (bindable) */
42
+ value = $bindable({}),
43
+ /** @type {((value: Record<string, any>) => void) | undefined} Fires when any filter changes */
44
+ onchange = undefined,
45
+ /** @type {(() => void) | undefined} Fires when "Clear all" is clicked */
46
+ onclear = undefined,
47
+ /** @type {string} */
48
+ class: className = '',
49
+ /** @type {import('svelte').Snippet | undefined} */
50
+ children = undefined,
51
+ ...rest
52
+ } = $props();
53
+
54
+ const activeCount = $derived(
55
+ Object.values(value).filter(v => {
56
+ if (Array.isArray(v)) return v.length > 0;
57
+ return v !== undefined && v !== null && v !== '';
58
+ }).length
59
+ );
60
+
61
+ const showClearAll = $derived(activeCount >= 2);
62
+
63
+ /**
64
+ * Toggle a filter value
65
+ * @param {string} key
66
+ * @param {string} optionValue
67
+ */
68
+ function toggle(key, optionValue) {
69
+ const filter = filters.find(f => f.key === key);
70
+ if (!filter) return;
71
+
72
+ if (filter.type === 'multi-select') {
73
+ const current = Array.isArray(value[key]) ? [...value[key]] : [];
74
+ const idx = current.indexOf(optionValue);
75
+ if (idx >= 0) {
76
+ current.splice(idx, 1);
77
+ } else {
78
+ current.push(optionValue);
79
+ }
80
+ value = { ...value, [key]: current.length > 0 ? current : undefined };
81
+ } else {
82
+ // toggle: same value = deactivate
83
+ value = {
84
+ ...value,
85
+ [key]: value[key] === optionValue ? undefined : optionValue,
86
+ };
87
+ }
88
+
89
+ onchange?.(value);
90
+ }
91
+
92
+ /**
93
+ * Check if a filter option is active
94
+ * @param {string} key
95
+ * @param {string} optionValue
96
+ * @returns {boolean}
97
+ */
98
+ function isActive(key, optionValue) {
99
+ const v = value[key];
100
+ if (Array.isArray(v)) return v.includes(optionValue);
101
+ return v === optionValue;
102
+ }
103
+
104
+ function clearAll() {
105
+ value = {};
106
+ onchange?.({});
107
+ onclear?.();
108
+ }
109
+
110
+ </script>
111
+
112
+ <div
113
+ class="filter-bar {className}"
114
+ role="group"
115
+ aria-label="Filters"
116
+ {...rest}
117
+ >
118
+ {#if children}
119
+ {@render children(toggle, isActive)}
120
+ {:else}
121
+ <div class="filter-bar-chips">
122
+ {#each filters as filter}
123
+ {#if filter.options}
124
+ {#each filter.options as option}
125
+ {@const active = isActive(filter.key, option.value)}
126
+ <button
127
+ class="filter-chip"
128
+ class:filter-chip-active={active}
129
+ onclick={() => toggle(filter.key, option.value)}
130
+ aria-pressed={active}
131
+ type="button"
132
+ >
133
+ {option.label}
134
+ </button>
135
+ {/each}
136
+ {/if}
137
+ {/each}
138
+ </div>
139
+
140
+ {#if showClearAll}
141
+ <button
142
+ class="filter-bar-clear"
143
+ onclick={clearAll}
144
+ type="button"
145
+ >
146
+ Clear all
147
+ </button>
148
+ {/if}
149
+ {/if}
150
+ </div>
151
+
152
+ <style>
153
+ .filter-bar {
154
+ display: flex;
155
+ align-items: center;
156
+ gap: var(--filter-bar-gap);
157
+ flex-wrap: wrap;
158
+ }
159
+
160
+ .filter-bar-chips {
161
+ display: flex;
162
+ align-items: center;
163
+ gap: var(--filter-chip-gap);
164
+ flex-wrap: wrap;
165
+ }
166
+
167
+ /* ─── Chip ─── */
168
+ .filter-chip {
169
+ all: unset;
170
+ box-sizing: border-box;
171
+ display: inline-flex;
172
+ align-items: center;
173
+ gap: var(--space-xs);
174
+ height: var(--filter-chip-height);
175
+ padding: var(--filter-chip-padding);
176
+ font-family: var(--filter-chip-font);
177
+ font-size: var(--filter-chip-size);
178
+ letter-spacing: var(--filter-chip-tracking);
179
+ color: var(--filter-chip-color);
180
+ background: var(--filter-chip-bg);
181
+ border: var(--filter-chip-border);
182
+ border-radius: var(--filter-chip-radius);
183
+ cursor: pointer;
184
+ white-space: nowrap;
185
+ transition: all var(--filter-chip-transition);
186
+ user-select: none;
187
+ }
188
+
189
+ .filter-chip:hover {
190
+ background: var(--filter-chip-bg-hover);
191
+ }
192
+
193
+ .filter-chip:focus-visible {
194
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
195
+ outline-offset: var(--focus-ring-offset);
196
+ }
197
+
198
+ .filter-chip-active {
199
+ background: var(--filter-chip-bg-active);
200
+ color: var(--filter-chip-color-active);
201
+ border: var(--filter-chip-border-active);
202
+ }
203
+
204
+ .filter-chip-active:hover {
205
+ background: var(--filter-chip-bg-active);
206
+ }
207
+
208
+ /* ─── Clear all ─── */
209
+ .filter-bar-clear {
210
+ all: unset;
211
+ box-sizing: border-box;
212
+ font-family: var(--filter-chip-font);
213
+ font-size: var(--filter-chip-size);
214
+ letter-spacing: var(--filter-chip-tracking);
215
+ color: var(--filter-bar-clear-color);
216
+ cursor: pointer;
217
+ white-space: nowrap;
218
+ transition: color var(--filter-chip-transition);
219
+ }
220
+
221
+ .filter-bar-clear:hover {
222
+ color: var(--filter-bar-clear-color-hover);
223
+ }
224
+
225
+ .filter-bar-clear:focus-visible {
226
+ outline: var(--focus-ring-width) solid var(--focus-ring-color);
227
+ outline-offset: var(--focus-ring-offset);
228
+ border-radius: var(--radius-sm);
229
+ }
230
+
231
+ /* ─── Mobile: horizontal scroll ─── */
232
+ @media (max-width: 640px) {
233
+ .filter-bar {
234
+ flex-wrap: nowrap;
235
+ overflow-x: auto;
236
+ -webkit-overflow-scrolling: touch;
237
+ scrollbar-width: none;
238
+ }
239
+
240
+ .filter-bar::-webkit-scrollbar {
241
+ display: none;
242
+ }
243
+
244
+ .filter-bar-chips {
245
+ flex-wrap: nowrap;
246
+ }
247
+ }
248
+
249
+ @media (prefers-reduced-motion: reduce) {
250
+ .filter-chip,
251
+ .filter-bar-clear {
252
+ transition: none;
253
+ }
254
+ }
255
+ </style>
@@ -205,8 +205,8 @@
205
205
  top: 50%;
206
206
  transform: translateY(-50%);
207
207
  display: flex;
208
- width: 16px;
209
- height: 16px;
208
+ width: var(--icon-size-sm);
209
+ height: var(--icon-size-sm);
210
210
  color: var(--input-placeholder);
211
211
  pointer-events: none;
212
212
  }
@@ -217,6 +217,6 @@
217
217
  }
218
218
 
219
219
  .input-with-icon {
220
- padding-left: calc(var(--input-md-padding-x) + 16px + var(--space-xs));
220
+ padding-left: calc(var(--input-md-padding-x) + var(--icon-size-sm) + var(--space-xs));
221
221
  }
222
222
  </style>
@@ -0,0 +1,410 @@
1
+ <!--
2
+ @component LogViewer
3
+
4
+ Structured log display for viewing timestamped, level-coded entries.
5
+ Optimized for scanning many entries at compact density.
6
+ Composes Badge, Input, Checkbox, Toggle, Alert, EmptyState, and Skeleton.
7
+ Consumes --log-viewer-* tokens from components.css.
8
+
9
+ @example Basic
10
+ <LogViewer entries={logEntries} />
11
+
12
+ @example With all states
13
+ <LogViewer
14
+ entries={logEntries}
15
+ available={true}
16
+ truncated={false}
17
+ fallbackUrl="https://temporal.example.com/..."
18
+ loading={false}
19
+ />
20
+
21
+ @example Unavailable (show fallback)
22
+ <LogViewer available={false} fallbackUrl="https://temporal.example.com/..." />
23
+ -->
24
+ <script>
25
+ import Badge from './Badge.svelte';
26
+ import Input from './Input.svelte';
27
+ import Checkbox from './Checkbox.svelte';
28
+ import Toggle from './Toggle.svelte';
29
+ import Alert from './Alert.svelte';
30
+ import EmptyState from './EmptyState.svelte';
31
+ import Skeleton from './Skeleton.svelte';
32
+
33
+ /**
34
+ * @typedef {{ timestamp: string, level: string, message: string, context?: Record<string, unknown> }} LogEntry
35
+ */
36
+
37
+ let {
38
+ /** @type {LogEntry[]} */
39
+ entries = [],
40
+ /** @type {boolean} */
41
+ available = true,
42
+ /** @type {boolean} */
43
+ truncated = false,
44
+ /** @type {string | undefined} */
45
+ fallbackUrl = undefined,
46
+ /** @type {boolean} */
47
+ loading = false,
48
+ /** @type {string} */
49
+ emptyHeading = 'No log entries',
50
+ /** @type {string} */
51
+ emptyBody = 'Logs will appear here once the run produces output.',
52
+ /** @type {string} */
53
+ unavailableHeading = 'Logs unavailable',
54
+ /** @type {string} */
55
+ unavailableBody = 'The orchestrator is unreachable. View logs in the external UI.',
56
+ /** @type {string} */
57
+ class: className = '',
58
+ /** @type {import('svelte').Snippet | undefined} */
59
+ emptyIcon = undefined,
60
+ /** @type {import('svelte').Snippet | undefined} */
61
+ unavailableIcon = undefined,
62
+ ...rest
63
+ } = $props();
64
+
65
+ /* ─── Local state ─── */
66
+ let search = $state('');
67
+ let showInfo = $state(true);
68
+ let showWarning = $state(true);
69
+ let showError = $state(true);
70
+ let relativeTime = $state(true);
71
+
72
+ /* ─── Level normalization ─── */
73
+ /** @param {string} level */
74
+ function normalizeLevel(level) {
75
+ const upper = level.toUpperCase();
76
+ if (upper === 'WARN' || upper === 'WARNING') return 'WARNING';
77
+ if (upper === 'ERR' || upper === 'ERROR') return 'ERROR';
78
+ return 'INFO';
79
+ }
80
+
81
+ /* ─── Derived data ─── */
82
+ const normalizedEntries = $derived(
83
+ entries.map((e) => ({ ...e, _level: normalizeLevel(e.level) }))
84
+ );
85
+
86
+ const counts = $derived({
87
+ INFO: normalizedEntries.filter((e) => e._level === 'INFO').length,
88
+ WARNING: normalizedEntries.filter((e) => e._level === 'WARNING').length,
89
+ ERROR: normalizedEntries.filter((e) => e._level === 'ERROR').length,
90
+ });
91
+
92
+ const searchLower = $derived(search.toLowerCase());
93
+
94
+ const filteredEntries = $derived(
95
+ normalizedEntries.filter((e) => {
96
+ // Level filter
97
+ if (e._level === 'INFO' && !showInfo) return false;
98
+ if (e._level === 'WARNING' && !showWarning) return false;
99
+ if (e._level === 'ERROR' && !showError) return false;
100
+ // Search filter
101
+ if (searchLower && !e.message.toLowerCase().includes(searchLower)) return false;
102
+ return true;
103
+ })
104
+ );
105
+
106
+ const hasActiveFilters = $derived(
107
+ !showInfo || !showWarning || !showError || search.length > 0
108
+ );
109
+
110
+ /* ─── Level → Badge variant mapping ─── */
111
+ /** @type {Record<string, 'neutral' | 'warning' | 'error'>} */
112
+ const levelVariant = {
113
+ INFO: 'neutral',
114
+ WARNING: 'warning',
115
+ ERROR: 'error',
116
+ };
117
+
118
+ /* ─── Timestamp formatting ─── */
119
+ /** @param {string} timestamp */
120
+ function formatTimestamp(timestamp) {
121
+ if (relativeTime) return formatRelative(timestamp);
122
+ return formatAbsolute(timestamp);
123
+ }
124
+
125
+ /** @param {string} timestamp */
126
+ function formatAbsolute(timestamp) {
127
+ try {
128
+ const d = new Date(timestamp);
129
+ const h = String(d.getHours()).padStart(2, '0');
130
+ const m = String(d.getMinutes()).padStart(2, '0');
131
+ const s = String(d.getSeconds()).padStart(2, '0');
132
+ const ms = String(d.getMilliseconds()).padStart(3, '0');
133
+ return `${h}:${m}:${s}.${ms}`;
134
+ } catch {
135
+ return timestamp;
136
+ }
137
+ }
138
+
139
+ /** @param {string} timestamp */
140
+ function formatRelative(timestamp) {
141
+ try {
142
+ const now = Date.now();
143
+ const then = new Date(timestamp).getTime();
144
+ const diff = now - then;
145
+ if (diff < 1000) return 'just now';
146
+ if (diff < 60_000) return `${Math.floor(diff / 1000)}s ago`;
147
+ if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m ago`;
148
+ if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h ago`;
149
+ return `${Math.floor(diff / 86_400_000)}d ago`;
150
+ } catch {
151
+ return timestamp;
152
+ }
153
+ }
154
+
155
+
156
+ </script>
157
+
158
+ <div
159
+ class="log-viewer {className}"
160
+ class:log-viewer--loading={loading}
161
+ {...rest}
162
+ >
163
+ {#if loading}
164
+ <!-- Loading state -->
165
+ <div class="log-viewer-loading">
166
+ <Skeleton width="100%" height="32px" />
167
+ <Skeleton width="100%" height="14px" />
168
+ <Skeleton width="90%" height="14px" />
169
+ <Skeleton width="95%" height="14px" />
170
+ <Skeleton width="85%" height="14px" />
171
+ <Skeleton width="92%" height="14px" />
172
+ </div>
173
+
174
+ {:else if !available}
175
+ <!-- Unavailable state -->
176
+ <EmptyState
177
+ heading={unavailableHeading}
178
+ body={unavailableBody}
179
+ actionLabel={fallbackUrl ? 'OPEN EXTERNAL LOGS' : undefined}
180
+ actionVariant="secondary"
181
+ onaction={fallbackUrl ? () => window.open(fallbackUrl, '_blank') : undefined}
182
+ icon={unavailableIcon}
183
+ />
184
+
185
+ {:else if entries.length === 0}
186
+ <!-- Empty state -->
187
+ <EmptyState
188
+ heading={emptyHeading}
189
+ body={emptyBody}
190
+ icon={emptyIcon}
191
+ />
192
+
193
+ {:else}
194
+ <!-- Toolbar -->
195
+ <div class="log-viewer-toolbar">
196
+ <div class="log-viewer-toolbar-row">
197
+ <div class="log-viewer-search">
198
+ <Input
199
+ size="sm"
200
+ placeholder="Filter logs..."
201
+ bind:value={search}
202
+ >
203
+ {#snippet leadingIcon()}
204
+ <svg viewBox="0 0 256 256" fill="none">
205
+ <path d="M229.66 218.34l-50.07-50.07a88.11 88.11 0 1 0-11.31 11.31l50.07 50.07a8 8 0 0 0 11.32-11.31ZM40 112a72 72 0 1 1 72 72 72.08 72.08 0 0 1-72-72Z" fill="currentColor"/>
206
+ </svg>
207
+ {/snippet}
208
+ </Input>
209
+ </div>
210
+
211
+ <span class="log-viewer-toolbar-separator" aria-hidden="true"></span>
212
+
213
+ <div class="log-viewer-filters">
214
+ <Checkbox label="Info ({counts.INFO})" bind:checked={showInfo} />
215
+ <Checkbox label="Warn ({counts.WARNING})" bind:checked={showWarning} />
216
+ <Checkbox label="Error ({counts.ERROR})" bind:checked={showError} />
217
+ </div>
218
+
219
+ <span class="log-viewer-toolbar-separator" aria-hidden="true"></span>
220
+
221
+ <div class="log-viewer-toggle">
222
+ <Toggle label="Relative" bind:checked={relativeTime} />
223
+ </div>
224
+ </div>
225
+ </div>
226
+
227
+ <!-- Truncation warning -->
228
+ {#if truncated}
229
+ <div class="log-viewer-alert">
230
+ <Alert variant="warning">
231
+ Log output was truncated. Some entries may be missing.
232
+ </Alert>
233
+ </div>
234
+ {/if}
235
+
236
+ <!-- Log entries -->
237
+ <div
238
+ class="log-viewer-entries"
239
+ role="log"
240
+ aria-live="polite"
241
+ aria-label="Log entries"
242
+ >
243
+ {#if filteredEntries.length === 0}
244
+ <div class="log-viewer-no-match">
245
+ No entries match the current filters.
246
+ </div>
247
+ {:else}
248
+ {#each filteredEntries as entry, i (entry.timestamp + i)}
249
+ <div class="log-viewer-entry log-viewer-entry--{entry._level.toLowerCase()}">
250
+ <span class="log-viewer-timestamp" title={entry.timestamp}>
251
+ {formatTimestamp(entry.timestamp)}
252
+ </span>
253
+ <Badge variant={levelVariant[entry._level]}>{entry._level}</Badge>
254
+ <span class="log-viewer-message">{entry.message}</span>
255
+ </div>
256
+ {/each}
257
+ {/if}
258
+ </div>
259
+
260
+ <!-- Status bar -->
261
+ <div class="log-viewer-status">
262
+ <span class="log-viewer-status-text">
263
+ {filteredEntries.length}{hasActiveFilters ? ` of ${entries.length}` : ''} entries
264
+ </span>
265
+ </div>
266
+ {/if}
267
+ </div>
268
+
269
+ <style>
270
+ .log-viewer {
271
+ display: flex;
272
+ flex-direction: column;
273
+ background: var(--log-viewer-bg);
274
+ border: var(--log-viewer-border);
275
+ border-radius: var(--log-viewer-radius);
276
+ overflow: hidden;
277
+ }
278
+
279
+ /* ─── Loading ─── */
280
+ .log-viewer-loading {
281
+ display: flex;
282
+ flex-direction: column;
283
+ gap: var(--space-sm);
284
+ padding: var(--space-md);
285
+ }
286
+
287
+ /* ─── Toolbar ─── */
288
+ .log-viewer-toolbar {
289
+ padding: var(--log-viewer-toolbar-padding);
290
+ border-bottom: var(--log-viewer-toolbar-border);
291
+ background: var(--color-surface);
292
+ }
293
+
294
+ .log-viewer-toolbar-row {
295
+ display: flex;
296
+ align-items: center;
297
+ gap: var(--log-viewer-toolbar-gap);
298
+ flex-wrap: wrap;
299
+ }
300
+
301
+ .log-viewer-search {
302
+ flex: 1;
303
+ min-width: 160px;
304
+ }
305
+
306
+ .log-viewer-filters {
307
+ display: flex;
308
+ align-items: center;
309
+ gap: var(--space-md);
310
+ flex-shrink: 0;
311
+ }
312
+
313
+ .log-viewer-toggle {
314
+ flex-shrink: 0;
315
+ }
316
+
317
+ .log-viewer-toolbar-separator {
318
+ width: var(--border-width-default);
319
+ height: 20px;
320
+ background: var(--color-border);
321
+ flex-shrink: 0;
322
+ }
323
+
324
+ /* ─── Truncation alert ─── */
325
+ .log-viewer-alert {
326
+ padding: 0 var(--space-md) var(--space-sm);
327
+ }
328
+
329
+ /* ─── Entries ─── */
330
+ .log-viewer-entries {
331
+ overflow-y: auto;
332
+ max-height: var(--log-viewer-max-height);
333
+ scroll-behavior: smooth;
334
+ }
335
+
336
+ .log-viewer-entry {
337
+ display: flex;
338
+ align-items: baseline;
339
+ gap: var(--space-md);
340
+ padding: var(--log-viewer-entry-padding-y) var(--log-viewer-entry-padding-x);
341
+ border-bottom: var(--log-viewer-entry-border);
342
+ font-family: var(--log-viewer-entry-font);
343
+ font-size: var(--log-viewer-entry-size);
344
+ line-height: var(--log-viewer-entry-leading);
345
+ transition: background var(--duration-instant) var(--easing-default);
346
+ }
347
+
348
+ .log-viewer-entry:last-child {
349
+ border-bottom: none;
350
+ }
351
+
352
+ .log-viewer-entry:hover {
353
+ background: var(--log-viewer-entry-hover-bg);
354
+ }
355
+
356
+ /* Level accent — left border stripe */
357
+ .log-viewer-entry--error {
358
+ border-left: var(--accent-stripe-width) solid var(--log-viewer-level-error-color);
359
+ }
360
+
361
+ .log-viewer-entry--warning {
362
+ border-left: var(--accent-stripe-width) solid var(--log-viewer-level-warning-color);
363
+ }
364
+
365
+ .log-viewer-entry--info {
366
+ border-left: var(--accent-stripe-width) solid transparent;
367
+ }
368
+
369
+ .log-viewer-timestamp {
370
+ flex-shrink: 0;
371
+ width: var(--log-viewer-timestamp-width);
372
+ color: var(--log-viewer-timestamp-color);
373
+ font-variant-numeric: tabular-nums;
374
+ white-space: nowrap;
375
+ overflow: hidden;
376
+ text-overflow: ellipsis;
377
+ }
378
+
379
+ .log-viewer-message {
380
+ flex: 1;
381
+ min-width: 0;
382
+ color: var(--log-viewer-entry-color);
383
+ white-space: pre-wrap;
384
+ word-break: break-word;
385
+ }
386
+
387
+ .log-viewer-no-match {
388
+ padding: var(--space-lg);
389
+ text-align: center;
390
+ font-family: var(--log-viewer-status-font);
391
+ font-size: var(--log-viewer-status-size);
392
+ color: var(--log-viewer-status-color);
393
+ }
394
+
395
+ /* ─── Status bar ─── */
396
+ .log-viewer-status {
397
+ display: flex;
398
+ align-items: center;
399
+ justify-content: space-between;
400
+ padding: var(--log-viewer-status-padding);
401
+ border-top: var(--log-viewer-toolbar-border);
402
+ background: var(--color-surface);
403
+ }
404
+
405
+ .log-viewer-status-text {
406
+ font-family: var(--log-viewer-status-font);
407
+ font-size: var(--log-viewer-status-size);
408
+ color: var(--log-viewer-status-color);
409
+ }
410
+ </style>
@@ -223,8 +223,8 @@
223
223
  }
224
224
 
225
225
  .modal-close svg {
226
- width: 16px;
227
- height: 16px;
226
+ width: var(--icon-size-sm);
227
+ height: var(--icon-size-sm);
228
228
  }
229
229
 
230
230
  .modal-body {