@nqmcreative/ui 0.4.0 → 0.4.1

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.
Files changed (132) hide show
  1. package/README.md +16 -18
  2. package/dist/core/calendar.d.ts +1 -1
  3. package/dist/core/calendar.js +1 -1
  4. package/dist/core/date.d.ts +3 -3
  5. package/dist/core/date.js +1 -1
  6. package/dist/core/files.d.ts +2 -3
  7. package/dist/core/files.js +2 -3
  8. package/dist/core/index.d.ts +1 -3
  9. package/dist/core/index.js +1 -3
  10. package/dist/core/password.d.ts +1 -1
  11. package/dist/core/password.js +1 -1
  12. package/dist/styles/matte/Alert.svelte +1 -3
  13. package/dist/styles/matte/Breadcrumb.svelte +1 -4
  14. package/dist/styles/matte/Calendar.svelte +6 -9
  15. package/dist/styles/matte/ColorInput.svelte +1 -4
  16. package/dist/styles/matte/Combobox.svelte +3 -6
  17. package/dist/styles/matte/CommandPalette.svelte +3 -6
  18. package/dist/styles/matte/ConfirmDialog.svelte +2 -5
  19. package/dist/styles/matte/ContextMenu.svelte +1 -4
  20. package/dist/styles/matte/CurrencyInput.svelte +1 -1
  21. package/dist/styles/matte/CurrencyInput.svelte.d.ts +1 -1
  22. package/dist/styles/matte/DatePicker.svelte +9 -9
  23. package/dist/styles/matte/DatePicker.svelte.d.ts +3 -0
  24. package/dist/styles/matte/Drawer.svelte +1 -3
  25. package/dist/styles/matte/Dropdown.svelte +2 -5
  26. package/dist/styles/matte/Dropdown.svelte.d.ts +1 -1
  27. package/dist/styles/matte/Dropzone.svelte +9 -12
  28. package/dist/styles/matte/Dropzone.svelte.d.ts +1 -1
  29. package/dist/styles/matte/FileInput.svelte +11 -14
  30. package/dist/styles/matte/FileInput.svelte.d.ts +2 -2
  31. package/dist/styles/matte/Footer.svelte +1 -1
  32. package/dist/styles/matte/Footer.svelte.d.ts +1 -1
  33. package/dist/styles/matte/Modal.svelte +1 -3
  34. package/dist/styles/matte/MultiSelect.svelte +5 -8
  35. package/dist/styles/matte/Navbar.svelte +3 -9
  36. package/dist/styles/matte/Pagination.svelte +3 -6
  37. package/dist/styles/matte/PinInput.svelte +1 -4
  38. package/dist/styles/matte/SearchInput.svelte +1 -4
  39. package/dist/styles/matte/Sidebar.svelte +3 -6
  40. package/dist/styles/matte/Spinner.svelte +2 -5
  41. package/dist/styles/matte/Spinner.svelte.d.ts +1 -1
  42. package/dist/styles/matte/Table.svelte +5 -8
  43. package/dist/styles/matte/TagsInput.svelte +5 -8
  44. package/dist/styles/matte/ThemeToggle.svelte +5 -8
  45. package/dist/styles/matte/TimeInput.svelte +2 -5
  46. package/dist/styles/matte/Toaster.svelte +2 -5
  47. package/dist/styles/matte/index.d.ts +2 -3
  48. package/dist/styles/matte/index.js +1 -3
  49. package/dist/styles/paper/Alert.svelte +1 -3
  50. package/dist/styles/paper/Breadcrumb.svelte +1 -4
  51. package/dist/styles/paper/Calendar.svelte +6 -9
  52. package/dist/styles/paper/ColorInput.svelte +1 -4
  53. package/dist/styles/paper/Combobox.svelte +3 -6
  54. package/dist/styles/paper/CommandPalette.svelte +3 -6
  55. package/dist/styles/paper/ConfirmDialog.svelte +2 -5
  56. package/dist/styles/paper/ContextMenu.svelte +1 -4
  57. package/dist/styles/paper/CurrencyInput.svelte +1 -1
  58. package/dist/styles/paper/CurrencyInput.svelte.d.ts +1 -1
  59. package/dist/styles/paper/DatePicker.svelte +9 -9
  60. package/dist/styles/paper/DatePicker.svelte.d.ts +3 -0
  61. package/dist/styles/paper/Drawer.svelte +1 -3
  62. package/dist/styles/paper/Dropdown.svelte +2 -5
  63. package/dist/styles/paper/Dropdown.svelte.d.ts +1 -1
  64. package/dist/styles/paper/Dropzone.svelte +9 -12
  65. package/dist/styles/paper/Dropzone.svelte.d.ts +1 -1
  66. package/dist/styles/paper/FileInput.svelte +11 -14
  67. package/dist/styles/paper/FileInput.svelte.d.ts +2 -2
  68. package/dist/styles/paper/Footer.svelte +1 -1
  69. package/dist/styles/paper/Footer.svelte.d.ts +1 -1
  70. package/dist/styles/paper/Modal.svelte +1 -3
  71. package/dist/styles/paper/MultiSelect.svelte +5 -8
  72. package/dist/styles/paper/Navbar.svelte +3 -9
  73. package/dist/styles/paper/Pagination.svelte +3 -6
  74. package/dist/styles/paper/PinInput.svelte +1 -4
  75. package/dist/styles/paper/SearchInput.svelte +1 -4
  76. package/dist/styles/paper/Sidebar.svelte +3 -6
  77. package/dist/styles/paper/Spinner.svelte +2 -5
  78. package/dist/styles/paper/Spinner.svelte.d.ts +1 -1
  79. package/dist/styles/paper/Table.svelte +5 -8
  80. package/dist/styles/paper/TagsInput.svelte +5 -8
  81. package/dist/styles/paper/ThemeToggle.svelte +5 -8
  82. package/dist/styles/paper/TimeInput.svelte +2 -5
  83. package/dist/styles/paper/Toaster.svelte +3 -6
  84. package/dist/styles/paper/index.d.ts +2 -3
  85. package/dist/styles/paper/index.js +1 -3
  86. package/dist/styles/sprout/Alert.svelte +1 -3
  87. package/dist/styles/sprout/Breadcrumb.svelte +1 -4
  88. package/dist/styles/sprout/Calendar.svelte +6 -9
  89. package/dist/styles/sprout/ColorInput.svelte +1 -4
  90. package/dist/styles/sprout/Combobox.svelte +3 -6
  91. package/dist/styles/sprout/CommandPalette.svelte +3 -6
  92. package/dist/styles/sprout/ConfirmDialog.svelte +2 -5
  93. package/dist/styles/sprout/ContextMenu.svelte +1 -4
  94. package/dist/styles/sprout/CurrencyInput.svelte +1 -1
  95. package/dist/styles/sprout/CurrencyInput.svelte.d.ts +1 -1
  96. package/dist/styles/sprout/DatePicker.svelte +9 -9
  97. package/dist/styles/sprout/DatePicker.svelte.d.ts +3 -0
  98. package/dist/styles/sprout/Drawer.svelte +1 -3
  99. package/dist/styles/sprout/Dropdown.svelte +2 -5
  100. package/dist/styles/sprout/Dropdown.svelte.d.ts +1 -1
  101. package/dist/styles/sprout/Dropzone.svelte +9 -12
  102. package/dist/styles/sprout/Dropzone.svelte.d.ts +1 -1
  103. package/dist/styles/sprout/FileInput.svelte +11 -14
  104. package/dist/styles/sprout/FileInput.svelte.d.ts +2 -2
  105. package/dist/styles/sprout/Footer.svelte +1 -1
  106. package/dist/styles/sprout/Footer.svelte.d.ts +1 -1
  107. package/dist/styles/sprout/Modal.svelte +1 -3
  108. package/dist/styles/sprout/MultiSelect.svelte +5 -8
  109. package/dist/styles/sprout/Navbar.svelte +3 -9
  110. package/dist/styles/sprout/Pagination.svelte +3 -6
  111. package/dist/styles/sprout/PinInput.svelte +1 -4
  112. package/dist/styles/sprout/SearchInput.svelte +1 -4
  113. package/dist/styles/sprout/Sidebar.svelte +3 -6
  114. package/dist/styles/sprout/Spinner.svelte +2 -5
  115. package/dist/styles/sprout/Spinner.svelte.d.ts +1 -1
  116. package/dist/styles/sprout/Table.svelte +5 -8
  117. package/dist/styles/sprout/TagsInput.svelte +5 -8
  118. package/dist/styles/sprout/ThemeToggle.svelte +5 -8
  119. package/dist/styles/sprout/TimeInput.svelte +2 -5
  120. package/dist/styles/sprout/Toaster.svelte +3 -6
  121. package/dist/styles/sprout/index.d.ts +2 -3
  122. package/dist/styles/sprout/index.js +1 -3
  123. package/package.json +2 -19
  124. package/registry.json +3 -44
  125. package/dist/core/locale.svelte.d.ts +0 -101
  126. package/dist/core/locale.svelte.js +0 -167
  127. package/dist/styles/matte/LocaleProvider.svelte +0 -24
  128. package/dist/styles/matte/LocaleProvider.svelte.d.ts +0 -10
  129. package/dist/styles/paper/LocaleProvider.svelte +0 -24
  130. package/dist/styles/paper/LocaleProvider.svelte.d.ts +0 -10
  131. package/dist/styles/sprout/LocaleProvider.svelte +0 -24
  132. package/dist/styles/sprout/LocaleProvider.svelte.d.ts +0 -10
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import { toneBorder, toneSoft, toneSurface, toneText, type Tone } from '../../core/tones.js';
4
- import { useLocale } from '../../core/locale.svelte.js';
5
4
  import { formatSize as toReadableSize, sortFiles, type RejectedFile } from '../../core/files.js';
6
5
 
7
6
  interface Props {
@@ -16,7 +15,7 @@
16
15
  maxFiles?: number;
17
16
  disabled?: boolean;
18
17
  tone?: Tone;
19
- /** Headline inside the zone. Defaults to the locale's `dropFiles`. */
18
+ /** Headline inside the zone. Defaults to `Drop files here`. */
20
19
  label?: string;
21
20
  hint?: string;
22
21
  /** Hide the built-in file list to render your own. */
@@ -45,8 +44,6 @@
45
44
  class: className = ''
46
45
  }: Props = $props();
47
46
 
48
- const t = useLocale();
49
-
50
47
  let input: HTMLInputElement | null = $state(null);
51
48
  let dragging = $state(false);
52
49
  let rejected = $state<RejectedFile[]>([]);
@@ -63,7 +60,7 @@
63
60
  return parts.join(' · ');
64
61
  });
65
62
 
66
- const formatSize = (bytes: number) => toReadableSize(bytes, t.current.byteUnits);
63
+ const formatSize = (bytes: number) => toReadableSize(bytes);
67
64
 
68
65
  function add(incoming: FileList | File[] | null) {
69
66
  if (!incoming || disabled) return;
@@ -96,9 +93,9 @@
96
93
  }
97
94
 
98
95
  const reasons: Record<RejectedFile['reason'], string> = $derived({
99
- type: t.current.rejectedType,
100
- size: t.current.rejectedSize,
101
- count: t.current.rejectedCount
96
+ type: 'wrong file type',
97
+ size: 'too large',
98
+ count: 'over the file limit'
102
99
  });
103
100
  </script>
104
101
 
@@ -135,10 +132,10 @@
135
132
  {/if}
136
133
  </span>
137
134
  <span class="flex flex-col gap-1">
138
- <span class="text-[15px] font-medium text-text">{label ?? t.current.dropFiles}</span>
135
+ <span class="text-[15px] font-medium text-text">{label ?? 'Drop files here'}</span>
139
136
  <span class="text-sm text-text-secondary">
140
- {t.current.or}
141
- <span class="underline underline-offset-4">{t.current.browse}</span>
137
+ or
138
+ <span class="underline underline-offset-4">browse</span>
142
139
  </span>
143
140
  {#if defaultHint}
144
141
  <span class="font-mono text-xs text-text-muted">{defaultHint}</span>
@@ -180,7 +177,7 @@
180
177
  <button
181
178
  type="button"
182
179
  onclick={() => remove(i)}
183
- aria-label="{t.current.remove} {file.name}"
180
+ aria-label="Remove {file.name}"
184
181
  class="-m-1 shrink-0 p-1 text-text-muted transition-colors duration-150 hover:text-danger focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger"
185
182
  >
186
183
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -13,7 +13,7 @@ interface Props {
13
13
  maxFiles?: number;
14
14
  disabled?: boolean;
15
15
  tone?: Tone;
16
- /** Headline inside the zone. Defaults to the locale's `dropFiles`. */
16
+ /** Headline inside the zone. Defaults to `Drop files here`. */
17
17
  label?: string;
18
18
  hint?: string;
19
19
  /** Hide the built-in file list to render your own. */
@@ -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 the locale's `chooseFile`. */
20
+ /** Text on the button. Defaults to `Choose file`. */
22
21
  label?: string;
23
- /** Shown while nothing is chosen. Defaults to the locale's `noFile`. */
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, t.current.byteUnits);
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 ?? t.current.noFile;
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} ${t.current.selected}`;
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: t.current.rejectedType,
94
- size: t.current.rejectedSize,
95
- count: t.current.rejectedCount
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 border border-hairline bg-bg-alt font-mono tracking-wide 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 ?? t.current.chooseFile}
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={t.current.remove}
137
+ aria-label="Remove"
141
138
  class="mr-1 shrink-0 p-1 text-text-muted transition-colors duration-150 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">
@@ -180,7 +177,7 @@
180
177
  <button
181
178
  type="button"
182
179
  onclick={() => (files = files.filter((_, at) => at !== i))}
183
- aria-label="{t.current.remove} {file.name}"
180
+ aria-label="Remove {file.name}"
184
181
  class="-m-1 shrink-0 p-1 text-text-muted transition-colors duration-150 hover:text-danger focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-danger"
185
182
  >
186
183
  <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 the locale's `chooseFile`. */
18
+ /** Text on the button. Defaults to `Choose file`. */
19
19
  label?: string;
20
- /** Shown while nothing is chosen. Defaults to the locale's `noFile`. */
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 locale picker. */
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;
@@ -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 locale picker. */
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;
@@ -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,6 @@
40
39
  ...rest
41
40
  }: Props = $props();
42
41
 
43
- const t = useLocale();
44
42
  let dialog: HTMLDialogElement | null = $state(null);
45
43
 
46
44
  const closable = $derived(showClose ?? dismissible);
@@ -92,7 +90,7 @@
92
90
  <button
93
91
  type="button"
94
92
  onclick={close}
95
- aria-label={t.current.close}
93
+ aria-label="Close"
96
94
  class="-m-1 shrink-0 p-1 text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand"
97
95
  >
98
96
  <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-full px-2.5 py-1 font-mono text-xs {toneSoft[tone]}"
150
147
  >
151
148
  {chosen.length}
152
- {t.current.selected}
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="{t.current.removeItem} {option.label}"
162
+ aria-label="Remove {option.label}"
166
163
  class="inline-flex size-4 items-center justify-center rounded-full transition-colors duration-150 hover:bg-bg 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">
@@ -184,7 +181,7 @@
184
181
  aria-expanded={open}
185
182
  aria-controls="{id ?? 'multiselect'}-listbox"
186
183
  aria-invalid={invalid ? 'true' : undefined}
187
- placeholder={chosen.length ? '' : (placeholder ?? t.current.comboboxPlaceholder)}
184
+ placeholder={chosen.length ? '' : (placeholder ?? 'Search…')}
188
185
  oninput={() => {
189
186
  open = true;
190
187
  cursor.index = 0;
@@ -206,7 +203,7 @@
206
203
  onchange?.([]);
207
204
  inputEl?.focus();
208
205
  }}
209
- aria-label={t.current.clear}
206
+ aria-label="Clear selection"
210
207
  class="shrink-0 p-1.5 text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
211
208
  >
212
209
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -229,7 +226,7 @@
229
226
  >
230
227
  {#if flat.length === 0}
231
228
  <p class="px-3 py-6 text-center text-sm text-text-muted">
232
- {emptyText ?? t.current.noMatches}
229
+ {emptyText ?? 'No matches'}
233
230
  </p>
234
231
  {:else}
235
232
  {#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, 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
  const link =
59
56
  'inline-flex items-center gap-2 font-sans text-[15px] transition-colors duration-150 ease-brand-out';
60
57
  </script>
@@ -64,10 +61,7 @@
64
61
  ? 'border-b border-hairline'
65
62
  : ''} {className}"
66
63
  >
67
- <nav
68
- aria-label={t.current.navigation}
69
- class="mx-auto flex h-16 w-full max-w-6xl items-center gap-6 px-6"
70
- >
64
+ <nav aria-label="Navigation" class="mx-auto flex h-16 w-full max-w-6xl items-center gap-6 px-6">
71
65
  {#if brand}
72
66
  <div class="flex shrink-0 items-center">{@render brand()}</div>
73
67
  {/if}
@@ -138,7 +132,7 @@
138
132
  <button
139
133
  type="button"
140
134
  onclick={() => (menuOpen = true)}
141
- aria-label={t.current.openMenu}
135
+ aria-label="Open menu"
142
136
  aria-expanded={menuOpen}
143
137
  class="inline-flex size-9 items-center justify-center text-text-secondary transition-colors duration-150 hover:text-text md:hidden {focusRing} {toneRing[
144
138
  tone
@@ -152,7 +146,7 @@
152
146
  </nav>
153
147
  </header>
154
148
 
155
- <Drawer bind:open={menuOpen} side="right" size="sm" title={t.current.navigation}>
149
+ <Drawer bind:open={menuOpen} side="right" size="sm" title="Navigation">
156
150
  <ul class="flex flex-col">
157
151
  {#each items as item (item.label)}
158
152
  <li class="border-b border-hairline last:border-b-0">
@@ -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 px-2 font-mono text-[13px] transition-colors duration-150 ease-brand-out disabled:opacity-40 disabled:pointer-events-none ${focusRing}`;
40
37
  </script>
41
38
 
42
- <nav aria-label={t.current.pagination} class="flex items-center gap-1 {className}" {...rest}>
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={t.current.previousPage}
44
+ aria-label="Previous page"
48
45
  onclick={() => go(page - 1)}
49
46
  >
50
47
 
@@ -71,7 +68,7 @@
71
68
  type="button"
72
69
  class="{cell} {toneRing[tone]} text-text-secondary hover:bg-bg-inset"
73
70
  disabled={page >= total}
74
- aria-label={t.current.nextPage}
71
+ aria-label="Next page"
75
72
  onclick={() => go(page + 1)}
76
73
  >
77
74
 
@@ -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="{t.current.digit} {index + 1}"
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)}
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLInputAttributes } from 'svelte/elements';
4
- import { useLocale } from '../../core/locale.svelte.js';
5
4
  import type { Tone } from '../../core/tones.js';
6
5
  import Input, { type InputSize } from './Input.svelte';
7
6
 
@@ -37,8 +36,6 @@
37
36
  ...rest
38
37
  }: Props = $props();
39
38
 
40
- const t = useLocale();
41
-
42
39
  // The timer is cleared by the effect's own teardown, so a keystroke that
43
40
  // lands mid-wait restarts the clock rather than queueing a second call.
44
41
  $effect(() => {
@@ -75,7 +72,7 @@
75
72
  <button
76
73
  type="button"
77
74
  onclick={clear}
78
- aria-label={t.current.clearSearch}
75
+ aria-label="Clear search"
79
76
  class="-mr-1 p-1 text-text-muted transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
80
77
  >
81
78
  <svg class="size-3.5" viewBox="0 0 14 14" fill="none" aria-hidden="true">
@@ -35,7 +35,6 @@
35
35
  </script>
36
36
 
37
37
  <script lang="ts">
38
- import { useLocale } from '../../core/locale.svelte.js';
39
38
  import { iconMd } from './icon.js';
40
39
  import { focusRing, toneRing, toneSoft, type Tone } from '../../core/tones.js';
41
40
 
@@ -69,8 +68,6 @@
69
68
  class: className = ''
70
69
  }: Props = $props();
71
70
 
72
- const t = useLocale();
73
-
74
71
  const keyOf = (item: SidebarItem) => item.id ?? item.href ?? item.label;
75
72
 
76
73
  /** Groups start open when asked, or when they contain the active item. */
@@ -141,7 +138,7 @@
141
138
  </div>
142
139
  {/if}
143
140
 
144
- <nav aria-label={t.current.navigation} class="flex-1 overflow-y-auto py-3">
141
+ <nav aria-label="Navigation" class="flex-1 overflow-y-auto py-3">
145
142
  {#each sections as section, i (section.label ?? i)}
146
143
  <div class="flex flex-col gap-0.5 {i > 0 ? 'mt-4' : ''}">
147
144
  {#if section.label && !collapsed}
@@ -230,7 +227,7 @@
230
227
  <button
231
228
  type="button"
232
229
  onclick={() => (collapsed = !collapsed)}
233
- aria-label={collapsed ? t.current.expandSidebar : t.current.collapseSidebar}
230
+ aria-label={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
234
231
  class="inline-flex h-8 items-center justify-center gap-2 font-sans text-xs text-text-muted transition-colors duration-150 hover:text-text {focusRing} {toneRing[
235
232
  tone
236
233
  ]}"
@@ -248,7 +245,7 @@
248
245
  stroke-linecap="square"
249
246
  />
250
247
  </svg>
251
- {#if !collapsed}<span>{t.current.collapseSidebar}</span>{/if}
248
+ {#if !collapsed}<span>Collapse sidebar</span>{/if}
252
249
  </button>
253
250
  {/if}
254
251
  </div>
@@ -1,7 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { HTMLAttributes } from 'svelte/elements';
3
3
  import { toneText, type Tone } from '../../core/tones.js';
4
- import { useLocale } from '../../core/locale.svelte.js';
5
4
 
6
5
  export type SpinnerSize = 'xs' | 'sm' | 'md' | 'lg';
7
6
 
@@ -9,12 +8,10 @@
9
8
  size?: SpinnerSize;
10
9
  /** Omit to inherit the parent's `currentColor` (e.g. inside a Button). */
11
10
  tone?: Tone;
12
- /** Defaults to the locale's loading string; pass `''` to hide it. */
11
+ /** Defaults to `Loading`; pass `''` to hide it. */
13
12
  label?: string;
14
13
  }
15
14
 
16
- const t = useLocale();
17
-
18
15
  let { size = 'md', tone, label, class: className = '', ...rest }: Props = $props();
19
16
 
20
17
  const sizes: Record<SpinnerSize, string> = {
@@ -27,7 +24,7 @@
27
24
 
28
25
  <span
29
26
  role="status"
30
- aria-label={label ?? t.current.loading}
27
+ aria-label={label ?? 'Loading'}
31
28
  class="inline-block shrink-0 animate-spin rounded-full border-current border-t-transparent
32
29
  {sizes[size]} {tone ? toneText[tone] : ''} {className}"
33
30
  {...rest}
@@ -5,7 +5,7 @@ interface Props extends HTMLAttributes<HTMLSpanElement> {
5
5
  size?: SpinnerSize;
6
6
  /** Omit to inherit the parent's `currentColor` (e.g. inside a Button). */
7
7
  tone?: Tone;
8
- /** Defaults to the locale's loading string; pass `''` to hide it. */
8
+ /** Defaults to `Loading`; pass `''` to hide it. */
9
9
  label?: string;
10
10
  }
11
11
  declare const Spinner: import("svelte").Component<Props, {}, "">;
@@ -16,7 +16,6 @@
16
16
  import Checkbox from './Checkbox.svelte';
17
17
  import type { Tone } from '../../core/tones.js';
18
18
  import { nextSort, sortRows, toggleKey, type TableSort } from '../../core/table.js';
19
- import { useLocale } from '../../core/locale.svelte.js';
20
19
 
21
20
  interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onselect'> {
22
21
  columns: TableColumn[];
@@ -82,8 +81,6 @@
82
81
  ...rest
83
82
  }: Props = $props();
84
83
 
85
- const t = useLocale();
86
-
87
84
  const aligns = {
88
85
  left: 'text-left',
89
86
  center: 'text-center',
@@ -136,14 +133,14 @@
136
133
  >
137
134
  <span class="font-mono text-xs font-medium tracking-wide text-text uppercase">
138
135
  {selected.length}
139
- {t.current.selected}
136
+ selected
140
137
  </span>
141
138
  <button
142
139
  type="button"
143
140
  onclick={clearSelection}
144
141
  class="font-mono text-xs tracking-wide text-text-muted uppercase transition-colors duration-150 hover:text-text focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-current"
145
142
  >
146
- {t.current.clear}
143
+ Clear selection
147
144
  </button>
148
145
  <div class="ml-auto flex flex-wrap items-center gap-2">
149
146
  {@render bulkActions?.({ selected, clear: clearSelection })}
@@ -173,7 +170,7 @@
173
170
  checked={allSelected}
174
171
  indeterminate={someSelected}
175
172
  {tone}
176
- aria-label={t.current.selectAllRows}
173
+ aria-label="Select all rows"
177
174
  onchange={toggleAll}
178
175
  />
179
176
  </th>
@@ -220,7 +217,7 @@
220
217
  colspan={columns.length + (selectable ? 1 : 0)}
221
218
  class="px-3.5 py-10 text-center text-sm text-text-muted"
222
219
  >
223
- {#if empty}{@render empty()}{:else}{t.current.noData}{/if}
220
+ {#if empty}{@render empty()}{:else}No data{/if}
224
221
  </td>
225
222
  </tr>
226
223
  {:else}
@@ -237,7 +234,7 @@
237
234
  <Checkbox
238
235
  checked={isSelected}
239
236
  {tone}
240
- aria-label={t.current.selectRow}
237
+ aria-label="Select row"
241
238
  onchange={() => toggleRow(row)}
242
239
  />
243
240
  </td>
@@ -1,5 +1,4 @@
1
1
  <script lang="ts">
2
- import { useLocale } from '../../core/locale.svelte.js';
3
2
  import { addTag, splitTags, type TagRejection } from '../../core/tags.js';
4
3
  import { toneFocusWithinBorder, toneSoft, type Tone } from '../../core/tones.js';
5
4
 
@@ -46,8 +45,6 @@
46
45
  class: className = ''
47
46
  }: Props = $props();
48
47
 
49
- const t = useLocale();
50
-
51
48
  let draft = $state('');
52
49
  let field: HTMLInputElement | null = $state(null);
53
50
  /** The last refusal, cleared as soon as the next keystroke lands. */
@@ -111,8 +108,8 @@
111
108
  const messages: Record<TagRejection, string> = $derived({
112
109
  empty: '',
113
110
  short: `${minLength}+`,
114
- duplicate: t.current.duplicateTag,
115
- full: `${t.current.selected} ${tags.length}/${max}`
111
+ duplicate: 'Already added',
112
+ full: `$selected ${tags.length}/${max}`
116
113
  });
117
114
  </script>
118
115
 
@@ -136,7 +133,7 @@
136
133
  <button
137
134
  type="button"
138
135
  onclick={() => remove(index)}
139
- aria-label="{t.current.removeItem} {tag}"
136
+ aria-label="Remove {tag}"
140
137
  class="inline-flex size-4 items-center justify-center rounded-full transition-colors duration-150 hover:bg-bg focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-current"
141
138
  >
142
139
  <svg class="size-2.5" viewBox="0 0 10 10" fill="none" aria-hidden="true">
@@ -151,8 +148,8 @@
151
148
  bind:value={draft}
152
149
  {disabled}
153
150
  readonly={full}
154
- placeholder={tags.length === 0 ? (placeholder ?? t.current.addTag) : ''}
155
- aria-label={placeholder ?? t.current.addTag}
151
+ placeholder={tags.length === 0 ? (placeholder ?? 'Add a tag') : ''}
152
+ aria-label={placeholder ?? 'Add a tag'}
156
153
  aria-invalid={invalid ? 'true' : undefined}
157
154
  onkeydown={onKeydown}
158
155
  onpaste={onPaste}
@@ -10,7 +10,6 @@
10
10
  storedTheme,
11
11
  type Theme
12
12
  } from '../../core/theme.svelte.js';
13
- import { useLocale } from '../../core/locale.svelte.js';
14
13
 
15
14
  interface Props {
16
15
  /** Bindable. `'system'` follows the OS until the visitor chooses. */
@@ -33,8 +32,6 @@
33
32
  class: className = ''
34
33
  }: Props = $props();
35
34
 
36
- const t = useLocale();
37
-
38
35
  $effect(() => {
39
36
  applyTheme(theme);
40
37
  if (persist) persistTheme(theme);
@@ -50,8 +47,8 @@
50
47
  }
51
48
 
52
49
  const options = $derived([
53
- { value: 'light' as Theme, label: t.current.lightMode },
54
- { value: 'dark' as Theme, label: t.current.darkMode },
50
+ { value: 'light' as Theme, label: 'Light' },
51
+ { value: 'dark' as Theme, label: 'Dark' },
55
52
  { value: 'system' as Theme, label: 'Auto' }
56
53
  ]);
57
54
  </script>
@@ -85,7 +82,7 @@
85
82
  {#if variant === 'segmented'}
86
83
  <div
87
84
  role="radiogroup"
88
- aria-label={t.current.toggleTheme}
85
+ aria-label="Toggle theme"
89
86
  class="inline-flex items-center gap-0 border border-hairline bg-bg-alt p-1 {className}"
90
87
  >
91
88
  {#each options as option (option.value)}
@@ -109,8 +106,8 @@
109
106
  <button
110
107
  type="button"
111
108
  onclick={cycle}
112
- aria-label="{t.current.toggleTheme} ({theme})"
113
- title={t.current.toggleTheme}
109
+ aria-label="Toggle theme ({theme})"
110
+ title="Toggle theme"
114
111
  class="inline-flex size-9 items-center justify-center border border-hairline transition-colors duration-150 ease-brand-out {focusRing} {toneRing[
115
112
  tone
116
113
  ]} {toneSoft[tone]} {className}"