@nqmcreative/ui 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.md +15 -15
  2. package/cli/index.mjs +81 -42
  3. package/dist/core/toast.svelte.d.ts +3 -0
  4. package/dist/styles/matte/AccordionItem.svelte +5 -0
  5. package/dist/styles/matte/AccordionItem.svelte.d.ts +2 -0
  6. package/dist/styles/matte/Avatar.svelte +15 -0
  7. package/dist/styles/matte/Avatar.svelte.d.ts +3 -0
  8. package/dist/styles/matte/Badge.svelte +5 -0
  9. package/dist/styles/matte/Badge.svelte.d.ts +2 -0
  10. package/dist/styles/matte/Breadcrumb.svelte +11 -4
  11. package/dist/styles/matte/Breadcrumb.svelte.d.ts +3 -0
  12. package/dist/styles/matte/Button.svelte +58 -7
  13. package/dist/styles/matte/Button.svelte.d.ts +14 -2
  14. package/dist/styles/matte/Card.svelte +6 -1
  15. package/dist/styles/matte/Card.svelte.d.ts +2 -0
  16. package/dist/styles/matte/CheckboxGroup.svelte +6 -0
  17. package/dist/styles/matte/CheckboxGroup.svelte.d.ts +2 -0
  18. package/dist/styles/matte/Combobox.svelte +5 -0
  19. package/dist/styles/matte/Combobox.svelte.d.ts +3 -0
  20. package/dist/styles/matte/CommandPalette.svelte +6 -0
  21. package/dist/styles/matte/CommandPalette.svelte.d.ts +3 -0
  22. package/dist/styles/matte/Drawer.svelte +5 -0
  23. package/dist/styles/matte/Drawer.svelte.d.ts +2 -0
  24. package/dist/styles/matte/Footer.svelte +6 -2
  25. package/dist/styles/matte/Footer.svelte.d.ts +2 -0
  26. package/dist/styles/matte/Link.svelte +9 -0
  27. package/dist/styles/matte/Link.svelte.d.ts +4 -0
  28. package/dist/styles/matte/MenuItem.svelte +2 -1
  29. package/dist/styles/matte/Modal.svelte +5 -0
  30. package/dist/styles/matte/Modal.svelte.d.ts +2 -0
  31. package/dist/styles/matte/MultiSelect.svelte +7 -1
  32. package/dist/styles/matte/MultiSelect.svelte.d.ts +3 -0
  33. package/dist/styles/matte/Navbar.svelte +18 -4
  34. package/dist/styles/matte/Navbar.svelte.d.ts +2 -0
  35. package/dist/styles/matte/PricingCard.svelte +16 -8
  36. package/dist/styles/matte/PricingCard.svelte.d.ts +2 -0
  37. package/dist/styles/matte/RadioGroup.svelte +6 -0
  38. package/dist/styles/matte/RadioGroup.svelte.d.ts +2 -0
  39. package/dist/styles/matte/SegmentedControl.svelte +16 -2
  40. package/dist/styles/matte/SegmentedControl.svelte.d.ts +5 -0
  41. package/dist/styles/matte/Sidebar.svelte +2 -1
  42. package/dist/styles/matte/Stat.svelte +8 -1
  43. package/dist/styles/matte/Stat.svelte.d.ts +3 -0
  44. package/dist/styles/matte/Steps.svelte +7 -0
  45. package/dist/styles/matte/Steps.svelte.d.ts +3 -0
  46. package/dist/styles/matte/Table.svelte +153 -97
  47. package/dist/styles/matte/Table.svelte.d.ts +12 -0
  48. package/dist/styles/matte/Tabs.svelte +5 -0
  49. package/dist/styles/matte/Tabs.svelte.d.ts +3 -0
  50. package/dist/styles/matte/Toaster.svelte +3 -0
  51. package/dist/styles/matte/icon.d.ts +21 -0
  52. package/dist/styles/matte/icon.js +21 -0
  53. package/dist/styles/matte/index.d.ts +0 -2
  54. package/dist/styles/matte/index.js +0 -2
  55. package/dist/styles/paper/AccordionItem.svelte +5 -0
  56. package/dist/styles/paper/AccordionItem.svelte.d.ts +2 -0
  57. package/dist/styles/paper/Avatar.svelte +15 -0
  58. package/dist/styles/paper/Avatar.svelte.d.ts +3 -0
  59. package/dist/styles/paper/Badge.svelte +5 -0
  60. package/dist/styles/paper/Badge.svelte.d.ts +2 -0
  61. package/dist/styles/paper/Breadcrumb.svelte +8 -4
  62. package/dist/styles/paper/Breadcrumb.svelte.d.ts +3 -0
  63. package/dist/styles/paper/Button.svelte +58 -7
  64. package/dist/styles/paper/Button.svelte.d.ts +14 -2
  65. package/dist/styles/paper/Card.svelte +6 -1
  66. package/dist/styles/paper/Card.svelte.d.ts +2 -0
  67. package/dist/styles/paper/CheckboxGroup.svelte +6 -0
  68. package/dist/styles/paper/CheckboxGroup.svelte.d.ts +2 -0
  69. package/dist/styles/paper/Combobox.svelte +5 -0
  70. package/dist/styles/paper/Combobox.svelte.d.ts +3 -0
  71. package/dist/styles/paper/CommandPalette.svelte +6 -0
  72. package/dist/styles/paper/CommandPalette.svelte.d.ts +3 -0
  73. package/dist/styles/paper/Drawer.svelte +5 -0
  74. package/dist/styles/paper/Drawer.svelte.d.ts +2 -0
  75. package/dist/styles/paper/Footer.svelte +6 -2
  76. package/dist/styles/paper/Footer.svelte.d.ts +2 -0
  77. package/dist/styles/paper/Link.svelte +9 -0
  78. package/dist/styles/paper/Link.svelte.d.ts +4 -0
  79. package/dist/styles/paper/MenuItem.svelte +2 -1
  80. package/dist/styles/paper/Modal.svelte +5 -0
  81. package/dist/styles/paper/Modal.svelte.d.ts +2 -0
  82. package/dist/styles/paper/MultiSelect.svelte +7 -1
  83. package/dist/styles/paper/MultiSelect.svelte.d.ts +3 -0
  84. package/dist/styles/paper/Navbar.svelte +18 -4
  85. package/dist/styles/paper/Navbar.svelte.d.ts +2 -0
  86. package/dist/styles/paper/PricingCard.svelte +23 -15
  87. package/dist/styles/paper/PricingCard.svelte.d.ts +2 -0
  88. package/dist/styles/paper/RadioGroup.svelte +6 -0
  89. package/dist/styles/paper/RadioGroup.svelte.d.ts +2 -0
  90. package/dist/styles/paper/SegmentedControl.svelte +16 -2
  91. package/dist/styles/paper/SegmentedControl.svelte.d.ts +5 -0
  92. package/dist/styles/paper/Sidebar.svelte +2 -1
  93. package/dist/styles/paper/Stat.svelte +8 -1
  94. package/dist/styles/paper/Stat.svelte.d.ts +3 -0
  95. package/dist/styles/paper/Steps.svelte +7 -0
  96. package/dist/styles/paper/Steps.svelte.d.ts +3 -0
  97. package/dist/styles/paper/Table.svelte +153 -97
  98. package/dist/styles/paper/Table.svelte.d.ts +12 -0
  99. package/dist/styles/paper/Tabs.svelte +5 -0
  100. package/dist/styles/paper/Tabs.svelte.d.ts +3 -0
  101. package/dist/styles/paper/Toaster.svelte +3 -0
  102. package/dist/styles/paper/icon.d.ts +21 -0
  103. package/dist/styles/paper/icon.js +21 -0
  104. package/dist/styles/paper/index.d.ts +0 -2
  105. package/dist/styles/paper/index.js +0 -2
  106. package/dist/styles/sprout/AccordionItem.svelte +5 -0
  107. package/dist/styles/sprout/AccordionItem.svelte.d.ts +2 -0
  108. package/dist/styles/sprout/Avatar.svelte +15 -0
  109. package/dist/styles/sprout/Avatar.svelte.d.ts +3 -0
  110. package/dist/styles/sprout/Badge.svelte +5 -0
  111. package/dist/styles/sprout/Badge.svelte.d.ts +2 -0
  112. package/dist/styles/sprout/Breadcrumb.svelte +8 -4
  113. package/dist/styles/sprout/Breadcrumb.svelte.d.ts +3 -0
  114. package/dist/styles/sprout/Button.svelte +58 -7
  115. package/dist/styles/sprout/Button.svelte.d.ts +14 -2
  116. package/dist/styles/sprout/Card.svelte +6 -1
  117. package/dist/styles/sprout/Card.svelte.d.ts +2 -0
  118. package/dist/styles/sprout/CheckboxGroup.svelte +6 -0
  119. package/dist/styles/sprout/CheckboxGroup.svelte.d.ts +2 -0
  120. package/dist/styles/sprout/Combobox.svelte +5 -0
  121. package/dist/styles/sprout/Combobox.svelte.d.ts +3 -0
  122. package/dist/styles/sprout/CommandPalette.svelte +6 -0
  123. package/dist/styles/sprout/CommandPalette.svelte.d.ts +3 -0
  124. package/dist/styles/sprout/Drawer.svelte +5 -0
  125. package/dist/styles/sprout/Drawer.svelte.d.ts +2 -0
  126. package/dist/styles/sprout/Footer.svelte +6 -2
  127. package/dist/styles/sprout/Footer.svelte.d.ts +2 -0
  128. package/dist/styles/sprout/Link.svelte +9 -0
  129. package/dist/styles/sprout/Link.svelte.d.ts +4 -0
  130. package/dist/styles/sprout/MenuItem.svelte +2 -1
  131. package/dist/styles/sprout/Modal.svelte +5 -0
  132. package/dist/styles/sprout/Modal.svelte.d.ts +2 -0
  133. package/dist/styles/sprout/MultiSelect.svelte +7 -1
  134. package/dist/styles/sprout/MultiSelect.svelte.d.ts +3 -0
  135. package/dist/styles/sprout/Navbar.svelte +18 -4
  136. package/dist/styles/sprout/Navbar.svelte.d.ts +2 -0
  137. package/dist/styles/sprout/PricingCard.svelte +23 -15
  138. package/dist/styles/sprout/PricingCard.svelte.d.ts +2 -0
  139. package/dist/styles/sprout/RadioGroup.svelte +6 -0
  140. package/dist/styles/sprout/RadioGroup.svelte.d.ts +2 -0
  141. package/dist/styles/sprout/SegmentedControl.svelte +16 -2
  142. package/dist/styles/sprout/SegmentedControl.svelte.d.ts +5 -0
  143. package/dist/styles/sprout/Sidebar.svelte +2 -1
  144. package/dist/styles/sprout/Stat.svelte +8 -1
  145. package/dist/styles/sprout/Stat.svelte.d.ts +3 -0
  146. package/dist/styles/sprout/Steps.svelte +7 -0
  147. package/dist/styles/sprout/Steps.svelte.d.ts +3 -0
  148. package/dist/styles/sprout/Table.svelte +153 -97
  149. package/dist/styles/sprout/Table.svelte.d.ts +12 -0
  150. package/dist/styles/sprout/Tabs.svelte +5 -0
  151. package/dist/styles/sprout/Tabs.svelte.d.ts +3 -0
  152. package/dist/styles/sprout/Toaster.svelte +3 -0
  153. package/dist/styles/sprout/icon.d.ts +21 -0
  154. package/dist/styles/sprout/icon.js +21 -0
  155. package/dist/styles/sprout/index.d.ts +0 -2
  156. package/dist/styles/sprout/index.js +0 -2
  157. package/package.json +2 -26
  158. package/registry.json +1 -35
  159. package/dist/styles/matte/NumberInput.svelte +0 -96
  160. package/dist/styles/matte/NumberInput.svelte.d.ts +0 -15
  161. package/dist/styles/matte/PasswordInput.svelte +0 -95
  162. package/dist/styles/matte/PasswordInput.svelte.d.ts +0 -12
  163. package/dist/styles/paper/NumberInput.svelte +0 -96
  164. package/dist/styles/paper/NumberInput.svelte.d.ts +0 -15
  165. package/dist/styles/paper/PasswordInput.svelte +0 -100
  166. package/dist/styles/paper/PasswordInput.svelte.d.ts +0 -12
  167. package/dist/styles/sprout/NumberInput.svelte +0 -97
  168. package/dist/styles/sprout/NumberInput.svelte.d.ts +0 -15
  169. package/dist/styles/sprout/PasswordInput.svelte +0 -101
  170. package/dist/styles/sprout/PasswordInput.svelte.d.ts +0 -12
@@ -2,6 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAnchorAttributes } from 'svelte/elements';
4
4
  import { focusRing, toneHoverText, toneRing, toneText, type Tone } from '../../core/tones.js';
5
+ import { iconSm } from './icon.js';
5
6
 
6
7
  interface Props extends HTMLAnchorAttributes {
7
8
  tone?: Tone;
@@ -11,6 +12,10 @@
11
12
  external?: boolean;
12
13
  /** Inherit the surrounding text colour instead of the tone. */
13
14
  muted?: boolean;
15
+ /** Leading icon, 14px. */
16
+ icon?: Snippet;
17
+ /** Trailing icon, before the external mark. */
18
+ iconEnd?: Snippet;
14
19
  children: Snippet;
15
20
  }
16
21
 
@@ -19,6 +24,8 @@
19
24
  underline = 'hover',
20
25
  external = false,
21
26
  muted = false,
27
+ icon,
28
+ iconEnd,
22
29
  class: className = '',
23
30
  children,
24
31
  ...rest
@@ -39,6 +46,8 @@
39
46
  {underlines[underline]} {focusRing} {toneRing[tone]} {className}"
40
47
  {...rest}
41
48
  >
49
+ {#if icon}<span class={iconSm}>{@render icon()}</span>{/if}
42
50
  {@render children()}
51
+ {#if iconEnd}<span class={iconSm}>{@render iconEnd()}</span>{/if}
43
52
  {#if external}<span aria-hidden="true" class="text-[0.85em]">↗</span>{/if}
44
53
  </a>
@@ -9,6 +9,10 @@ interface Props extends HTMLAnchorAttributes {
9
9
  external?: boolean;
10
10
  /** Inherit the surrounding text colour instead of the tone. */
11
11
  muted?: boolean;
12
+ /** Leading icon, 14px. */
13
+ icon?: Snippet;
14
+ /** Trailing icon, before the external mark. */
15
+ iconEnd?: Snippet;
12
16
  children: Snippet;
13
17
  }
14
18
  declare const Link: import("svelte").Component<Props, {}, "">;
@@ -2,6 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLButtonAttributes } from 'svelte/elements';
4
4
  import { toneSoftHover, toneText, type Tone } from '../../core/tones.js';
5
+ import { iconMd } from './icon.js';
5
6
 
6
7
  interface Props extends HTMLButtonAttributes {
7
8
  /** Renders an `<a>` instead of a `<button>`. */
@@ -55,7 +56,7 @@
55
56
  />
56
57
  </svg>
57
58
  {:else if icon}
58
- <span class="flex size-3.5 shrink-0 items-center justify-center">{@render icon()}</span>
59
+ <span class={iconMd}>{@render icon()}</span>
59
60
  {/if}
60
61
  <span class="min-w-0 flex-1 truncate">{@render children()}</span>
61
62
  {#if shortcut}
@@ -1,5 +1,6 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from 'svelte';
3
+ import { iconLg } from './icon.js';
3
4
  import type { HTMLDialogAttributes } from 'svelte/elements';
4
5
  import { useLocale } from '../../core/locale.svelte.js';
5
6
  import { isBackdropClick, syncDialog } from '../../core/dialog.svelte.js';
@@ -12,6 +13,8 @@
12
13
  open?: boolean;
13
14
  title?: string;
14
15
  description?: string;
16
+ /** Leading icon beside the title. */
17
+ icon?: Snippet;
15
18
  size?: ModalSize;
16
19
  /** Clicking the backdrop or pressing Escape closes the dialog. */
17
20
  dismissible?: boolean;
@@ -27,6 +30,7 @@
27
30
  open = $bindable(false),
28
31
  title,
29
32
  description,
33
+ icon,
30
34
  size = 'md',
31
35
  dismissible = true,
32
36
  showClose,
@@ -76,6 +80,7 @@
76
80
  <div class="flex flex-col">
77
81
  {#if title || closable}
78
82
  <div class="flex items-start gap-4 px-6 pt-5 pb-3">
83
+ {#if icon}<span class={iconLg}>{@render icon()}</span>{/if}
79
84
  <div class="flex min-w-0 flex-1 flex-col gap-1">
80
85
  {#if title}
81
86
  <h2 class="font-heading text-base font-semibold text-text">{title}</h2>
@@ -6,6 +6,8 @@ interface Props extends HTMLDialogAttributes {
6
6
  open?: boolean;
7
7
  title?: string;
8
8
  description?: string;
9
+ /** Leading icon beside the title. */
10
+ icon?: Snippet;
9
11
  size?: ModalSize;
10
12
  /** Clicking the backdrop or pressing Escape closes the dialog. */
11
13
  dismissible?: boolean;
@@ -1,7 +1,11 @@
1
1
  <script module lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
2
4
  export interface MultiSelectOption {
3
5
  value: string;
4
6
  label: string;
7
+ /** Leading icon, 16px. */
8
+ icon?: Snippet;
5
9
  description?: string;
6
10
  disabled?: boolean;
7
11
  group?: string;
@@ -13,6 +17,7 @@
13
17
  import { clickOutside, portal } from '../../core/actions/dismissable.js';
14
18
  import { ListCursor, groupItems, matchQuery, revealIndex } from '../../core/list.svelte.js';
15
19
  import { useLocale } from '../../core/locale.svelte.js';
20
+ import { iconMd, iconSm } from './icon.js';
16
21
  import { toneFocusWithinBorder, toneSoft, toneText, type Tone } from '../../core/tones.js';
17
22
  import { edge, soft } from './lift.js';
18
23
 
@@ -154,7 +159,7 @@
154
159
  tone
155
160
  ]}"
156
161
  >
157
- {option.label}
162
+ {#if option.icon}<span class={iconSm}>{@render option.icon()}</span>{/if}{option.label}
158
163
  <button
159
164
  type="button"
160
165
  onclick={() => remove(option)}
@@ -274,6 +279,7 @@
274
279
  </svg>
275
280
  {/if}
276
281
  </span>
282
+ {#if option.icon}<span class={iconMd}>{@render option.icon()}</span>{/if}
277
283
  <span class="flex min-w-0 flex-1 flex-col gap-0.5">
278
284
  <span class="truncate">{option.label}</span>
279
285
  {#if option.description}
@@ -1,6 +1,9 @@
1
+ import type { Snippet } from 'svelte';
1
2
  export interface MultiSelectOption {
2
3
  value: string;
3
4
  label: string;
5
+ /** Leading icon, 16px. */
6
+ icon?: Snippet;
4
7
  description?: string;
5
8
  disabled?: boolean;
6
9
  group?: string;
@@ -2,6 +2,8 @@
2
2
  export interface NavItem {
3
3
  label: string;
4
4
  href?: string;
5
+ /** Leading icon, 16px. */
6
+ icon?: Snippet;
5
7
  /** Marks the current page — also sets `aria-current`. */
6
8
  active?: boolean;
7
9
  disabled?: boolean;
@@ -15,6 +17,7 @@
15
17
  <script lang="ts">
16
18
  import type { Snippet } from 'svelte';
17
19
  import { useLocale } from '../../core/locale.svelte.js';
20
+ import { iconMd } from './icon.js';
18
21
  import { focusRing, toneRing, toneSoft, toneText, type Tone } from '../../core/tones.js';
19
22
  import Drawer from './Drawer.svelte';
20
23
  import Dropdown from './Dropdown.svelte';
@@ -84,6 +87,7 @@
84
87
  ? `${toneSoft[tone]}`
85
88
  : 'text-text-secondary hover:bg-bg-inset hover:text-text'}"
86
89
  >
90
+ {#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}
87
91
  {item.label}
88
92
  <svg class="size-3" viewBox="0 0 16 16" fill="none" aria-hidden="true">
89
93
  <path
@@ -96,7 +100,12 @@
96
100
  </button>
97
101
  {/snippet}
98
102
  {#each item.items as child (child.label)}
99
- <MenuItem href={child.href} disabled={child.disabled} selected={child.active}>
103
+ <MenuItem
104
+ href={child.href}
105
+ disabled={child.disabled}
106
+ selected={child.active}
107
+ icon={child.icon}
108
+ >
100
109
  {child.label}
101
110
  </MenuItem>
102
111
  {/each}
@@ -111,6 +120,7 @@
111
120
  ? 'pointer-events-none opacity-50'
112
121
  : ''}"
113
122
  >
123
+ {#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}
114
124
  {item.label}
115
125
  {#if item.badge !== undefined}
116
126
  <span class="text-[11px] text-text-muted tabular-nums">{item.badge}</span>
@@ -163,7 +173,10 @@
163
173
  ? 'pointer-events-none opacity-50'
164
174
  : ''}"
165
175
  >
166
- {item.label}
176
+ <span class="flex min-w-0 items-center gap-2.5">
177
+ {#if item.icon}<span class={iconMd}>{@render item.icon()}</span>{/if}
178
+ {item.label}
179
+ </span>
167
180
  {#if item.badge !== undefined}
168
181
  <span class="text-[11px] text-text-muted tabular-nums">{item.badge}</span>
169
182
  {/if}
@@ -175,11 +188,12 @@
175
188
  <a
176
189
  href={child.href}
177
190
  onclick={() => (menuOpen = false)}
178
- class="block rounded-xl px-3 py-2 font-sans text-[13px] text-text-muted hover:bg-bg-inset {toneText[
191
+ class="flex items-center gap-2.5 rounded-xl px-3 py-2 font-sans text-[13px] text-text-muted hover:bg-bg-inset {toneText[
179
192
  tone
180
193
  ]}"
181
194
  >
182
- {child.label}
195
+ {#if child.icon}<span class={iconMd}>{@render child.icon()}</span
196
+ >{/if}{child.label}
183
197
  </a>
184
198
  </li>
185
199
  {/each}
@@ -1,6 +1,8 @@
1
1
  export interface NavItem {
2
2
  label: string;
3
3
  href?: string;
4
+ /** Leading icon, 16px. */
5
+ icon?: Snippet;
4
6
  /** Marks the current page — also sets `aria-current`. */
5
7
  active?: boolean;
6
8
  disabled?: boolean;
@@ -2,6 +2,7 @@
2
2
  import type { Snippet } from 'svelte';
3
3
  import type { HTMLAttributes } from 'svelte/elements';
4
4
  import { toneBorder, toneSoft, toneSolid, type Tone } from '../../core/tones.js';
5
+ import { iconMd } from './icon.js';
5
6
  import { edge, soft } from './lift.js';
6
7
 
7
8
  interface Props extends HTMLAttributes<HTMLDivElement> {
@@ -13,6 +14,8 @@
13
14
  description?: string;
14
15
  /** One line each. Rendered with a tick. */
15
16
  features?: string[];
17
+ /** Replaces the tick beside every feature. */
18
+ featureIcon?: Snippet;
16
19
  /** Pill in the top corner, e.g. "Most popular". */
17
20
  badge?: string;
18
21
  /** Ring, lift and a tinted header — for the plan you want chosen. */
@@ -30,6 +33,7 @@
30
33
  period,
31
34
  description,
32
35
  features = [],
36
+ featureIcon,
33
37
  badge,
34
38
  featured = false,
35
39
  tone = 'brand',
@@ -78,21 +82,25 @@
78
82
  <ul class="flex flex-1 flex-col gap-3 border-t border-hairline p-6">
79
83
  {#each features as feature (feature)}
80
84
  <li class="flex items-start gap-3 font-sans text-sm text-text-secondary">
81
- <span
82
- class="mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full {toneSoft[
83
- tone
84
- ]}"
85
- >
86
- <svg class="size-2.5" viewBox="0 0 16 16" fill="none" aria-hidden="true">
87
- <path
88
- d="m3 8 3.2 3.2L13 4.4"
89
- stroke="currentColor"
90
- stroke-width="2.4"
91
- stroke-linecap="round"
92
- stroke-linejoin="round"
93
- />
94
- </svg>
95
- </span>
85
+ {#if featureIcon}
86
+ <span class="mt-0.5 {iconMd}">{@render featureIcon()}</span>
87
+ {:else}
88
+ <span
89
+ class="mt-0.5 flex size-4 shrink-0 items-center justify-center rounded-full {toneSoft[
90
+ tone
91
+ ]}"
92
+ >
93
+ <svg class="size-2.5" viewBox="0 0 16 16" fill="none" aria-hidden="true">
94
+ <path
95
+ d="m3 8 3.2 3.2L13 4.4"
96
+ stroke="currentColor"
97
+ stroke-width="2.4"
98
+ stroke-linecap="round"
99
+ stroke-linejoin="round"
100
+ />
101
+ </svg>
102
+ </span>
103
+ {/if}
96
104
  <span class="leading-snug">{feature}</span>
97
105
  </li>
98
106
  {/each}
@@ -10,6 +10,8 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
10
10
  description?: string;
11
11
  /** One line each. Rendered with a tick. */
12
12
  features?: string[];
13
+ /** Replaces the tick beside every feature. */
14
+ featureIcon?: Snippet;
13
15
  /** Pill in the top corner, e.g. "Most popular". */
14
16
  badge?: string;
15
17
  /** Ring, lift and a tinted header — for the plan you want chosen. */
@@ -3,11 +3,14 @@
3
3
  import type { HTMLFieldsetAttributes } from 'svelte/elements';
4
4
  import Radio from './Radio.svelte';
5
5
  import { toneBorder, toneSurface, type Tone } from '../../core/tones.js';
6
+ import { iconLg } from './icon.js';
6
7
  import { edge } from './lift.js';
7
8
 
8
9
  export interface RadioOption {
9
10
  value: string;
10
11
  label: string;
12
+ /** Leading icon, 20px. Drawn in `boxed` mode only. */
13
+ icon?: Snippet;
11
14
  description?: string;
12
15
  disabled?: boolean;
13
16
  }
@@ -63,6 +66,9 @@
63
66
  : 'border-hairline bg-bg hover:border-hairline-strong'}
64
67
  {option.disabled ? 'pointer-events-none opacity-50' : ''}"
65
68
  >
69
+ {#if option.icon}
70
+ <span class={iconLg}>{@render option.icon()}</span>
71
+ {/if}
66
72
  <Radio
67
73
  bind:group={value}
68
74
  value={option.value}
@@ -4,6 +4,8 @@ import { type Tone } from '../../core/tones.js';
4
4
  export interface RadioOption {
5
5
  value: string;
6
6
  label: string;
7
+ /** Leading icon, 20px. Drawn in `boxed` mode only. */
8
+ icon?: Snippet;
7
9
  description?: string;
8
10
  disabled?: boolean;
9
11
  }
@@ -1,11 +1,15 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
2
3
  import type { HTMLAttributes } from 'svelte/elements';
3
4
  import { focusRing, toneRing, type Tone } from '../../core/tones.js';
5
+ import { iconMd } from './icon.js';
4
6
  import { edge } from './lift.js';
5
7
 
6
8
  export interface SegmentOption {
7
9
  value: string;
8
10
  label: string;
11
+ /** Leading icon, 16px. */
12
+ icon?: Snippet;
9
13
  disabled?: boolean;
10
14
  }
11
15
 
@@ -21,6 +25,8 @@
21
25
  tone?: Tone;
22
26
  /** Spread the segments evenly across the full width. */
23
27
  fullWidth?: boolean;
28
+ /** Icons only — each option's `label` becomes its accessible name. */
29
+ iconOnly?: boolean;
24
30
  label?: string;
25
31
  disabled?: boolean;
26
32
  onchange?: (value: string) => void;
@@ -33,6 +39,7 @@
33
39
  size = 'md',
34
40
  tone = 'brand',
35
41
  fullWidth = false,
42
+ iconOnly = false,
36
43
  label,
37
44
  disabled = false,
38
45
  class: className = '',
@@ -45,6 +52,11 @@
45
52
  md: 'h-8 px-3.5 text-sm'
46
53
  };
47
54
 
55
+ const squares: Record<SegmentedSize, string> = {
56
+ sm: 'size-7 text-[13px]',
57
+ md: 'size-8 text-sm'
58
+ };
59
+
48
60
  function select(option: SegmentOption) {
49
61
  if (option.disabled || disabled) return;
50
62
  value = option.value;
@@ -75,14 +87,16 @@
75
87
  type="button"
76
88
  role="radio"
77
89
  aria-checked={value === option.value}
90
+ aria-label={iconOnly ? option.label : undefined}
78
91
  disabled={option.disabled || disabled}
79
92
  tabindex={value === option.value ? 0 : -1}
80
93
  onclick={() => select(option)}
81
94
  class="inline-flex items-center justify-center rounded-xl font-sans font-medium whitespace-nowrap transition-all duration-150 ease-brand-out disabled:pointer-events-none disabled:opacity-40
82
- {sizes[size]} {focusRing} {toneRing[tone]}
95
+ {iconOnly ? squares[size] : sizes[size]} {focusRing} {toneRing[tone]}
83
96
  {value === option.value ? `bg-bg text-text ${edge}` : 'text-text-muted hover:text-text'}"
84
97
  >
85
- {option.label}
98
+ {#if option.icon}<span class={iconMd}>{@render option.icon()}</span>{/if}
99
+ {#if !iconOnly}{option.label}{/if}
86
100
  </button>
87
101
  {/each}
88
102
  {#if name}<input type="hidden" {name} {value} />{/if}
@@ -1,8 +1,11 @@
1
+ import type { Snippet } from 'svelte';
1
2
  import type { HTMLAttributes } from 'svelte/elements';
2
3
  import { type Tone } from '../../core/tones.js';
3
4
  export interface SegmentOption {
4
5
  value: string;
5
6
  label: string;
7
+ /** Leading icon, 16px. */
8
+ icon?: Snippet;
6
9
  disabled?: boolean;
7
10
  }
8
11
  export type SegmentedSize = 'sm' | 'md';
@@ -16,6 +19,8 @@ interface Props extends Omit<HTMLAttributes<HTMLDivElement>, 'onchange'> {
16
19
  tone?: Tone;
17
20
  /** Spread the segments evenly across the full width. */
18
21
  fullWidth?: boolean;
22
+ /** Icons only — each option's `label` becomes its accessible name. */
23
+ iconOnly?: boolean;
19
24
  label?: string;
20
25
  disabled?: boolean;
21
26
  onchange?: (value: string) => void;
@@ -36,6 +36,7 @@
36
36
 
37
37
  <script lang="ts">
38
38
  import { useLocale } from '../../core/locale.svelte.js';
39
+ import { iconMd } from './icon.js';
39
40
  import { focusRing, toneRing, toneSoft, type Tone } from '../../core/tones.js';
40
41
 
41
42
  interface Props {
@@ -126,7 +127,7 @@
126
127
  <!-- One box for every glyph, so rows line up whether or not an item has one. -->
127
128
  {#snippet glyph(item: SidebarItem)}
128
129
  {#if item.icon}
129
- <span class="grid size-4 shrink-0 place-items-center *:size-4">{@render item.icon()}</span>
130
+ <span class={iconMd}>{@render item.icon()}</span>
130
131
  {/if}
131
132
  {/snippet}
132
133
 
@@ -1,6 +1,8 @@
1
1
  <script lang="ts">
2
+ import type { Snippet } from 'svelte';
2
3
  import type { HTMLAttributes } from 'svelte/elements';
3
4
  import { toneText, type Tone } from '../../core/tones.js';
5
+ import { iconMd } from './icon.js';
4
6
 
5
7
  export type StatTrend = 'up' | 'down' | 'flat';
6
8
 
@@ -11,6 +13,8 @@
11
13
  delta?: string;
12
14
  trend?: StatTrend;
13
15
  hint?: string;
16
+ /** Leading icon on the label row. */
17
+ icon?: Snippet;
14
18
  /** Colour of the value itself. Leave unset for plain `text`. */
15
19
  tone?: Tone;
16
20
  }
@@ -21,6 +25,7 @@
21
25
  delta,
22
26
  trend = 'flat',
23
27
  hint,
28
+ icon,
24
29
  tone,
25
30
  class: className = '',
26
31
  ...rest
@@ -31,7 +36,9 @@
31
36
  </script>
32
37
 
33
38
  <div class="flex flex-col gap-1 font-sans {className}" {...rest}>
34
- <span class="text-sm font-medium text-text-muted">{label}</span>
39
+ <span class="flex items-center gap-2 text-sm font-medium text-text-muted"
40
+ >{#if icon}<span class={iconMd}>{@render icon()}</span>{/if}{label}</span
41
+ >
35
42
  <div class="flex items-baseline gap-2">
36
43
  <span
37
44
  class="font-heading text-3xl font-semibold tracking-tight tabular-nums {tone
@@ -1,3 +1,4 @@
1
+ import type { Snippet } from 'svelte';
1
2
  import type { HTMLAttributes } from 'svelte/elements';
2
3
  import { type Tone } from '../../core/tones.js';
3
4
  export type StatTrend = 'up' | 'down' | 'flat';
@@ -8,6 +9,8 @@ interface Props extends HTMLAttributes<HTMLDivElement> {
8
9
  delta?: string;
9
10
  trend?: StatTrend;
10
11
  hint?: string;
12
+ /** Leading icon on the label row. */
13
+ icon?: Snippet;
11
14
  /** Colour of the value itself. Leave unset for plain `text`. */
12
15
  tone?: Tone;
13
16
  }
@@ -1,7 +1,11 @@
1
1
  <script module lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
2
4
  export interface StepItem {
3
5
  label: string;
4
6
  description?: string;
7
+ /** Replaces the step number. A finished step still shows its check. */
8
+ icon?: Snippet;
5
9
  /** Marks the step as failed — shown in `danger` regardless of position. */
6
10
  error?: boolean;
7
11
  disabled?: boolean;
@@ -10,6 +14,7 @@
10
14
 
11
15
  <script lang="ts">
12
16
  import type { HTMLAttributes } from 'svelte/elements';
17
+ import { iconMd } from './icon.js';
13
18
  import { focusRing, toneFill, toneRing, toneText, type Tone } from '../../core/tones.js';
14
19
  import { liftSm } from './lift.js';
15
20
 
@@ -75,6 +80,8 @@
75
80
  stroke-linejoin="round"
76
81
  />
77
82
  </svg>
83
+ {:else if item.icon}
84
+ <span class={iconMd}>{@render item.icon()}</span>
78
85
  {:else}
79
86
  {index + 1}
80
87
  {/if}
@@ -1,6 +1,9 @@
1
+ import type { Snippet } from 'svelte';
1
2
  export interface StepItem {
2
3
  label: string;
3
4
  description?: string;
5
+ /** Replaces the step number. A finished step still shows its check. */
6
+ icon?: Snippet;
4
7
  /** Marks the step as failed — shown in `danger` regardless of position. */
5
8
  error?: boolean;
6
9
  disabled?: boolean;