@sveltier/ui 0.0.0-stage → 0.1.0-alpha.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 (276) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +82 -2
  3. package/dist/components/Alert/Alert.svelte +88 -0
  4. package/dist/components/Alert/Alert.svelte.d.ts +4 -0
  5. package/dist/components/Alert/alert.css +78 -0
  6. package/dist/components/Alert/index.d.ts +2 -0
  7. package/dist/components/Alert/index.js +1 -0
  8. package/dist/components/Alert/types.d.ts +67 -0
  9. package/dist/components/Alert/types.js +1 -0
  10. package/dist/components/Avatar/Avatar.svelte +47 -0
  11. package/dist/components/Avatar/Avatar.svelte.d.ts +4 -0
  12. package/dist/components/Avatar/avatar.css +34 -0
  13. package/dist/components/Avatar/index.d.ts +2 -0
  14. package/dist/components/Avatar/index.js +1 -0
  15. package/dist/components/Avatar/types.d.ts +47 -0
  16. package/dist/components/Avatar/types.js +1 -0
  17. package/dist/components/Badge/Badge.svelte +53 -0
  18. package/dist/components/Badge/Badge.svelte.d.ts +4 -0
  19. package/dist/components/Badge/badge.css +57 -0
  20. package/dist/components/Badge/index.d.ts +2 -0
  21. package/dist/components/Badge/index.js +1 -0
  22. package/dist/components/Badge/types.d.ts +43 -0
  23. package/dist/components/Badge/types.js +1 -0
  24. package/dist/components/Button/Button.svelte +85 -0
  25. package/dist/components/Button/Button.svelte.d.ts +4 -0
  26. package/dist/components/Button/button.css +132 -0
  27. package/dist/components/Button/index.d.ts +2 -0
  28. package/dist/components/Button/index.js +1 -0
  29. package/dist/components/Button/types.d.ts +76 -0
  30. package/dist/components/Button/types.js +1 -0
  31. package/dist/components/ButtonGroup/ButtonGroup.svelte +61 -0
  32. package/dist/components/ButtonGroup/ButtonGroup.svelte.d.ts +4 -0
  33. package/dist/components/ButtonGroup/button-group.css +39 -0
  34. package/dist/components/ButtonGroup/index.d.ts +2 -0
  35. package/dist/components/ButtonGroup/index.js +1 -0
  36. package/dist/components/ButtonGroup/types.d.ts +48 -0
  37. package/dist/components/ButtonGroup/types.js +1 -0
  38. package/dist/components/Card/Card.svelte +53 -0
  39. package/dist/components/Card/Card.svelte.d.ts +4 -0
  40. package/dist/components/Card/card.css +62 -0
  41. package/dist/components/Card/index.d.ts +2 -0
  42. package/dist/components/Card/index.js +1 -0
  43. package/dist/components/Card/types.d.ts +56 -0
  44. package/dist/components/Card/types.js +1 -0
  45. package/dist/components/Checkbox/Checkbox.svelte +105 -0
  46. package/dist/components/Checkbox/Checkbox.svelte.d.ts +4 -0
  47. package/dist/components/Checkbox/checkbox.css +138 -0
  48. package/dist/components/Checkbox/index.d.ts +2 -0
  49. package/dist/components/Checkbox/index.js +1 -0
  50. package/dist/components/Checkbox/types.d.ts +76 -0
  51. package/dist/components/Checkbox/types.js +1 -0
  52. package/dist/components/Chip/Chip.svelte +109 -0
  53. package/dist/components/Chip/Chip.svelte.d.ts +4 -0
  54. package/dist/components/Chip/chip.css +101 -0
  55. package/dist/components/Chip/index.d.ts +2 -0
  56. package/dist/components/Chip/index.js +1 -0
  57. package/dist/components/Chip/types.d.ts +69 -0
  58. package/dist/components/Chip/types.js +1 -0
  59. package/dist/components/Choice/Choice.svelte +60 -0
  60. package/dist/components/Choice/Choice.svelte.d.ts +4 -0
  61. package/dist/components/Choice/choice.css +75 -0
  62. package/dist/components/Choice/types.d.ts +43 -0
  63. package/dist/components/Choice/types.js +1 -0
  64. package/dist/components/Divider/Divider.svelte +19 -0
  65. package/dist/components/Divider/Divider.svelte.d.ts +4 -0
  66. package/dist/components/Divider/divider.css +23 -0
  67. package/dist/components/Divider/index.d.ts +2 -0
  68. package/dist/components/Divider/index.js +1 -0
  69. package/dist/components/Divider/types.d.ts +10 -0
  70. package/dist/components/Divider/types.js +1 -0
  71. package/dist/components/Field/Field.svelte +95 -0
  72. package/dist/components/Field/Field.svelte.d.ts +4 -0
  73. package/dist/components/Field/field.css +210 -0
  74. package/dist/components/Field/index.d.ts +3 -0
  75. package/dist/components/Field/index.js +2 -0
  76. package/dist/components/Field/types.d.ts +109 -0
  77. package/dist/components/Field/types.js +2 -0
  78. package/dist/components/Form/Form.svelte +93 -0
  79. package/dist/components/Form/Form.svelte.d.ts +8 -0
  80. package/dist/components/Form/form.css +16 -0
  81. package/dist/components/Form/index.d.ts +2 -0
  82. package/dist/components/Form/index.js +1 -0
  83. package/dist/components/Form/types.d.ts +38 -0
  84. package/dist/components/Form/types.js +1 -0
  85. package/dist/components/Icon/Icon.svelte +38 -0
  86. package/dist/components/Icon/Icon.svelte.d.ts +4 -0
  87. package/dist/components/Icon/icon.css +33 -0
  88. package/dist/components/Icon/index.d.ts +2 -0
  89. package/dist/components/Icon/index.js +1 -0
  90. package/dist/components/Icon/types.d.ts +34 -0
  91. package/dist/components/Icon/types.js +1 -0
  92. package/dist/components/List/List.svelte +39 -0
  93. package/dist/components/List/List.svelte.d.ts +4 -0
  94. package/dist/components/List/ListItem.svelte +57 -0
  95. package/dist/components/List/ListItem.svelte.d.ts +4 -0
  96. package/dist/components/List/index.d.ts +3 -0
  97. package/dist/components/List/index.js +2 -0
  98. package/dist/components/List/list.css +127 -0
  99. package/dist/components/List/types.d.ts +67 -0
  100. package/dist/components/List/types.js +1 -0
  101. package/dist/components/NativeSelect/NativeSelect.svelte +110 -0
  102. package/dist/components/NativeSelect/NativeSelect.svelte.d.ts +25 -0
  103. package/dist/components/NativeSelect/index.d.ts +2 -0
  104. package/dist/components/NativeSelect/index.js +1 -0
  105. package/dist/components/NativeSelect/native-select.css +45 -0
  106. package/dist/components/NativeSelect/types.d.ts +58 -0
  107. package/dist/components/NativeSelect/types.js +1 -0
  108. package/dist/components/NumberField/NumberField.svelte +246 -0
  109. package/dist/components/NumberField/NumberField.svelte.d.ts +4 -0
  110. package/dist/components/NumberField/index.d.ts +2 -0
  111. package/dist/components/NumberField/index.js +1 -0
  112. package/dist/components/NumberField/number-field.css +74 -0
  113. package/dist/components/NumberField/types.d.ts +73 -0
  114. package/dist/components/NumberField/types.js +1 -0
  115. package/dist/components/Progress/ProgressCircular.svelte +61 -0
  116. package/dist/components/Progress/ProgressCircular.svelte.d.ts +4 -0
  117. package/dist/components/Progress/ProgressLinear.svelte +44 -0
  118. package/dist/components/Progress/ProgressLinear.svelte.d.ts +4 -0
  119. package/dist/components/Progress/index.d.ts +3 -0
  120. package/dist/components/Progress/index.js +2 -0
  121. package/dist/components/Progress/progress-circular.css +61 -0
  122. package/dist/components/Progress/progress-linear.css +67 -0
  123. package/dist/components/Progress/types.d.ts +53 -0
  124. package/dist/components/Progress/types.js +1 -0
  125. package/dist/components/RadioGroup/RadioGroup.svelte +149 -0
  126. package/dist/components/RadioGroup/RadioGroup.svelte.d.ts +25 -0
  127. package/dist/components/RadioGroup/index.d.ts +2 -0
  128. package/dist/components/RadioGroup/index.js +1 -0
  129. package/dist/components/RadioGroup/radio-group.css +139 -0
  130. package/dist/components/RadioGroup/types.d.ts +93 -0
  131. package/dist/components/RadioGroup/types.js +1 -0
  132. package/dist/components/SegmentedControl/SegmentedControl.svelte +70 -0
  133. package/dist/components/SegmentedControl/SegmentedControl.svelte.d.ts +25 -0
  134. package/dist/components/SegmentedControl/index.d.ts +2 -0
  135. package/dist/components/SegmentedControl/index.js +1 -0
  136. package/dist/components/SegmentedControl/segmented-control.css +68 -0
  137. package/dist/components/SegmentedControl/types.d.ts +60 -0
  138. package/dist/components/SegmentedControl/types.js +1 -0
  139. package/dist/components/Skeleton/Skeleton.svelte +33 -0
  140. package/dist/components/Skeleton/Skeleton.svelte.d.ts +4 -0
  141. package/dist/components/Skeleton/index.d.ts +2 -0
  142. package/dist/components/Skeleton/index.js +1 -0
  143. package/dist/components/Skeleton/skeleton.css +70 -0
  144. package/dist/components/Skeleton/types.d.ts +25 -0
  145. package/dist/components/Skeleton/types.js +1 -0
  146. package/dist/components/Slider/Slider.svelte +153 -0
  147. package/dist/components/Slider/Slider.svelte.d.ts +4 -0
  148. package/dist/components/Slider/index.d.ts +2 -0
  149. package/dist/components/Slider/index.js +1 -0
  150. package/dist/components/Slider/slider.css +206 -0
  151. package/dist/components/Slider/types.d.ts +102 -0
  152. package/dist/components/Slider/types.js +1 -0
  153. package/dist/components/Switch/Switch.svelte +94 -0
  154. package/dist/components/Switch/Switch.svelte.d.ts +4 -0
  155. package/dist/components/Switch/index.d.ts +2 -0
  156. package/dist/components/Switch/index.js +1 -0
  157. package/dist/components/Switch/switch.css +118 -0
  158. package/dist/components/Switch/types.d.ts +67 -0
  159. package/dist/components/Switch/types.js +1 -0
  160. package/dist/components/Tabs/Tabs.svelte +93 -0
  161. package/dist/components/Tabs/Tabs.svelte.d.ts +25 -0
  162. package/dist/components/Tabs/index.d.ts +2 -0
  163. package/dist/components/Tabs/index.js +1 -0
  164. package/dist/components/Tabs/tabs.css +73 -0
  165. package/dist/components/Tabs/types.d.ts +59 -0
  166. package/dist/components/Tabs/types.js +1 -0
  167. package/dist/components/TextField/TextField.svelte +115 -0
  168. package/dist/components/TextField/TextField.svelte.d.ts +4 -0
  169. package/dist/components/TextField/index.d.ts +2 -0
  170. package/dist/components/TextField/index.js +1 -0
  171. package/dist/components/TextField/types.d.ts +57 -0
  172. package/dist/components/TextField/types.js +1 -0
  173. package/dist/components/Textarea/Textarea.svelte +116 -0
  174. package/dist/components/Textarea/Textarea.svelte.d.ts +4 -0
  175. package/dist/components/Textarea/index.d.ts +2 -0
  176. package/dist/components/Textarea/index.js +1 -0
  177. package/dist/components/Textarea/textarea.css +35 -0
  178. package/dist/components/Textarea/types.d.ts +60 -0
  179. package/dist/components/Textarea/types.js +1 -0
  180. package/dist/core/defaults/DefaultsProvider.svelte +30 -0
  181. package/dist/core/defaults/DefaultsProvider.svelte.d.ts +13 -0
  182. package/dist/core/defaults/context.d.ts +45 -0
  183. package/dist/core/defaults/context.js +38 -0
  184. package/dist/core/display/display.d.ts +39 -0
  185. package/dist/core/display/display.js +53 -0
  186. package/dist/core/errors/codes.d.ts +112 -0
  187. package/dist/core/errors/codes.js +96 -0
  188. package/dist/core/errors/errors.d.ts +8 -0
  189. package/dist/core/errors/errors.js +7 -0
  190. package/dist/core/form/constraints.d.ts +15 -0
  191. package/dist/core/form/constraints.js +42 -0
  192. package/dist/core/form/context.d.ts +19 -0
  193. package/dist/core/form/context.js +4 -0
  194. package/dist/core/form/field.svelte.d.ts +34 -0
  195. package/dist/core/form/field.svelte.js +66 -0
  196. package/dist/core/form/number.d.ts +24 -0
  197. package/dist/core/form/number.js +71 -0
  198. package/dist/core/form/types.d.ts +22 -0
  199. package/dist/core/form/types.js +1 -0
  200. package/dist/core/locale/locale.d.ts +55 -0
  201. package/dist/core/locale/locale.js +80 -0
  202. package/dist/core/props/classes.d.ts +21 -0
  203. package/dist/core/props/classes.js +44 -0
  204. package/dist/core/props/element.d.ts +18 -0
  205. package/dist/core/props/element.js +1 -0
  206. package/dist/core/props/types.d.ts +47 -0
  207. package/dist/core/props/types.js +16 -0
  208. package/dist/core/provider/UIProvider.svelte +60 -0
  209. package/dist/core/provider/UIProvider.svelte.d.ts +29 -0
  210. package/dist/core/theme/ThemeProvider.svelte +178 -0
  211. package/dist/core/theme/ThemeProvider.svelte.d.ts +25 -0
  212. package/dist/core/theme/color.d.ts +23 -0
  213. package/dist/core/theme/color.js +54 -0
  214. package/dist/core/theme/context.d.ts +25 -0
  215. package/dist/core/theme/context.js +14 -0
  216. package/dist/core/theme/css.d.ts +25 -0
  217. package/dist/core/theme/css.js +69 -0
  218. package/dist/core/theme/define.d.ts +11 -0
  219. package/dist/core/theme/define.js +21 -0
  220. package/dist/core/theme/resolve.d.ts +6 -0
  221. package/dist/core/theme/resolve.js +18 -0
  222. package/dist/core/theme/theme-provider.css +13 -0
  223. package/dist/core/theme/types.d.ts +35 -0
  224. package/dist/core/theme/types.js +61 -0
  225. package/dist/dev/off.d.ts +4 -0
  226. package/dist/dev/off.js +3 -0
  227. package/dist/dev/on.d.ts +8 -0
  228. package/dist/dev/on.js +33 -0
  229. package/dist/index.d.ts +58 -0
  230. package/dist/index.js +72 -0
  231. package/dist/primitives/anchored/anchored.d.ts +39 -0
  232. package/dist/primitives/anchored/anchored.js +104 -0
  233. package/dist/primitives/anchored/fallback.d.ts +31 -0
  234. package/dist/primitives/anchored/fallback.js +73 -0
  235. package/dist/primitives/focus/focus-trap.d.ts +25 -0
  236. package/dist/primitives/focus/focus-trap.js +86 -0
  237. package/dist/primitives/focus/roving-focus.d.ts +22 -0
  238. package/dist/primitives/focus/roving-focus.js +86 -0
  239. package/dist/primitives/focus/tabbable.d.ts +9 -0
  240. package/dist/primitives/focus/tabbable.js +75 -0
  241. package/dist/primitives/inert/inert-others.d.ts +12 -0
  242. package/dist/primitives/inert/inert-others.js +44 -0
  243. package/dist/primitives/keyboard/keymap.d.ts +17 -0
  244. package/dist/primitives/keyboard/keymap.js +39 -0
  245. package/dist/primitives/keyboard/typeahead.d.ts +18 -0
  246. package/dist/primitives/keyboard/typeahead.js +50 -0
  247. package/dist/primitives/layer/dismissable.d.ts +26 -0
  248. package/dist/primitives/layer/dismissable.js +84 -0
  249. package/dist/primitives/motion/easing.d.ts +9 -0
  250. package/dist/primitives/motion/easing.js +46 -0
  251. package/dist/primitives/motion/motion.d.ts +59 -0
  252. package/dist/primitives/motion/motion.js +80 -0
  253. package/dist/primitives/portal/Portal.svelte +37 -0
  254. package/dist/primitives/portal/Portal.svelte.d.ts +9 -0
  255. package/dist/primitives/portal/PortalContent.svelte +15 -0
  256. package/dist/primitives/portal/PortalContent.svelte.d.ts +9 -0
  257. package/dist/primitives/portal/portal.css +8 -0
  258. package/dist/primitives/scroll-lock/scroll-lock.d.ts +10 -0
  259. package/dist/primitives/scroll-lock/scroll-lock.js +33 -0
  260. package/dist/primitives/virtual/virtual-list.svelte.d.ts +65 -0
  261. package/dist/primitives/virtual/virtual-list.svelte.js +291 -0
  262. package/dist/styles/base.css +44 -0
  263. package/dist/styles/props.css +137 -0
  264. package/dist/styles/tokens.css +84 -0
  265. package/dist/styles/visually-hidden.css +16 -0
  266. package/dist/themes/aurora.d.ts +13 -0
  267. package/dist/themes/aurora.js +103 -0
  268. package/dist/themes/heritage.d.ts +13 -0
  269. package/dist/themes/heritage.js +102 -0
  270. package/dist/themes/maison.d.ts +13 -0
  271. package/dist/themes/maison.js +99 -0
  272. package/dist/themes/milano.d.ts +13 -0
  273. package/dist/themes/milano.js +101 -0
  274. package/dist/themes/monolith.d.ts +13 -0
  275. package/dist/themes/monolith.js +101 -0
  276. package/package.json +87 -5
@@ -0,0 +1,93 @@
1
+ <script lang="ts">
2
+ // A form that checks its fields on submit. See FormProps for the props.
3
+ import { flushSync } from 'svelte';
4
+ import DefaultsProvider from '../../core/defaults/DefaultsProvider.svelte';
5
+ import type { DefaultsConfig } from '../../core/defaults/context.js';
6
+ import { setFormContext, type FormField } from '../../core/form/context.js';
7
+ import './form.css';
8
+ import type { FormProps } from './types.js';
9
+
10
+ let {
11
+ validateOn,
12
+ disabled = false,
13
+ onsubmit,
14
+ onreset,
15
+ children,
16
+ class: className,
17
+ ref = $bindable(null),
18
+ ...rest
19
+ }: FormProps = $props();
20
+
21
+ // A registry, not state: nothing renders from it, so a plain Set rather than a SvelteSet.
22
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity -- never read reactively
23
+ const fields = new Set<FormField>();
24
+ setFormContext({
25
+ get validateOn() {
26
+ return validateOn;
27
+ },
28
+ register(field) {
29
+ fields.add(field);
30
+ return () => fields.delete(field);
31
+ }
32
+ });
33
+
34
+ // Always rendered, so turning `disabled` on and off never remounts the fields.
35
+ const defaults: DefaultsConfig = $derived(disabled ? { global: { disabled: true } } : {});
36
+
37
+ /** Shows the message of every field. Returns `true` when every field is valid. */
38
+ export function validate(): boolean {
39
+ let valid = true;
40
+ for (const field of fields) if (!field.validate()) valid = false;
41
+ return valid;
42
+ }
43
+
44
+ /** Puts every field back to the value it started with, and hides every message. */
45
+ export function reset(): void {
46
+ ref?.reset();
47
+ }
48
+
49
+ /** Hides every message and keeps the values. */
50
+ export function resetValidation(): void {
51
+ for (const field of fields) field.resetValidation();
52
+ }
53
+
54
+ function submit(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }) {
55
+ const invalid = [...fields].filter((field) => !field.validate());
56
+ if (invalid.length === 0) {
57
+ onsubmit?.(event);
58
+ return;
59
+ }
60
+ // Stop here, including listeners added later such as SvelteKit's enhance, which would
61
+ // otherwise send the form anyway.
62
+ event.preventDefault();
63
+ event.stopImmediatePropagation();
64
+ // Focus the first invalid control in document order, once its message is in the DOM, so
65
+ // its label and error are read out together.
66
+ flushSync();
67
+ const controls = invalid
68
+ .map((field) => field.control())
69
+ .filter((control): control is HTMLElement => control != null);
70
+ controls.sort((a, b) =>
71
+ a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1
72
+ );
73
+ controls[0]?.focus();
74
+ }
75
+
76
+ function afterReset(event: Event & { currentTarget: EventTarget & HTMLFormElement }) {
77
+ resetValidation();
78
+ (onreset as ((event: Event) => void) | null | undefined)?.(event);
79
+ }
80
+ </script>
81
+
82
+ <form
83
+ bind:this={ref}
84
+ {...rest}
85
+ novalidate
86
+ class={['sv-form', className]}
87
+ onsubmit={submit}
88
+ onreset={afterReset}
89
+ >
90
+ <DefaultsProvider {defaults}>
91
+ {@render children?.()}
92
+ </DefaultsProvider>
93
+ </form>
@@ -0,0 +1,8 @@
1
+ import type { FormProps } from './types.js';
2
+ declare const Form: import("svelte").Component<FormProps, {
3
+ validate: () => boolean;
4
+ reset: () => void;
5
+ resetValidation: () => void;
6
+ }, "ref">;
7
+ type Form = ReturnType<typeof Form>;
8
+ export default Form;
@@ -0,0 +1,16 @@
1
+ @layer sv.tokens, sv.base, sv.components, sv.utilities;
2
+
3
+ @layer sv.components {
4
+ /*
5
+ * Fields stack at one rhythm and take the full width; buttons keep their own width. A class
6
+ * on the form changes the layout, for example to a row for a search bar.
7
+ */
8
+ .sv-form {
9
+ display: grid;
10
+ gap: var(--sv-space-21);
11
+ }
12
+
13
+ .sv-form > :where(.sv-button) {
14
+ justify-self: start;
15
+ }
16
+ }
@@ -0,0 +1,2 @@
1
+ export { default as Form } from './Form.svelte';
2
+ export type { FormProps } from './types.js';
@@ -0,0 +1 @@
1
+ export { default as Form } from './Form.svelte';
@@ -0,0 +1,38 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLFormAttributes } from 'svelte/elements';
3
+ import type { ValidateOn } from '../../core/form/types.js';
4
+ import type { ElementProps } from '../../core/props/element.js';
5
+ /**
6
+ * A `<form>` that checks its fields when it is submitted. An invalid form does not submit: every
7
+ * field shows its message and the first invalid one takes focus. A valid form calls `onsubmit`,
8
+ * or submits as HTML does when there is no `onsubmit`, so SvelteKit form actions work as they
9
+ * are. Other attributes go to the `<form>`. Through `bind:this` it offers `validate()`,
10
+ * `reset()` and `resetValidation()`.
11
+ *
12
+ * @example
13
+ * <Form method="POST" onsubmit={(event) => { event.preventDefault(); save(); }}>
14
+ * <TextField label="Email" name="email" type="email" required />
15
+ * <Button type="submit">Sign up</Button>
16
+ * </Form>
17
+ */
18
+ export type FormProps = ElementProps<HTMLFormElement, HTMLFormAttributes, {
19
+ /**
20
+ * When the fields show their messages, unless a field sets its own.
21
+ * @default 'blur'
22
+ */
23
+ validateOn?: ValidateOn;
24
+ /**
25
+ * Disables every control inside, Buttons included, for example while a submission runs.
26
+ * @default false
27
+ */
28
+ disabled?: boolean;
29
+ /**
30
+ * Called when the form is submitted and every field is valid. Without it, the form submits
31
+ * as HTML does; call `event.preventDefault()` to handle the submission yourself.
32
+ */
33
+ onsubmit?: (event: SubmitEvent & {
34
+ currentTarget: EventTarget & HTMLFormElement;
35
+ }) => void;
36
+ /** The fields and buttons. */
37
+ children?: Snippet;
38
+ }>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ // An icon from path data or any SVG of your own. See IconProps for the props.
3
+ import { sharedClasses } from '../../core/props/classes.js';
4
+ import './icon.css';
5
+ import type { IconProps } from './types.js';
6
+
7
+ let {
8
+ path,
9
+ viewBox = '0 0 24 24',
10
+ size,
11
+ color,
12
+ label,
13
+ children,
14
+ class: className,
15
+ ref = $bindable(null),
16
+ ...rest
17
+ }: IconProps = $props();
18
+ </script>
19
+
20
+ <span
21
+ bind:this={ref}
22
+ {...rest}
23
+ role={label ? 'img' : undefined}
24
+ aria-label={label}
25
+ aria-hidden={label ? undefined : 'true'}
26
+ class={[
27
+ 'sv-icon',
28
+ sharedClasses({ size, color }, 'Icon'),
29
+ color && 'sv-icon--colored',
30
+ className
31
+ ]}
32
+ >
33
+ {#if path}
34
+ <svg class="sv-icon__paths" {viewBox} aria-hidden="true"><path d={path} /></svg>
35
+ {:else}
36
+ {@render children?.()}
37
+ {/if}
38
+ </span>
@@ -0,0 +1,4 @@
1
+ import type { IconProps } from './types.js';
2
+ declare const Icon: import("svelte").Component<IconProps, {}, "ref">;
3
+ type Icon = ReturnType<typeof Icon>;
4
+ export default Icon;
@@ -0,0 +1,33 @@
1
+ @layer sv.tokens, sv.base, sv.components, sv.utilities;
2
+
3
+ @layer sv.components {
4
+ /* φ^½ of the text: the same proportion as icons in buttons. */
5
+ .sv-icon {
6
+ display: inline-flex;
7
+ flex: none;
8
+ align-items: center;
9
+ justify-content: center;
10
+ inline-size: 1.272em;
11
+ block-size: 1.272em;
12
+ line-height: 1;
13
+ vertical-align: -0.236em;
14
+ }
15
+
16
+ /* Only an icon with its own size reads the shared size; otherwise it follows its text. */
17
+ .sv-icon:is(.sv-size-xs, .sv-size-sm, .sv-size-md, .sv-size-lg, .sv-size-xl) {
18
+ font-size: var(--sv-size-text);
19
+ }
20
+
21
+ .sv-icon--colored {
22
+ color: var(--sv-color);
23
+ }
24
+
25
+ .sv-icon > svg {
26
+ inline-size: 100%;
27
+ block-size: 100%;
28
+ }
29
+
30
+ .sv-icon__paths {
31
+ fill: currentColor;
32
+ }
33
+ }
@@ -0,0 +1,2 @@
1
+ export { default as Icon } from './Icon.svelte';
2
+ export type { IconProps } from './types.js';
@@ -0,0 +1 @@
1
+ export { default as Icon } from './Icon.svelte';
@@ -0,0 +1,34 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAttributes } from 'svelte/elements';
3
+ import type { ElementProps } from '../../core/props/element.js';
4
+ import type { Color, Size } from '../../core/props/types.js';
5
+ /**
6
+ * An icon: SVG path data, or any icon of your own as `children`. Decorative unless it has a
7
+ * `label`.
8
+ */
9
+ export type IconProps = ElementProps<HTMLSpanElement, HTMLAttributes<HTMLSpanElement>, {
10
+ /**
11
+ * SVG path data, filled with the current colour: the format of path-based icon sets such
12
+ * as Material Design Icons. Several shapes go in one string (`M… Z M… Z`).
13
+ */
14
+ path?: string;
15
+ /**
16
+ * The coordinate box of `path`.
17
+ * @default '0 0 24 24'
18
+ */
19
+ viewBox?: string;
20
+ /**
21
+ * The text size the icon is drawn at, φ^½ larger. Without it, the icon follows the text
22
+ * around it.
23
+ */
24
+ size?: Size;
25
+ /** The icon's colour. Without it, the icon takes the colour of the text around it. */
26
+ color?: Color;
27
+ /**
28
+ * A name for an icon that means something on its own, for assistive technology. Without
29
+ * it, the icon is decorative and hidden from it.
30
+ */
31
+ label?: string;
32
+ /** Any other icon (an `<svg>`, an icon component), sized and coloured like a path. */
33
+ children?: Snippet;
34
+ }>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ <script lang="ts">
2
+ // A list of ListItems. See ListProps for the props.
3
+ import { useDefaults } from '../../core/defaults/context.js';
4
+ import { sharedClasses } from '../../core/props/classes.js';
5
+ import './list.css';
6
+ import type { ListDefaults, ListProps } from './types.js';
7
+
8
+ let {
9
+ density,
10
+ color,
11
+ nav,
12
+ children,
13
+ class: className,
14
+ ref = $bindable(null),
15
+ ...rest
16
+ }: ListProps = $props();
17
+
18
+ const resolve = useDefaults<ListDefaults>('List', {
19
+ density: 'default',
20
+ color: 'primary',
21
+ nav: false
22
+ });
23
+ const p = $derived(resolve({ density, color, nav }));
24
+ </script>
25
+
26
+ <!-- role="list": Safari drops the list semantics of a list without bullets. -->
27
+ <ul
28
+ bind:this={ref}
29
+ {...rest}
30
+ role="list"
31
+ class={[
32
+ 'sv-list',
33
+ sharedClasses({ density: p.density, color: p.color }, 'List'),
34
+ p.nav && 'sv-list--nav',
35
+ className
36
+ ]}
37
+ >
38
+ {@render children?.()}
39
+ </ul>
@@ -0,0 +1,4 @@
1
+ import type { ListProps } from './types.js';
2
+ declare const List: import("svelte").Component<ListProps, {}, "ref">;
3
+ type List = ReturnType<typeof List>;
4
+ export default List;
@@ -0,0 +1,57 @@
1
+ <script lang="ts">
2
+ // A row of a List: text, a link or a button. See ListItemProps for the props.
3
+ import './list.css';
4
+ import type { ListItemProps } from './types.js';
5
+
6
+ let {
7
+ title,
8
+ subtitle,
9
+ href,
10
+ target,
11
+ rel,
12
+ onclick,
13
+ active = false,
14
+ disabled = false,
15
+ prepend,
16
+ append,
17
+ children,
18
+ class: className,
19
+ ref = $bindable(null),
20
+ ...rest
21
+ }: ListItemProps = $props();
22
+
23
+ const tag = $derived(href != null ? 'a' : onclick ? 'button' : 'div');
24
+ </script>
25
+
26
+ <li
27
+ bind:this={ref}
28
+ {...rest}
29
+ class={[
30
+ 'sv-list-item',
31
+ active && 'sv-list-item--active',
32
+ disabled && 'sv-list-item--disabled',
33
+ className
34
+ ]}
35
+ >
36
+ <svelte:element
37
+ this={tag}
38
+ class="sv-list-item__main"
39
+ href={tag === 'a' && !disabled ? href : undefined}
40
+ target={tag === 'a' ? target : undefined}
41
+ rel={tag === 'a' ? rel : undefined}
42
+ type={tag === 'button' ? 'button' : undefined}
43
+ role={tag === 'a' && disabled ? 'link' : undefined}
44
+ disabled={(tag === 'button' && disabled) || undefined}
45
+ aria-disabled={(tag === 'a' && disabled) || undefined}
46
+ aria-current={tag === 'a' && active ? 'page' : undefined}
47
+ onclick={tag === 'button' ? onclick : undefined}
48
+ >
49
+ {#if prepend}<span class="sv-list-item__prepend">{@render prepend()}</span>{/if}
50
+ <span class="sv-list-item__text">
51
+ {#if title}<span class="sv-list-item__title">{title}</span>{/if}
52
+ {#if subtitle}<span class="sv-list-item__subtitle">{subtitle}</span>{/if}
53
+ {@render children?.()}
54
+ </span>
55
+ </svelte:element>
56
+ {#if append}<span class="sv-list-item__append">{@render append()}</span>{/if}
57
+ </li>
@@ -0,0 +1,4 @@
1
+ import type { ListItemProps } from './types.js';
2
+ declare const ListItem: import("svelte").Component<ListItemProps, {}, "ref">;
3
+ type ListItem = ReturnType<typeof ListItem>;
4
+ export default ListItem;
@@ -0,0 +1,3 @@
1
+ export { default as List } from './List.svelte';
2
+ export { default as ListItem } from './ListItem.svelte';
3
+ export type { ListDefaults, ListItemProps, ListProps } from './types.js';
@@ -0,0 +1,2 @@
1
+ export { default as List } from './List.svelte';
2
+ export { default as ListItem } from './ListItem.svelte';
@@ -0,0 +1,127 @@
1
+ @layer sv.tokens, sv.base, sv.components, sv.utilities;
2
+
3
+ @layer sv.components {
4
+ .sv-list {
5
+ display: grid;
6
+ margin: 0;
7
+ padding: calc(var(--sv-space-5) * var(--sv-density)) 0;
8
+ list-style: none;
9
+ }
10
+
11
+ .sv-list-item {
12
+ display: flex;
13
+ align-items: center;
14
+ min-inline-size: 0;
15
+ }
16
+
17
+ /* The row: a control tall, its text a φ step from what is before it. */
18
+ .sv-list-item__main {
19
+ position: relative;
20
+ isolation: isolate;
21
+ display: flex;
22
+ flex: 1;
23
+ gap: var(--sv-space-13);
24
+ align-items: center;
25
+ box-sizing: border-box;
26
+ min-inline-size: 0;
27
+ min-block-size: calc(var(--sv-control-md) * var(--sv-density));
28
+ margin: 0;
29
+ padding: calc(var(--sv-space-5) * var(--sv-density))
30
+ calc(var(--sv-space-13) * var(--sv-density));
31
+ border: 0;
32
+ background: transparent;
33
+ color: inherit;
34
+ font: inherit;
35
+ font-size: var(--sv-text-sm);
36
+ line-height: var(--sv-leading-snug);
37
+ text-align: start;
38
+ text-decoration: none;
39
+ }
40
+
41
+ /* Rows that act share Button's state layer. */
42
+ :is(a, button).sv-list-item__main {
43
+ cursor: pointer;
44
+ -webkit-tap-highlight-color: transparent;
45
+ }
46
+
47
+ :is(a, button).sv-list-item__main::before {
48
+ content: '';
49
+ position: absolute;
50
+ inset: 0;
51
+ z-index: -1;
52
+ border-radius: inherit;
53
+ background-color: currentColor;
54
+ opacity: 0;
55
+ pointer-events: none;
56
+ transition: opacity var(--sv-duration-fast) var(--sv-ease);
57
+ }
58
+
59
+ @media (hover: hover) {
60
+ .sv-list-item:not(.sv-list-item--disabled) :is(a, button).sv-list-item__main:hover::before {
61
+ opacity: var(--sv-state-hover);
62
+ }
63
+ }
64
+
65
+ .sv-list-item:not(.sv-list-item--disabled) :is(a, button).sv-list-item__main:active::before {
66
+ opacity: var(--sv-state-press);
67
+ }
68
+
69
+ /* Navigation: rounded rows, set in from the edges. */
70
+ .sv-list--nav {
71
+ padding-inline: var(--sv-space-5);
72
+ }
73
+
74
+ .sv-list--nav .sv-list-item__main {
75
+ border-radius: var(--sv-radius-md);
76
+ }
77
+
78
+ .sv-list-item--active .sv-list-item__main {
79
+ background-color: color-mix(in oklch, var(--sv-color) var(--sv-tonal-wash), transparent);
80
+ color: color-mix(in oklch, var(--sv-color) var(--sv-tonal-text-mix), var(--sv-color-text));
81
+ font-weight: var(--sv-weight-medium);
82
+ }
83
+
84
+ .sv-list-item--disabled {
85
+ opacity: 0.382;
86
+ }
87
+
88
+ .sv-list-item--disabled .sv-list-item__main {
89
+ cursor: not-allowed;
90
+ }
91
+
92
+ .sv-list-item__prepend,
93
+ .sv-list-item__append {
94
+ display: flex;
95
+ flex: none;
96
+ align-items: center;
97
+ }
98
+
99
+ .sv-list-item__append {
100
+ padding-inline-end: calc(var(--sv-space-13) * var(--sv-density));
101
+ font-size: var(--sv-text-sm);
102
+ }
103
+
104
+ :where(.sv-list-item__prepend, .sv-list-item__append) svg {
105
+ inline-size: 1.272em;
106
+ block-size: 1.272em;
107
+ }
108
+
109
+ .sv-list-item__text {
110
+ display: grid;
111
+ flex: 1;
112
+ gap: var(--sv-space-2);
113
+ min-inline-size: 0;
114
+ }
115
+
116
+ .sv-list-item__title {
117
+ overflow: hidden;
118
+ text-overflow: ellipsis;
119
+ white-space: nowrap;
120
+ }
121
+
122
+ .sv-list-item__subtitle {
123
+ color: var(--sv-color-text-subtle);
124
+ font-size: var(--sv-text-xs);
125
+ font-weight: var(--sv-weight-regular);
126
+ }
127
+ }
@@ -0,0 +1,67 @@
1
+ import type { Snippet } from 'svelte';
2
+ import type { HTMLAnchorAttributes, HTMLAttributes, HTMLLiAttributes } from 'svelte/elements';
3
+ import type { ElementProps } from '../../core/props/element.js';
4
+ import type { Color, Density } from '../../core/props/types.js';
5
+ /** The List props that `DefaultsProvider` and `UIProvider` can set for a subtree. */
6
+ export interface ListDefaults {
7
+ /**
8
+ * Multiplies the height and padding of its items.
9
+ * @default 'default'
10
+ */
11
+ density: Density;
12
+ /**
13
+ * The colour of the active item.
14
+ * @default 'primary'
15
+ */
16
+ color: Color;
17
+ /**
18
+ * Rounded rows set in from the edges, for navigation.
19
+ * @default false
20
+ */
21
+ nav: boolean;
22
+ }
23
+ declare module '../../core/defaults/context.js' {
24
+ interface ComponentDefaults {
25
+ List: ListDefaults;
26
+ }
27
+ }
28
+ /** A list of rows: `ListItem`s. Density and colour are set here, for every item. */
29
+ export type ListProps = ElementProps<HTMLUListElement, HTMLAttributes<HTMLUListElement>, Partial<ListDefaults> & {
30
+ /** The `ListItem`s. */
31
+ children?: Snippet;
32
+ }>;
33
+ /**
34
+ * A row of a `List`. With `href` it is a link and with `onclick` a button, filling the row;
35
+ * `append` stays outside it, so it can hold controls of its own.
36
+ */
37
+ export type ListItemProps = ElementProps<HTMLLIElement, HTMLLiAttributes, {
38
+ /** The main line. */
39
+ title?: string;
40
+ /** A second line, under the title. */
41
+ subtitle?: string;
42
+ /** Makes the row a link. */
43
+ href?: string;
44
+ /** Where a link opens, as on `<a>`. */
45
+ target?: HTMLAnchorAttributes['target'];
46
+ /** The relationship of a link, as on `<a>`. */
47
+ rel?: string;
48
+ /** Makes the row a button that calls this. */
49
+ onclick?: (event: MouseEvent) => void;
50
+ /**
51
+ * The current page or the selected row: tinted in the list's colour, and
52
+ * `aria-current="page"` on a link.
53
+ * @default false
54
+ */
55
+ active?: boolean;
56
+ /**
57
+ * A disabled row cannot be pressed; a disabled link loses its `href`.
58
+ * @default false
59
+ */
60
+ disabled?: boolean;
61
+ /** Content before the text, such as an avatar or an icon. */
62
+ prepend?: Snippet;
63
+ /** Content after the row's link or button, such as a value or a menu button. */
64
+ append?: Snippet;
65
+ /** More content under the title and subtitle. */
66
+ children?: Snippet;
67
+ }>;
@@ -0,0 +1 @@
1
+ export {};