lutra 0.1.69 → 0.1.70

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,111 @@
1
+ <script lang="ts">
2
+ import type { DataListItem } from "./DataListTypes.js";
3
+
4
+ /**
5
+ * @description
6
+ * Renders an array of label/value pairs as a semantic description list (`<dl>`).
7
+ * Supports horizontal (side-by-side), vertical (stacked), or auto (container-query
8
+ * responsive) layouts. Values can be plain strings, snippets, render functions,
9
+ * or components.
10
+ *
11
+ * @cssprop --data-list-gap -- Gap between items in the list.
12
+ *
13
+ * @example
14
+ * <DataList items={[
15
+ * { label: 'Name', value: 'Alice' },
16
+ * { label: 'Email', value: 'alice@example.com' },
17
+ * ]} />
18
+ */
19
+ let {
20
+ items,
21
+ direction = 'auto',
22
+ }: {
23
+ /** The data items to display. */
24
+ items: DataListItem[];
25
+ /** Layout direction. 'auto' switches from horizontal to vertical based on container width. */
26
+ direction?: 'horizontal' | 'vertical' | 'auto';
27
+ } = $props();
28
+ </script>
29
+
30
+ <div class="DataList">
31
+ <dl class={direction}>
32
+ {#each items as item}
33
+ <div>
34
+ <dt>{item.label}</dt>
35
+ <dd>
36
+ {#if item.snippet}
37
+ {@render item.snippet()}
38
+ {:else if item.render}
39
+ {@render item.render()}
40
+ {:else if item.component}
41
+ <item.component />
42
+ {:else if item.value}
43
+ {item.value}
44
+ {/if}
45
+ </dd>
46
+ </div>
47
+ {/each}
48
+ </dl>
49
+ </div>
50
+
51
+ <style>
52
+ .DataList {
53
+ container-type: inline-size;
54
+ }
55
+
56
+ dl {
57
+ margin: 0;
58
+ padding: 0;
59
+ }
60
+
61
+ dt {
62
+ color: var(--text-color-p-subtle);
63
+ }
64
+
65
+ dd {
66
+ margin: 0;
67
+ color: var(--text-color-p);
68
+ }
69
+
70
+ /**
71
+ * Horizontal layout: two-column grid with subgrid for aligned labels
72
+ */
73
+ dl.horizontal,
74
+ dl.auto {
75
+ display: grid;
76
+ grid-template-columns: auto 1fr;
77
+ gap: var(--data-list-gap);
78
+ }
79
+
80
+ dl.horizontal > div,
81
+ dl.auto > div {
82
+ display: grid;
83
+ grid-column: 1 / -1;
84
+ grid-template-columns: subgrid;
85
+ align-items: baseline;
86
+ }
87
+
88
+ /**
89
+ * Vertical layout: stacked label over value
90
+ */
91
+ dl.vertical {
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: var(--data-list-gap);
95
+ }
96
+
97
+ /**
98
+ * Auto: switch to vertical when the container is narrow
99
+ */
100
+ @container (max-width: 300px) {
101
+ dl.auto {
102
+ display: flex;
103
+ flex-direction: column;
104
+ }
105
+
106
+ dl.auto > div {
107
+ display: flex;
108
+ flex-direction: column;
109
+ }
110
+ }
111
+ </style>
@@ -0,0 +1,10 @@
1
+ import type { DataListItem } from "./DataListTypes.js";
2
+ type $$ComponentProps = {
3
+ /** The data items to display. */
4
+ items: DataListItem[];
5
+ /** Layout direction. 'auto' switches from horizontal to vertical based on container width. */
6
+ direction?: 'horizontal' | 'vertical' | 'auto';
7
+ };
8
+ declare const DataList: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type DataList = ReturnType<typeof DataList>;
10
+ export default DataList;
@@ -0,0 +1,14 @@
1
+ import type { Component, Snippet } from "svelte";
2
+ import type { RenderFn } from "../types.js";
3
+ export type DataListItem = {
4
+ /** Text label for the item (e.g. "Status", "Email") */
5
+ label: string;
6
+ /** Text value to display */
7
+ value?: string;
8
+ /** Snippet to render as the value */
9
+ snippet?: Snippet;
10
+ /** Render function for the value (works in object literals) */
11
+ render?: RenderFn;
12
+ /** Component to render as the value */
13
+ component?: Component;
14
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,7 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
3
  import { BROWSER } from "esm-env";
4
+ import UIContent from "./UIContent.svelte";
4
5
 
5
6
  /**
6
7
  * @description
@@ -207,7 +208,9 @@
207
208
  class:unstyled
208
209
  style={popoverStyle}
209
210
  >
210
- {@render children()}
211
+ <UIContent>
212
+ {@render children()}
213
+ </UIContent>
211
214
  </div>
212
215
 
213
216
  <style>
@@ -2,6 +2,7 @@ export { default as AspectRatio } from './AspectRatio.svelte';
2
2
  export { default as Avatar } from './Avatar.svelte';
3
3
  export { default as Close } from './Close.svelte';
4
4
  export { default as ContextTip } from './ContextTip.svelte';
5
+ export { default as DataList } from './DataList.svelte';
5
6
  export { default as Dialog } from './Dialog.svelte';
6
7
  export { default as Icon } from './Icon.svelte';
7
8
  export { default as IconButton } from './IconButton.svelte';
@@ -22,6 +23,7 @@ export { default as Toast } from './Toast.svelte';
22
23
  export { default as ToastContainer } from './ToastContainer.svelte';
23
24
  export { default as Tooltip } from './Tooltip.svelte';
24
25
  export { default as UIContent } from './UIContent.svelte';
26
+ export * from './DataListTypes.js';
25
27
  export * from './MenuTypes.js';
26
28
  export * from './ModalTypes.js';
27
29
  export * from './toasts.svelte.js';
@@ -2,6 +2,7 @@ export { default as AspectRatio } from './AspectRatio.svelte';
2
2
  export { default as Avatar } from './Avatar.svelte';
3
3
  export { default as Close } from './Close.svelte';
4
4
  export { default as ContextTip } from './ContextTip.svelte';
5
+ export { default as DataList } from './DataList.svelte';
5
6
  export { default as Dialog } from './Dialog.svelte';
6
7
  export { default as Icon } from './Icon.svelte';
7
8
  export { default as IconButton } from './IconButton.svelte';
@@ -23,6 +24,7 @@ export { default as ToastContainer } from './ToastContainer.svelte';
23
24
  export { default as Tooltip } from './Tooltip.svelte';
24
25
  export { default as UIContent } from './UIContent.svelte';
25
26
  // Types and APIs
27
+ export * from './DataListTypes.js';
26
28
  export * from './MenuTypes.js';
27
29
  export * from './ModalTypes.js';
28
30
  export * from './toasts.svelte.js';
@@ -196,50 +196,51 @@
196
196
  @property --field-background-interest { syntax: "<color>"; inherits: true; initial-value: #f0f9ff; }
197
197
  @property --field-background-loading { syntax: "<color>"; inherits: true; initial-value: #f0f9ff; }
198
198
 
199
- @property --field-border-color { syntax: "*"; inherits: true; initial-value: #d1d5db; }
200
- @property --field-border-color-active { syntax: "*"; inherits: true; initial-value: #2563eb; }
201
- @property --field-border-color-inactive { syntax: "*"; inherits: true; initial-value: #d1d5db; }
202
- @property --field-border-color-disabled { syntax: "*"; inherits: true; initial-value: #d1d5db; }
203
- @property --field-border-color-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
204
- @property --field-border-color-invalid { syntax: "*"; inherits: true; initial-value: #dc2626; }
205
- @property --field-border-color-valid { syntax: "*"; inherits: true; initial-value: #16a34a; }
206
- @property --field-border-color-success { syntax: "*"; inherits: true; initial-value: #16a34a; }
207
- @property --field-border-color-danger { syntax: "*"; inherits: true; initial-value: #dc2626; }
208
- @property --field-border-color-warn { syntax: "*"; inherits: true; initial-value: #ca8a04; }
209
- @property --field-border-color-info { syntax: "*"; inherits: true; initial-value: #2563eb; }
210
- @property --field-border-color-selected { syntax: "*"; inherits: true; initial-value: #2563eb; }
211
- @property --field-border-color-interest { syntax: "*"; inherits: true; initial-value: #7c3aed; }
212
- @property --field-border-color-loading { syntax: "*"; inherits: true; initial-value: #7c3aed; }
199
+ @property --field-border-color { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
200
+ @property --field-border-color-active { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
201
+ @property --field-border-color-inactive { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
202
+ @property --field-border-color-disabled { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
203
+ @property --field-border-color-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
204
+ @property --field-border-color-invalid { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
205
+ @property --field-border-color-valid { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
206
+ @property --field-border-color-success { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
207
+ @property --field-border-color-danger { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
208
+ @property --field-border-color-warn { syntax: "<color>"; inherits: true; initial-value: #ca8a04; }
209
+ @property --field-border-color-info { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
210
+ @property --field-border-color-selected { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
211
+ @property --field-border-color-interest { syntax: "<color>"; inherits: true; initial-value: #7c3aed; }
212
+ @property --field-border-color-loading { syntax: "<color>"; inherits: true; initial-value: #7c3aed; }
213
213
 
214
214
  @property --field-border-size { syntax: "<length>"; inherits: true; initial-value: 1px; }
215
215
  @property --field-border-style { syntax: "solid | dashed | dotted | double | groove | ridge | inset | outset"; inherits: true; initial-value: solid; }
216
216
  @property --field-border-radius { syntax: "<length>"; inherits: true; initial-value: 8px; }
217
217
 
218
- @property --field-color { syntax: "*"; inherits: true; initial-value: #1a1a1a; }
219
- @property --field-color-active { syntax: "*"; inherits: true; initial-value: #2563eb; }
220
- @property --field-color-inactive { syntax: "*"; inherits: true; initial-value: #666666; }
221
- @property --field-color-disabled { syntax: "*"; inherits: true; initial-value: #cccccc; }
222
- @property --field-color-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
223
- @property --field-color-invalid { syntax: "*"; inherits: true; initial-value: #dc2626; }
224
- @property --field-color-valid { syntax: "*"; inherits: true; initial-value: #16a34a; }
225
- @property --field-color-success { syntax: "*"; inherits: true; initial-value: #16a34a; }
226
- @property --field-color-danger { syntax: "*"; inherits: true; initial-value: #dc2626; }
227
- @property --field-color-warn { syntax: "*"; inherits: true; initial-value: #ca8a04; }
228
- @property --field-color-info { syntax: "*"; inherits: true; initial-value: #2563eb; }
229
- @property --field-color-selected { syntax: "*"; inherits: true; initial-value: #2563eb; }
230
- @property --field-color-interest { syntax: "*"; inherits: true; initial-value: #7c3aed; }
231
- @property --field-color-loading { syntax: "*"; inherits: true; initial-value: #7c3aed; }
232
-
233
- @property --field-label-color { syntax: "*"; inherits: true; initial-value: #111111; }
218
+ @property --field-color { syntax: "<color>"; inherits: true; initial-value: #1a1a1a; }
219
+ @property --field-color-active { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
220
+ @property --field-color-inactive { syntax: "<color>"; inherits: true; initial-value: #666666; }
221
+ @property --field-color-disabled { syntax: "<color>"; inherits: true; initial-value: #cccccc; }
222
+ @property --field-color-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
223
+ @property --field-color-invalid { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
224
+ @property --field-color-valid { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
225
+ @property --field-color-success { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
226
+ @property --field-color-danger { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
227
+ @property --field-color-warn { syntax: "<color>"; inherits: true; initial-value: #ca8a04; }
228
+ @property --field-color-info { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
229
+ @property --field-color-selected { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
230
+ @property --field-color-interest { syntax: "<color>"; inherits: true; initial-value: #7c3aed; }
231
+ @property --field-color-loading { syntax: "<color>"; inherits: true; initial-value: #7c3aed; }
232
+
233
+ @property --field-label-color { syntax: "<color>"; inherits: true; initial-value: #111111; }
234
234
  @property --field-label-font-size { syntax: "<length>"; inherits: true; initial-value: 16px; }
235
235
  @property --field-label-font-weight { syntax: "<number>"; inherits: true; initial-value: 600; }
236
236
 
237
- @property --field-placeholder-color { syntax: "*"; inherits: true; initial-value: #999999; }
237
+ @property --field-placeholder-color { syntax: "<color>"; inherits: true; initial-value: #999999; }
238
238
  @property --field-padding-inline { syntax: "<length>"; inherits: true; initial-value: 8px; }
239
239
  @property --field-padding-block { syntax: "<length>"; inherits: true; initial-value: 8px; }
240
240
 
241
241
  @property --field-icon-size { syntax: "<length>"; inherits: true; initial-value: 24px; }
242
242
  @property --field-icon-order { syntax: "<number>"; inherits: true; initial-value: 1; }
243
+ @property --field-group-gap { syntax: "<length>"; inherits: true; initial-value: 8px; }
243
244
 
244
245
  /**
245
246
  * Checkbox and Radio
@@ -249,93 +250,107 @@
249
250
  @property --checkbox-border-radius { syntax: "<length>"; inherits: true; initial-value: 4px; }
250
251
  @property --checkbox-background { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
251
252
  @property --checkbox-background-checked { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
252
- @property --checkbox-border-color { syntax: "*"; inherits: true; initial-value: #d1d5db; }
253
- @property --checkbox-border-color-checked { syntax: "*"; inherits: true; initial-value: #2563eb; }
253
+ @property --checkbox-border-color { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
254
+ @property --checkbox-border-color-checked { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
254
255
  @property --checkbox-indicator-color { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
255
256
 
257
+ /**
258
+ * Toggle
259
+ */
260
+
261
+ @property --toggle-width { syntax: "<length>"; inherits: true; initial-value: 44px; }
262
+ @property --toggle-height { syntax: "<length>"; inherits: true; initial-value: 24px; }
263
+ @property --toggle-background { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
264
+ @property --toggle-background-checked { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
265
+ @property --toggle-border-color { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
266
+ @property --toggle-border-color-checked { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
267
+ @property --toggle-thumb-size { syntax: "<length>"; inherits: true; initial-value: 18px; }
268
+ @property --toggle-thumb-color { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
269
+ @property --toggle-thumb-shadow { syntax: "*"; inherits: true; initial-value: 0 1px 3px rgba(0, 0, 0, 0.2); }
270
+
256
271
  /**
257
272
  * Button Component
258
273
  */
259
274
 
260
- @property --button-base-color { syntax: "*"; inherits: true; initial-value: #ffffff; }
261
- @property --button-base-color-hover { syntax: "*"; inherits: true; initial-value: #ffffff; }
275
+ @property --button-base-color { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
276
+ @property --button-base-color-hover { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
262
277
 
263
278
  /* primary */
264
279
 
265
- @property --button-submit-color { syntax: "*"; inherits: true; initial-value: #ffffff; }
266
- @property --button-submit-color-hover { syntax: "*"; inherits: true; initial-value: #ffffff; }
267
- @property --button-submit-color-disabled { syntax: "*"; inherits: true; initial-value: #cccccc; }
268
- @property --button-submit-color-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
269
- @property --button-submit-color-loading { syntax: "*"; inherits: true; initial-value: #7c3aed; }
280
+ @property --button-submit-color { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
281
+ @property --button-submit-color-hover { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
282
+ @property --button-submit-color-disabled { syntax: "<color>"; inherits: true; initial-value: #cccccc; }
283
+ @property --button-submit-color-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
284
+ @property --button-submit-color-loading { syntax: "<color>"; inherits: true; initial-value: #7c3aed; }
270
285
 
271
- @property --button-submit-background { syntax: "*"; inherits: true; initial-value: #2563eb; }
272
- @property --button-submit-background-hover { syntax: "*"; inherits: true; initial-value: #1d4ed8; }
273
- @property --button-submit-background-disabled { syntax: "*"; inherits: true; initial-value: #f3f4f6; }
274
- @property --button-submit-background-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
275
- @property --button-submit-background-loading { syntax: "*"; inherits: true; initial-value: #f0f9ff; }
286
+ @property --button-submit-background { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
287
+ @property --button-submit-background-hover { syntax: "<color>"; inherits: true; initial-value: #1d4ed8; }
288
+ @property --button-submit-background-disabled { syntax: "<color>"; inherits: true; initial-value: #f3f4f6; }
289
+ @property --button-submit-background-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
290
+ @property --button-submit-background-loading { syntax: "<color>"; inherits: true; initial-value: #f0f9ff; }
276
291
 
277
- @property --button-submit-border-color { syntax: "*"; inherits: true; initial-value: #2563eb; }
278
- @property --button-submit-border-color-hover { syntax: "*"; inherits: true; initial-value: #1d4ed8; }
279
- @property --button-submit-border-color-disabled { syntax: "*"; inherits: true; initial-value: #d1d5db; }
280
- @property --button-submit-border-color-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
281
- @property --button-submit-border-color-loading { syntax: "*"; inherits: true; initial-value: #7c3aed; }
292
+ @property --button-submit-border-color { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
293
+ @property --button-submit-border-color-hover { syntax: "<color>"; inherits: true; initial-value: #1d4ed8; }
294
+ @property --button-submit-border-color-disabled { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
295
+ @property --button-submit-border-color-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
296
+ @property --button-submit-border-color-loading { syntax: "<color>"; inherits: true; initial-value: #7c3aed; }
282
297
 
283
298
  /* action */
284
- @property --button-action-color { syntax: "*"; inherits: true; initial-value: #ffffff; }
285
- @property --button-action-color-hover { syntax: "*"; inherits: true; initial-value: #ffffff; }
286
- @property --button-action-color-disabled { syntax: "*"; inherits: true; initial-value: #ffffff; }
287
- @property --button-action-color-focus { syntax: "*"; inherits: true; initial-value: #ffffff; }
288
- @property --button-action-color-loading { syntax: "*"; inherits: true; initial-value: #ffffff; }
289
-
290
- @property --button-action-background { syntax: "*"; inherits: true; initial-value: #111111; }
291
- @property --button-action-background-hover { syntax: "*"; inherits: true; initial-value: #111111; }
292
- @property --button-action-background-disabled { syntax: "*"; inherits: true; initial-value: #f3f4f6; }
293
- @property --button-action-background-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
294
- @property --button-action-background-loading { syntax: "*"; inherits: true; initial-value: #f0f9ff; }
295
-
296
- @property --button-action-border-color { syntax: "*"; inherits: true; initial-value: #111111; }
297
- @property --button-action-border-color-hover { syntax: "*"; inherits: true; initial-value: #111111; }
298
- @property --button-action-border-color-disabled { syntax: "*"; inherits: true; initial-value: #111111; }
299
- @property --button-action-border-color-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
300
- @property --button-action-border-color-loading { syntax: "*"; inherits: true; initial-value: #7c3aed; }
299
+ @property --button-action-color { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
300
+ @property --button-action-color-hover { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
301
+ @property --button-action-color-disabled { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
302
+ @property --button-action-color-focus { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
303
+ @property --button-action-color-loading { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
304
+
305
+ @property --button-action-background { syntax: "<color>"; inherits: true; initial-value: #111111; }
306
+ @property --button-action-background-hover { syntax: "<color>"; inherits: true; initial-value: #111111; }
307
+ @property --button-action-background-disabled { syntax: "<color>"; inherits: true; initial-value: #f3f4f6; }
308
+ @property --button-action-background-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
309
+ @property --button-action-background-loading { syntax: "<color>"; inherits: true; initial-value: #f0f9ff; }
310
+
311
+ @property --button-action-border-color { syntax: "<color>"; inherits: true; initial-value: #111111; }
312
+ @property --button-action-border-color-hover { syntax: "<color>"; inherits: true; initial-value: #111111; }
313
+ @property --button-action-border-color-disabled { syntax: "<color>"; inherits: true; initial-value: #111111; }
314
+ @property --button-action-border-color-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
315
+ @property --button-action-border-color-loading { syntax: "<color>"; inherits: true; initial-value: #7c3aed; }
301
316
 
302
317
  /* danger */
303
- @property --button-danger-color { syntax: "*"; inherits: true; initial-value: #dc2626; }
304
- @property --button-danger-color-hover { syntax: "*"; inherits: true; initial-value: #dc2626; }
305
- @property --button-danger-color-disabled { syntax: "*"; inherits: true; initial-value: #dc2626; }
306
- @property --button-danger-color-focus { syntax: "*"; inherits: true; initial-value: #dc2626; }
307
- @property --button-danger-color-loading { syntax: "*"; inherits: true; initial-value: #dc2626; }
308
-
309
- @property --button-danger-background { syntax: "*"; inherits: true; initial-value: #dc2626; }
310
- @property --button-danger-background-hover { syntax: "*"; inherits: true; initial-value: #dc2626; }
311
- @property --button-danger-background-disabled { syntax: "*"; inherits: true; initial-value: #f3f4f6; }
312
- @property --button-danger-background-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
313
- @property --button-danger-background-loading { syntax: "*"; inherits: true; initial-value: #f0f9ff; }
314
-
315
- @property --button-danger-border-color { syntax: "*"; inherits: true; initial-value: #dc2626; }
316
- @property --button-danger-border-color-hover { syntax: "*"; inherits: true; initial-value: #dc2626; }
317
- @property --button-danger-border-color-disabled { syntax: "*"; inherits: true; initial-value: #d1d5db; }
318
- @property --button-danger-border-color-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
319
- @property --button-danger-border-color-loading { syntax: "*"; inherits: true; initial-value: #7c3aed; }
318
+ @property --button-danger-color { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
319
+ @property --button-danger-color-hover { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
320
+ @property --button-danger-color-disabled { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
321
+ @property --button-danger-color-focus { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
322
+ @property --button-danger-color-loading { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
323
+
324
+ @property --button-danger-background { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
325
+ @property --button-danger-background-hover { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
326
+ @property --button-danger-background-disabled { syntax: "<color>"; inherits: true; initial-value: #f3f4f6; }
327
+ @property --button-danger-background-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
328
+ @property --button-danger-background-loading { syntax: "<color>"; inherits: true; initial-value: #f0f9ff; }
329
+
330
+ @property --button-danger-border-color { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
331
+ @property --button-danger-border-color-hover { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
332
+ @property --button-danger-border-color-disabled { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
333
+ @property --button-danger-border-color-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
334
+ @property --button-danger-border-color-loading { syntax: "<color>"; inherits: true; initial-value: #7c3aed; }
320
335
 
321
336
  /* success */
322
- @property --button-success-color { syntax: "*"; inherits: true; initial-value: #16a34a; }
323
- @property --button-success-color-hover { syntax: "*"; inherits: true; initial-value: #16a34a; }
324
- @property --button-success-color-disabled { syntax: "*"; inherits: true; initial-value: #16a34a; }
325
- @property --button-success-color-focus { syntax: "*"; inherits: true; initial-value: #16a34a; }
326
- @property --button-success-color-loading { syntax: "*"; inherits: true; initial-value: #16a34a; }
327
-
328
- @property --button-success-background { syntax: "*"; inherits: true; initial-value: #16a34a; }
329
- @property --button-success-background-hover { syntax: "*"; inherits: true; initial-value: #16a34a; }
330
- @property --button-success-background-disabled { syntax: "*"; inherits: true; initial-value: #f3f4f6; }
331
- @property --button-success-background-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
332
- @property --button-success-background-loading { syntax: "*"; inherits: true; initial-value: #f0f9ff; }
333
-
334
- @property --button-success-border-color { syntax: "*"; inherits: true; initial-value: #16a34a; }
335
- @property --button-success-border-color-hover { syntax: "*"; inherits: true; initial-value: #16a34a; }
336
- @property --button-success-border-color-disabled { syntax: "*"; inherits: true; initial-value: #d1d5db; }
337
- @property --button-success-border-color-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
338
- @property --button-success-border-color-loading { syntax: "*"; inherits: true; initial-value: #7c3aed; }
337
+ @property --button-success-color { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
338
+ @property --button-success-color-hover { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
339
+ @property --button-success-color-disabled { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
340
+ @property --button-success-color-focus { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
341
+ @property --button-success-color-loading { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
342
+
343
+ @property --button-success-background { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
344
+ @property --button-success-background-hover { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
345
+ @property --button-success-background-disabled { syntax: "<color>"; inherits: true; initial-value: #f3f4f6; }
346
+ @property --button-success-background-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
347
+ @property --button-success-background-loading { syntax: "<color>"; inherits: true; initial-value: #f0f9ff; }
348
+
349
+ @property --button-success-border-color { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
350
+ @property --button-success-border-color-hover { syntax: "<color>"; inherits: true; initial-value: #16a34a; }
351
+ @property --button-success-border-color-disabled { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
352
+ @property --button-success-border-color-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
353
+ @property --button-success-border-color-loading { syntax: "<color>"; inherits: true; initial-value: #7c3aed; }
339
354
 
340
355
  @property --button-border-size { syntax: "<length>"; inherits: true; initial-value: 1px; }
341
356
  @property --button-border-style { syntax: "solid | dashed | dotted | double | groove | ridge | inset | outset"; inherits: true; initial-value: solid; }
@@ -343,7 +358,7 @@
343
358
 
344
359
  @property --button-padding-inline { syntax: "<length>"; inherits: true; initial-value: 16px; }
345
360
  @property --button-padding-block { syntax: "<length>"; inherits: true; initial-value: 8px; }
346
- @property --button-padding { syntax: "*"; inherits: true; initial-value: 8px; }
361
+ @property --button-padding { syntax: "<length>"; inherits: true; initial-value: 8px; }
347
362
  @property --button-border { syntax: "*"; inherits: true; initial-value: none; }
348
363
 
349
364
  @property --button-font-weight { syntax: "<number>"; inherits: true; initial-value: 500; }
@@ -355,16 +370,16 @@
355
370
  * Shadows
356
371
  */
357
372
 
358
- @property --shadow-color { syntax: "*"; inherits: true; initial-value: rgba(0, 0, 0, 0.1); }
373
+ @property --shadow-color { syntax: "<color>"; inherits: true; initial-value: rgba(0, 0, 0, 0.1); }
359
374
  @property --shadow-base { syntax: "*"; inherits: true; initial-value: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06); }
360
375
 
361
376
  /**
362
377
  * Code
363
378
  */
364
379
 
365
- @property --code-background { syntax: "*"; inherits: true; initial-value: #f8fafc; }
366
- @property --code-color { syntax: "*"; inherits: true; initial-value: #1a1a1a; }
367
- @property --code-border-color { syntax: "*"; inherits: true; initial-value: #d1d5db; }
380
+ @property --code-background { syntax: "<color>"; inherits: true; initial-value: #f8fafc; }
381
+ @property --code-color { syntax: "<color>"; inherits: true; initial-value: #1a1a1a; }
382
+ @property --code-border-color { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
368
383
  @property --code-border-size { syntax: "<length>"; inherits: true; initial-value: 0; }
369
384
  @property --code-border-style { syntax: "solid | dashed | dotted | double | groove | ridge | inset | outset"; inherits: true; initial-value: solid; }
370
385
  @property --code-border-radius { syntax: "<length>"; inherits: true; initial-value: 8px; }
@@ -376,18 +391,18 @@
376
391
  * Backgrounds
377
392
  */
378
393
 
379
- @property --background-body { syntax: "*"; inherits: true; initial-value: #ffffff; }
380
- @property --background-main { syntax: "*"; inherits: true; initial-value: #ffffff; }
381
- @property --background-main-subtle { syntax: "*"; inherits: true; initial-value: #f8fafc; }
382
-
383
- @property --background-active { syntax: "*"; inherits: true; initial-value: #2563eb; }
384
- @property --background-inactive { syntax: "*"; inherits: true; initial-value: #f3f4f6; }
385
- @property --background-disabled { syntax: "*"; inherits: true; initial-value: #f3f4f6; }
386
- @property --background-focus { syntax: "*"; inherits: true; initial-value: #2563eb; }
387
- @property --background-invalid { syntax: "*"; inherits: true; initial-value: #fef2f2; }
388
- @property --background-valid { syntax: "*"; inherits: true; initial-value: #dcfce7; }
389
- @property --background-success { syntax: "*"; inherits: true; initial-value: #dcfce7; }
390
- @property --background-danger { syntax: "*"; inherits: true; initial-value: #fef2f2; }
394
+ @property --background-body { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
395
+ @property --background-main { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
396
+ @property --background-main-subtle { syntax: "<color>"; inherits: true; initial-value: #f8fafc; }
397
+
398
+ @property --background-active { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
399
+ @property --background-inactive { syntax: "<color>"; inherits: true; initial-value: #f3f4f6; }
400
+ @property --background-disabled { syntax: "<color>"; inherits: true; initial-value: #f3f4f6; }
401
+ @property --background-focus { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
402
+ @property --background-invalid { syntax: "<color>"; inherits: true; initial-value: #fef2f2; }
403
+ @property --background-valid { syntax: "<color>"; inherits: true; initial-value: #dcfce7; }
404
+ @property --background-success { syntax: "<color>"; inherits: true; initial-value: #dcfce7; }
405
+ @property --background-danger { syntax: "<color>"; inherits: true; initial-value: #fef2f2; }
391
406
  @property --background-warn { syntax: "<color>"; inherits: true; initial-value: #fefce8; }
392
407
  @property --background-info { syntax: "<color>"; inherits: true; initial-value: #eff6ff; }
393
408
  @property --background-selected { syntax: "<color>"; inherits: true; initial-value: #eff6ff; }
@@ -417,8 +432,8 @@
417
432
 
418
433
  @property --focus-ring { syntax: "*"; inherits: true; initial-value: 2px solid #2563eb; }
419
434
  @property --focus-ring-size { syntax: "<length>"; inherits: true; initial-value: 2px; }
420
- @property --focus-ring-color { syntax: "*"; inherits: true; initial-value: #2563eb; }
421
- @property --focus-ring-color-invalid { syntax: "*"; inherits: true; initial-value: #dc2626; }
435
+ @property --focus-ring-color { syntax: "<color>"; inherits: true; initial-value: #2563eb; }
436
+ @property --focus-ring-color-invalid { syntax: "<color>"; inherits: true; initial-value: #dc2626; }
422
437
  @property --focus-ring-offset { syntax: "<length>"; inherits: true; initial-value: 0px; }
423
438
 
424
439
  /**
@@ -435,7 +450,7 @@
435
450
  */
436
451
 
437
452
  @property --tooltip-background { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
438
- @property --tooltip-color { syntax: "*"; inherits: true; initial-value: #1a1a1a; }
453
+ @property --tooltip-color { syntax: "<color>"; inherits: true; initial-value: #1a1a1a; }
439
454
  @property --tooltip-border { syntax: "*"; inherits: true; initial-value: 1px solid #d1d5db; }
440
455
  @property --tooltip-border-radius { syntax: "<length>"; inherits: true; initial-value: 8px; }
441
456
  @property --tooltip-shadow { syntax: "*"; inherits: true; initial-value: 0 4px 8px rgba(0, 0, 0, 0.15); }
@@ -485,21 +500,27 @@
485
500
  @property --table-cell-padding-block { syntax: "<length>"; inherits: true; initial-value: 8px; }
486
501
 
487
502
  @property --table-header-background { syntax: "<color>"; inherits: true; initial-value: #f8fafc; }
488
- @property --table-header-color { syntax: "*"; inherits: true; initial-value: #1a1a1a; }
503
+ @property --table-header-color { syntax: "<color>"; inherits: true; initial-value: #1a1a1a; }
489
504
  @property --table-header-font-weight { syntax: "<number>"; inherits: true; initial-value: 600; }
490
505
 
491
506
  @property --table-row-background { syntax: "<color>"; inherits: true; initial-value: transparent; }
492
507
  @property --table-row-background-even { syntax: "<color>"; inherits: true; initial-value: #f8fafc; }
493
508
  @property --table-row-background-hover { syntax: "<color>"; inherits: true; initial-value: #f1f5f9; }
494
509
 
495
- @property --table-cell-color { syntax: "*"; inherits: true; initial-value: #1a1a1a; }
510
+ @property --table-cell-color { syntax: "<color>"; inherits: true; initial-value: #1a1a1a; }
511
+
512
+ /**
513
+ * Data List
514
+ */
515
+
516
+ @property --data-list-gap { syntax: "<length>"; inherits: true; initial-value: 12px; }
496
517
 
497
518
  /**
498
519
  * Modal
499
520
  */
500
521
 
501
522
  @property --modal-background { syntax: "<color>"; inherits: true; initial-value: #ffffff; }
502
- @property --modal-border-color { syntax: "*"; inherits: true; initial-value: #d1d5db; }
523
+ @property --modal-border-color { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
503
524
  @property --modal-border-size { syntax: "<length>"; inherits: true; initial-value: 1px; }
504
525
  @property --modal-border-style { syntax: "solid | dashed | dotted | double | groove | ridge | inset | outset"; inherits: true; initial-value: solid; }
505
526
  @property --modal-border-radius { syntax: "<length>"; inherits: true; initial-value: 8px; }
@@ -509,12 +530,12 @@
509
530
  @property --modal-padding-block { syntax: "<length>"; inherits: true; initial-value: 24px; }
510
531
  @property --modal-width { syntax: "*"; inherits: true; initial-value: fit-content; }
511
532
  @property --modal-min-width { syntax: "*"; inherits: true; initial-value: auto; }
512
- @property --modal-max-width { syntax: "*"; inherits: true; initial-value: 640px; }
513
- @property --modal-max-height { syntax: "*"; inherits: true; initial-value: 600px; }
533
+ @property --modal-max-width { syntax: "<length>"; inherits: true; initial-value: 640px; }
534
+ @property --modal-max-height { syntax: "<length>"; inherits: true; initial-value: 600px; }
514
535
  @property --modal-gap { syntax: "<length>"; inherits: true; initial-value: 0px; }
515
536
 
516
537
  @property --modal-actions-background { syntax: "<color>"; inherits: true; initial-value: #f8fafc; }
517
- @property --modal-actions-border-color { syntax: "*"; inherits: true; initial-value: #d1d5db; }
538
+ @property --modal-actions-border-color { syntax: "<color>"; inherits: true; initial-value: #d1d5db; }
518
539
  @property --modal-actions-padding-inline { syntax: "<length>"; inherits: true; initial-value: 16px; }
519
540
  @property --modal-actions-padding-block { syntax: "<length>"; inherits: true; initial-value: 16px; }
520
541
 
@@ -106,7 +106,7 @@
106
106
  --link-color-disabled: #cccccc;
107
107
  --link-color-loading: #7c3aed;
108
108
 
109
- --link-icon-size: 24px;
109
+ --link-icon-size: 1.5em;
110
110
  --link-icon-order: 1;
111
111
 
112
112
  /* Borders */
@@ -210,25 +210,38 @@
210
210
  --field-label-font-weight: 600;
211
211
 
212
212
  --field-placeholder-color: #999999;
213
- --field-padding-inline: 8px;
214
- --field-padding-block: 8px;
213
+ --field-padding-inline: 0.5em;
214
+ --field-padding-block: 0.5em;
215
215
 
216
- --field-icon-size: 24px;
216
+ --field-icon-size: 1.5em;
217
217
  --field-icon-order: 1;
218
+ --field-group-gap: var(--space-xs);
218
219
 
219
220
  /* Shorthand compound: field */
220
221
  --field-padding: var(--field-padding-block) var(--field-padding-inline);
221
222
  --field-border: var(--field-border-size) var(--field-border-style) var(--field-border-color);
222
223
 
223
224
  /* Checkbox and Radio */
224
- --checkbox-size: 18px;
225
- --checkbox-border-radius: 4px;
225
+ --checkbox-size: 1.125em;
226
+ --checkbox-border-radius: 0.25em;
226
227
  --checkbox-background: #ffffff;
227
228
  --checkbox-background-checked: #2563eb;
228
229
  --checkbox-border-color: #d1d5db;
229
230
  --checkbox-border-color-checked: #2563eb;
230
231
  --checkbox-indicator-color: #ffffff;
231
232
 
233
+ /* Toggle */
234
+ --toggle-width: 2.75em;
235
+ --toggle-height: 1.5em;
236
+ --toggle-background: #d1d5db;
237
+ --toggle-background-checked: #2563eb;
238
+ --toggle-border-color: #d1d5db;
239
+ --toggle-border-color-checked: #2563eb;
240
+ --toggle-thumb-size: 1.125em;
241
+ --toggle-thumb-color: #ffffff;
242
+ --toggle-thumb-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
243
+ --toggle-thumb-offset: calc((var(--toggle-height) - var(--toggle-thumb-size)) / 2 - var(--field-border-size));
244
+
232
245
  /* Button Component */
233
246
  --button-base-color: #ffffff;
234
247
  --button-base-color-hover: #ffffff;
@@ -314,8 +327,8 @@
314
327
  --button-border-radius: var(--border-radius-base);
315
328
  --button-border-color: var(--border-color);
316
329
 
317
- --button-padding-inline: 16px;
318
- --button-padding-block: 8px;
330
+ --button-padding-inline: 1em;
331
+ --button-padding-block: 0.5em;
319
332
 
320
333
  --button-font-weight: 500;
321
334
 
@@ -341,10 +354,10 @@
341
354
  --code-border-color: #d1d5db;
342
355
  --code-border-size: 0;
343
356
  --code-border-style: solid;
344
- --code-border-radius: 8px;
357
+ --code-border-radius: 0.5em;
345
358
 
346
- --code-padding-inline: 16px;
347
- --code-padding-block: 8px;
359
+ --code-padding-inline: 1em;
360
+ --code-padding-block: 0.5em;
348
361
 
349
362
  /* Backgrounds */
350
363
  --background-body: #ffffff;
@@ -402,7 +415,7 @@
402
415
  /* Toast */
403
416
  --toast-background: #ffffff;
404
417
  --toast-border: 1px solid #d1d5db;
405
- --toast-border-radius: 8px;
418
+ --toast-border-radius: 0.5em;
406
419
  --toast-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
407
420
  --toast-padding-inline: 1rem;
408
421
  --toast-padding-block: 0.75rem;
@@ -423,16 +436,16 @@
423
436
  --menu-border-color: #cccccc;
424
437
  --menu-border-size: 1px;
425
438
  --menu-border-style: solid;
426
- --menu-border-radius: 8px;
439
+ --menu-border-radius: 0.5em;
427
440
 
428
441
  /* Table */
429
442
  --table-border-color: #d1d5db;
430
443
  --table-border-size: 1px;
431
444
  --table-border-style: solid;
432
- --table-border-radius: 8px;
445
+ --table-border-radius: 0.5em;
433
446
 
434
- --table-cell-padding-inline: 12px;
435
- --table-cell-padding-block: 8px;
447
+ --table-cell-padding-inline: 0.75em;
448
+ --table-cell-padding-block: 0.5em;
436
449
 
437
450
  --table-header-background: #f8fafc;
438
451
  --table-header-color: #1a1a1a;
@@ -448,16 +461,19 @@
448
461
  --table-border: var(--table-border-size) var(--table-border-style) var(--table-border-color);
449
462
  --table-cell-padding: var(--table-cell-padding-block) var(--table-cell-padding-inline);
450
463
 
464
+ /* Data List */
465
+ --data-list-gap: var(--space-sm);
466
+
451
467
  /* Modal */
452
468
  --modal-background: #ffffff;
453
469
  --modal-border-color: #d1d5db;
454
470
  --modal-border-size: 1px;
455
471
  --modal-border-style: solid;
456
- --modal-border-radius: 8px;
472
+ --modal-border-radius: 0.5em;
457
473
  --modal-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
458
474
 
459
- --modal-padding-inline: 24px;
460
- --modal-padding-block: 24px;
475
+ --modal-padding-inline: 1.5em;
476
+ --modal-padding-block: 1.5em;
461
477
  --modal-width: fit-content;
462
478
  --modal-min-width: auto;
463
479
  --modal-max-width: 40rem;
@@ -466,8 +482,8 @@
466
482
 
467
483
  --modal-actions-background: #f8fafc;
468
484
  --modal-actions-border-color: #d1d5db;
469
- --modal-actions-padding-inline: 16px;
470
- --modal-actions-padding-block: 16px;
485
+ --modal-actions-padding-inline: 1em;
486
+ --modal-actions-padding-block: 1em;
471
487
 
472
488
  /* Shorthand compound: modal */
473
489
  --modal-border: var(--modal-border-size) var(--modal-border-style) var(--modal-border-color);
@@ -477,13 +493,13 @@
477
493
  /* Shared Surface System */
478
494
  --surface-background: #ffffff;
479
495
  --surface-border: 1px solid #d1d5db;
480
- --surface-border-radius: 8px;
496
+ --surface-border-radius: 0.5em;
481
497
  --surface-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
482
498
 
483
499
  /* Popover Component */
484
500
  --popover-background: #ffffff;
485
501
  --popover-border: 1px solid #d1d5db;
486
- --popover-border-radius: 8px;
502
+ --popover-border-radius: 0.5em;
487
503
  --popover-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
488
504
  --popover-width: max-content;
489
505
  --popover-max-width: calc(100vw - 2rem);
@@ -172,6 +172,16 @@
172
172
  --checkbox-border-color-checked: var(--button-submit-base-color);
173
173
  --checkbox-indicator-color: light-dark(#ffffff, #ffffff);
174
174
 
175
+ /**
176
+ * Toggle
177
+ */
178
+
179
+ --toggle-background: var(--border-color);
180
+ --toggle-background-checked: var(--checkbox-background-checked);
181
+ --toggle-border-color: var(--toggle-background);
182
+ --toggle-border-color-checked: var(--toggle-background-checked);
183
+ --toggle-thumb-color: var(--checkbox-indicator-color);
184
+
175
185
  --field-label-color: var(--text-color-p);
176
186
 
177
187
  --form-background: var(--background-body);
@@ -179,9 +179,9 @@
179
179
  border-radius: calc(var(--field-border-radius) - 2px);
180
180
  }
181
181
  .Help {
182
- font-size: min(11px, var(--font-size-075));
182
+ font-size: var(--font-size-sm);
183
183
  line-height: var(--font-line-height-tight);
184
184
  color: var(--text-color-p-subtle);
185
- font-weight: var(--font-weight-normal);
185
+ font-weight: var(--font-weight-light);
186
186
  }
187
187
  </style>
@@ -0,0 +1,84 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from "svelte";
3
+ import Label from "./Label.svelte";
4
+ import StringOrSnippet from "../util/StringOrSnippet.svelte";
5
+
6
+ /**
7
+ * @description
8
+ * Groups related form fields (checkboxes, radios, toggles) under a shared label
9
+ * with a tighter gap than the default form field spacing.
10
+ *
11
+ * @cssprop --field-group-gap -- Gap between items within the group.
12
+ * @cssprop --form-label-gap -- Gap between the group label and the items.
13
+ *
14
+ * @example
15
+ * <FieldGroup label="Permissions">
16
+ * <Input name="read" type="checkbox" label="Read" />
17
+ * <Input name="write" type="checkbox" label="Write" />
18
+ * <Input name="admin" type="checkbox" label="Admin" />
19
+ * </FieldGroup>
20
+ */
21
+ let {
22
+ id = crypto.randomUUID(),
23
+ label,
24
+ labelHelp,
25
+ labelTip,
26
+ help,
27
+ required,
28
+ children,
29
+ }: {
30
+ /** A unique id for the group. Auto-generated if not provided. */
31
+ id?: string;
32
+ /** The group label. */
33
+ label?: string | Snippet;
34
+ /** Help text to display alongside the label. */
35
+ labelHelp?: string | Snippet;
36
+ /** Context tooltip for the label. */
37
+ labelTip?: string | Snippet;
38
+ /** Help text to display below the group. */
39
+ help?: string | Snippet;
40
+ /** Whether the group is required. */
41
+ required?: boolean;
42
+ /** The grouped fields to render. */
43
+ children: Snippet;
44
+ } = $props();
45
+ </script>
46
+
47
+ <div
48
+ class="FieldGroup"
49
+ role="group"
50
+ aria-labelledby={label ? `fg-${id}` : undefined}
51
+ >
52
+ {#if label}
53
+ <div id="fg-{id}">
54
+ <Label {label} tip={labelTip} help={labelHelp} {required} />
55
+ </div>
56
+ {/if}
57
+ <div class="FieldGroupContent">
58
+ {@render children()}
59
+ </div>
60
+ {#if help}
61
+ <div class="Help">
62
+ <StringOrSnippet content={help} />
63
+ </div>
64
+ {/if}
65
+ </div>
66
+
67
+ <style>
68
+ .FieldGroup {
69
+ display: flex;
70
+ flex-direction: column;
71
+ gap: var(--form-label-gap, var(--space-xs));
72
+ }
73
+ .FieldGroupContent {
74
+ display: flex;
75
+ flex-direction: column;
76
+ gap: var(--field-group-gap);
77
+ }
78
+ .Help {
79
+ font-size: var(--font-size-sm);
80
+ line-height: var(--font-line-height-tight);
81
+ color: var(--text-color-p-subtle);
82
+ font-weight: var(--font-weight-light);
83
+ }
84
+ </style>
@@ -0,0 +1,20 @@
1
+ import type { Snippet } from "svelte";
2
+ type $$ComponentProps = {
3
+ /** A unique id for the group. Auto-generated if not provided. */
4
+ id?: string;
5
+ /** The group label. */
6
+ label?: string | Snippet;
7
+ /** Help text to display alongside the label. */
8
+ labelHelp?: string | Snippet;
9
+ /** Context tooltip for the label. */
10
+ labelTip?: string | Snippet;
11
+ /** Help text to display below the group. */
12
+ help?: string | Snippet;
13
+ /** Whether the group is required. */
14
+ required?: boolean;
15
+ /** The grouped fields to render. */
16
+ children: Snippet;
17
+ };
18
+ declare const FieldGroup: import("svelte").Component<$$ComponentProps, {}, "">;
19
+ type FieldGroup = ReturnType<typeof FieldGroup>;
20
+ export default FieldGroup;
@@ -1,4 +1,166 @@
1
1
  <script lang="ts">
2
+ import { getContext, onMount, type Snippet } from "svelte";
3
+ import type { LutraForm } from "./types.js";
4
+ import { fieldChange } from "./client.svelte.js";
5
+ import FieldContent from "./FieldContent.svelte";
6
+ import { getFromObjWithStringPath } from "./form.js";
7
+ import { ZodType } from "zod";
2
8
 
9
+ /**
10
+ * @description
11
+ * A mobile-style toggle switch. Renders as `<input type="checkbox" role="switch">`
12
+ * for full accessibility. Integrates with the Lutra form system for validation,
13
+ * error display and field state tracking.
14
+ *
15
+ * @example
16
+ * <Toggle name="notifications" label="Enable notifications" />
17
+ * <Toggle name="darkMode" label="Dark mode" value={true} />
18
+ */
19
+ let {
20
+ disabled,
21
+ help,
22
+ id = $bindable(crypto.randomUUID()),
23
+ label,
24
+ labelHelp,
25
+ labelTip,
26
+ name,
27
+ onblur,
28
+ onchange,
29
+ onfocus,
30
+ required,
31
+ title,
32
+ value = $bindable(false),
33
+ ...rest
34
+ }: {
35
+ /** Whether the toggle is disabled. */
36
+ disabled?: boolean;
37
+ /** Help text to display below the toggle. */
38
+ help?: string | Snippet;
39
+ /** A random id is generated if not provided. */
40
+ id?: string;
41
+ /** The label for the toggle. */
42
+ label?: string | Snippet;
43
+ /** Help text to display below the label. */
44
+ labelHelp?: string | Snippet;
45
+ /** Context tooltip for the label. Renders with a questionmark using ContextTip. */
46
+ labelTip?: string | Snippet;
47
+ /** The name of the toggle field. */
48
+ name: string;
49
+ /** The onblur event handler. */
50
+ onblur?: (e: FocusEvent) => void;
51
+ /** Onchange event handler. */
52
+ onchange?: (e: Event) => void;
53
+ /** Onfocus event handler. */
54
+ onfocus?: (e: FocusEvent) => void;
55
+ /** Whether the toggle is required. */
56
+ required?: boolean;
57
+ /** A string that defines the title of the toggle. */
58
+ title?: string;
59
+ /** The checked state of the toggle. */
60
+ value?: boolean;
61
+ } = $props();
62
+
63
+ let el: HTMLInputElement | undefined = $state();
64
+
65
+ const form = getContext<LutraForm<any>>('form');
66
+ const field = $derived(form?.fields[name]);
67
+ const issue = $derived(form?.issues?.find((issue) => issue.name === name));
68
+ const validator = getContext<Record<string, ZodType>>('form.validators')?.[name];
69
+ const data = form?.data;
70
+ const originalData = form?.originalData;
71
+
72
+ if(!value) value = form ? (getFromObjWithStringPath(Object.assign(originalData ?? {}, data ?? {}), name) as boolean) : false;
73
+
74
+ onMount(() => {
75
+ if(value) fieldChange(form, name, () => el)({} as any);
76
+ });
3
77
  </script>
4
78
 
79
+ <FieldContent
80
+ {id}
81
+ {label}
82
+ {labelHelp}
83
+ {labelTip}
84
+ contained={false}
85
+ direction="row"
86
+ {field}
87
+ {issue}
88
+ type="toggle"
89
+ {help}
90
+ {required}
91
+ >
92
+ <input
93
+ type="checkbox"
94
+ role="switch"
95
+ class="Toggle"
96
+ bind:this={el}
97
+ {disabled}
98
+ {id}
99
+ {name}
100
+ {title}
101
+ required={required || field?.required}
102
+ onchange={fieldChange(form, name, () => el, validator, onchange)}
103
+ {onblur}
104
+ {onfocus}
105
+ bind:checked={value}
106
+ aria-checked={value}
107
+ {...rest}
108
+ />
109
+ </FieldContent>
110
+
111
+ <style>
112
+ input.Toggle {
113
+ appearance: none;
114
+ width: var(--toggle-width);
115
+ height: var(--toggle-height);
116
+ border-radius: calc(infinity * 1px);
117
+ background: var(--toggle-background);
118
+ border: var(--field-border-size) var(--field-border-style) var(--toggle-border-color);
119
+ cursor: pointer;
120
+ position: relative;
121
+ flex-shrink: 0;
122
+ padding: 0;
123
+ margin: 0;
124
+ transition:
125
+ background var(--transition-duration-fast) var(--transition-timing-function),
126
+ border-color var(--transition-duration-fast) var(--transition-timing-function);
127
+ }
128
+
129
+ input.Toggle::before {
130
+ content: "";
131
+ position: absolute;
132
+ top: 50%;
133
+ inset-inline-start: var(--toggle-thumb-offset);
134
+ width: var(--toggle-thumb-size);
135
+ height: var(--toggle-thumb-size);
136
+ border-radius: 50%;
137
+ background: var(--toggle-thumb-color);
138
+ translate: 0 -50%;
139
+ transition: translate var(--transition-duration-fast) var(--transition-timing-function);
140
+ box-shadow: var(--toggle-thumb-shadow);
141
+ }
142
+
143
+ input.Toggle:checked {
144
+ background: var(--toggle-background-checked);
145
+ border-color: var(--toggle-border-color-checked);
146
+ }
147
+
148
+ input.Toggle:checked::before {
149
+ translate: calc(var(--toggle-width) - var(--toggle-height)) -50%;
150
+ }
151
+
152
+ input.Toggle:checked::after {
153
+ content: none;
154
+ display: none;
155
+ }
156
+
157
+ input.Toggle:focus-visible {
158
+ outline: var(--focus-ring-size) solid var(--focus-ring-color);
159
+ outline-offset: var(--focus-ring-offset);
160
+ }
161
+
162
+ input.Toggle:disabled {
163
+ opacity: 0.5;
164
+ cursor: not-allowed;
165
+ }
166
+ </style>
@@ -1,18 +1,32 @@
1
- interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
- new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
- $$bindings?: Bindings;
4
- } & Exports;
5
- (internal: unknown, props: {
6
- $$events?: Events;
7
- $$slots?: Slots;
8
- }): Exports & {
9
- $set?: any;
10
- $on?: any;
11
- };
12
- z_$$bindings?: Bindings;
13
- }
14
- declare const Toggle: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
- [evt: string]: CustomEvent<any>;
16
- }, {}, {}, string>;
17
- type Toggle = InstanceType<typeof Toggle>;
1
+ import { type Snippet } from "svelte";
2
+ type $$ComponentProps = {
3
+ /** Whether the toggle is disabled. */
4
+ disabled?: boolean;
5
+ /** Help text to display below the toggle. */
6
+ help?: string | Snippet;
7
+ /** A random id is generated if not provided. */
8
+ id?: string;
9
+ /** The label for the toggle. */
10
+ label?: string | Snippet;
11
+ /** Help text to display below the label. */
12
+ labelHelp?: string | Snippet;
13
+ /** Context tooltip for the label. Renders with a questionmark using ContextTip. */
14
+ labelTip?: string | Snippet;
15
+ /** The name of the toggle field. */
16
+ name: string;
17
+ /** The onblur event handler. */
18
+ onblur?: (e: FocusEvent) => void;
19
+ /** Onchange event handler. */
20
+ onchange?: (e: Event) => void;
21
+ /** Onfocus event handler. */
22
+ onfocus?: (e: FocusEvent) => void;
23
+ /** Whether the toggle is required. */
24
+ required?: boolean;
25
+ /** A string that defines the title of the toggle. */
26
+ title?: string;
27
+ /** The checked state of the toggle. */
28
+ value?: boolean;
29
+ };
30
+ declare const Toggle: import("svelte").Component<$$ComponentProps, {}, "id" | "value">;
31
+ type Toggle = ReturnType<typeof Toggle>;
18
32
  export default Toggle;
@@ -1,6 +1,7 @@
1
1
  export { default as Button } from './Button.svelte';
2
2
  export { default as FormActions } from './FormActions.svelte';
3
3
  export { default as FieldError } from './FieldError.svelte';
4
+ export { default as FieldGroup } from './FieldGroup.svelte';
4
5
  export { default as FormSection } from './FormSection.svelte';
5
6
  export { default as Fieldset } from './Fieldset.svelte';
6
7
  export { default as Form } from './Form.svelte';
@@ -1,6 +1,7 @@
1
1
  export { default as Button } from './Button.svelte';
2
2
  export { default as FormActions } from './FormActions.svelte';
3
3
  export { default as FieldError } from './FieldError.svelte';
4
+ export { default as FieldGroup } from './FieldGroup.svelte';
4
5
  export { default as FormSection } from './FormSection.svelte';
5
6
  export { default as Fieldset } from './Fieldset.svelte';
6
7
  export { default as Form } from './Form.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lutra",
3
- "version": "0.1.69",
3
+ "version": "0.1.70",
4
4
  "scripts": {
5
5
  "dev": "vite dev",
6
6
  "bump-and-publish:patch": "pnpm version:patch && pnpm build && npm publish",