@tabelhadev/tabelhawebui 0.28.4

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 (175) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +222 -0
  3. package/README.pt-BR.md +217 -0
  4. package/dist/actions/click-outside.d.ts +3 -0
  5. package/dist/actions/click-outside.js +12 -0
  6. package/dist/actions/combobox-keyboard-nav.d.ts +9 -0
  7. package/dist/actions/combobox-keyboard-nav.js +28 -0
  8. package/dist/actions/focus-trap.d.ts +1 -0
  9. package/dist/actions/focus-trap.js +34 -0
  10. package/dist/actions/scroll-lock.d.ts +1 -0
  11. package/dist/actions/scroll-lock.js +17 -0
  12. package/dist/components/actions/Button.svelte +169 -0
  13. package/dist/components/actions/Button.svelte.d.ts +19 -0
  14. package/dist/components/appshell/AppShell.svelte +50 -0
  15. package/dist/components/appshell/AppShell.svelte.d.ts +19 -0
  16. package/dist/components/appshell/AppShellBottomNav.svelte +67 -0
  17. package/dist/components/appshell/AppShellBottomNav.svelte.d.ts +9 -0
  18. package/dist/components/appshell/AppShellContent.svelte +38 -0
  19. package/dist/components/appshell/AppShellContent.svelte.d.ts +10 -0
  20. package/dist/components/appshell/AppShellSidebar.svelte +196 -0
  21. package/dist/components/appshell/AppShellSidebar.svelte.d.ts +15 -0
  22. package/dist/components/appshell/types.d.ts +6 -0
  23. package/dist/components/appshell/types.js +1 -0
  24. package/dist/components/appshell/utils.d.ts +1 -0
  25. package/dist/components/appshell/utils.js +3 -0
  26. package/dist/components/card/Card.svelte +31 -0
  27. package/dist/components/card/Card.svelte.d.ts +9 -0
  28. package/dist/components/card/CardAction.svelte +57 -0
  29. package/dist/components/card/CardAction.svelte.d.ts +10 -0
  30. package/dist/components/card/CardContent.svelte +25 -0
  31. package/dist/components/card/CardContent.svelte.d.ts +8 -0
  32. package/dist/components/card/CardDescription.svelte +24 -0
  33. package/dist/components/card/CardDescription.svelte.d.ts +8 -0
  34. package/dist/components/card/CardFooter.svelte +25 -0
  35. package/dist/components/card/CardFooter.svelte.d.ts +8 -0
  36. package/dist/components/card/CardHeader.svelte +54 -0
  37. package/dist/components/card/CardHeader.svelte.d.ts +10 -0
  38. package/dist/components/card/CardTitle.svelte +25 -0
  39. package/dist/components/card/CardTitle.svelte.d.ts +8 -0
  40. package/dist/components/chat/ChatMessage.svelte +104 -0
  41. package/dist/components/chat/ChatMessage.svelte.d.ts +10 -0
  42. package/dist/components/choice/Listbox.svelte +183 -0
  43. package/dist/components/choice/Listbox.svelte.d.ts +15 -0
  44. package/dist/components/choice/MultiSelect.svelte +278 -0
  45. package/dist/components/choice/MultiSelect.svelte.d.ts +18 -0
  46. package/dist/components/choice/Rating.svelte +91 -0
  47. package/dist/components/choice/Rating.svelte.d.ts +12 -0
  48. package/dist/components/choice/Toggle.svelte +112 -0
  49. package/dist/components/choice/Toggle.svelte.d.ts +12 -0
  50. package/dist/components/feedback/Badge.svelte +66 -0
  51. package/dist/components/feedback/Badge.svelte.d.ts +9 -0
  52. package/dist/components/feedback/ErrorPage.svelte +99 -0
  53. package/dist/components/feedback/ErrorPage.svelte.d.ts +13 -0
  54. package/dist/components/feedback/Message.svelte +130 -0
  55. package/dist/components/feedback/Message.svelte.d.ts +14 -0
  56. package/dist/components/feedback/ProgressBar.svelte +69 -0
  57. package/dist/components/feedback/ProgressBar.svelte.d.ts +8 -0
  58. package/dist/components/feedback/Skeleton.svelte +47 -0
  59. package/dist/components/feedback/Skeleton.svelte.d.ts +9 -0
  60. package/dist/components/feedback/Status.svelte +75 -0
  61. package/dist/components/feedback/Status.svelte.d.ts +10 -0
  62. package/dist/components/feedback/StatusPill.svelte +94 -0
  63. package/dist/components/feedback/StatusPill.svelte.d.ts +13 -0
  64. package/dist/components/feedback/Toaster.svelte +122 -0
  65. package/dist/components/feedback/Toaster.svelte.d.ts +7 -0
  66. package/dist/components/feedback/Tooltip.svelte +126 -0
  67. package/dist/components/feedback/Tooltip.svelte.d.ts +10 -0
  68. package/dist/components/feedback/toast.svelte.d.ts +27 -0
  69. package/dist/components/feedback/toast.svelte.js +23 -0
  70. package/dist/components/forms/Checkbox.svelte +96 -0
  71. package/dist/components/forms/Checkbox.svelte.d.ts +11 -0
  72. package/dist/components/forms/DatePicker.svelte +372 -0
  73. package/dist/components/forms/DatePicker.svelte.d.ts +21 -0
  74. package/dist/components/forms/Field.svelte +47 -0
  75. package/dist/components/forms/Field.svelte.d.ts +11 -0
  76. package/dist/components/forms/FileUpload.svelte +504 -0
  77. package/dist/components/forms/FileUpload.svelte.d.ts +43 -0
  78. package/dist/components/forms/Input.svelte +93 -0
  79. package/dist/components/forms/Input.svelte.d.ts +9 -0
  80. package/dist/components/forms/Label.svelte +30 -0
  81. package/dist/components/forms/Label.svelte.d.ts +10 -0
  82. package/dist/components/forms/Radio.svelte +80 -0
  83. package/dist/components/forms/Radio.svelte.d.ts +11 -0
  84. package/dist/components/forms/Select.svelte +322 -0
  85. package/dist/components/forms/Select.svelte.d.ts +22 -0
  86. package/dist/components/forms/TagInput.svelte +336 -0
  87. package/dist/components/forms/TagInput.svelte.d.ts +16 -0
  88. package/dist/components/forms/Textarea.svelte +64 -0
  89. package/dist/components/forms/Textarea.svelte.d.ts +9 -0
  90. package/dist/components/landing/Landing.svelte +47 -0
  91. package/dist/components/landing/Landing.svelte.d.ts +8 -0
  92. package/dist/components/landing/LandingFeatures.svelte +92 -0
  93. package/dist/components/landing/LandingFeatures.svelte.d.ts +15 -0
  94. package/dist/components/landing/LandingHero.svelte +92 -0
  95. package/dist/components/landing/LandingHero.svelte.d.ts +13 -0
  96. package/dist/components/landing/LandingRoadmap.svelte +42 -0
  97. package/dist/components/landing/LandingRoadmap.svelte.d.ts +11 -0
  98. package/dist/components/landing/LandingSection.svelte +25 -0
  99. package/dist/components/landing/LandingSection.svelte.d.ts +9 -0
  100. package/dist/components/landing/LandingSectionHeading.svelte +19 -0
  101. package/dist/components/landing/LandingSectionHeading.svelte.d.ts +10 -0
  102. package/dist/components/landing/LandingSteps.svelte +100 -0
  103. package/dist/components/landing/LandingSteps.svelte.d.ts +13 -0
  104. package/dist/components/layout/BracketLink.svelte +41 -0
  105. package/dist/components/layout/BracketLink.svelte.d.ts +9 -0
  106. package/dist/components/layout/Divider.svelte +70 -0
  107. package/dist/components/layout/Divider.svelte.d.ts +9 -0
  108. package/dist/components/layout/Eyebrow.svelte +24 -0
  109. package/dist/components/layout/Eyebrow.svelte.d.ts +8 -0
  110. package/dist/components/layout/PageHeader.svelte +106 -0
  111. package/dist/components/layout/PageHeader.svelte.d.ts +15 -0
  112. package/dist/components/layout/PageSeo.svelte +34 -0
  113. package/dist/components/layout/PageSeo.svelte.d.ts +10 -0
  114. package/dist/components/layout/PageShell.svelte +25 -0
  115. package/dist/components/layout/PageShell.svelte.d.ts +8 -0
  116. package/dist/components/layout/Panel.svelte +105 -0
  117. package/dist/components/layout/Panel.svelte.d.ts +12 -0
  118. package/dist/components/layout/Prose.svelte +101 -0
  119. package/dist/components/layout/Prose.svelte.d.ts +7 -0
  120. package/dist/components/layout/RuleCard.svelte +63 -0
  121. package/dist/components/layout/RuleCard.svelte.d.ts +13 -0
  122. package/dist/components/layout/SectionHeading.svelte +57 -0
  123. package/dist/components/layout/SectionHeading.svelte.d.ts +10 -0
  124. package/dist/components/layout/StatTile.svelte +76 -0
  125. package/dist/components/layout/StatTile.svelte.d.ts +12 -0
  126. package/dist/components/layout/TerminalWindow.svelte +81 -0
  127. package/dist/components/layout/TerminalWindow.svelte.d.ts +9 -0
  128. package/dist/components/layout/Wordmark.svelte +26 -0
  129. package/dist/components/layout/Wordmark.svelte.d.ts +8 -0
  130. package/dist/components/list/List.svelte +40 -0
  131. package/dist/components/list/List.svelte.d.ts +13 -0
  132. package/dist/components/list/ListItem.svelte +81 -0
  133. package/dist/components/list/ListItem.svelte.d.ts +13 -0
  134. package/dist/components/marketing/MarketingShell.svelte +170 -0
  135. package/dist/components/marketing/MarketingShell.svelte.d.ts +23 -0
  136. package/dist/components/marketing/MarketingShellFooter.svelte +92 -0
  137. package/dist/components/marketing/MarketingShellFooter.svelte.d.ts +14 -0
  138. package/dist/components/navigation/Accordion.svelte +110 -0
  139. package/dist/components/navigation/Accordion.svelte.d.ts +15 -0
  140. package/dist/components/navigation/Carousel.svelte +115 -0
  141. package/dist/components/navigation/Carousel.svelte.d.ts +12 -0
  142. package/dist/components/navigation/InstructionStep.svelte +115 -0
  143. package/dist/components/navigation/InstructionStep.svelte.d.ts +10 -0
  144. package/dist/components/navigation/InstructionSteps.svelte +39 -0
  145. package/dist/components/navigation/InstructionSteps.svelte.d.ts +8 -0
  146. package/dist/components/navigation/Nav.svelte +102 -0
  147. package/dist/components/navigation/Nav.svelte.d.ts +14 -0
  148. package/dist/components/navigation/Stepper.svelte +112 -0
  149. package/dist/components/navigation/Stepper.svelte.d.ts +12 -0
  150. package/dist/components/navigation/TabCard.svelte +62 -0
  151. package/dist/components/navigation/TabCard.svelte.d.ts +10 -0
  152. package/dist/components/navigation/Tabs.svelte +83 -0
  153. package/dist/components/navigation/Tabs.svelte.d.ts +12 -0
  154. package/dist/components/navigation/ThemeToggle.svelte +118 -0
  155. package/dist/components/navigation/ThemeToggle.svelte.d.ts +20 -0
  156. package/dist/components/overlay/Dialog.svelte +200 -0
  157. package/dist/components/overlay/Dialog.svelte.d.ts +19 -0
  158. package/dist/components/overlay/DialogActions.svelte +50 -0
  159. package/dist/components/overlay/DialogActions.svelte.d.ts +9 -0
  160. package/dist/components/overlay/Dropdown.svelte +110 -0
  161. package/dist/components/overlay/Dropdown.svelte.d.ts +10 -0
  162. package/dist/components/overlay/FloatingActionPill.svelte +96 -0
  163. package/dist/components/overlay/FloatingActionPill.svelte.d.ts +14 -0
  164. package/dist/components/overlay/Sidebar.svelte +258 -0
  165. package/dist/components/overlay/Sidebar.svelte.d.ts +18 -0
  166. package/dist/components/table/Table.svelte +550 -0
  167. package/dist/components/table/Table.svelte.d.ts +42 -0
  168. package/dist/components/timeline/Timeline.svelte +25 -0
  169. package/dist/components/timeline/Timeline.svelte.d.ts +8 -0
  170. package/dist/components/timeline/TimelineItem.svelte +220 -0
  171. package/dist/components/timeline/TimelineItem.svelte.d.ts +18 -0
  172. package/dist/index.d.ts +156 -0
  173. package/dist/index.js +149 -0
  174. package/dist/theme/theme.css +348 -0
  175. package/package.json +52 -0
@@ -0,0 +1,336 @@
1
+ <script lang="ts">
2
+ import { fade } from 'svelte/transition';
3
+ import { clickOutside } from '../../actions/click-outside';
4
+ import { comboboxKeydown } from '../../actions/combobox-keyboard-nav';
5
+
6
+ let {
7
+ options = [],
8
+ value = $bindable([]),
9
+ label,
10
+ name,
11
+ disabled = false,
12
+ placeholder = 'Adicione uma tag…',
13
+ createLabel = 'Criar tag',
14
+ 'aria-label': ariaLabel,
15
+ class: className = '',
16
+ ...rest
17
+ }: {
18
+ options?: string[];
19
+ value?: string[];
20
+ label?: string;
21
+ name?: string;
22
+ disabled?: boolean;
23
+ placeholder?: string;
24
+ createLabel?: string;
25
+ class?: string;
26
+ } & { 'aria-label'?: string; id?: string } = $props();
27
+
28
+ let open = $state(false);
29
+ let query = $state('');
30
+ let activeIndex = $state(-1);
31
+ let rootEl = $state<HTMLDivElement | undefined>();
32
+ let inputEl = $state<HTMLInputElement | undefined>();
33
+
34
+ const trimmedQuery = $derived(query.trim());
35
+ const filtered = $derived(
36
+ trimmedQuery
37
+ ? options.filter(
38
+ (o) =>
39
+ !value.includes(o) &&
40
+ o.toLowerCase().includes(trimmedQuery.toLowerCase())
41
+ )
42
+ : options.filter((o) => !value.includes(o))
43
+ );
44
+ const canCreate = $derived(
45
+ trimmedQuery !== '' &&
46
+ !options.some((o) => o.toLowerCase() === trimmedQuery.toLowerCase())
47
+ );
48
+ const matches = $derived(canCreate ? [...filtered, trimmedQuery] : filtered);
49
+
50
+ function addTag(tag: string) {
51
+ const trimmed = tag.trim();
52
+ if (!trimmed || value.includes(trimmed)) {
53
+ query = '';
54
+ activeIndex = -1;
55
+ return;
56
+ }
57
+ value = [...value, trimmed];
58
+ query = '';
59
+ activeIndex = -1;
60
+ }
61
+
62
+ function removeTag(tag: string) {
63
+ value = value.filter((v) => v !== tag);
64
+ }
65
+
66
+ const onKeydown = comboboxKeydown({
67
+ get open() { return open; },
68
+ get activeIndex() { return activeIndex; },
69
+ get itemCount() { return matches.length; },
70
+ onActiveIndexChange: (i) => (activeIndex = i),
71
+ onClose: () => (open = false),
72
+ onConfirm: (i) => addTag(matches[i]),
73
+ onOpen: () => { open = true; }
74
+ });
75
+
76
+ function handleKeydown(e: KeyboardEvent) {
77
+ if (disabled) return;
78
+ onKeydown(e);
79
+ if (e.key === 'Enter') {
80
+ e.preventDefault();
81
+ if (open && activeIndex >= 0 && matches[activeIndex] !== undefined) {
82
+ addTag(matches[activeIndex]);
83
+ } else {
84
+ addTag(query);
85
+ }
86
+ inputEl?.focus();
87
+ return;
88
+ }
89
+ if (e.key === 'Backspace' && query === '' && value.length > 0) {
90
+ removeTag(value[value.length - 1]);
91
+ }
92
+ }
93
+ </script>
94
+
95
+ <div
96
+ class="twui-tag-input {className}"
97
+ bind:this={rootEl}
98
+ use:clickOutside={() => (open = false)}
99
+ >
100
+ {#if label}
101
+ <label class="twui-tag-input-label" for={rest.id}>{label}</label>
102
+ {/if}
103
+
104
+ <div class="twui-tag-input-field">
105
+ {#each value as tag (tag)}
106
+ <span class="twui-tag-input-chip">
107
+ {tag}
108
+ <button
109
+ type="button"
110
+ class="twui-tag-input-chip-remove"
111
+ aria-label={`Remover ${tag}`}
112
+ disabled={disabled}
113
+ onclick={() => removeTag(tag)}
114
+ >×</button>
115
+ </span>
116
+ {/each}
117
+ <input
118
+ type="text"
119
+ class="twui-tag-input-text"
120
+ bind:this={inputEl}
121
+ bind:value={query}
122
+ {...rest}
123
+ placeholder={value.length ? '' : placeholder}
124
+ disabled={disabled}
125
+ role="combobox"
126
+ aria-haspopup="listbox"
127
+ aria-expanded={open}
128
+ aria-autocomplete="list"
129
+ aria-label={ariaLabel ?? label}
130
+ onkeydown={handleKeydown}
131
+ onfocus={() => (open = true)}
132
+ oninput={() => {
133
+ open = true;
134
+ activeIndex = -1;
135
+ }}
136
+ />
137
+ </div>
138
+
139
+ {#if name}
140
+ <input type="hidden" {name} value={value.join(',')} />
141
+ {/if}
142
+
143
+ {#if open && (filtered.length > 0 || canCreate)}
144
+ <ul
145
+ class="twui-tag-input-popover"
146
+ role="listbox"
147
+ aria-multiselectable="true"
148
+ transition:fade={{ duration: 100 }}
149
+ >
150
+ {#each matches as opt, i (opt)}
151
+ {#if canCreate && i === matches.length - 1}
152
+ <li>
153
+ <button
154
+ type="button"
155
+ role="option"
156
+ aria-selected="false"
157
+ class="twui-tag-input-option twui-tag-input-option-create {i ===
158
+ activeIndex
159
+ ? 'twui-tag-input-option-active'
160
+ : ''}"
161
+ onclick={() => addTag(opt)}
162
+ onmouseenter={() => (activeIndex = i)}
163
+ >
164
+ <span class="twui-tag-input-create-icon" aria-hidden="true">+</span>
165
+ <span>{createLabel}: {opt}</span>
166
+ </button>
167
+ </li>
168
+ {:else}
169
+ <li>
170
+ <button
171
+ type="button"
172
+ role="option"
173
+ aria-selected="false"
174
+ class="twui-tag-input-option {i === activeIndex
175
+ ? 'twui-tag-input-option-active'
176
+ : ''}"
177
+ onclick={() => addTag(opt)}
178
+ onmouseenter={() => (activeIndex = i)}
179
+ >
180
+ {opt}
181
+ </button>
182
+ </li>
183
+ {/if}
184
+ {/each}
185
+ </ul>
186
+ {/if}
187
+ </div>
188
+
189
+ <style>
190
+ .twui-tag-input {
191
+ position: relative;
192
+ width: 100%;
193
+ }
194
+
195
+ .twui-tag-input-label {
196
+ display: inline-flex;
197
+ margin-bottom: 6px;
198
+ font-family: var(--twui-font-mono);
199
+ font-size: 13px;
200
+ font-weight: 500;
201
+ color: var(--twui-ink);
202
+ }
203
+
204
+ .twui-tag-input-field {
205
+ display: flex;
206
+ flex-wrap: wrap;
207
+ align-items: center;
208
+ gap: 6px;
209
+ padding: 6px 8px;
210
+ border: 1px solid var(--twui-rule);
211
+ background: var(--twui-paper);
212
+ transition:
213
+ border-color 0.15s ease,
214
+ box-shadow 0.15s ease;
215
+ }
216
+
217
+ .twui-tag-input-field:focus-within {
218
+ border-color: var(--twui-accent);
219
+ }
220
+
221
+ .twui-tag-input-chip {
222
+ display: inline-flex;
223
+ align-items: center;
224
+ gap: 6px;
225
+ padding: 2px 4px 2px 8px;
226
+ border: 1px solid var(--twui-rule);
227
+ background: var(--twui-paper-raised);
228
+ font-family: var(--twui-font-mono);
229
+ font-size: 13px;
230
+ color: var(--twui-ink);
231
+ }
232
+
233
+ .twui-tag-input-chip-remove {
234
+ display: inline-flex;
235
+ align-items: center;
236
+ justify-content: center;
237
+ width: 18px;
238
+ height: 18px;
239
+ padding: 0;
240
+ border: none;
241
+ background: transparent;
242
+ font-size: 14px;
243
+ line-height: 1;
244
+ color: var(--twui-ink-faint);
245
+ cursor: pointer;
246
+ transition: color 0.15s ease;
247
+ }
248
+
249
+ .twui-tag-input-chip-remove:hover {
250
+ color: var(--twui-danger);
251
+ }
252
+
253
+ .twui-tag-input-chip-remove:disabled {
254
+ opacity: 0.5;
255
+ cursor: not-allowed;
256
+ }
257
+
258
+ .twui-tag-input-text {
259
+ flex: 1;
260
+ min-width: 8rem;
261
+ padding: 4px;
262
+ border: none;
263
+ background: transparent;
264
+ font-family: var(--twui-font-mono);
265
+ font-size: 14px;
266
+ color: var(--twui-ink);
267
+ outline: none;
268
+ }
269
+
270
+ .twui-tag-input-text::placeholder {
271
+ color: var(--twui-ink-faint);
272
+ }
273
+
274
+ .twui-tag-input-text:disabled {
275
+ opacity: 0.5;
276
+ cursor: not-allowed;
277
+ }
278
+
279
+ .twui-tag-input-popover {
280
+ position: absolute;
281
+ top: calc(100% + 4px);
282
+ right: 0;
283
+ left: 0;
284
+ z-index: 50;
285
+ max-height: 240px;
286
+ overflow-y: auto;
287
+ margin: 0;
288
+ padding: 4px;
289
+ list-style: none;
290
+ border: 1px solid var(--twui-rule);
291
+ background: var(--twui-paper-raised);
292
+ box-shadow: var(--twui-shadow-offset, 3px 3px 0 0 var(--twui-rule));
293
+ }
294
+
295
+ .twui-tag-input-popover li {
296
+ padding: 0;
297
+ }
298
+
299
+ .twui-tag-input-option {
300
+ display: flex;
301
+ align-items: center;
302
+ gap: 8px;
303
+ width: 100%;
304
+ padding: 6px 10px;
305
+ border: none;
306
+ background: transparent;
307
+ font-family: var(--twui-font-mono);
308
+ font-size: 13px;
309
+ text-align: left;
310
+ color: var(--twui-ink);
311
+ cursor: pointer;
312
+ }
313
+
314
+ .twui-tag-input-option:hover,
315
+ .twui-tag-input-option-active {
316
+ background: var(--twui-accent-soft);
317
+ color: var(--twui-accent);
318
+ }
319
+
320
+ .twui-tag-input-option-create {
321
+ color: var(--twui-ink-soft);
322
+ }
323
+
324
+ .twui-tag-input-option-create .twui-tag-input-create-icon {
325
+ display: inline-flex;
326
+ align-items: center;
327
+ justify-content: center;
328
+ width: 16px;
329
+ height: 16px;
330
+ border: 1px solid var(--twui-rule);
331
+ border-radius: 2px;
332
+ font-size: 12px;
333
+ line-height: 1;
334
+ color: var(--twui-accent);
335
+ }
336
+ </style>
@@ -0,0 +1,16 @@
1
+ type $$ComponentProps = {
2
+ options?: string[];
3
+ value?: string[];
4
+ label?: string;
5
+ name?: string;
6
+ disabled?: boolean;
7
+ placeholder?: string;
8
+ createLabel?: string;
9
+ class?: string;
10
+ } & {
11
+ 'aria-label'?: string;
12
+ id?: string;
13
+ };
14
+ declare const TagInput: import("svelte").Component<$$ComponentProps, {}, "value">;
15
+ type TagInput = ReturnType<typeof TagInput>;
16
+ export default TagInput;
@@ -0,0 +1,64 @@
1
+ <script lang="ts">
2
+ import type { HTMLTextareaAttributes } from 'svelte/elements';
3
+
4
+ let {
5
+ class: className = '',
6
+ value = $bindable(''),
7
+ rows = 4,
8
+ invalid = false,
9
+ ...rest
10
+ }: HTMLTextareaAttributes & {
11
+ value?: string;
12
+ rows?: number;
13
+ invalid?: boolean;
14
+ } = $props();
15
+ </script>
16
+
17
+ <textarea
18
+ {...rest}
19
+ {rows}
20
+ value={value}
21
+ oninput={(e) => (value = e.currentTarget.value)}
22
+ aria-invalid={invalid}
23
+ class="twui-textarea {invalid ? 'twui-textarea-invalid' : ''} {className}"
24
+ ></textarea>
25
+
26
+ <style>
27
+ .twui-textarea {
28
+ width: 100%;
29
+ min-height: 96px;
30
+ padding: 8px 12px;
31
+ border: 1px solid var(--twui-rule);
32
+ background: var(--twui-paper);
33
+ font-family: var(--twui-font-mono);
34
+ font-size: 14px;
35
+ line-height: 1.5;
36
+ color: var(--twui-ink);
37
+ outline: none;
38
+ resize: vertical;
39
+ transition:
40
+ border-color 0.15s ease,
41
+ box-shadow 0.15s ease;
42
+ }
43
+
44
+ .twui-textarea::placeholder {
45
+ color: var(--twui-ink-faint);
46
+ }
47
+
48
+ .twui-textarea:focus-visible {
49
+ border-color: var(--twui-accent);
50
+ }
51
+
52
+ .twui-textarea:disabled {
53
+ opacity: 0.5;
54
+ cursor: not-allowed;
55
+ }
56
+
57
+ .twui-textarea-invalid {
58
+ border-color: var(--twui-danger);
59
+ }
60
+
61
+ .twui-textarea-invalid:focus-visible {
62
+ border-color: var(--twui-danger);
63
+ }
64
+ </style>
@@ -0,0 +1,9 @@
1
+ import type { HTMLTextareaAttributes } from 'svelte/elements';
2
+ type $$ComponentProps = HTMLTextareaAttributes & {
3
+ value?: string;
4
+ rows?: number;
5
+ invalid?: boolean;
6
+ };
7
+ declare const Textarea: import("svelte").Component<$$ComponentProps, {}, "value">;
8
+ type Textarea = ReturnType<typeof Textarea>;
9
+ export default Textarea;
@@ -0,0 +1,47 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let {
5
+ children,
6
+ class: className = ''
7
+ }: {
8
+ children?: Snippet;
9
+ class?: string;
10
+ } = $props();
11
+ </script>
12
+
13
+ <div class="twui-landing {className}">
14
+ <div
15
+ aria-hidden="true"
16
+ class="pointer-events-none absolute inset-x-0 top-0 -z-10 h-150"
17
+ style="background-image: var(--gradient-hero)"
18
+ ></div>
19
+
20
+ <div class="twui-landing-inner">
21
+ {@render children?.()}
22
+ </div>
23
+ </div>
24
+
25
+ <style>
26
+ .twui-landing {
27
+ position: relative;
28
+ display: flex;
29
+ width: 100%;
30
+ flex: 1;
31
+ flex-direction: column;
32
+ }
33
+
34
+ .twui-landing-inner {
35
+ display: flex;
36
+ width: 100%;
37
+ flex-direction: column;
38
+ gap: 6rem;
39
+ padding: 4rem 1.5rem 2rem;
40
+ }
41
+
42
+ @media (min-width: 768px) {
43
+ .twui-landing-inner {
44
+ padding-top: 6rem;
45
+ }
46
+ }
47
+ </style>
@@ -0,0 +1,8 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children?: Snippet;
4
+ class?: string;
5
+ };
6
+ declare const Landing: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Landing = ReturnType<typeof Landing>;
8
+ export default Landing;
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import Card from '../card/Card.svelte';
4
+ import CardContent from '../card/CardContent.svelte';
5
+
6
+ let {
7
+ features,
8
+ justify = false,
9
+ class: className = ''
10
+ }: {
11
+ // icon is a Snippet (e.g. `icon: () => <Icon class="..." />`). Don't use
12
+ // a Component directly: there's no way to distinguish snippet from component in
13
+ // Svelte 5 runtime (would break on SSR).
14
+ features: Array<{
15
+ icon: Snippet;
16
+ iconBg?: string;
17
+ iconColor?: string;
18
+ title: string;
19
+ body: string;
20
+ }>;
21
+ justify?: boolean;
22
+ class?: string;
23
+ } = $props();
24
+ </script>
25
+
26
+ <div class="twui-landing-features {className}">
27
+ {#each features as feature (feature.title)}
28
+ <Card>
29
+ <CardContent>
30
+ <div
31
+ class="twui-landing-feature-icon"
32
+ style="background:{feature.iconBg ?? 'var(--twui-accent-soft)'}; color:{feature.iconColor ?? 'var(--twui-accent)'}"
33
+ >
34
+ {@render feature.icon()}
35
+ </div>
36
+ <h3 class="twui-landing-feature-title">{feature.title}</h3>
37
+ <p class="twui-landing-feature-body {justify ? 'twui-landing-feature-body-justify' : ''}">{feature.body}</p>
38
+ </CardContent>
39
+ </Card>
40
+ {/each}
41
+ </div>
42
+
43
+ <style>
44
+ .twui-landing-features {
45
+ display: grid;
46
+ grid-template-columns: 1fr;
47
+ gap: 16px;
48
+ }
49
+
50
+ .twui-landing-feature-icon {
51
+ display: flex;
52
+ align-items: center;
53
+ justify-content: center;
54
+ width: 36px;
55
+ height: 36px;
56
+ margin-bottom: 12px;
57
+ border-radius: 8px;
58
+ }
59
+
60
+ .twui-landing-feature-title {
61
+ margin: 0;
62
+ font-family: var(--twui-font-mono);
63
+ font-size: 14px;
64
+ font-weight: 600;
65
+ color: var(--twui-ink);
66
+ }
67
+
68
+ .twui-landing-feature-body {
69
+ margin: 6px 0 0;
70
+ font-family: var(--twui-font-serif, 'Newsreader', Georgia, serif);
71
+ font-size: 15px;
72
+ line-height: 1.55;
73
+ color: var(--twui-ink-soft);
74
+ }
75
+
76
+ .twui-landing-feature-body-justify {
77
+ text-align: justify;
78
+ text-justify: inter-word;
79
+ }
80
+
81
+ @media (min-width: 640px) {
82
+ .twui-landing-features {
83
+ grid-template-columns: repeat(2, 1fr);
84
+ }
85
+ }
86
+
87
+ @media (min-width: 1024px) {
88
+ .twui-landing-features {
89
+ grid-template-columns: repeat(3, 1fr);
90
+ }
91
+ }
92
+ </style>
@@ -0,0 +1,15 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ features: Array<{
4
+ icon: Snippet;
5
+ iconBg?: string;
6
+ iconColor?: string;
7
+ title: string;
8
+ body: string;
9
+ }>;
10
+ justify?: boolean;
11
+ class?: string;
12
+ };
13
+ declare const LandingFeatures: import("svelte").Component<$$ComponentProps, {}, "">;
14
+ type LandingFeatures = ReturnType<typeof LandingFeatures>;
15
+ export default LandingFeatures;
@@ -0,0 +1,92 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+
4
+ let {
5
+ eyebrow,
6
+ title,
7
+ lead,
8
+ actions,
9
+ note,
10
+ justify = false,
11
+ class: className = ''
12
+ }: {
13
+ eyebrow?: string;
14
+ title?: string | Snippet;
15
+ lead?: string;
16
+ actions?: Snippet;
17
+ note?: string;
18
+ justify?: boolean;
19
+ class?: string;
20
+ } = $props();
21
+ </script>
22
+
23
+ <section class="twui-landing-hero {className}">
24
+ {#if eyebrow}
25
+ <div class="twui-landing-eyebrow">{eyebrow}</div>
26
+ {/if}
27
+ <h1 class="twui-landing-hero-title">
28
+ {#if typeof title === 'function'}
29
+ {@render title()}
30
+ {:else}
31
+ {title}
32
+ {/if}
33
+ </h1>
34
+ {#if lead}
35
+ <p class="twui-landing-hero-lead {justify ? 'twui-landing-hero-lead-justify' : ''}">{lead}</p>
36
+ {/if}
37
+ {#if actions}
38
+ <div class="twui-landing-hero-actions">{@render actions()}</div>
39
+ {/if}
40
+ {#if note}
41
+ <p class="twui-landing-hero-note">{note}</p>
42
+ {/if}
43
+ </section>
44
+
45
+ <style>
46
+ .twui-landing-eyebrow {
47
+ font-family: var(--twui-font-mono);
48
+ font-size: 12px;
49
+ text-transform: uppercase;
50
+ letter-spacing: 0.05em;
51
+ color: var(--twui-accent);
52
+ }
53
+
54
+ .twui-landing-hero-title {
55
+ margin: 16px 0 0;
56
+ font-family: var(--twui-font-mono);
57
+ font-size: 40px;
58
+ font-weight: 700;
59
+ line-height: 1.15;
60
+ letter-spacing: -0.02em;
61
+ text-wrap: balance;
62
+ color: var(--twui-ink);
63
+ }
64
+
65
+ .twui-landing-hero-lead {
66
+ margin: 16px 0 0;
67
+ max-width: 36rem;
68
+ font-family: var(--twui-font-serif, 'Newsreader', Georgia, serif);
69
+ font-size: 18px;
70
+ line-height: 1.6;
71
+ color: var(--twui-ink-soft);
72
+ }
73
+
74
+ .twui-landing-hero-lead-justify {
75
+ text-align: justify;
76
+ text-justify: inter-word;
77
+ }
78
+
79
+ .twui-landing-hero-actions {
80
+ display: flex;
81
+ flex-wrap: wrap;
82
+ gap: 12px;
83
+ margin-top: 24px;
84
+ }
85
+
86
+ .twui-landing-hero-note {
87
+ margin: 16px 0 0;
88
+ font-family: var(--twui-font-mono);
89
+ font-size: 12px;
90
+ color: var(--twui-ink-faint);
91
+ }
92
+ </style>
@@ -0,0 +1,13 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ eyebrow?: string;
4
+ title?: string | Snippet;
5
+ lead?: string;
6
+ actions?: Snippet;
7
+ note?: string;
8
+ justify?: boolean;
9
+ class?: string;
10
+ };
11
+ declare const LandingHero: import("svelte").Component<$$ComponentProps, {}, "">;
12
+ type LandingHero = ReturnType<typeof LandingHero>;
13
+ export default LandingHero;