@nqmcreative/ui 0.4.0 → 0.4.2
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/README.md +17 -19
- package/dist/core/calendar.d.ts +1 -1
- package/dist/core/calendar.js +1 -1
- package/dist/core/date.d.ts +3 -3
- package/dist/core/date.js +1 -1
- package/dist/core/files.d.ts +2 -3
- package/dist/core/files.js +2 -3
- package/dist/core/index.d.ts +2 -4
- package/dist/core/index.js +2 -4
- package/dist/core/password.d.ts +1 -1
- package/dist/core/password.js +1 -1
- package/dist/core/tones.d.ts +10 -0
- package/dist/core/tones.js +19 -0
- package/dist/core/trigger.d.ts +8 -0
- package/dist/core/trigger.js +11 -0
- package/dist/styles/matte/Alert.svelte +2 -4
- package/dist/styles/matte/Avatar.svelte +16 -3
- package/dist/styles/matte/Breadcrumb.svelte +1 -4
- package/dist/styles/matte/Button.svelte +6 -2
- package/dist/styles/matte/Calendar.svelte +6 -9
- package/dist/styles/matte/Checkbox.svelte +49 -30
- package/dist/styles/matte/CheckboxGroup.svelte +15 -4
- package/dist/styles/matte/ColorInput.svelte +1 -4
- package/dist/styles/matte/Combobox.svelte +14 -8
- package/dist/styles/matte/CommandPalette.svelte +3 -6
- package/dist/styles/matte/ConfirmDialog.svelte +11 -6
- package/dist/styles/matte/ContextMenu.svelte +1 -4
- package/dist/styles/matte/CurrencyInput.svelte +1 -1
- package/dist/styles/matte/CurrencyInput.svelte.d.ts +1 -1
- package/dist/styles/matte/DatePicker.svelte +18 -11
- package/dist/styles/matte/DatePicker.svelte.d.ts +3 -0
- package/dist/styles/matte/Divider.svelte +1 -1
- package/dist/styles/matte/Drawer.svelte +11 -5
- package/dist/styles/matte/Dropdown.svelte +2 -5
- package/dist/styles/matte/Dropdown.svelte.d.ts +1 -1
- package/dist/styles/matte/Dropzone.svelte +9 -12
- package/dist/styles/matte/Dropzone.svelte.d.ts +1 -1
- package/dist/styles/matte/Field.svelte +59 -3
- package/dist/styles/matte/FileInput.svelte +11 -14
- package/dist/styles/matte/FileInput.svelte.d.ts +2 -2
- package/dist/styles/matte/Footer.svelte +15 -4
- package/dist/styles/matte/Footer.svelte.d.ts +1 -1
- package/dist/styles/matte/Input.svelte +9 -2
- package/dist/styles/matte/Label.svelte +3 -1
- package/dist/styles/matte/Link.svelte +13 -2
- package/dist/styles/matte/MenuItem.svelte +11 -4
- package/dist/styles/matte/Modal.svelte +11 -5
- package/dist/styles/matte/MultiSelect.svelte +5 -8
- package/dist/styles/matte/Navbar.svelte +5 -11
- package/dist/styles/matte/Pagination.svelte +3 -6
- package/dist/styles/matte/PinInput.svelte +1 -4
- package/dist/styles/matte/Radio.svelte +41 -22
- package/dist/styles/matte/RadioGroup.svelte +15 -4
- package/dist/styles/matte/SearchInput.svelte +1 -4
- package/dist/styles/matte/Select.svelte +4 -2
- package/dist/styles/matte/Sidebar.svelte +3 -6
- package/dist/styles/matte/Skeleton.svelte +2 -1
- package/dist/styles/matte/Spinner.svelte +2 -5
- package/dist/styles/matte/Spinner.svelte.d.ts +1 -1
- package/dist/styles/matte/Stat.svelte +27 -3
- package/dist/styles/matte/Switch.svelte +6 -1
- package/dist/styles/matte/Table.svelte +5 -8
- package/dist/styles/matte/Tabs.svelte +1 -1
- package/dist/styles/matte/TagsInput.svelte +5 -8
- package/dist/styles/matte/Textarea.svelte +4 -2
- package/dist/styles/matte/ThemeToggle.svelte +5 -8
- package/dist/styles/matte/TimeInput.svelte +2 -5
- package/dist/styles/matte/Toaster.svelte +23 -8
- package/dist/styles/matte/Tooltip.svelte +8 -1
- package/dist/styles/matte/index.d.ts +2 -3
- package/dist/styles/matte/index.js +1 -3
- package/dist/styles/matte/theme.css +21 -0
- package/dist/styles/paper/Alert.svelte +3 -5
- package/dist/styles/paper/Avatar.svelte +14 -3
- package/dist/styles/paper/Breadcrumb.svelte +1 -4
- package/dist/styles/paper/Button.svelte +5 -1
- package/dist/styles/paper/Calendar.svelte +6 -9
- package/dist/styles/paper/Card.svelte +1 -1
- package/dist/styles/paper/Checkbox.svelte +55 -36
- package/dist/styles/paper/CheckboxGroup.svelte +15 -4
- package/dist/styles/paper/ColorInput.svelte +1 -4
- package/dist/styles/paper/Combobox.svelte +14 -8
- package/dist/styles/paper/CommandPalette.svelte +3 -6
- package/dist/styles/paper/ConfirmDialog.svelte +9 -6
- package/dist/styles/paper/ContextMenu.svelte +1 -4
- package/dist/styles/paper/CurrencyInput.svelte +1 -1
- package/dist/styles/paper/CurrencyInput.svelte.d.ts +1 -1
- package/dist/styles/paper/DatePicker.svelte +18 -11
- package/dist/styles/paper/DatePicker.svelte.d.ts +3 -0
- package/dist/styles/paper/Divider.svelte +1 -1
- package/dist/styles/paper/Drawer.svelte +9 -5
- package/dist/styles/paper/Dropdown.svelte +2 -5
- package/dist/styles/paper/Dropdown.svelte.d.ts +1 -1
- package/dist/styles/paper/Dropzone.svelte +9 -12
- package/dist/styles/paper/Dropzone.svelte.d.ts +1 -1
- package/dist/styles/paper/Field.svelte +59 -3
- package/dist/styles/paper/FileInput.svelte +11 -14
- package/dist/styles/paper/FileInput.svelte.d.ts +2 -2
- package/dist/styles/paper/Footer.svelte +15 -4
- package/dist/styles/paper/Footer.svelte.d.ts +1 -1
- package/dist/styles/paper/Input.svelte +9 -2
- package/dist/styles/paper/Label.svelte +3 -1
- package/dist/styles/paper/Link.svelte +13 -2
- package/dist/styles/paper/MenuItem.svelte +11 -4
- package/dist/styles/paper/Modal.svelte +9 -5
- package/dist/styles/paper/MultiSelect.svelte +5 -8
- package/dist/styles/paper/Navbar.svelte +5 -11
- package/dist/styles/paper/Pagination.svelte +3 -6
- package/dist/styles/paper/PinInput.svelte +1 -4
- package/dist/styles/paper/Radio.svelte +41 -22
- package/dist/styles/paper/RadioGroup.svelte +15 -4
- package/dist/styles/paper/SearchInput.svelte +1 -4
- package/dist/styles/paper/SegmentedControl.svelte +2 -2
- package/dist/styles/paper/Select.svelte +4 -2
- package/dist/styles/paper/Sidebar.svelte +3 -6
- package/dist/styles/paper/Skeleton.svelte +2 -1
- package/dist/styles/paper/Spinner.svelte +2 -5
- package/dist/styles/paper/Spinner.svelte.d.ts +1 -1
- package/dist/styles/paper/Stat.svelte +24 -2
- package/dist/styles/paper/Switch.svelte +6 -1
- package/dist/styles/paper/Table.svelte +5 -8
- package/dist/styles/paper/Tabs.svelte +1 -1
- package/dist/styles/paper/TagsInput.svelte +5 -8
- package/dist/styles/paper/Textarea.svelte +4 -2
- package/dist/styles/paper/ThemeToggle.svelte +5 -8
- package/dist/styles/paper/TimeInput.svelte +2 -5
- package/dist/styles/paper/Toaster.svelte +22 -9
- package/dist/styles/paper/Tooltip.svelte +8 -1
- package/dist/styles/paper/index.d.ts +2 -3
- package/dist/styles/paper/index.js +1 -3
- package/dist/styles/paper/theme.css +21 -0
- package/dist/styles/sprout/Alert.svelte +4 -6
- package/dist/styles/sprout/Avatar.svelte +14 -3
- package/dist/styles/sprout/Breadcrumb.svelte +2 -5
- package/dist/styles/sprout/Button.svelte +8 -4
- package/dist/styles/sprout/Calendar.svelte +7 -10
- package/dist/styles/sprout/Card.svelte +2 -2
- package/dist/styles/sprout/Checkbox.svelte +55 -36
- package/dist/styles/sprout/CheckboxGroup.svelte +15 -4
- package/dist/styles/sprout/ColorInput.svelte +2 -5
- package/dist/styles/sprout/Combobox.svelte +15 -9
- package/dist/styles/sprout/CommandPalette.svelte +3 -6
- package/dist/styles/sprout/ConfirmDialog.svelte +9 -6
- package/dist/styles/sprout/ContextMenu.svelte +1 -4
- package/dist/styles/sprout/CurrencyInput.svelte +1 -1
- package/dist/styles/sprout/CurrencyInput.svelte.d.ts +1 -1
- package/dist/styles/sprout/DatePicker.svelte +20 -13
- package/dist/styles/sprout/DatePicker.svelte.d.ts +3 -0
- package/dist/styles/sprout/Divider.svelte +1 -1
- package/dist/styles/sprout/Drawer.svelte +9 -5
- package/dist/styles/sprout/Dropdown.svelte +2 -5
- package/dist/styles/sprout/Dropdown.svelte.d.ts +1 -1
- package/dist/styles/sprout/Dropzone.svelte +10 -13
- package/dist/styles/sprout/Dropzone.svelte.d.ts +1 -1
- package/dist/styles/sprout/Field.svelte +59 -3
- package/dist/styles/sprout/FileInput.svelte +14 -17
- package/dist/styles/sprout/FileInput.svelte.d.ts +2 -2
- package/dist/styles/sprout/Footer.svelte +15 -4
- package/dist/styles/sprout/Footer.svelte.d.ts +1 -1
- package/dist/styles/sprout/Input.svelte +9 -2
- package/dist/styles/sprout/Label.svelte +3 -1
- package/dist/styles/sprout/Link.svelte +13 -2
- package/dist/styles/sprout/LogoCloud.svelte +1 -1
- package/dist/styles/sprout/MenuItem.svelte +11 -4
- package/dist/styles/sprout/Modal.svelte +9 -5
- package/dist/styles/sprout/MultiSelect.svelte +8 -11
- package/dist/styles/sprout/Navbar.svelte +5 -11
- package/dist/styles/sprout/Pagination.svelte +3 -6
- package/dist/styles/sprout/PinInput.svelte +1 -4
- package/dist/styles/sprout/Radio.svelte +41 -22
- package/dist/styles/sprout/RadioGroup.svelte +15 -4
- package/dist/styles/sprout/SearchInput.svelte +2 -5
- package/dist/styles/sprout/SegmentedControl.svelte +2 -2
- package/dist/styles/sprout/Select.svelte +4 -2
- package/dist/styles/sprout/Sidebar.svelte +5 -7
- package/dist/styles/sprout/Skeleton.svelte +2 -1
- package/dist/styles/sprout/Spinner.svelte +2 -5
- package/dist/styles/sprout/Spinner.svelte.d.ts +1 -1
- package/dist/styles/sprout/Stat.svelte +24 -2
- package/dist/styles/sprout/Switch.svelte +6 -1
- package/dist/styles/sprout/Table.svelte +6 -9
- package/dist/styles/sprout/Tabs.svelte +1 -1
- package/dist/styles/sprout/TagsInput.svelte +6 -9
- package/dist/styles/sprout/Textarea.svelte +4 -2
- package/dist/styles/sprout/ThemeToggle.svelte +5 -8
- package/dist/styles/sprout/TimeInput.svelte +3 -6
- package/dist/styles/sprout/Toaster.svelte +25 -12
- package/dist/styles/sprout/Tooltip.svelte +8 -1
- package/dist/styles/sprout/index.d.ts +3 -4
- package/dist/styles/sprout/index.js +2 -4
- package/dist/styles/sprout/lift.d.ts +14 -0
- package/dist/styles/sprout/lift.js +23 -0
- package/dist/styles/sprout/theme.css +50 -24
- package/package.json +2 -19
- package/registry.json +7 -46
- package/dist/core/locale.svelte.d.ts +0 -101
- package/dist/core/locale.svelte.js +0 -167
- package/dist/styles/matte/LocaleProvider.svelte +0 -24
- package/dist/styles/matte/LocaleProvider.svelte.d.ts +0 -10
- package/dist/styles/paper/LocaleProvider.svelte +0 -24
- package/dist/styles/paper/LocaleProvider.svelte.d.ts +0 -10
- package/dist/styles/sprout/LocaleProvider.svelte +0 -24
- package/dist/styles/sprout/LocaleProvider.svelte.d.ts +0 -10
|
@@ -24,9 +24,65 @@
|
|
|
24
24
|
children,
|
|
25
25
|
...rest
|
|
26
26
|
}: Props = $props();
|
|
27
|
+
|
|
28
|
+
const uid = $props.id();
|
|
29
|
+
const messageId = $derived(error ? `${uid}-error` : hint ? `${uid}-hint` : undefined);
|
|
30
|
+
|
|
31
|
+
interface Described {
|
|
32
|
+
for?: string;
|
|
33
|
+
messageId?: string;
|
|
34
|
+
required: boolean;
|
|
35
|
+
invalid: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A hint nobody can hear is not a hint. `aria-describedby` has to sit on the
|
|
40
|
+
* control, and the control arrives through `children` — out of reach of this
|
|
41
|
+
* template — so the field finds it once the DOM exists and keeps the three
|
|
42
|
+
* attributes in step itself. It touches only what it set: an `aria-invalid`
|
|
43
|
+
* the control already carries from its own `invalid` prop stays that
|
|
44
|
+
* control's business.
|
|
45
|
+
*/
|
|
46
|
+
function describes(node: HTMLElement, state: Described) {
|
|
47
|
+
let ourId: string | undefined;
|
|
48
|
+
let ownsInvalid = false;
|
|
49
|
+
|
|
50
|
+
const apply = (next: Described) => {
|
|
51
|
+
const control =
|
|
52
|
+
(next.for ? node.querySelector<HTMLElement>(`#${CSS.escape(next.for)}`) : null) ??
|
|
53
|
+
node.querySelector<HTMLElement>('input, select, textarea');
|
|
54
|
+
if (!control) return;
|
|
55
|
+
|
|
56
|
+
const ids = (control.getAttribute('aria-describedby') ?? '')
|
|
57
|
+
.split(/\s+/)
|
|
58
|
+
.filter((id) => id && id !== ourId);
|
|
59
|
+
if (next.messageId) ids.push(next.messageId);
|
|
60
|
+
if (ids.length) control.setAttribute('aria-describedby', ids.join(' '));
|
|
61
|
+
else control.removeAttribute('aria-describedby');
|
|
62
|
+
ourId = next.messageId;
|
|
63
|
+
|
|
64
|
+
if (next.required) control.setAttribute('aria-required', 'true');
|
|
65
|
+
else control.removeAttribute('aria-required');
|
|
66
|
+
|
|
67
|
+
if (next.invalid) {
|
|
68
|
+
ownsInvalid ||= !control.hasAttribute('aria-invalid');
|
|
69
|
+
control.setAttribute('aria-invalid', 'true');
|
|
70
|
+
} else if (ownsInvalid) {
|
|
71
|
+
control.removeAttribute('aria-invalid');
|
|
72
|
+
ownsInvalid = false;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
apply(state);
|
|
77
|
+
return { update: apply };
|
|
78
|
+
}
|
|
27
79
|
</script>
|
|
28
80
|
|
|
29
|
-
<div
|
|
81
|
+
<div
|
|
82
|
+
use:describes={{ for: htmlFor, messageId, required, invalid: Boolean(error) }}
|
|
83
|
+
class="flex w-full flex-col gap-1.5 {className}"
|
|
84
|
+
{...rest}
|
|
85
|
+
>
|
|
30
86
|
{#if label}
|
|
31
87
|
<label for={htmlFor} class="font-sans text-sm font-medium text-text">
|
|
32
88
|
{label}
|
|
@@ -35,8 +91,8 @@
|
|
|
35
91
|
{/if}
|
|
36
92
|
{@render children()}
|
|
37
93
|
{#if error}
|
|
38
|
-
<p class="font-sans text-[13px] text-danger">{error}</p>
|
|
94
|
+
<p id="{uid}-error" role="alert" class="font-sans text-[13px] text-danger">{error}</p>
|
|
39
95
|
{:else if hint}
|
|
40
|
-
<p class="font-sans text-[13px] text-text-muted">{hint}</p>
|
|
96
|
+
<p id="{uid}-hint" class="font-sans text-[13px] text-text-muted">{hint}</p>
|
|
41
97
|
{/if}
|
|
42
98
|
</div>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { formatSize as toReadableSize, sortFiles, type RejectedFile } from '../../core/files.js';
|
|
3
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
4
3
|
import { toneFocusWithinBorder, type Tone } from '../../core/tones.js';
|
|
5
4
|
import type { InputSize } from './Input.svelte';
|
|
6
5
|
|
|
@@ -18,9 +17,9 @@
|
|
|
18
17
|
size?: InputSize;
|
|
19
18
|
tone?: Tone;
|
|
20
19
|
invalid?: boolean;
|
|
21
|
-
/** Text on the button. Defaults to
|
|
20
|
+
/** Text on the button. Defaults to `Choose file`. */
|
|
22
21
|
label?: string;
|
|
23
|
-
/** Shown while nothing is chosen. Defaults to
|
|
22
|
+
/** Shown while nothing is chosen. Defaults to `No file chosen`. */
|
|
24
23
|
placeholder?: string;
|
|
25
24
|
/** Show the list of held files below the row. */
|
|
26
25
|
showList?: boolean;
|
|
@@ -50,18 +49,16 @@
|
|
|
50
49
|
class: className = ''
|
|
51
50
|
}: Props = $props();
|
|
52
51
|
|
|
53
|
-
const t = useLocale();
|
|
54
|
-
|
|
55
52
|
let input: HTMLInputElement | null = $state(null);
|
|
56
53
|
let rejected = $state<RejectedFile[]>([]);
|
|
57
54
|
|
|
58
|
-
const formatSize = (bytes: number) => toReadableSize(bytes
|
|
55
|
+
const formatSize = (bytes: number) => toReadableSize(bytes);
|
|
59
56
|
|
|
60
57
|
/** One file reads as its name and weight; several read as a count. */
|
|
61
58
|
const summary = $derived.by(() => {
|
|
62
|
-
if (files.length === 0) return placeholder ??
|
|
59
|
+
if (files.length === 0) return placeholder ?? 'No file chosen';
|
|
63
60
|
if (files.length === 1) return `${files[0].name} · ${formatSize(files[0].size)}`;
|
|
64
|
-
return `${files.length} $
|
|
61
|
+
return `${files.length} $selected`;
|
|
65
62
|
});
|
|
66
63
|
|
|
67
64
|
function add(incoming: FileList | File[] | null) {
|
|
@@ -90,9 +87,9 @@
|
|
|
90
87
|
}
|
|
91
88
|
|
|
92
89
|
const reasons: Record<RejectedFile['reason'], string> = $derived({
|
|
93
|
-
type:
|
|
94
|
-
size:
|
|
95
|
-
count:
|
|
90
|
+
type: 'wrong file type',
|
|
91
|
+
size: 'too large',
|
|
92
|
+
count: 'over the file limit'
|
|
96
93
|
});
|
|
97
94
|
|
|
98
95
|
const sizes: Record<InputSize, string> = {
|
|
@@ -124,7 +121,7 @@
|
|
|
124
121
|
class="shrink-0 rounded border border-hairline-strong bg-bg-alt font-sans font-medium text-text-secondary transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current
|
|
125
122
|
{buttons[size]}"
|
|
126
123
|
>
|
|
127
|
-
{label ??
|
|
124
|
+
{label ?? 'Choose file'}
|
|
128
125
|
</button>
|
|
129
126
|
|
|
130
127
|
<span
|
|
@@ -137,7 +134,7 @@
|
|
|
137
134
|
<button
|
|
138
135
|
type="button"
|
|
139
136
|
onclick={clear}
|
|
140
|
-
aria-label=
|
|
137
|
+
aria-label="Remove"
|
|
141
138
|
class="mr-1 shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-danger focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger"
|
|
142
139
|
>
|
|
143
140
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -185,7 +182,7 @@
|
|
|
185
182
|
<button
|
|
186
183
|
type="button"
|
|
187
184
|
onclick={() => (files = files.filter((_, at) => at !== i))}
|
|
188
|
-
aria-label="
|
|
185
|
+
aria-label="Remove {file.name}"
|
|
189
186
|
class="-m-1 shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-danger focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger"
|
|
190
187
|
>
|
|
191
188
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -15,9 +15,9 @@ interface Props {
|
|
|
15
15
|
size?: InputSize;
|
|
16
16
|
tone?: Tone;
|
|
17
17
|
invalid?: boolean;
|
|
18
|
-
/** Text on the button. Defaults to
|
|
18
|
+
/** Text on the button. Defaults to `Choose file`. */
|
|
19
19
|
label?: string;
|
|
20
|
-
/** Shown while nothing is chosen. Defaults to
|
|
20
|
+
/** Shown while nothing is chosen. Defaults to `No file chosen`. */
|
|
21
21
|
placeholder?: string;
|
|
22
22
|
/** Show the list of held files below the row. */
|
|
23
23
|
showList?: boolean;
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
columns?: FooterColumn[];
|
|
23
23
|
/** Logo, wordmark and a line of copy on the left. */
|
|
24
24
|
brand?: Snippet;
|
|
25
|
-
/** Right side of the bottom bar — social links, a
|
|
25
|
+
/** Right side of the bottom bar — social links, a language picker. */
|
|
26
26
|
bottom?: Snippet;
|
|
27
27
|
/** Left side of the bottom bar. Ignored when `bottom` covers both. */
|
|
28
28
|
copyright?: string;
|
|
@@ -59,15 +59,26 @@
|
|
|
59
59
|
<a
|
|
60
60
|
href={item.href}
|
|
61
61
|
target={item.external ? '_blank' : undefined}
|
|
62
|
-
rel={item.external ? 'noreferrer' : undefined}
|
|
62
|
+
rel={item.external ? 'noopener noreferrer' : undefined}
|
|
63
63
|
class="inline-flex items-center gap-2 font-sans text-[13px] text-text-muted transition-colors duration-150 ease-brand-out {toneHoverText[
|
|
64
64
|
tone
|
|
65
65
|
]}"
|
|
66
66
|
>
|
|
67
67
|
{#if item.icon}<span class={iconSm}>{@render item.icon()}</span
|
|
68
68
|
>{/if}{item.label}
|
|
69
|
-
{#if item.external}<
|
|
70
|
-
|
|
69
|
+
{#if item.external}<svg
|
|
70
|
+
class="inline-block size-[0.85em] align-[-0.05em]"
|
|
71
|
+
viewBox="0 0 14 14"
|
|
72
|
+
fill="none"
|
|
73
|
+
aria-hidden="true"
|
|
74
|
+
><path
|
|
75
|
+
d="M5.5 2.5h6v6M11.5 2.5 3.5 10.5"
|
|
76
|
+
stroke="currentColor"
|
|
77
|
+
stroke-width="1.5"
|
|
78
|
+
stroke-linecap="round"
|
|
79
|
+
stroke-linejoin="round"
|
|
80
|
+
/></svg
|
|
81
|
+
><span class="sr-only">(opens in a new tab)</span>{/if}
|
|
71
82
|
</a>
|
|
72
83
|
</li>
|
|
73
84
|
{/each}
|
|
@@ -15,7 +15,7 @@ interface Props {
|
|
|
15
15
|
columns?: FooterColumn[];
|
|
16
16
|
/** Logo, wordmark and a line of copy on the left. */
|
|
17
17
|
brand?: Snippet;
|
|
18
|
-
/** Right side of the bottom bar — social links, a
|
|
18
|
+
/** Right side of the bottom bar — social links, a language picker. */
|
|
19
19
|
bottom?: Snippet;
|
|
20
20
|
/** Left side of the bottom bar. Ignored when `bottom` covers both. */
|
|
21
21
|
copyright?: string;
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLInputAttributes } from 'svelte/elements';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
focusWithinRing,
|
|
6
|
+
toneFocusWithinBorder,
|
|
7
|
+
toneFocusWithinRing,
|
|
8
|
+
type Tone
|
|
9
|
+
} from '../../core/tones.js';
|
|
5
10
|
|
|
6
11
|
export type InputSize = 'sm' | 'md' | 'lg';
|
|
7
12
|
|
|
@@ -37,7 +42,9 @@
|
|
|
37
42
|
</script>
|
|
38
43
|
|
|
39
44
|
<span
|
|
40
|
-
class="inline-flex w-full items-center gap-2 rounded-md border bg-bg px-3 shadow-xs transition-colors duration-150 ease-brand-out
|
|
45
|
+
class="inline-flex w-full items-center gap-2 rounded-md border bg-bg px-3 shadow-xs transition-colors duration-150 ease-brand-out {focusWithinRing} {toneFocusWithinRing[
|
|
46
|
+
tone
|
|
47
|
+
]}
|
|
41
48
|
{sizes[size]}
|
|
42
49
|
{invalid
|
|
43
50
|
? 'border-danger focus-within:border-danger'
|
|
@@ -12,5 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
<label class="font-sans text-sm font-medium text-text {className}" {...rest}>
|
|
14
14
|
{@render children()}
|
|
15
|
-
{#if required}<span class="ml-0.5 text-danger" aria-hidden="true">*</span
|
|
15
|
+
{#if required}<span class="ml-0.5 text-danger" aria-hidden="true">*</span><span class="sr-only"
|
|
16
|
+
>(required)</span
|
|
17
|
+
>{/if}
|
|
16
18
|
</label>
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
|
|
41
41
|
<a
|
|
42
42
|
target={external ? '_blank' : undefined}
|
|
43
|
-
rel={external ? 'noreferrer' : undefined}
|
|
43
|
+
rel={external ? 'noopener noreferrer' : undefined}
|
|
44
44
|
class="inline-flex items-center gap-1 rounded-sm font-sans transition-colors duration-150 ease-brand-out
|
|
45
45
|
{muted ? `text-text-secondary ${toneHoverText[tone]}` : toneText[tone]}
|
|
46
46
|
{underlines[underline]} {focusRing} {toneRing[tone]} {className}"
|
|
@@ -49,5 +49,16 @@
|
|
|
49
49
|
{#if icon}<span class={iconSm}>{@render icon()}</span>{/if}
|
|
50
50
|
{@render children()}
|
|
51
51
|
{#if iconEnd}<span class={iconSm}>{@render iconEnd()}</span>{/if}
|
|
52
|
-
{#if external}
|
|
52
|
+
{#if external}
|
|
53
|
+
<svg class="size-[0.85em] shrink-0" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
54
|
+
<path
|
|
55
|
+
d="M5.5 2.5h6v6M11.5 2.5 3.5 10.5"
|
|
56
|
+
stroke="currentColor"
|
|
57
|
+
stroke-width="1.5"
|
|
58
|
+
stroke-linecap="round"
|
|
59
|
+
stroke-linejoin="round"
|
|
60
|
+
/>
|
|
61
|
+
</svg>
|
|
62
|
+
<span class="sr-only">(opens in a new tab)</span>
|
|
63
|
+
{/if}
|
|
53
64
|
</a>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import type { HTMLButtonAttributes } from 'svelte/elements';
|
|
4
|
-
import { toneSoftHover, toneText, type Tone } from '../../core/tones.js';
|
|
4
|
+
import { toneSoft, toneSoftHover, toneText, type Tone } from '../../core/tones.js';
|
|
5
5
|
import { iconMd } from './icon.js';
|
|
6
6
|
|
|
7
7
|
interface Props extends HTMLButtonAttributes {
|
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
children,
|
|
29
29
|
...rest
|
|
30
30
|
}: Props = $props();
|
|
31
|
+
|
|
32
|
+
// A chosen row speaks in the brand, the same as an active nav link — a tick
|
|
33
|
+
// alone leaves it the same grey as every row above it. An item that already
|
|
34
|
+
// carries a tone (a red `Delete`) keeps its own.
|
|
35
|
+
const chosen = $derived(toneSoft[tone === 'neutral' ? 'brand' : tone]);
|
|
31
36
|
</script>
|
|
32
37
|
|
|
33
38
|
<svelte:element
|
|
@@ -39,9 +44,11 @@
|
|
|
39
44
|
aria-disabled={disabled ? 'true' : undefined}
|
|
40
45
|
tabindex={disabled ? undefined : -1}
|
|
41
46
|
class="mx-1 flex items-center gap-2.5 rounded-md px-2.5 py-1.5 text-left text-sm transition-colors duration-100 ease-brand-out focus:outline-none disabled:pointer-events-none disabled:opacity-40
|
|
42
|
-
{
|
|
43
|
-
?
|
|
44
|
-
:
|
|
47
|
+
{selected
|
|
48
|
+
? `${chosen} font-medium`
|
|
49
|
+
: tone === 'neutral'
|
|
50
|
+
? 'text-text-secondary hover:bg-bg-inset focus-visible:bg-bg-inset'
|
|
51
|
+
: `${toneText[tone]} ${toneSoftHover[tone]}`}
|
|
45
52
|
{className}"
|
|
46
53
|
{...rest}
|
|
47
54
|
>
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
import { iconLg } from './icon.js';
|
|
4
4
|
import type { HTMLDialogAttributes } from 'svelte/elements';
|
|
5
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
6
5
|
import { isBackdropClick, syncDialog } from '../../core/dialog.svelte.js';
|
|
7
6
|
|
|
8
7
|
export type ModalSize = 'sm' | 'md' | 'lg';
|
|
@@ -40,7 +39,8 @@
|
|
|
40
39
|
...rest
|
|
41
40
|
}: Props = $props();
|
|
42
41
|
|
|
43
|
-
const
|
|
42
|
+
const uid = $props.id();
|
|
43
|
+
|
|
44
44
|
let dialog: HTMLDialogElement | null = $state(null);
|
|
45
45
|
|
|
46
46
|
const closable = $derived(showClose ?? dismissible);
|
|
@@ -66,6 +66,8 @@
|
|
|
66
66
|
|
|
67
67
|
<dialog
|
|
68
68
|
bind:this={dialog}
|
|
69
|
+
aria-labelledby={title ? `${uid}-title` : undefined}
|
|
70
|
+
aria-describedby={description ? `${uid}-description` : undefined}
|
|
69
71
|
onclose={close}
|
|
70
72
|
onclick={onBackdrop}
|
|
71
73
|
oncancel={(event) => {
|
|
@@ -82,17 +84,19 @@
|
|
|
82
84
|
{#if icon}<span class={iconLg}>{@render icon()}</span>{/if}
|
|
83
85
|
<div class="flex min-w-0 flex-1 flex-col gap-1">
|
|
84
86
|
{#if title}
|
|
85
|
-
<h2 class="font-heading text-base font-semibold text-text">{title}</h2>
|
|
87
|
+
<h2 id="{uid}-title" class="font-heading text-base font-semibold text-text">{title}</h2>
|
|
86
88
|
{/if}
|
|
87
89
|
{#if description}
|
|
88
|
-
<p class="font-sans text-[13px] text-text-muted">
|
|
90
|
+
<p id="{uid}-description" class="font-sans text-[13px] text-text-muted">
|
|
91
|
+
{description}
|
|
92
|
+
</p>
|
|
89
93
|
{/if}
|
|
90
94
|
</div>
|
|
91
95
|
{#if closable}
|
|
92
96
|
<button
|
|
93
97
|
type="button"
|
|
94
98
|
onclick={close}
|
|
95
|
-
aria-label=
|
|
99
|
+
aria-label="Close"
|
|
96
100
|
class="-m-1 shrink-0 rounded-md p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand"
|
|
97
101
|
>
|
|
98
102
|
<svg class="size-4" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
import { anchored } from '../../core/actions/anchor.js';
|
|
17
17
|
import { clickOutside, portal } from '../../core/actions/dismissable.js';
|
|
18
18
|
import { ListCursor, groupItems, matchQuery, revealIndex } from '../../core/list.svelte.js';
|
|
19
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
20
19
|
import { iconMd, iconSm } from './icon.js';
|
|
21
20
|
import { toneFocusWithinBorder, toneSoft, toneText, type Tone } from '../../core/tones.js';
|
|
22
21
|
|
|
@@ -58,8 +57,6 @@
|
|
|
58
57
|
class: className = ''
|
|
59
58
|
}: Props = $props();
|
|
60
59
|
|
|
61
|
-
const t = useLocale();
|
|
62
|
-
|
|
63
60
|
let open = $state(false);
|
|
64
61
|
let query = $state('');
|
|
65
62
|
let wrapper: HTMLElement | null = $state(null);
|
|
@@ -149,7 +146,7 @@
|
|
|
149
146
|
class="inline-flex items-center rounded-md px-2 py-0.5 text-xs font-medium {toneSoft[tone]}"
|
|
150
147
|
>
|
|
151
148
|
{chosen.length}
|
|
152
|
-
|
|
149
|
+
selected
|
|
153
150
|
</span>
|
|
154
151
|
{:else}
|
|
155
152
|
{#each chosen as option (option.value)}
|
|
@@ -162,7 +159,7 @@
|
|
|
162
159
|
<button
|
|
163
160
|
type="button"
|
|
164
161
|
onclick={() => remove(option)}
|
|
165
|
-
aria-label="
|
|
162
|
+
aria-label="Remove {option.label}"
|
|
166
163
|
class="inline-flex size-4 items-center justify-center rounded transition-colors duration-150 hover:bg-bg/70 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-current"
|
|
167
164
|
>
|
|
168
165
|
<svg class="size-2.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -189,7 +186,7 @@
|
|
|
189
186
|
aria-expanded={open}
|
|
190
187
|
aria-controls="{id ?? 'multiselect'}-listbox"
|
|
191
188
|
aria-invalid={invalid ? 'true' : undefined}
|
|
192
|
-
placeholder={chosen.length ? '' : (placeholder ??
|
|
189
|
+
placeholder={chosen.length ? '' : (placeholder ?? 'Search…')}
|
|
193
190
|
oninput={() => {
|
|
194
191
|
open = true;
|
|
195
192
|
cursor.index = 0;
|
|
@@ -211,7 +208,7 @@
|
|
|
211
208
|
onchange?.([]);
|
|
212
209
|
inputEl?.focus();
|
|
213
210
|
}}
|
|
214
|
-
aria-label=
|
|
211
|
+
aria-label="Clear selection"
|
|
215
212
|
class="shrink-0 rounded p-1 text-text-muted transition-colors duration-150 hover:bg-bg-inset hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
|
|
216
213
|
>
|
|
217
214
|
<svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
|
|
@@ -239,7 +236,7 @@
|
|
|
239
236
|
>
|
|
240
237
|
{#if flat.length === 0}
|
|
241
238
|
<p class="px-3 py-6 text-center text-sm text-text-muted">
|
|
242
|
-
{emptyText ??
|
|
239
|
+
{emptyText ?? 'No matches'}
|
|
243
240
|
</p>
|
|
244
241
|
{:else}
|
|
245
242
|
{#each groups as [group, items] (group)}
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
|
|
17
17
|
<script lang="ts">
|
|
18
18
|
import type { Snippet } from 'svelte';
|
|
19
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
20
19
|
import { iconMd } from './icon.js';
|
|
21
20
|
import { focusRing, toneRing, toneSoft, toneText, type Tone } from '../../core/tones.js';
|
|
22
21
|
import Drawer from './Drawer.svelte';
|
|
@@ -53,8 +52,6 @@
|
|
|
53
52
|
class: className = ''
|
|
54
53
|
}: Props = $props();
|
|
55
54
|
|
|
56
|
-
const t = useLocale();
|
|
57
|
-
|
|
58
55
|
// paper gives the active link a pill rather than a colour change, so the
|
|
59
56
|
// current page is legible without relying on hue alone.
|
|
60
57
|
const link =
|
|
@@ -66,10 +63,7 @@
|
|
|
66
63
|
? 'border-b border-hairline'
|
|
67
64
|
: ''} {className}"
|
|
68
65
|
>
|
|
69
|
-
<nav
|
|
70
|
-
aria-label={t.current.navigation}
|
|
71
|
-
class="mx-auto flex h-16 w-full max-w-6xl items-center gap-6 px-6"
|
|
72
|
-
>
|
|
66
|
+
<nav aria-label="Navigation" class="mx-auto flex h-16 w-full max-w-6xl items-center gap-6 px-6">
|
|
73
67
|
{#if brand}
|
|
74
68
|
<div class="flex shrink-0 items-center">{@render brand()}</div>
|
|
75
69
|
{/if}
|
|
@@ -117,7 +111,7 @@
|
|
|
117
111
|
class="{link} {focusRing} {toneRing[tone]} {item.active
|
|
118
112
|
? toneSoft[tone]
|
|
119
113
|
: 'text-text-secondary hover:bg-bg-inset hover:text-text'} {item.disabled
|
|
120
|
-
? 'pointer-events-none opacity-
|
|
114
|
+
? 'pointer-events-none opacity-40'
|
|
121
115
|
: ''}"
|
|
122
116
|
>
|
|
123
117
|
{#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}
|
|
@@ -140,7 +134,7 @@
|
|
|
140
134
|
<button
|
|
141
135
|
type="button"
|
|
142
136
|
onclick={() => (menuOpen = true)}
|
|
143
|
-
aria-label=
|
|
137
|
+
aria-label="Open menu"
|
|
144
138
|
aria-expanded={menuOpen}
|
|
145
139
|
class="inline-flex size-9 items-center justify-center rounded-md text-text-secondary transition-colors duration-150 hover:bg-bg-inset hover:text-text md:hidden {focusRing} {toneRing[
|
|
146
140
|
tone
|
|
@@ -159,7 +153,7 @@
|
|
|
159
153
|
</nav>
|
|
160
154
|
</header>
|
|
161
155
|
|
|
162
|
-
<Drawer bind:open={menuOpen} side="right" size="sm" title=
|
|
156
|
+
<Drawer bind:open={menuOpen} side="right" size="sm" title="Navigation">
|
|
163
157
|
<ul class="flex flex-col gap-0.5">
|
|
164
158
|
{#each items as item (item.label)}
|
|
165
159
|
<li>
|
|
@@ -170,7 +164,7 @@
|
|
|
170
164
|
class="flex items-center justify-between gap-3 rounded-md px-3 py-2.5 font-sans text-sm font-medium {item.active
|
|
171
165
|
? `${toneSoft[tone]}`
|
|
172
166
|
: 'text-text-secondary hover:bg-bg-inset'} {item.disabled
|
|
173
|
-
? 'pointer-events-none opacity-
|
|
167
|
+
? 'pointer-events-none opacity-40'
|
|
174
168
|
: ''}"
|
|
175
169
|
>
|
|
176
170
|
<span class="flex min-w-0 items-center gap-2.5">
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
3
|
import { focusRing, toneRing, toneSolid, type Tone } from '../../core/tones.js';
|
|
4
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
5
4
|
import { pageRange } from '../../core/pagination.js';
|
|
6
5
|
|
|
7
6
|
interface Props extends Omit<HTMLAttributes<HTMLElement>, 'onchange'> {
|
|
@@ -25,8 +24,6 @@
|
|
|
25
24
|
...rest
|
|
26
25
|
}: Props = $props();
|
|
27
26
|
|
|
28
|
-
const t = useLocale();
|
|
29
|
-
|
|
30
27
|
const pages = $derived(pageRange(page, total, siblings));
|
|
31
28
|
|
|
32
29
|
function go(next: number) {
|
|
@@ -39,12 +36,12 @@
|
|
|
39
36
|
const cell = `inline-flex h-9 min-w-9 items-center justify-center rounded-md px-2 font-sans text-sm font-medium tabular-nums transition-colors duration-150 ease-brand-out disabled:opacity-40 disabled:pointer-events-none ${focusRing}`;
|
|
40
37
|
</script>
|
|
41
38
|
|
|
42
|
-
<nav aria-label=
|
|
39
|
+
<nav aria-label="Pagination" class="flex items-center gap-1 {className}" {...rest}>
|
|
43
40
|
<button
|
|
44
41
|
type="button"
|
|
45
42
|
class="{cell} {toneRing[tone]} text-text-secondary hover:bg-bg-inset"
|
|
46
43
|
disabled={page <= 1}
|
|
47
|
-
aria-label=
|
|
44
|
+
aria-label="Previous page"
|
|
48
45
|
onclick={() => go(page - 1)}
|
|
49
46
|
>
|
|
50
47
|
<svg class="size-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
@@ -73,7 +70,7 @@
|
|
|
73
70
|
type="button"
|
|
74
71
|
class="{cell} {toneRing[tone]} text-text-secondary hover:bg-bg-inset"
|
|
75
72
|
disabled={page >= total}
|
|
76
|
-
aria-label=
|
|
73
|
+
aria-label="Next page"
|
|
77
74
|
onclick={() => go(page + 1)}
|
|
78
75
|
>
|
|
79
76
|
<svg class="size-4" viewBox="0 0 16 16" fill="none" aria-hidden="true">
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import { useLocale } from '../../core/locale.svelte.js';
|
|
3
2
|
import { caretIndex, clearFrom, fillFrom, sanitisePin, type PinMode } from '../../core/pin.js';
|
|
4
3
|
import { toneFocusBorder, type Tone } from '../../core/tones.js';
|
|
5
4
|
|
|
@@ -38,8 +37,6 @@
|
|
|
38
37
|
class: className = ''
|
|
39
38
|
}: Props = $props();
|
|
40
39
|
|
|
41
|
-
const t = useLocale();
|
|
42
|
-
|
|
43
40
|
let boxes: HTMLInputElement[] = $state([]);
|
|
44
41
|
|
|
45
42
|
const chars = $derived(Array.from({ length }, (_, i) => value[i] ?? ''));
|
|
@@ -119,7 +116,7 @@
|
|
|
119
116
|
autocapitalize="characters"
|
|
120
117
|
spellcheck="false"
|
|
121
118
|
maxlength={length}
|
|
122
|
-
aria-label="
|
|
119
|
+
aria-label="Digit {index + 1}"
|
|
123
120
|
aria-invalid={invalid ? 'true' : undefined}
|
|
124
121
|
oninput={(event) => onInput(event, index)}
|
|
125
122
|
onkeydown={(event) => onKeydown(event, index)}
|
|
@@ -24,32 +24,51 @@
|
|
|
24
24
|
children,
|
|
25
25
|
...rest
|
|
26
26
|
}: Props = $props();
|
|
27
|
+
|
|
28
|
+
const uid = $props.id();
|
|
27
29
|
</script>
|
|
28
30
|
|
|
29
|
-
<
|
|
30
|
-
class="
|
|
31
|
-
|
|
31
|
+
<div
|
|
32
|
+
class="inline-block font-sans text-sm {disabled
|
|
33
|
+
? 'pointer-events-none opacity-50'
|
|
34
|
+
: ''} {className}"
|
|
32
35
|
>
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
+
<label class="group inline-flex items-start gap-2.5 {disabled ? '' : 'cursor-pointer'}">
|
|
37
|
+
<input
|
|
38
|
+
type="radio"
|
|
39
|
+
bind:group
|
|
40
|
+
{value}
|
|
41
|
+
{disabled}
|
|
42
|
+
aria-describedby={description ? `${uid}-description` : undefined}
|
|
43
|
+
class="peer sr-only"
|
|
44
|
+
{...rest}
|
|
45
|
+
/>
|
|
46
|
+
<span
|
|
47
|
+
class="mt-0.5 flex size-[18px] shrink-0 items-center justify-center rounded-full border border-hairline-strong bg-bg shadow-xs transition-colors duration-150 ease-brand-out
|
|
36
48
|
{tonePeerChecked[tone]} {peerFocusRing} {tonePeerFocus[tone]}"
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
aria-hidden="true"
|
|
50
|
+
>
|
|
51
|
+
<span
|
|
52
|
+
class="size-1.5 rounded-full bg-text-inverse opacity-0 transition-opacity duration-150 group-has-[:checked]:opacity-100"
|
|
53
|
+
></span>
|
|
54
|
+
</span>
|
|
55
|
+
{#if label || children}
|
|
56
|
+
<span class="flex flex-col gap-0.5">
|
|
57
|
+
{#if children}
|
|
58
|
+
{@render children()}
|
|
59
|
+
{:else if label}
|
|
60
|
+
<span class="leading-snug text-text">{label}</span>
|
|
61
|
+
{/if}
|
|
62
|
+
</span>
|
|
63
|
+
{/if}
|
|
64
|
+
</label>
|
|
65
|
+
{#if description}
|
|
66
|
+
<!-- ponytail: the indent tracks the 18px dot plus the label's gap; move one, move both. -->
|
|
39
67
|
<span
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<span class="flex flex-col gap-0.5">
|
|
45
|
-
{#if children}
|
|
46
|
-
{@render children()}
|
|
47
|
-
{:else if label}
|
|
48
|
-
<span class="leading-snug text-text">{label}</span>
|
|
49
|
-
{/if}
|
|
50
|
-
{#if description}
|
|
51
|
-
<span class="text-[13px] leading-snug text-text-muted">{description}</span>
|
|
52
|
-
{/if}
|
|
68
|
+
id="{uid}-description"
|
|
69
|
+
class="mt-0.5 block pl-[28px] text-[13px] leading-snug text-text-muted"
|
|
70
|
+
>
|
|
71
|
+
{description}
|
|
53
72
|
</span>
|
|
54
73
|
{/if}
|
|
55
|
-
</
|
|
74
|
+
</div>
|