@lyeve-labs/ui-kit 0.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +199 -0
  3. package/dist/components/Accordion.svelte +48 -0
  4. package/dist/components/Accordion.svelte.d.ts +10 -0
  5. package/dist/components/AccordionItem.svelte +55 -0
  6. package/dist/components/AccordionItem.svelte.d.ts +10 -0
  7. package/dist/components/Alert.svelte +60 -0
  8. package/dist/components/Alert.svelte.d.ts +13 -0
  9. package/dist/components/Autocomplete.svelte +198 -0
  10. package/dist/components/Autocomplete.svelte.d.ts +23 -0
  11. package/dist/components/Avatar.svelte +65 -0
  12. package/dist/components/Avatar.svelte.d.ts +9 -0
  13. package/dist/components/AvatarGroup.svelte +45 -0
  14. package/dist/components/AvatarGroup.svelte.d.ts +14 -0
  15. package/dist/components/Badge.svelte +48 -0
  16. package/dist/components/Badge.svelte.d.ts +13 -0
  17. package/dist/components/Banner.svelte +73 -0
  18. package/dist/components/Banner.svelte.d.ts +13 -0
  19. package/dist/components/Breadcrumb.svelte +47 -0
  20. package/dist/components/Breadcrumb.svelte.d.ts +10 -0
  21. package/dist/components/Button.svelte +71 -0
  22. package/dist/components/Button.svelte.d.ts +18 -0
  23. package/dist/components/ButtonGroup.svelte +33 -0
  24. package/dist/components/ButtonGroup.svelte.d.ts +9 -0
  25. package/dist/components/Card.svelte +72 -0
  26. package/dist/components/Card.svelte.d.ts +17 -0
  27. package/dist/components/Checkbox.svelte +80 -0
  28. package/dist/components/Checkbox.svelte.d.ts +14 -0
  29. package/dist/components/DatePicker.svelte +274 -0
  30. package/dist/components/DatePicker.svelte.d.ts +17 -0
  31. package/dist/components/Divider.svelte +23 -0
  32. package/dist/components/Divider.svelte.d.ts +8 -0
  33. package/dist/components/Drawer.svelte +120 -0
  34. package/dist/components/Drawer.svelte.d.ts +16 -0
  35. package/dist/components/Dropdown.svelte +88 -0
  36. package/dist/components/Dropdown.svelte.d.ts +23 -0
  37. package/dist/components/EmptyState.svelte +51 -0
  38. package/dist/components/EmptyState.svelte.d.ts +13 -0
  39. package/dist/components/FileInput.svelte +102 -0
  40. package/dist/components/FileInput.svelte.d.ts +15 -0
  41. package/dist/components/Indicator.svelte +38 -0
  42. package/dist/components/Indicator.svelte.d.ts +11 -0
  43. package/dist/components/Input.svelte +60 -0
  44. package/dist/components/Input.svelte.d.ts +17 -0
  45. package/dist/components/Kbd.svelte +18 -0
  46. package/dist/components/Kbd.svelte.d.ts +8 -0
  47. package/dist/components/Label.svelte +30 -0
  48. package/dist/components/Label.svelte.d.ts +11 -0
  49. package/dist/components/Modal.svelte +105 -0
  50. package/dist/components/Modal.svelte.d.ts +14 -0
  51. package/dist/components/MultiSelect.svelte +212 -0
  52. package/dist/components/MultiSelect.svelte.d.ts +22 -0
  53. package/dist/components/NumberInput.svelte +98 -0
  54. package/dist/components/NumberInput.svelte.d.ts +15 -0
  55. package/dist/components/PageHeader.svelte +22 -0
  56. package/dist/components/PageHeader.svelte.d.ts +10 -0
  57. package/dist/components/Pagination.svelte +112 -0
  58. package/dist/components/Pagination.svelte.d.ts +10 -0
  59. package/dist/components/Progress.svelte +70 -0
  60. package/dist/components/Progress.svelte.d.ts +15 -0
  61. package/dist/components/Radio.svelte +65 -0
  62. package/dist/components/Radio.svelte.d.ts +14 -0
  63. package/dist/components/RadioGroup.svelte +96 -0
  64. package/dist/components/RadioGroup.svelte.d.ts +22 -0
  65. package/dist/components/SearchInput.svelte +81 -0
  66. package/dist/components/SearchInput.svelte.d.ts +10 -0
  67. package/dist/components/Select.svelte +69 -0
  68. package/dist/components/Select.svelte.d.ts +18 -0
  69. package/dist/components/Skeleton.svelte +20 -0
  70. package/dist/components/Skeleton.svelte.d.ts +9 -0
  71. package/dist/components/Spinner.svelte +19 -0
  72. package/dist/components/Spinner.svelte.d.ts +7 -0
  73. package/dist/components/Stat.svelte +45 -0
  74. package/dist/components/Stat.svelte.d.ts +14 -0
  75. package/dist/components/StepIndicator.svelte +142 -0
  76. package/dist/components/StepIndicator.svelte.d.ts +13 -0
  77. package/dist/components/Table.svelte +36 -0
  78. package/dist/components/Table.svelte.d.ts +11 -0
  79. package/dist/components/Tabs.svelte +39 -0
  80. package/dist/components/Tabs.svelte.d.ts +13 -0
  81. package/dist/components/Tag.svelte +56 -0
  82. package/dist/components/Tag.svelte.d.ts +11 -0
  83. package/dist/components/Textarea.svelte +75 -0
  84. package/dist/components/Textarea.svelte.d.ts +26 -0
  85. package/dist/components/ThemeToggle.svelte +33 -0
  86. package/dist/components/ThemeToggle.svelte.d.ts +6 -0
  87. package/dist/components/Toaster.svelte +54 -0
  88. package/dist/components/Toaster.svelte.d.ts +18 -0
  89. package/dist/components/Toggle.svelte +74 -0
  90. package/dist/components/Toggle.svelte.d.ts +11 -0
  91. package/dist/components/Tooltip.svelte +44 -0
  92. package/dist/components/Tooltip.svelte.d.ts +11 -0
  93. package/dist/components/dialog/ConfirmDialog.svelte +95 -0
  94. package/dist/components/dialog/ConfirmDialog.svelte.d.ts +8 -0
  95. package/dist/components/dialog/Dialog.svelte +225 -0
  96. package/dist/components/dialog/Dialog.svelte.d.ts +9 -0
  97. package/dist/components/dialog/DialogContainer.svelte +17 -0
  98. package/dist/components/dialog/DialogContainer.svelte.d.ts +18 -0
  99. package/dist/components/dialog/dialog-manager.svelte.d.ts +54 -0
  100. package/dist/components/dialog/dialog-manager.svelte.js +164 -0
  101. package/dist/components/dialog/types.d.ts +33 -0
  102. package/dist/components/dialog/types.js +10 -0
  103. package/dist/index.d.ts +63 -0
  104. package/dist/index.js +72 -0
  105. package/dist/stores/toast.svelte.d.ts +18 -0
  106. package/dist/stores/toast.svelte.js +28 -0
  107. package/dist/styles/theme.css +148 -0
  108. package/dist/utils/cn.d.ts +13 -0
  109. package/dist/utils/cn.js +24 -0
  110. package/dist/utils/theme.d.ts +26 -0
  111. package/dist/utils/theme.js +55 -0
  112. package/package.json +81 -0
  113. package/src/lib/styles/theme.css +148 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Dialog Manager - module-level Svelte 5 reactive state.
3
+ *
4
+ * Manages a stack of dialogs. Dialogs render in last-to-first order (newest on top).
5
+ * Each dialog returns a Promise that resolves/rejects when closed.
6
+ *
7
+ * Usage:
8
+ * import { openDialog } from '@lyeve/ui-kit';
9
+ * // Inside a .svelte.ts file or $effect:
10
+ * const result = await openDialog({ title: 'Delete?', body: mySnippet, size: 'sm' });
11
+ *
12
+ * For confirm/cancel pattern:
13
+ * import { confirm } from '@lyeve/ui-kit';
14
+ * const ok = await confirm('Delete this item?', 'This action cannot be undone.');
15
+ * if (ok) { /* ... *\/ }
16
+ */
17
+ // ──────────────────────────────────────────────────────────
18
+ // Module-level reactive state
19
+ // ──────────────────────────────────────────────────────────
20
+ let idCounter = 0;
21
+ /** Reactive stack - topmost dialog = last element */
22
+ let stack = $state([]);
23
+ // ──────────────────────────────────────────────────────────
24
+ // Body scroll lock (counter-based - handles stacked dialogs)
25
+ // ──────────────────────────────────────────────────────────
26
+ let bodyLockCount = 0;
27
+ export function _lockBodyScroll() {
28
+ bodyLockCount++;
29
+ if (typeof document !== 'undefined') {
30
+ document.body.style.overflow = 'hidden';
31
+ }
32
+ }
33
+ export function _unlockBodyScroll() {
34
+ bodyLockCount--;
35
+ if (bodyLockCount <= 0) {
36
+ bodyLockCount = 0;
37
+ if (typeof document !== 'undefined') {
38
+ document.body.style.overflow = '';
39
+ }
40
+ }
41
+ }
42
+ /** Readonly snapshot for components */
43
+ export function getDialogStack() {
44
+ return stack;
45
+ }
46
+ // ──────────────────────────────────────────────────────────
47
+ // Public API
48
+ // ──────────────────────────────────────────────────────────
49
+ /**
50
+ * Open a new dialog. Returns a promise that resolves when the dialog is closed
51
+ * (via closeDialog with a value) or rejects if dismissed without a value.
52
+ *
53
+ * The dialog is pushed onto the stack and becomes the topmost.
54
+ */
55
+ export function openDialog(options) {
56
+ const id = options.id ?? `dialog-${++idCounter}`;
57
+ return new Promise((resolve, reject) => {
58
+ const entry = {
59
+ id,
60
+ options: { ...options, id },
61
+ resolve,
62
+ reject,
63
+ depth: stack.length,
64
+ };
65
+ stack = [...stack, entry];
66
+ // Update depths for all entries (each re-renders because depth is in state)
67
+ for (let i = 0; i < stack.length; i++) {
68
+ stack[i].depth = i;
69
+ }
70
+ });
71
+ }
72
+ /**
73
+ * Close a dialog by id and resolve its promise with a value.
74
+ * If omitted, closes the topmost dialog.
75
+ */
76
+ export function closeDialog(value, id) {
77
+ const targetId = id ?? stack[stack.length - 1]?.id;
78
+ if (!targetId)
79
+ return;
80
+ const idx = stack.findIndex((e) => e.id === targetId);
81
+ if (idx === -1)
82
+ return;
83
+ const entry = stack[idx];
84
+ const options = entry.options;
85
+ // Respect onClose guard
86
+ if (options.onClose) {
87
+ const allowed = options.onClose();
88
+ if (allowed === false)
89
+ return;
90
+ }
91
+ // Remove from stack
92
+ stack = stack.filter((e) => e.id !== targetId);
93
+ // Resolve the promise
94
+ entry.resolve(value);
95
+ // Update depths
96
+ for (let i = 0; i < stack.length; i++) {
97
+ stack[i].depth = i;
98
+ }
99
+ }
100
+ /**
101
+ * Dismiss (cancel) a dialog - rejects the promise.
102
+ * Called when user clicks backdrop or presses ESC on a non-persistent dialog.
103
+ */
104
+ export function dismissDialog(id) {
105
+ const targetId = id ?? stack[stack.length - 1]?.id;
106
+ if (!targetId)
107
+ return;
108
+ const idx = stack.findIndex((e) => e.id === targetId);
109
+ if (idx === -1)
110
+ return;
111
+ const entry = stack[idx];
112
+ const options = entry.options;
113
+ // Persistent dialogs can only be dismissed programmatically (by id)
114
+ if (options.persistent && !id)
115
+ return;
116
+ // Honor onClose guard (applies regardless of persistent)
117
+ if (options.onClose) {
118
+ const allowed = options.onClose();
119
+ if (allowed === false)
120
+ return;
121
+ }
122
+ stack = stack.filter((e) => e.id !== targetId);
123
+ // Reject the promise - caller can .catch() or let it be
124
+ entry.reject(new DOMException('Dialog dismissed', 'AbortError'));
125
+ for (let i = 0; i < stack.length; i++) {
126
+ stack[i].depth = i;
127
+ }
128
+ }
129
+ /**
130
+ * Close all open dialogs, rejecting their promises.
131
+ */
132
+ export function dismissAllDialogs() {
133
+ const copy = [...stack];
134
+ stack = [];
135
+ bodyLockCount = 0;
136
+ if (typeof document !== 'undefined') {
137
+ document.body.style.overflow = '';
138
+ }
139
+ for (const entry of copy) {
140
+ entry.reject(new DOMException('All dialogs dismissed', 'AbortError'));
141
+ }
142
+ }
143
+ /**
144
+ * Convenience: confirm dialog.
145
+ * Returns `true` if user confirmed, `false` if cancelled.
146
+ *
147
+ * const ok = await confirm('Delete item?', 'This cannot be undone.');
148
+ */
149
+ export function confirm(title, message) {
150
+ const id = `confirm-${++idCounter}`;
151
+ const promise = openDialog({ id, size: 'sm', title });
152
+ setDialogMeta(id, { confirmTitle: title, confirmMessage: message ?? '' });
153
+ return promise;
154
+ }
155
+ /**
156
+ * Set metadata on a dialog entry after it's opened.
157
+ * Used by convenience wrappers to pass extra data without polluting DialogOptions.
158
+ */
159
+ export function setDialogMeta(id, meta) {
160
+ const idx = stack.findIndex((e) => e.id === id);
161
+ if (idx === -1)
162
+ return;
163
+ stack[idx].meta = { ...stack[idx].meta, ...meta };
164
+ }
@@ -0,0 +1,33 @@
1
+ import type { Snippet } from 'svelte';
2
+ export type DialogSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
3
+ export interface DialogOptions<T = void> {
4
+ /** Unique id - auto-generated if omitted */
5
+ id?: string;
6
+ /** Dialog heading */
7
+ title?: string | Snippet;
8
+ /** Main body content */
9
+ body?: Snippet;
10
+ /** Footer content (actions). If omitted and not persistent, no footer. */
11
+ footer?: Snippet;
12
+ /** Max-width preset */
13
+ size?: DialogSize;
14
+ /** If true, backdrop click and ESC won't close the dialog */
15
+ persistent?: boolean;
16
+ /** Called when dialog is about to close. Return false to prevent. */
17
+ onClose?: () => boolean | void;
18
+ /** Resolve payload - passed to closeDialog */
19
+ resolve?: (value: T) => void;
20
+ /** Reject payload - passed to closeDialog */
21
+ reject?: (reason?: unknown) => void;
22
+ }
23
+ export interface DialogEntry<T = void> {
24
+ id: string;
25
+ options: DialogOptions<T>;
26
+ resolve: (value: T) => void;
27
+ reject: (reason?: unknown) => void;
28
+ /** Stack position (0 = bottom, N-1 = top) */
29
+ depth: number;
30
+ /** Arbitrary data passed by convenience functions (confirm, prompt, etc.) */
31
+ meta?: Record<string, unknown>;
32
+ }
33
+ export declare function sizeClass(size: DialogSize): string;
@@ -0,0 +1,10 @@
1
+ const SIZE_CLASSES = {
2
+ sm: 'max-w-sm',
3
+ md: 'max-w-md',
4
+ lg: 'max-w-lg',
5
+ xl: 'max-w-xl',
6
+ full: 'max-w-3xl',
7
+ };
8
+ export function sizeClass(size) {
9
+ return SIZE_CLASSES[size];
10
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @lyeve/ui-kit - public API.
3
+ *
4
+ * Every component, utility, and store the library exposes is re-exported
5
+ * from this single entry point. Consumers should import from
6
+ * `'@lyeve/ui-kit'` and never reach into deep paths - the directory layout
7
+ * is an implementation detail.
8
+ */
9
+ export { default as Card } from './components/Card.svelte';
10
+ export { default as PageHeader } from './components/PageHeader.svelte';
11
+ export { default as Divider } from './components/Divider.svelte';
12
+ export { default as Accordion } from './components/Accordion.svelte';
13
+ export { default as AccordionItem } from './components/AccordionItem.svelte';
14
+ export { default as Table } from './components/Table.svelte';
15
+ export { default as Button } from './components/Button.svelte';
16
+ export { default as ButtonGroup } from './components/ButtonGroup.svelte';
17
+ export { default as Input } from './components/Input.svelte';
18
+ export { default as Textarea } from './components/Textarea.svelte';
19
+ export { default as NumberInput } from './components/NumberInput.svelte';
20
+ export { default as SearchInput } from './components/SearchInput.svelte';
21
+ export { default as FileInput } from './components/FileInput.svelte';
22
+ export { default as Label } from './components/Label.svelte';
23
+ export { default as Select } from './components/Select.svelte';
24
+ export { default as MultiSelect } from './components/MultiSelect.svelte';
25
+ export { default as Autocomplete } from './components/Autocomplete.svelte';
26
+ export { default as DatePicker } from './components/DatePicker.svelte';
27
+ export { default as Checkbox } from './components/Checkbox.svelte';
28
+ export { default as Radio } from './components/Radio.svelte';
29
+ export { default as RadioGroup } from './components/RadioGroup.svelte';
30
+ export { default as Toggle } from './components/Toggle.svelte';
31
+ export { default as Breadcrumb } from './components/Breadcrumb.svelte';
32
+ export { default as Tabs } from './components/Tabs.svelte';
33
+ export { default as Pagination } from './components/Pagination.svelte';
34
+ export { default as StepIndicator } from './components/StepIndicator.svelte';
35
+ export { default as Dropdown } from './components/Dropdown.svelte';
36
+ export { default as Modal } from './components/Modal.svelte';
37
+ export { default as Drawer } from './components/Drawer.svelte';
38
+ export { default as Tooltip } from './components/Tooltip.svelte';
39
+ export { default as Alert } from './components/Alert.svelte';
40
+ export { default as Banner } from './components/Banner.svelte';
41
+ export { default as Badge } from './components/Badge.svelte';
42
+ export { default as Tag } from './components/Tag.svelte';
43
+ export { default as Indicator } from './components/Indicator.svelte';
44
+ export { default as Progress } from './components/Progress.svelte';
45
+ export { default as Spinner } from './components/Spinner.svelte';
46
+ export { default as Skeleton } from './components/Skeleton.svelte';
47
+ export { default as EmptyState } from './components/EmptyState.svelte';
48
+ export { default as Stat } from './components/Stat.svelte';
49
+ export { default as Kbd } from './components/Kbd.svelte';
50
+ export { default as Avatar } from './components/Avatar.svelte';
51
+ export { default as AvatarGroup } from './components/AvatarGroup.svelte';
52
+ export { default as ThemeToggle } from './components/ThemeToggle.svelte';
53
+ export { default as Toaster } from './components/Toaster.svelte';
54
+ export { toast } from './stores/toast.svelte.js';
55
+ export type { Toast, ToastTone } from './stores/toast.svelte.js';
56
+ export { default as DialogContainer } from './components/dialog/DialogContainer.svelte';
57
+ export { default as Dialog } from './components/dialog/Dialog.svelte';
58
+ export { default as ConfirmDialog } from './components/dialog/ConfirmDialog.svelte';
59
+ export { openDialog, closeDialog, dismissDialog, dismissAllDialogs, confirm, setDialogMeta, getDialogStack, } from './components/dialog/dialog-manager.svelte.js';
60
+ export type { DialogOptions, DialogEntry, DialogSize } from './components/dialog/types.js';
61
+ export { cn, type ClassValue } from './utils/cn.js';
62
+ export { getTheme, setTheme, toggleTheme, themeBootScript, type Theme } from './utils/theme.js';
63
+ export declare const VERSION = "0.8.2";
package/dist/index.js ADDED
@@ -0,0 +1,72 @@
1
+ /**
2
+ * @lyeve/ui-kit - public API.
3
+ *
4
+ * Every component, utility, and store the library exposes is re-exported
5
+ * from this single entry point. Consumers should import from
6
+ * `'@lyeve/ui-kit'` and never reach into deep paths - the directory layout
7
+ * is an implementation detail.
8
+ */
9
+ // ── Layout & structure ─────────────────────────────────────────────────────
10
+ export { default as Card } from './components/Card.svelte';
11
+ export { default as PageHeader } from './components/PageHeader.svelte';
12
+ export { default as Divider } from './components/Divider.svelte';
13
+ export { default as Accordion } from './components/Accordion.svelte';
14
+ export { default as AccordionItem } from './components/AccordionItem.svelte';
15
+ export { default as Table } from './components/Table.svelte';
16
+ // ── Forms & inputs ─────────────────────────────────────────────────────────
17
+ export { default as Button } from './components/Button.svelte';
18
+ export { default as ButtonGroup } from './components/ButtonGroup.svelte';
19
+ export { default as Input } from './components/Input.svelte';
20
+ export { default as Textarea } from './components/Textarea.svelte';
21
+ export { default as NumberInput } from './components/NumberInput.svelte';
22
+ export { default as SearchInput } from './components/SearchInput.svelte';
23
+ export { default as FileInput } from './components/FileInput.svelte';
24
+ export { default as Label } from './components/Label.svelte';
25
+ export { default as Select } from './components/Select.svelte';
26
+ export { default as MultiSelect } from './components/MultiSelect.svelte';
27
+ export { default as Autocomplete } from './components/Autocomplete.svelte';
28
+ export { default as DatePicker } from './components/DatePicker.svelte';
29
+ export { default as Checkbox } from './components/Checkbox.svelte';
30
+ export { default as Radio } from './components/Radio.svelte';
31
+ export { default as RadioGroup } from './components/RadioGroup.svelte';
32
+ export { default as Toggle } from './components/Toggle.svelte';
33
+ // ── Navigation ─────────────────────────────────────────────────────────────
34
+ export { default as Breadcrumb } from './components/Breadcrumb.svelte';
35
+ export { default as Tabs } from './components/Tabs.svelte';
36
+ export { default as Pagination } from './components/Pagination.svelte';
37
+ export { default as StepIndicator } from './components/StepIndicator.svelte';
38
+ export { default as Dropdown } from './components/Dropdown.svelte';
39
+ // ── Overlays ───────────────────────────────────────────────────────────────
40
+ export { default as Modal } from './components/Modal.svelte';
41
+ export { default as Drawer } from './components/Drawer.svelte';
42
+ export { default as Tooltip } from './components/Tooltip.svelte';
43
+ // ── Feedback & status ──────────────────────────────────────────────────────
44
+ export { default as Alert } from './components/Alert.svelte';
45
+ export { default as Banner } from './components/Banner.svelte';
46
+ export { default as Badge } from './components/Badge.svelte';
47
+ export { default as Tag } from './components/Tag.svelte';
48
+ export { default as Indicator } from './components/Indicator.svelte';
49
+ export { default as Progress } from './components/Progress.svelte';
50
+ export { default as Spinner } from './components/Spinner.svelte';
51
+ export { default as Skeleton } from './components/Skeleton.svelte';
52
+ export { default as EmptyState } from './components/EmptyState.svelte';
53
+ export { default as Stat } from './components/Stat.svelte';
54
+ export { default as Kbd } from './components/Kbd.svelte';
55
+ // ── Media ──────────────────────────────────────────────────────────────────
56
+ export { default as Avatar } from './components/Avatar.svelte';
57
+ export { default as AvatarGroup } from './components/AvatarGroup.svelte';
58
+ // ── Theming & toasts ───────────────────────────────────────────────────────
59
+ export { default as ThemeToggle } from './components/ThemeToggle.svelte';
60
+ export { default as Toaster } from './components/Toaster.svelte';
61
+ // ── Stores ─────────────────────────────────────────────────────────────────
62
+ export { toast } from './stores/toast.svelte.js';
63
+ // ── Dialogs (imperative stack: openDialog / confirm) ────────────────────────
64
+ export { default as DialogContainer } from './components/dialog/DialogContainer.svelte';
65
+ export { default as Dialog } from './components/dialog/Dialog.svelte';
66
+ export { default as ConfirmDialog } from './components/dialog/ConfirmDialog.svelte';
67
+ export { openDialog, closeDialog, dismissDialog, dismissAllDialogs, confirm, setDialogMeta, getDialogStack, } from './components/dialog/dialog-manager.svelte.js';
68
+ // ── Utilities ──────────────────────────────────────────────────────────────
69
+ export { cn } from './utils/cn.js';
70
+ export { getTheme, setTheme, toggleTheme, themeBootScript } from './utils/theme.js';
71
+ // ── Version ────────────────────────────────────────────────────────────────
72
+ export const VERSION = '0.8.2';
@@ -0,0 +1,18 @@
1
+ export type ToastTone = 'info' | 'success' | 'warn' | 'danger';
2
+ export interface Toast {
3
+ id: number;
4
+ tone: ToastTone;
5
+ message: string;
6
+ timeout: number;
7
+ }
8
+ declare class ToastStore {
9
+ items: Toast[];
10
+ push(tone: ToastTone, message: string, timeout?: number): number;
11
+ dismiss(id: number): void;
12
+ info(message: string, timeout?: number): number;
13
+ success(message: string, timeout?: number): number;
14
+ warn(message: string, timeout?: number): number;
15
+ error(message: string, timeout?: number): number;
16
+ }
17
+ export declare const toast: ToastStore;
18
+ export {};
@@ -0,0 +1,28 @@
1
+ let seq = 0;
2
+ class ToastStore {
3
+ items = $state([]);
4
+ push(tone, message, timeout = 4000) {
5
+ const id = ++seq;
6
+ this.items.push({ id, tone, message, timeout });
7
+ if (timeout > 0) {
8
+ setTimeout(() => this.dismiss(id), timeout);
9
+ }
10
+ return id;
11
+ }
12
+ dismiss(id) {
13
+ this.items = this.items.filter((t) => t.id !== id);
14
+ }
15
+ info(message, timeout) {
16
+ return this.push('info', message, timeout);
17
+ }
18
+ success(message, timeout) {
19
+ return this.push('success', message, timeout);
20
+ }
21
+ warn(message, timeout) {
22
+ return this.push('warn', message, timeout);
23
+ }
24
+ error(message, timeout) {
25
+ return this.push('danger', message, timeout);
26
+ }
27
+ }
28
+ export const toast = new ToastStore();
@@ -0,0 +1,148 @@
1
+ /*
2
+ * @lyeve/ui-kit - theme tokens
3
+ *
4
+ * A single CSS file imported once at the top of your app's root stylesheet.
5
+ * Declares two palettes - Soft Dark (default) and Soft Light - built from
6
+ * the LyEve brand guideline (Ink #0B1422 + Cyan #00D4FF) and exposes them
7
+ * as Tailwind v4 `@theme` tokens.
8
+ *
9
+ * import '@lyeve/ui-kit/styles.css';
10
+ *
11
+ * Switching themes is a single attribute on `<html>`:
12
+ * <html data-theme="light"> // light mode
13
+ * <html> // dark mode (default)
14
+ */
15
+
16
+ @import 'tailwindcss';
17
+
18
+ /* ── Soft Dark · default palette ─────────────────────────────────────────── */
19
+ @theme {
20
+ --color-ink: #0b1422;
21
+ --color-surface: #101a2b;
22
+ --color-surface-2: #162233;
23
+ --color-line: #1e2d40;
24
+ --color-fg: #e6e8ee;
25
+ --color-muted: #9aa1ad;
26
+ --color-faint: #525866;
27
+
28
+ --color-brand: #00d4ff;
29
+ --color-brand-light: #5fd9f4;
30
+ --color-brand-deep: #0099c4;
31
+ --color-violet: #7c6aef;
32
+
33
+ --color-success: #15803d;
34
+ --color-warn: #b45309;
35
+ --color-danger: #b91c1c;
36
+
37
+ /* ── Radius ─────────────────────────────────────────────────── */
38
+ --radius-xs: 4px;
39
+ --radius-sm: 6px;
40
+ --radius-md: 8px;
41
+ --radius-lg: 12px;
42
+ --radius-xl: 16px;
43
+ --radius-2xl: 24px;
44
+
45
+ /* ── Typography · families ───────────────────────────────────── */
46
+ --font-sans:
47
+ 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
48
+ 'Noto Sans', sans-serif;
49
+ --font-mono:
50
+ 'JetBrains Mono', ui-monospace, 'SF Mono', 'Cascadia Code', 'Fira Code', Menlo, Consolas,
51
+ monospace;
52
+
53
+ /* ── Typography · brand type scale (from brand guideline) ────── *
54
+ * Token Size Line Tracking Weight Use *
55
+ * Display 64px 1.05 −0.04em 700 Hero headlines *
56
+ * H1 44px 1.10 −0.025em 700 Page titles *
57
+ * H2 32px 1.15 −0.02em 600 Section headings *
58
+ * H3 22px 1.25 −0.015em 600 Card / group heads *
59
+ * Body 16px 1.55 0 400 Default body copy *
60
+ * Caption 13px 1.50 0 400 Labels, metadata *
61
+ * Mono 18px 1.50 0 500 Code, eyebrows *
62
+ * */
63
+ --font-size-display: 4rem; /* 64px */
64
+ --font-size-h1: 2.75rem; /* 44px */
65
+ --font-size-h2: 2rem; /* 32px */
66
+ --font-size-h3: 1.375rem; /* 22px */
67
+ --font-size-body: 1rem; /* 16px */
68
+ --font-size-caption: 0.8125rem; /* 13px */
69
+ --font-size-mono: 1.125rem; /* 18px */
70
+
71
+ --leading-display: 1.05;
72
+ --leading-h1: 1.1;
73
+ --leading-h2: 1.15;
74
+ --leading-h3: 1.25;
75
+ --leading-body: 1.55;
76
+ --leading-caption: 1.5;
77
+ --leading-mono: 1.5;
78
+
79
+ --tracking-display: -0.04em;
80
+ --tracking-h1: -0.025em;
81
+ --tracking-h2: -0.02em;
82
+ --tracking-h3: -0.015em;
83
+
84
+ /* ── Spacing grid · 4px base (semantic shortcuts) ────────────── *
85
+ * Tailwind's default 0.25rem (4px) grid is unchanged. *
86
+ * These tokens add named slots for common component spacing. */
87
+ --spacing-page-x: 1.5rem; /* 24px - page horizontal gutter */
88
+ --spacing-page-y: 2rem; /* 32px - page vertical gutter */
89
+ --spacing-card: 1.5rem; /* 24px - card inner padding */
90
+ --spacing-card-sm: 1rem; /* 16px - compact card padding */
91
+ --spacing-section: 3rem; /* 48px - section gap */
92
+ --spacing-input-x: 0.75rem; /* 12px - input horizontal padding */
93
+ --spacing-input-y: 0.5rem; /* 8px - input vertical padding */
94
+ --spacing-stack: 1rem; /* 16px - default vertical stack gap */
95
+ --spacing-inline: 0.5rem; /* 8px - default inline gap */
96
+ }
97
+
98
+ /* ── Soft Light · activated by html[data-theme="light"] ──────────────────── */
99
+ html[data-theme='light'] {
100
+ --color-ink: #f6f7f9;
101
+ --color-surface: #ffffff;
102
+ --color-surface-2: #e6e8ee;
103
+ --color-line: #d6d9e0;
104
+ --color-fg: #111827;
105
+ --color-muted: #525866;
106
+ --color-faint: #9aa1ad;
107
+
108
+ --color-brand: #007ea6;
109
+ --color-brand-light: #00b4dd;
110
+ --color-brand-deep: #006a8a;
111
+ --color-violet: #6b5cdf;
112
+
113
+ --color-success: #15803d;
114
+ --color-warn: #b45309;
115
+ --color-danger: #b91c1c;
116
+ }
117
+
118
+ @layer base {
119
+ html {
120
+ background-color: var(--color-ink);
121
+ color-scheme: dark;
122
+ }
123
+
124
+ html[data-theme='light'] {
125
+ color-scheme: light;
126
+ }
127
+
128
+ body {
129
+ background-color: var(--color-ink);
130
+ color: var(--color-fg);
131
+ font-family: var(--font-sans);
132
+ -webkit-font-smoothing: antialiased;
133
+ -moz-osx-font-smoothing: grayscale;
134
+ }
135
+
136
+ ::selection {
137
+ background-color: color-mix(in srgb, var(--color-brand) 30%, transparent);
138
+ }
139
+
140
+ :focus-visible {
141
+ outline: 2px solid color-mix(in srgb, var(--color-brand) 60%, transparent);
142
+ outline-offset: 2px;
143
+ }
144
+
145
+ :focus:not(:focus-visible) {
146
+ outline: none;
147
+ }
148
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Join an arbitrary list of class-name expressions into a single string.
3
+ *
4
+ * Falsy entries (`undefined`, `null`, `false`, `0`, `''`) are dropped so you
5
+ * can write conditionals inline without sprinkling `&&` and ternaries:
6
+ *
7
+ * cn('btn', isPrimary && 'btn-primary', { 'is-disabled': disabled })
8
+ *
9
+ * Object entries are treated as `{ className: boolean }` maps, matching the
10
+ * behaviour of `clsx` so existing muscle memory carries over.
11
+ */
12
+ export type ClassValue = string | number | null | false | undefined | Record<string, boolean | null | undefined> | ClassValue[];
13
+ export declare function cn(...inputs: ClassValue[]): string;
@@ -0,0 +1,24 @@
1
+ export function cn(...inputs) {
2
+ const out = [];
3
+ for (const value of inputs) {
4
+ if (!value)
5
+ continue;
6
+ if (typeof value === 'string' || typeof value === 'number') {
7
+ out.push(String(value));
8
+ continue;
9
+ }
10
+ if (Array.isArray(value)) {
11
+ const nested = cn(...value);
12
+ if (nested)
13
+ out.push(nested);
14
+ continue;
15
+ }
16
+ if (typeof value === 'object') {
17
+ for (const key in value) {
18
+ if (value[key])
19
+ out.push(key);
20
+ }
21
+ }
22
+ }
23
+ return out.join(' ');
24
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Theme utilities - small, framework-agnostic helpers for managing the
3
+ * `data-theme` attribute on `<html>` and persisting the user's choice.
4
+ *
5
+ * The `<ThemeToggle />` component uses these under the hood; you can also
6
+ * call them directly when you need to set the theme from a layout, route
7
+ * loader, or an inline `<script>` that runs before paint.
8
+ */
9
+ export type Theme = 'dark' | 'light';
10
+ /**
11
+ * Inline-script payload that should run *before* the first paint to avoid
12
+ * the flash-of-wrong-theme. Drop the returned string into a `<script>` tag
13
+ * in the `<head>` of your app shell.
14
+ *
15
+ * Picks the theme in this order:
16
+ * 1. `localStorage["lyeve-theme"]` if set
17
+ * 2. The user's OS preference (`prefers-color-scheme`)
18
+ * 3. Dark (the default brand palette)
19
+ */
20
+ export declare function themeBootScript(): string;
21
+ /** Read the current theme. SSR-safe; returns `'dark'` on the server. */
22
+ export declare function getTheme(): Theme;
23
+ /** Apply a theme and persist the choice to `localStorage`. */
24
+ export declare function setTheme(theme: Theme): void;
25
+ /** Flip between dark and light. Returns the new theme. */
26
+ export declare function toggleTheme(): Theme;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Theme utilities - small, framework-agnostic helpers for managing the
3
+ * `data-theme` attribute on `<html>` and persisting the user's choice.
4
+ *
5
+ * The `<ThemeToggle />` component uses these under the hood; you can also
6
+ * call them directly when you need to set the theme from a layout, route
7
+ * loader, or an inline `<script>` that runs before paint.
8
+ */
9
+ const STORAGE_KEY = 'lyeve-theme';
10
+ /**
11
+ * Inline-script payload that should run *before* the first paint to avoid
12
+ * the flash-of-wrong-theme. Drop the returned string into a `<script>` tag
13
+ * in the `<head>` of your app shell.
14
+ *
15
+ * Picks the theme in this order:
16
+ * 1. `localStorage["lyeve-theme"]` if set
17
+ * 2. The user's OS preference (`prefers-color-scheme`)
18
+ * 3. Dark (the default brand palette)
19
+ */
20
+ export function themeBootScript() {
21
+ return `(function () {
22
+ try {
23
+ var stored = localStorage.getItem('${STORAGE_KEY}');
24
+ var prefersLight = window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches;
25
+ var theme = stored === 'light' || stored === 'dark' ? stored : (prefersLight ? 'light' : 'dark');
26
+ document.documentElement.setAttribute('data-theme', theme);
27
+ } catch (_) {
28
+ document.documentElement.setAttribute('data-theme', 'dark');
29
+ }
30
+ })();`;
31
+ }
32
+ /** Read the current theme. SSR-safe; returns `'dark'` on the server. */
33
+ export function getTheme() {
34
+ if (typeof document === 'undefined')
35
+ return 'dark';
36
+ return document.documentElement.getAttribute('data-theme') === 'light' ? 'light' : 'dark';
37
+ }
38
+ /** Apply a theme and persist the choice to `localStorage`. */
39
+ export function setTheme(theme) {
40
+ if (typeof document === 'undefined')
41
+ return;
42
+ document.documentElement.setAttribute('data-theme', theme);
43
+ try {
44
+ localStorage.setItem(STORAGE_KEY, theme);
45
+ }
46
+ catch {
47
+ // Storage may be disabled (private mode, quota); fall through silently.
48
+ }
49
+ }
50
+ /** Flip between dark and light. Returns the new theme. */
51
+ export function toggleTheme() {
52
+ const next = getTheme() === 'dark' ? 'light' : 'dark';
53
+ setTheme(next);
54
+ return next;
55
+ }