yaxa-svelte 2.0.0 → 2.0.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 (41) hide show
  1. package/dist/admin/components/AdminDashboard.svelte +10 -8
  2. package/dist/admin/components/DevSandbox.svelte +4 -4
  3. package/dist/admin/components/ImpersonationBanner.svelte +4 -4
  4. package/dist/admin/components/RecordDrawer.svelte +5 -5
  5. package/dist/components/ai/AiChat.svelte +3 -3
  6. package/dist/components/ai/PromptBar.svelte +5 -5
  7. package/dist/components/charts/BarChart.svelte +11 -6
  8. package/dist/components/charts/DonutChart.svelte +3 -3
  9. package/dist/components/charts/LineChart.svelte +21 -9
  10. package/dist/components/elements/Carousel.svelte +4 -3
  11. package/dist/components/elements/CodeBlock.svelte +6 -6
  12. package/dist/components/elements/CodeDiff.svelte +1 -1
  13. package/dist/components/elements/DataTable.svelte +5 -5
  14. package/dist/components/elements/SortableList.svelte +3 -3
  15. package/dist/components/elements/Terminal.svelte +3 -3
  16. package/dist/components/forms/DatePicker.svelte +2 -1
  17. package/dist/components/forms/DateRangePicker.svelte +2 -1
  18. package/dist/components/forms/InputOTP.svelte +8 -4
  19. package/dist/components/forms/RadioGroup.svelte +3 -1
  20. package/dist/components/forms/RichTextEditor.svelte +1 -1
  21. package/dist/components/forms/ToggleGroup.svelte +1 -1
  22. package/dist/components/layout/Footer.svelte +8 -6
  23. package/dist/components/layout/Header.svelte +66 -6
  24. package/dist/components/layout/ResizablePanels.svelte +4 -2
  25. package/dist/components/legal/LegalDocument.svelte +5 -5
  26. package/dist/components/navigation/Breadcrumb.svelte +1 -1
  27. package/dist/components/navigation/Pagination.svelte +1 -1
  28. package/dist/components/navigation/Stepper.svelte +2 -2
  29. package/dist/components/navigation/Tabs.svelte +2 -2
  30. package/dist/components/overlays/Modal.svelte +1 -1
  31. package/dist/components/overlays/Modal.svelte.d.ts +1 -1
  32. package/dist/components/overlays/NotificationCenter.svelte +2 -1
  33. package/dist/components/overlays/Popover.svelte +2 -1
  34. package/dist/components/overlays/Toaster.svelte +7 -7
  35. package/dist/components/saas/AuthCard.svelte +1 -1
  36. package/dist/components/saas/OrgSwitcher.svelte +2 -1
  37. package/dist/components/saas/PasskeyUI.svelte +2 -2
  38. package/dist/components/saas/SubscriptionCard.svelte +1 -1
  39. package/dist/components/seo/OgImage.svelte +1 -1
  40. package/dist/components/theme/ThemeStudio.svelte +51 -24
  41. package/package.json +1 -1
@@ -167,7 +167,7 @@
167
167
  >
168
168
  <!-- Top Navigation Bar -->
169
169
  <header
170
- class="sticky top-0 z-30 flex h-14 items-center justify-between border-b border-neutral-200 bg-white/80 px-6 backdrop-blur-md dark:border-neutral-800 dark:bg-neutral-900/80"
170
+ class="sticky top-0 z-30 flex min-h-14 flex-wrap items-center justify-between gap-2 border-b border-neutral-200 bg-white/80 px-4 py-2 backdrop-blur-md sm:h-14 sm:flex-nowrap sm:px-6 sm:py-0 dark:border-neutral-800 dark:bg-neutral-900/80"
171
171
  >
172
172
  <div class="flex items-center gap-3">
173
173
  <div
@@ -177,7 +177,9 @@
177
177
  </div>
178
178
  <div class="flex items-center gap-2">
179
179
  <h1 class="text-sm font-bold text-neutral-900 dark:text-neutral-100">{title}</h1>
180
- <Badge color="primary" variant="subtle" size="xs">Drizzle Introspect</Badge>
180
+ <Badge color="primary" variant="subtle" size="xs" class="hidden sm:inline-flex"
181
+ >Drizzle Introspect</Badge
182
+ >
181
183
  </div>
182
184
  </div>
183
185
 
@@ -224,7 +226,7 @@
224
226
  </header>
225
227
 
226
228
  <!-- Main Workspace -->
227
- <main class="mx-auto max-w-7xl p-6">
229
+ <main class="mx-auto max-w-7xl p-4 sm:p-6">
228
230
  {#if activeTab === 'sandbox'}
229
231
  <div class="space-y-6">
230
232
  <div class="space-y-1">
@@ -281,8 +283,8 @@
281
283
  <Badge size="xs" variant="subtle" color="neutral">{data.total} rows</Badge>
282
284
  </div>
283
285
 
284
- <div class="flex items-center gap-2">
285
- <form onsubmit={handleSearch} class="relative">
286
+ <div class="flex w-full items-center gap-2 sm:w-auto">
287
+ <form onsubmit={handleSearch} class="relative flex-1 sm:flex-initial">
286
288
  <input
287
289
  id="admin-search-input"
288
290
  name="adminSearch"
@@ -290,7 +292,7 @@
290
292
  placeholder="Search records..."
291
293
  aria-label="Search records"
292
294
  bind:value={searchQuery}
293
- class="w-48 rounded-lg border border-neutral-300 bg-white py-1.5 pr-3 pl-8 text-xs text-neutral-900 placeholder-neutral-400 transition-all focus:w-60 focus:border-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100"
295
+ class="w-full rounded-lg border border-neutral-300 bg-white py-1.5 pr-3 pl-8 text-xs text-neutral-900 placeholder-neutral-400 transition-all focus:border-primary-500 focus:ring-1 focus:ring-primary-500 focus:outline-none sm:w-48 sm:focus:w-60 dark:border-neutral-700 dark:bg-neutral-900 dark:text-neutral-100"
294
296
  />
295
297
  <div
296
298
  class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-2.5 text-neutral-400"
@@ -303,7 +305,7 @@
303
305
  variant="solid"
304
306
  color="primary"
305
307
  size="sm"
306
- class="gap-1 text-xs"
308
+ class="shrink-0 gap-1 text-xs"
307
309
  onclick={() => (isCreateModalOpen = true)}
308
310
  >
309
311
  <Icon name="sparkles" class="h-3.5 w-3.5" />
@@ -379,7 +381,7 @@
379
381
  <!-- Pagination Footer -->
380
382
  {#if data.total > data.pageSize}
381
383
  <div
382
- class="flex items-center justify-between border-t border-neutral-200 px-4 py-3 dark:border-neutral-800"
384
+ class="flex flex-wrap items-center justify-between gap-3 border-t border-neutral-200 px-4 py-3 dark:border-neutral-800"
383
385
  >
384
386
  <span class="text-xs text-neutral-500">
385
387
  Showing {(data.page - 1) * data.pageSize + 1} to {Math.min(
@@ -70,7 +70,7 @@
70
70
  <!-- 1. Email Sandbox (Resend) -->
71
71
  <Card class="space-y-5 border-neutral-200 p-6 shadow-xs dark:border-neutral-800">
72
72
  <div
73
- class="flex items-center justify-between border-b border-neutral-200 pb-3 dark:border-neutral-800"
73
+ class="flex flex-wrap items-center justify-between gap-2 border-b border-neutral-200 pb-3 dark:border-neutral-800"
74
74
  >
75
75
  <div class="flex items-center gap-2.5">
76
76
  <div
@@ -148,7 +148,7 @@
148
148
  <!-- 2. S3 / Cloudflare R2 Upload Sandbox -->
149
149
  <Card class="space-y-5 border-neutral-200 p-6 shadow-xs dark:border-neutral-800">
150
150
  <div
151
- class="flex items-center justify-between border-b border-neutral-200 pb-3 dark:border-neutral-800"
151
+ class="flex flex-wrap items-center justify-between gap-2 border-b border-neutral-200 pb-3 dark:border-neutral-800"
152
152
  >
153
153
  <div class="flex items-center gap-2.5">
154
154
  <div
@@ -244,7 +244,7 @@
244
244
  <!-- 3. Auth & RBAC State Inspection -->
245
245
  <Card class="space-y-4 border-neutral-200 p-6 shadow-xs lg:col-span-2 dark:border-neutral-800">
246
246
  <div
247
- class="flex items-center justify-between border-b border-neutral-200 pb-3 dark:border-neutral-800"
247
+ class="flex flex-wrap items-center justify-between gap-2 border-b border-neutral-200 pb-3 dark:border-neutral-800"
248
248
  >
249
249
  <div class="flex items-center gap-2.5">
250
250
  <div
@@ -267,7 +267,7 @@
267
267
  <div class="grid grid-cols-1 gap-4 text-xs sm:grid-cols-3">
268
268
  <div class="space-y-1">
269
269
  <span class="font-medium text-neutral-500">User Email</span>
270
- <p class="font-mono font-semibold text-neutral-900 dark:text-neutral-100">
270
+ <p class="truncate font-mono font-semibold text-neutral-900 dark:text-neutral-100">
271
271
  {user?.email || 'N/A'}
272
272
  </p>
273
273
  </div>
@@ -45,11 +45,11 @@
45
45
 
46
46
  {#if impersonatedUser}
47
47
  <div
48
- class="sticky top-0 z-50 flex items-center justify-between border-b border-amber-300 bg-amber-500/90 px-4 py-2 text-xs font-medium text-amber-950 shadow-xs backdrop-blur-md dark:border-amber-600/50 dark:bg-amber-600/90 dark:text-amber-50 {className}"
48
+ class="sticky top-0 z-50 flex flex-wrap items-center justify-between gap-2 border-b border-amber-300 bg-amber-500/90 px-4 py-2 text-xs font-medium text-amber-950 shadow-xs backdrop-blur-md dark:border-amber-600/50 dark:bg-amber-600/90 dark:text-amber-50 {className}"
49
49
  >
50
- <div class="flex items-center gap-2">
50
+ <div class="flex min-w-0 flex-1 items-center gap-2">
51
51
  <Icon name="alert" class="h-4 w-4 shrink-0 text-amber-950 dark:text-amber-100" />
52
- <span>
52
+ <span class="truncate">
53
53
  You are currently impersonating <strong
54
54
  >{impersonatedUser.name || impersonatedUser.email || impersonatedUser.id}</strong
55
55
  >.
@@ -62,7 +62,7 @@
62
62
  color="neutral"
63
63
  loading={isStopping}
64
64
  onclick={handleStop}
65
- class="gap-1 border-none bg-amber-950 text-white hover:bg-amber-900 dark:bg-amber-100 dark:text-amber-950 dark:hover:bg-white"
65
+ class="shrink-0 gap-1 border-none bg-amber-950 text-white hover:bg-amber-900 dark:bg-amber-100 dark:text-amber-950 dark:hover:bg-white"
66
66
  >
67
67
  <Icon name="cross" class="h-3 w-3" />
68
68
  <span>Exit Impersonation</span>
@@ -66,16 +66,16 @@
66
66
 
67
67
  <Slideover bind:open title={`Inspect Record: ${tableName}`} class="w-full max-w-xl">
68
68
  {#if record}
69
- <div class="flex flex-col gap-5 p-6">
69
+ <div class="flex flex-col gap-5">
70
70
  <!-- Header Summary -->
71
71
  <div
72
- class="flex items-center justify-between rounded-lg border border-neutral-200 bg-neutral-50 p-3.5 dark:border-neutral-800 dark:bg-neutral-900"
72
+ class="flex flex-wrap items-center justify-between gap-3 rounded-lg border border-neutral-200 bg-neutral-50 p-3.5 dark:border-neutral-800 dark:bg-neutral-900"
73
73
  >
74
- <div class="space-y-0.5">
74
+ <div class="min-w-0 space-y-0.5">
75
75
  <span class="text-xs font-semibold tracking-wider text-neutral-500 uppercase"
76
76
  >Primary Key</span
77
77
  >
78
- <div class="font-mono text-sm font-bold text-neutral-900 dark:text-neutral-100">
78
+ <div class="truncate font-mono text-sm font-bold text-neutral-900 dark:text-neutral-100">
79
79
  {primaryKey}: {record[primaryKey]}
80
80
  </div>
81
81
  </div>
@@ -192,7 +192,7 @@
192
192
 
193
193
  <!-- Action Footer -->
194
194
  <div
195
- class="flex items-center justify-between border-t border-neutral-200 pt-4 dark:border-neutral-800"
195
+ class="flex flex-wrap items-center justify-between gap-2 border-t border-neutral-200 pt-4 dark:border-neutral-800"
196
196
  >
197
197
  <Button
198
198
  variant="outline"
@@ -209,7 +209,7 @@
209
209
  : 'border border-neutral-200/80 bg-neutral-50 text-neutral-900 dark:border-neutral-800/80 dark:bg-neutral-900 dark:text-neutral-100'
210
210
  )}
211
211
  >
212
- <div class="whitespace-pre-wrap">{msg.content}</div>
212
+ <div class="break-words whitespace-pre-wrap">{msg.content}</div>
213
213
 
214
214
  {#if msg.isStreaming}
215
215
  <span class="inline-block h-4 w-1.5 animate-pulse bg-current"></span>
@@ -221,7 +221,7 @@
221
221
  <button
222
222
  type="button"
223
223
  onclick={() => copyMessage(msg.content)}
224
- class="hover:text-neutral-700 dark:hover:text-neutral-200"
224
+ class="rounded-md p-1.5 hover:bg-neutral-100 hover:text-neutral-700 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
225
225
  title="Copy message"
226
226
  >
227
227
  <Icon name="copy" size="xs" />
@@ -231,7 +231,7 @@
231
231
  <button
232
232
  type="button"
233
233
  onclick={() => onretry(msg)}
234
- class="hover:text-neutral-700 dark:hover:text-neutral-200"
234
+ class="rounded-md p-1.5 hover:bg-neutral-100 hover:text-neutral-700 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
235
235
  title="Regenerate response"
236
236
  >
237
237
  <Icon name="rotate-cw" size="xs" />
@@ -156,11 +156,11 @@
156
156
  : 'text-neutral-700 hover:bg-neutral-100 dark:text-neutral-300 dark:hover:bg-neutral-800/60'
157
157
  )}
158
158
  >
159
- <div class="flex items-center gap-2">
160
- <span class="font-mono font-medium">{cmd.name}</span>
161
- <span class="text-neutral-500">{cmd.description}</span>
159
+ <div class="flex min-w-0 items-center gap-2">
160
+ <span class="shrink-0 font-mono font-medium">{cmd.name}</span>
161
+ <span class="truncate text-neutral-500">{cmd.description}</span>
162
162
  </div>
163
- <Icon name="corner-down-left" size="xs" class="text-neutral-400" />
163
+ <Icon name="corner-down-left" size="xs" class="shrink-0 text-neutral-400" />
164
164
  </button>
165
165
  {/each}
166
166
  </div>
@@ -192,7 +192,7 @@
192
192
  <button
193
193
  type="button"
194
194
  onclick={() => removeAttachment(idx)}
195
- class="text-neutral-400 hover:text-rose-500"
195
+ class="rounded p-1 text-neutral-400 hover:text-rose-500"
196
196
  aria-label="Remove attachment"
197
197
  >
198
198
  <Icon name="x" size="xs" />
@@ -122,10 +122,12 @@
122
122
  <!-- X-Axis Labels -->
123
123
  <g class="fill-neutral-400 text-[10px] select-none dark:fill-neutral-500">
124
124
  {#each data as d, i}
125
- {@const x = padding.left + i * groupWidth + groupWidth / 2}
126
- <text {x} y={height - 8} text-anchor="middle">
127
- {xAxisFormat(d.label)}
128
- </text>
125
+ {#if data.length <= 8 || i % Math.ceil(data.length / 6) === 0 || i === data.length - 1}
126
+ {@const x = padding.left + i * groupWidth + groupWidth / 2}
127
+ <text {x} y={height - 8} text-anchor="middle">
128
+ {xAxisFormat(d.label)}
129
+ </text>
130
+ {/if}
129
131
  {/each}
130
132
  </g>
131
133
 
@@ -137,11 +139,14 @@
137
139
  {@const barWidth = isStacked ? availableWidth : availableWidth / (series.length || 1)}
138
140
  {@const startOffset = (groupWidth - availableWidth) / 2}
139
141
 
142
+ <!-- svelte-ignore a11y_click_events_have_key_events -->
143
+ <!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
140
144
  <g
141
145
  role="group"
142
146
  aria-label={d.label}
143
147
  onmouseenter={() => (hoveredIdx = dIdx)}
144
148
  onmouseleave={() => (hoveredIdx = null)}
149
+ onclick={() => (hoveredIdx = hoveredIdx === dIdx ? null : dIdx)}
145
150
  class="cursor-pointer"
146
151
  >
147
152
  <!-- Group Hover Background highlight -->
@@ -207,8 +212,8 @@
207
212
  {@const activeX = padding.left + hoveredIdx * groupWidth + groupWidth / 2}
208
213
  <div
209
214
  class="pointer-events-none absolute top-2 rounded-xl border border-neutral-200 bg-white/95 px-3 py-2 text-xs shadow-lg backdrop-blur-xs transition-all dark:border-neutral-800 dark:bg-neutral-900/95"
210
- style="left: clamp(10px, {(activeX / width) *
211
- 100}%, calc(100% - 140px)); transform: translateX(-50%);"
215
+ style="left: clamp(70px, {(activeX / width) *
216
+ 100}%, calc(100% - 70px)); transform: translateX(-50%);"
212
217
  >
213
218
  <div class="mb-1 font-semibold text-neutral-800 dark:text-neutral-200">
214
219
  {xAxisFormat(activeItem.label)}
@@ -83,7 +83,7 @@
83
83
  />
84
84
 
85
85
  <!-- Segments -->
86
- {#each segments as seg, idx}
86
+ {#each segments as seg, idx (seg.item.label)}
87
87
  <circle
88
88
  role="graphics-symbol"
89
89
  aria-label={seg.item.label}
@@ -131,9 +131,9 @@
131
131
  <!-- Legend -->
132
132
  {#if showLegend && data.length > 0}
133
133
  <div
134
- class="mt-4 flex flex-wrap justify-center gap-x-4 gap-y-2 text-xs text-neutral-600 dark:text-neutral-300"
134
+ class="mt-4 flex flex-wrap justify-center gap-x-3 gap-y-1.5 text-[11px] text-neutral-600 sm:gap-x-4 sm:gap-y-2 sm:text-xs dark:text-neutral-300"
135
135
  >
136
- {#each segments as seg, idx}
136
+ {#each segments as seg, idx (seg.item.label)}
137
137
  <button
138
138
  type="button"
139
139
  onmouseenter={() => (hoveredIdx = idx)}
@@ -152,10 +152,9 @@
152
152
  });
153
153
  });
154
154
 
155
- function handleMouseMove(e: MouseEvent) {
156
- const svg = e.currentTarget as SVGSVGElement;
155
+ function updateClosestFromClientX(svg: SVGSVGElement, rawClientX: number) {
157
156
  const rect = svg.getBoundingClientRect();
158
- const clientX = e.clientX - rect.left;
157
+ const clientX = rawClientX - rect.left;
159
158
  const svgX = (clientX / rect.width) * width;
160
159
 
161
160
  if (data.length <= 1) {
@@ -176,6 +175,15 @@
176
175
  hoveredIdx = closestIdx;
177
176
  }
178
177
 
178
+ function handleMouseMove(e: MouseEvent) {
179
+ updateClosestFromClientX(e.currentTarget as SVGSVGElement, e.clientX);
180
+ }
181
+
182
+ function handleTouch(e: TouchEvent) {
183
+ if (!e.touches[0]) return;
184
+ updateClosestFromClientX(e.currentTarget as SVGSVGElement, e.touches[0].clientX);
185
+ }
186
+
179
187
  function handleMouseLeave() {
180
188
  hoveredIdx = null;
181
189
  }
@@ -188,6 +196,8 @@
188
196
  class="h-auto w-full overflow-visible"
189
197
  onmousemove={handleMouseMove}
190
198
  onmouseleave={handleMouseLeave}
199
+ ontouchstart={handleTouch}
200
+ ontouchmove={handleTouch}
191
201
  role="img"
192
202
  aria-label="Interactive Line Chart"
193
203
  >
@@ -225,10 +235,12 @@
225
235
  <!-- X-Axis Labels -->
226
236
  <g class="fill-neutral-400 text-[10px] select-none dark:fill-neutral-500">
227
237
  {#each data as d, i}
228
- {@const x = getX(i)}
229
- <text {x} y={height - 8} text-anchor="middle">
230
- {xAxisFormat(d.label)}
231
- </text>
238
+ {#if data.length <= 8 || i % Math.ceil(data.length / 6) === 0 || i === data.length - 1}
239
+ {@const x = getX(i)}
240
+ <text {x} y={height - 8} text-anchor="middle">
241
+ {xAxisFormat(d.label)}
242
+ </text>
243
+ {/if}
232
244
  {/each}
233
245
  </g>
234
246
 
@@ -295,8 +307,8 @@
295
307
  {@const activeItem = data[hoveredIdx]}
296
308
  <div
297
309
  class="pointer-events-none absolute top-2 rounded-xl border border-neutral-200 bg-white/95 px-3 py-2 text-xs shadow-lg backdrop-blur-xs transition-all dark:border-neutral-800 dark:bg-neutral-900/95"
298
- style="left: clamp(10px, {(getX(hoveredIdx) / width) *
299
- 100}%, calc(100% - 140px)); transform: translateX(-50%);"
310
+ style="left: clamp(70px, {(getX(hoveredIdx) / width) *
311
+ 100}%, calc(100% - 70px)); transform: translateX(-50%);"
300
312
  >
301
313
  <div class="mb-1 font-semibold text-neutral-800 dark:text-neutral-200">
302
314
  {xAxisFormat(activeItem.label)}
@@ -7,11 +7,12 @@
7
7
  root: 'relative w-full overflow-hidden rounded-2xl border border-neutral-200 bg-white dark:border-neutral-800 dark:bg-neutral-900',
8
8
  viewport: 'relative flex w-full overflow-hidden',
9
9
  slide: 'min-w-full transition-transform duration-500 ease-out',
10
- controls: 'absolute inset-0 flex items-center justify-between pointer-events-none p-4',
10
+ controls: 'absolute inset-0 flex items-center justify-between pointer-events-none p-2 sm:p-4',
11
11
  arrowBtn:
12
- 'pointer-events-auto inline-flex h-10 w-10 items-center justify-center rounded-full bg-white/80 shadow-md backdrop-blur-xs transition hover:bg-white hover:scale-105 active:scale-95 dark:bg-neutral-900/80 dark:hover:bg-neutral-800 text-neutral-800 dark:text-neutral-100',
12
+ 'pointer-events-auto inline-flex h-8 w-8 sm:h-10 sm:w-10 items-center justify-center rounded-full bg-white/80 shadow-md backdrop-blur-xs transition hover:bg-white hover:scale-105 active:scale-95 dark:bg-neutral-900/80 dark:hover:bg-neutral-800 text-neutral-800 dark:text-neutral-100',
13
13
  dots: 'absolute bottom-4 left-0 right-0 flex items-center justify-center gap-2 pointer-events-none',
14
- dotBtn: 'pointer-events-auto h-2 rounded-full transition-all duration-300'
14
+ dotBtn:
15
+ 'pointer-events-auto relative h-2 rounded-full transition-all duration-300 after:absolute after:-inset-2 after:content-[""]'
15
16
  }
16
17
  });
17
18
 
@@ -282,19 +282,19 @@
282
282
  <!-- Header Bar -->
283
283
  {#if filename || language}
284
284
  <div
285
- class="flex items-center justify-between border-b px-4 py-2 font-mono text-xs {isAdaptive
285
+ class="flex items-center justify-between gap-2 border-b px-4 py-2 font-mono text-xs {isAdaptive
286
286
  ? 'border-neutral-200 bg-neutral-100/80 text-neutral-600 dark:border-neutral-800 dark:bg-neutral-900/80 dark:text-neutral-400'
287
287
  : 'border-neutral-800 bg-neutral-900/80 text-neutral-400'}"
288
288
  >
289
- <div class="flex items-center gap-2">
289
+ <div class="flex min-w-0 items-center gap-2">
290
290
  {#if filename}
291
291
  <span
292
- class="flex items-center gap-1.5 font-medium {isAdaptive
292
+ class="flex min-w-0 items-center gap-1.5 truncate font-medium {isAdaptive
293
293
  ? 'text-neutral-800 dark:text-neutral-200'
294
294
  : 'text-neutral-200'}"
295
295
  >
296
- <span class="h-2 w-2 rounded-full bg-emerald-500"></span>
297
- {filename}
296
+ <span class="h-2 w-2 shrink-0 rounded-full bg-emerald-500"></span>
297
+ <span class="truncate">{filename}</span>
298
298
  </span>
299
299
  {:else}
300
300
  <span
@@ -307,7 +307,7 @@
307
307
  {/if}
308
308
  </div>
309
309
 
310
- <div class="flex items-center gap-1.5">
310
+ <div class="flex shrink-0 items-center gap-2">
311
311
  {#if filename && language}
312
312
  <span
313
313
  class="rounded px-1.5 py-0.5 text-[10px] font-bold tracking-wider uppercase {isAdaptive
@@ -328,7 +328,7 @@
328
328
  {:else}
329
329
  <!-- Split Side-by-Side View -->
330
330
  <div
331
- class="grid grid-cols-2 divide-x divide-neutral-200 overflow-x-auto p-2 font-mono text-xs leading-relaxed dark:divide-neutral-800 {isAdaptive
331
+ class="grid grid-cols-1 divide-y divide-neutral-200 overflow-x-auto p-2 font-mono text-xs leading-relaxed md:grid-cols-2 md:divide-x md:divide-y-0 dark:divide-neutral-800 {isAdaptive
332
332
  ? 'text-neutral-800 dark:text-neutral-200'
333
333
  : 'text-neutral-200'}"
334
334
  >
@@ -305,9 +305,9 @@
305
305
  <!-- Optional Header Toolbar -->
306
306
  {#if showSearch || showExport || showColumnToggle || toolbar}
307
307
  <div class="flex flex-wrap items-center justify-between gap-3">
308
- <div class="flex items-center gap-2">
308
+ <div class="flex flex-1 flex-wrap items-center gap-2 sm:flex-initial">
309
309
  {#if showSearch}
310
- <div class="relative w-64">
310
+ <div class="relative w-full sm:w-64">
311
311
  <Icon
312
312
  name="search"
313
313
  size="xs"
@@ -383,7 +383,7 @@
383
383
  <!-- Bulk Actions Bar -->
384
384
  {#if selectable && selected.length > 0 && bulkActions}
385
385
  <div
386
- class="flex items-center justify-between rounded-xl border border-primary-500/30 bg-primary-50/50 px-4 py-2.5 dark:bg-primary-950/30"
386
+ class="flex flex-wrap items-center justify-between gap-2 rounded-xl border border-primary-500/30 bg-primary-50/50 px-4 py-2.5 dark:bg-primary-950/30"
387
387
  >
388
388
  <span class="text-xs font-semibold text-primary-900 dark:text-primary-200">
389
389
  {selected.length} row{selected.length > 1 ? 's' : ''} selected
@@ -451,7 +451,7 @@
451
451
  {#if column.resizable}
452
452
  <button
453
453
  type="button"
454
- class="absolute top-0 right-0 h-full w-1.5 cursor-col-resize opacity-0 transition-opacity group-hover/th:opacity-100 hover:bg-primary-500"
454
+ class="absolute top-0 -right-1 h-full w-3 cursor-col-resize touch-none opacity-0 transition-opacity group-hover/th:opacity-100 hover:bg-primary-500"
455
455
  onmousedown={(e) => startColumnResize(colKey, e)}
456
456
  ontouchstart={(e) => startColumnResize(colKey, e)}
457
457
  aria-label={`Resize column ${column.label}`}
@@ -537,7 +537,7 @@
537
537
  <div
538
538
  class="flex flex-wrap items-center justify-between gap-3 border-t border-neutral-200/80 bg-neutral-50/50 px-4 py-3 text-xs text-neutral-500 dark:border-neutral-800/80 dark:bg-neutral-900/40 dark:text-neutral-400"
539
539
  >
540
- <div class="flex items-center gap-2">
540
+ <div class="flex flex-wrap items-center gap-2">
541
541
  <span>Rows per page:</span>
542
542
  <select
543
543
  value={pageSize}
@@ -118,7 +118,7 @@
118
118
 
119
119
  <!-- Reorder Arrow Buttons for Keyboard / Touch Users -->
120
120
  <div
121
- class="flex items-center gap-1 opacity-0 transition-opacity group-hover:opacity-100 sm:flex"
121
+ class="flex items-center gap-1 opacity-100 transition-opacity focus-within:opacity-100 sm:opacity-0 sm:group-hover:opacity-100"
122
122
  >
123
123
  <button
124
124
  type="button"
@@ -127,7 +127,7 @@
127
127
  sortable.moveUp(index);
128
128
  items = sortable.items;
129
129
  }}
130
- class="rounded p-1 text-neutral-400 hover:bg-neutral-100 hover:text-neutral-700 disabled:opacity-30 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
130
+ class="rounded p-2 text-neutral-400 hover:bg-neutral-100 hover:text-neutral-700 disabled:opacity-30 sm:p-1 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
131
131
  aria-label="Move item up"
132
132
  >
133
133
  <Icon name="chevron-up" class="h-3.5 w-3.5" />
@@ -139,7 +139,7 @@
139
139
  sortable.moveDown(index);
140
140
  items = sortable.items;
141
141
  }}
142
- class="rounded p-1 text-neutral-400 hover:bg-neutral-100 hover:text-neutral-700 disabled:opacity-30 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
142
+ class="rounded p-2 text-neutral-400 hover:bg-neutral-100 hover:text-neutral-700 disabled:opacity-30 sm:p-1 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
143
143
  aria-label="Move item down"
144
144
  >
145
145
  <Icon name="chevron-down" class="h-3.5 w-3.5" />
@@ -47,19 +47,19 @@
47
47
  class="flex items-center justify-between border-b border-neutral-800/80 bg-neutral-900/80 px-4 py-2.5"
48
48
  >
49
49
  <!-- Window Control Dots -->
50
- <div class="flex items-center gap-1.5" aria-hidden="true">
50
+ <div class="flex shrink-0 items-center gap-1.5" aria-hidden="true">
51
51
  <span class="h-3 w-3 rounded-full bg-rose-500/80"></span>
52
52
  <span class="h-3 w-3 rounded-full bg-amber-500/80"></span>
53
53
  <span class="h-3 w-3 rounded-full bg-emerald-500/80"></span>
54
54
  </div>
55
55
 
56
56
  <!-- Title -->
57
- <div class="text-[11px] font-medium text-neutral-400">
57
+ <div class="min-w-0 truncate px-2 text-[11px] font-medium text-neutral-400">
58
58
  {title}
59
59
  </div>
60
60
 
61
61
  <!-- Copy Button -->
62
- <div>
62
+ <div class="shrink-0">
63
63
  {#if copyable && fullCommandText}
64
64
  <button
65
65
  type="button"
@@ -219,7 +219,8 @@
219
219
 
220
220
  <Popover.Portal>
221
221
  <Popover.Content
222
- class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-72 rounded-xl border border-neutral-200 bg-white p-3 shadow-xl duration-150 outline-none dark:border-neutral-800 dark:bg-neutral-900"
222
+ collisionPadding={8}
223
+ class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-72 max-w-[calc(100vw-1rem)] rounded-xl border border-neutral-200 bg-white p-3 shadow-xl duration-150 outline-none dark:border-neutral-800 dark:bg-neutral-900"
223
224
  sideOffset={6}
224
225
  >
225
226
  <!-- Presets Bar -->
@@ -279,7 +279,8 @@
279
279
 
280
280
  <Popover.Portal>
281
281
  <Popover.Content
282
- class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-80 rounded-xl border border-neutral-200 bg-white p-3.5 shadow-xl duration-150 outline-none dark:border-neutral-800 dark:bg-neutral-900"
282
+ collisionPadding={8}
283
+ class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-80 max-w-[calc(100vw-1rem)] rounded-xl border border-neutral-200 bg-white p-3.5 shadow-xl duration-150 outline-none dark:border-neutral-800 dark:bg-neutral-900"
283
284
  sideOffset={6}
284
285
  >
285
286
  <!-- Quick Presets -->
@@ -129,10 +129,10 @@
129
129
  case 'sm':
130
130
  return 'w-8 h-9 text-base';
131
131
  case 'lg':
132
- return 'w-14 h-16 text-2xl';
132
+ return 'w-10 h-12 text-lg sm:w-14 sm:h-16 sm:text-2xl';
133
133
  case 'md':
134
134
  default:
135
- return 'w-11 h-12 text-xl';
135
+ return 'w-9 h-10 text-base sm:w-11 sm:h-12 sm:text-xl';
136
136
  }
137
137
  });
138
138
 
@@ -143,10 +143,14 @@
143
143
  });
144
144
  </script>
145
145
 
146
- <div class={cn('flex items-center gap-2 select-none', className)}>
146
+ <div
147
+ class={cn('flex max-w-full items-center justify-center gap-1 select-none sm:gap-2', className)}
148
+ >
147
149
  {#each Array.from({ length }, (_, i) => i) as index}
148
150
  {#if separator && index === separatorIndex}
149
- <span class="px-1 font-bold text-neutral-400 select-none dark:text-neutral-600"> − </span>
151
+ <span class="px-0.5 font-bold text-neutral-400 select-none sm:px-1 dark:text-neutral-600">
152
+ −
153
+ </span>
150
154
  {/if}
151
155
 
152
156
  <input
@@ -28,7 +28,9 @@
28
28
  </script>
29
29
 
30
30
  <div
31
- class="flex {orientation === 'horizontal' ? 'flex-row gap-4' : 'flex-col gap-2'} {className}"
31
+ class="flex {orientation === 'horizontal'
32
+ ? 'flex-row flex-wrap gap-4'
33
+ : 'flex-col gap-2'} {className}"
32
34
  role="radiogroup"
33
35
  >
34
36
  {#each options as opt}
@@ -69,7 +69,7 @@
69
69
  >
70
70
  <!-- Formatting Toolbar -->
71
71
  <div
72
- class="flex flex-wrap items-center justify-between border-b border-neutral-200/80 bg-neutral-50/70 p-1.5 dark:border-neutral-800/80 dark:bg-neutral-900/60"
72
+ class="flex flex-wrap items-center justify-between gap-1.5 border-b border-neutral-200/80 bg-neutral-50/70 p-1.5 dark:border-neutral-800/80 dark:bg-neutral-900/60"
73
73
  >
74
74
  <div class="flex flex-wrap items-center gap-0.5">
75
75
  <!-- Bold -->
@@ -106,7 +106,7 @@
106
106
  <div
107
107
  role="group"
108
108
  class={cn(
109
- 'inline-flex items-center select-none',
109
+ 'inline-flex max-w-full flex-wrap items-center select-none',
110
110
  block ? 'flex w-full' : '',
111
111
  sizeClasses.container,
112
112
  containerVariantClasses,
@@ -52,7 +52,7 @@
52
52
  <div class="grid grid-cols-1 gap-8 md:grid-cols-4 lg:grid-cols-5">
53
53
  <!-- Brand Info -->
54
54
  <div class="space-y-4 md:col-span-2 lg:col-span-2">
55
- <div class="flex items-center gap-3">
55
+ <div class="flex flex-wrap items-center gap-3">
56
56
  <a href="/" class="inline-flex items-center gap-2.5 font-bold tracking-tight">
57
57
  <Logo size="sm" showText text={currentConfig.name} />
58
58
  </a>
@@ -68,7 +68,9 @@
68
68
  {currentConfig.description}
69
69
  </p>
70
70
  {#if currentConfig.email}
71
- <div class="flex items-center gap-2 text-xs text-neutral-500 dark:text-neutral-400">
71
+ <div
72
+ class="flex flex-wrap items-center gap-2 text-xs text-neutral-500 dark:text-neutral-400"
73
+ >
72
74
  <span>Contact:</span>
73
75
  <a
74
76
  href="mailto:{currentConfig.email}"
@@ -79,7 +81,7 @@
79
81
  <button
80
82
  type="button"
81
83
  onclick={() => copyEmail(currentConfig.email!)}
82
- class="inline-flex items-center justify-center rounded p-1 text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-neutral-700 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
84
+ class="inline-flex items-center justify-center rounded p-1.5 text-neutral-400 transition-colors hover:bg-neutral-100 hover:text-neutral-700 dark:hover:bg-neutral-800 dark:hover:text-neutral-200"
83
85
  aria-label="Copy email address"
84
86
  title={copiedEmail ? 'Copied to clipboard!' : 'Copy email address'}
85
87
  >
@@ -188,7 +190,7 @@
188
190
  href={currentConfig.socials?.github || currentConfig.project?.repositoryUrl}
189
191
  target="_blank"
190
192
  rel="noopener noreferrer"
191
- class="text-neutral-500 hover:text-neutral-900 dark:hover:text-white"
193
+ class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-neutral-500 hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-800 dark:hover:text-white"
192
194
  aria-label="GitHub"
193
195
  >
194
196
  <Icon name="github" size="sm" />
@@ -199,7 +201,7 @@
199
201
  href={currentConfig.socials.npm}
200
202
  target="_blank"
201
203
  rel="noopener noreferrer"
202
- class="text-neutral-500 hover:text-red-500 dark:hover:text-red-400"
204
+ class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-neutral-500 hover:bg-neutral-100 hover:text-red-500 dark:hover:bg-neutral-800 dark:hover:text-red-400"
203
205
  aria-label="npm"
204
206
  >
205
207
  <Icon name="npm" size="sm" />
@@ -210,7 +212,7 @@
210
212
  href={currentConfig.socials.twitter}
211
213
  target="_blank"
212
214
  rel="noopener noreferrer"
213
- class="text-neutral-500 hover:text-neutral-900 dark:hover:text-white"
215
+ class="inline-flex h-8 w-8 items-center justify-center rounded-lg text-neutral-500 hover:bg-neutral-100 hover:text-neutral-900 dark:hover:bg-neutral-800 dark:hover:text-white"
214
216
  aria-label="Twitter"
215
217
  >
216
218
  <Icon name="twitter" size="sm" />
@@ -80,8 +80,22 @@
80
80
  </div>
81
81
 
82
82
  <!-- Right Section: Search, Theme Toggle, Actions, Socials -->
83
- <div class="flex items-center gap-2">
83
+ <div class="flex items-center gap-1.5 sm:gap-2">
84
84
  {#if onOpenCommand}
85
+ <!-- Mobile Search Button -->
86
+ <Button
87
+ variant="ghost"
88
+ color="neutral"
89
+ square
90
+ size="sm"
91
+ class="sm:hidden"
92
+ onclick={onOpenCommand}
93
+ aria-label="Search documentation"
94
+ >
95
+ <Icon name="search" size="sm" />
96
+ </Button>
97
+
98
+ <!-- Desktop Search Pill -->
85
99
  <button
86
100
  onclick={onOpenCommand}
87
101
  type="button"
@@ -110,7 +124,7 @@
110
124
  {/if}
111
125
  </Button>
112
126
 
113
- <!-- GitHub Link -->
127
+ <!-- GitHub Link (Desktop) -->
114
128
  {#if currentConfig.socials?.github}
115
129
  <Button
116
130
  href={currentConfig.socials.github}
@@ -118,13 +132,14 @@
118
132
  color="neutral"
119
133
  square
120
134
  size="sm"
135
+ class="hidden sm:inline-flex"
121
136
  aria-label="GitHub Repository"
122
137
  >
123
138
  <Icon name="github" size="sm" />
124
139
  </Button>
125
140
  {/if}
126
141
 
127
- <!-- npm Link -->
142
+ <!-- npm Link (Desktop) -->
128
143
  {#if currentConfig.socials?.npm}
129
144
  <Button
130
145
  href={currentConfig.socials.npm}
@@ -132,6 +147,7 @@
132
147
  color="neutral"
133
148
  square
134
149
  size="sm"
150
+ class="hidden sm:inline-flex"
135
151
  aria-label="npm Package"
136
152
  >
137
153
  <Icon name="npm" size="sm" />
@@ -173,17 +189,61 @@
173
189
  <!-- Mobile Navigation Dropdown -->
174
190
  {#if mobileMenuOpen}
175
191
  <div class="border-t border-neutral-200 py-4 md:hidden dark:border-neutral-800">
176
- <div class="flex flex-col space-y-2">
192
+ <div class="flex flex-col space-y-1">
177
193
  {#each resolvedLinks as item (item.href)}
178
194
  <a
179
195
  href={item.href}
180
196
  onclick={() => (mobileMenuOpen = false)}
181
- class="rounded-lg px-3 py-2 text-base font-medium text-neutral-700 hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800"
197
+ class="flex items-center justify-between rounded-lg px-3 py-2 text-base font-medium text-neutral-700 hover:bg-neutral-100 dark:text-neutral-200 dark:hover:bg-neutral-800"
182
198
  >
183
- {item.label}
199
+ <span class="flex items-center gap-2">
200
+ {#if item.icon}
201
+ <Icon name={item.icon} size="xs" />
202
+ {/if}
203
+ {item.label}
204
+ </span>
205
+ {#if item.badge}
206
+ <span
207
+ class="rounded bg-primary-100 px-1.5 py-0.5 text-xs font-semibold text-primary-700 dark:bg-primary-950 dark:text-primary-300"
208
+ >
209
+ {item.badge}
210
+ </span>
211
+ {/if}
184
212
  </a>
185
213
  {/each}
186
214
  </div>
215
+
216
+ <!-- Mobile Menu Socials -->
217
+ {#if currentConfig.socials?.github || currentConfig.socials?.npm}
218
+ <div
219
+ class="mt-4 flex items-center gap-2 border-t border-neutral-200 pt-4 dark:border-neutral-800"
220
+ >
221
+ {#if currentConfig.socials?.github}
222
+ <Button
223
+ href={currentConfig.socials.github}
224
+ variant="outline"
225
+ size="sm"
226
+ class="flex-1 justify-center"
227
+ aria-label="GitHub Repository"
228
+ >
229
+ <Icon name="github" size="xs" class="mr-1.5" />
230
+ GitHub
231
+ </Button>
232
+ {/if}
233
+ {#if currentConfig.socials?.npm}
234
+ <Button
235
+ href={currentConfig.socials.npm}
236
+ variant="outline"
237
+ size="sm"
238
+ class="flex-1 justify-center"
239
+ aria-label="npm Package"
240
+ >
241
+ <Icon name="npm" size="xs" class="mr-1.5" />
242
+ npm
243
+ </Button>
244
+ {/if}
245
+ </div>
246
+ {/if}
187
247
  </div>
188
248
  {/if}
189
249
  </Container>
@@ -13,12 +13,14 @@
13
13
  direction: {
14
14
  horizontal: {
15
15
  root: 'flex-row',
16
- handle: 'w-2 cursor-col-resize select-none',
16
+ handle:
17
+ 'relative w-2 cursor-col-resize select-none after:absolute after:inset-y-0 after:-inset-x-2 after:content-[""]',
17
18
  grip: 'h-6 w-1'
18
19
  },
19
20
  vertical: {
20
21
  root: 'flex-col',
21
- handle: 'h-2 cursor-row-resize select-none',
22
+ handle:
23
+ 'relative h-2 cursor-row-resize select-none after:absolute after:inset-x-0 after:-inset-y-2 after:content-[""]',
22
24
  grip: 'h-1 w-6'
23
25
  }
24
26
  }
@@ -334,21 +334,21 @@
334
334
  </p>
335
335
 
336
336
  <div
337
- class="mt-6 flex flex-wrap items-center gap-6 text-sm text-neutral-500 dark:text-neutral-400"
337
+ class="mt-6 flex flex-wrap items-center gap-x-3 gap-y-2 text-sm text-neutral-500 dark:text-neutral-400"
338
338
  >
339
339
  <span class="inline-flex items-center gap-1.5">
340
340
  <Icon name="info" size="xs" />
341
341
  Last Updated:
342
342
  <strong class="font-medium text-neutral-700 dark:text-neutral-200">{lastUpdated}</strong>
343
343
  </span>
344
- <span>•</span>
344
+ <span class="hidden sm:inline">•</span>
345
345
  <span
346
346
  >Entity:
347
347
  <strong class="font-medium text-neutral-700 dark:text-neutral-200">{entityName}</strong
348
348
  ></span
349
349
  >
350
350
  {#if contactEmail}
351
- <span>•</span>
351
+ <span class="hidden sm:inline">•</span>
352
352
  <div class="inline-flex items-center gap-1.5">
353
353
  <a
354
354
  href="mailto:{contactEmail}"
@@ -406,11 +406,11 @@
406
406
  id={sec.id}
407
407
  class="scroll-mt-24 space-y-4 border-b border-neutral-100 pb-10 last:border-0 dark:border-neutral-900"
408
408
  >
409
- <h2 class="text-xl font-bold tracking-tight text-neutral-900 dark:text-white">
409
+ <h2 class="text-xl font-bold tracking-tight break-words text-neutral-900 dark:text-white">
410
410
  {sec.title}
411
411
  </h2>
412
412
  <div
413
- class="prose max-w-none text-neutral-600 prose-neutral dark:text-neutral-300 dark:prose-invert"
413
+ class="prose max-w-none break-words text-neutral-600 prose-neutral dark:text-neutral-300 dark:prose-invert"
414
414
  >
415
415
  {@html formatLegalHtml(sec.content || '')}
416
416
  </div>
@@ -35,7 +35,7 @@
35
35
  <ol
36
36
  data-slot="list"
37
37
  class={cn(
38
- 'inline-flex items-center space-x-1.5 text-sm text-neutral-500 md:space-x-2 dark:text-neutral-400',
38
+ 'flex flex-wrap items-center space-x-1.5 gap-y-1 text-sm text-neutral-500 md:space-x-2 dark:text-neutral-400',
39
39
  ui?.list
40
40
  )}
41
41
  >
@@ -66,7 +66,7 @@
66
66
  });
67
67
  </script>
68
68
 
69
- <nav aria-label="Pagination" class="flex items-center gap-1 {className}">
69
+ <nav aria-label="Pagination" class="flex flex-wrap items-center justify-center gap-1 {className}">
70
70
  <!-- Previous -->
71
71
  <Button
72
72
  variant="outline"
@@ -100,7 +100,7 @@
100
100
  </div>
101
101
 
102
102
  <!-- Step Label -->
103
- <div class="hidden sm:block">
103
+ <div class={orientation === 'vertical' ? 'block' : 'hidden sm:block'}>
104
104
  <div
105
105
  class="text-xs font-semibold {status === 'active'
106
106
  ? 'text-primary-700 dark:text-primary-400'
@@ -122,7 +122,7 @@
122
122
  {#if !isLast}
123
123
  {#if orientation === 'horizontal'}
124
124
  <div
125
- class="mx-3 h-0.5 flex-1 transition-colors duration-200 {index < currentStep
125
+ class="mx-1.5 h-0.5 flex-1 transition-colors duration-200 sm:mx-3 {index < currentStep
126
126
  ? 'bg-primary-600 dark:bg-primary-500'
127
127
  : 'bg-neutral-200 dark:bg-neutral-800'}"
128
128
  ></div>
@@ -79,7 +79,7 @@
79
79
  bind:this={listElement}
80
80
  data-slot="list"
81
81
  class={cn(
82
- 'relative flex',
82
+ 'relative scrollbar-none flex max-w-full overflow-x-auto',
83
83
  variant === 'segmented' &&
84
84
  'rounded-xl border border-neutral-200/60 bg-neutral-100 p-1 dark:border-neutral-800/60 dark:bg-neutral-900',
85
85
  variant === 'underline' && 'gap-6 border-b border-neutral-200 dark:border-neutral-800',
@@ -111,7 +111,7 @@
111
111
  disabled={tab.disabled}
112
112
  onclick={() => (value = tab.value)}
113
113
  class={cn(
114
- 'relative z-10 inline-flex items-center justify-center gap-2 text-sm font-medium transition-all duration-150 select-none focus-visible:ring-2 focus-visible:ring-[var(--yaxa-ring)] focus-visible:outline-none motion-reduce:transition-none',
114
+ 'relative z-10 inline-flex shrink-0 items-center justify-center gap-2 text-sm font-medium whitespace-nowrap transition-all duration-150 select-none focus-visible:ring-2 focus-visible:ring-(--yaxa-ring) focus-visible:outline-none motion-reduce:transition-none',
115
115
  tab.disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer',
116
116
  variant === 'segmented' && [
117
117
  'rounded-lg px-3 py-1.5',
@@ -3,7 +3,7 @@
3
3
  import type { Snippet } from 'svelte';
4
4
 
5
5
  export const modalVariants = tv({
6
- base: 'fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 rounded-2xl border border-neutral-200 bg-white p-6 shadow-2xl duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 dark:border-neutral-800 dark:bg-neutral-900 yaxa-surface-elevated motion-reduce:transition-none',
6
+ base: 'fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-2xl border border-neutral-200 bg-white p-4 sm:p-6 shadow-2xl duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 dark:border-neutral-800 dark:bg-neutral-900 yaxa-surface-elevated motion-reduce:transition-none',
7
7
  variants: {
8
8
  size: {
9
9
  sm: 'max-w-sm',
@@ -8,7 +8,7 @@ export declare const modalVariants: import("tailwind-variants").TVReturnType<{
8
8
  xl: "max-w-4xl";
9
9
  full: "max-w-[calc(100vw-2rem)] h-[calc(100vh-2rem)]";
10
10
  };
11
- }, undefined, "fixed left-1/2 top-1/2 z-50 w-full -translate-x-1/2 -translate-y-1/2 rounded-2xl border border-neutral-200 bg-white p-6 shadow-2xl duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 dark:border-neutral-800 dark:bg-neutral-900 yaxa-surface-elevated motion-reduce:transition-none", {
11
+ }, undefined, "fixed left-1/2 top-1/2 z-50 w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 rounded-2xl border border-neutral-200 bg-white p-4 sm:p-6 shadow-2xl duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 dark:border-neutral-800 dark:bg-neutral-900 yaxa-surface-elevated motion-reduce:transition-none", {
12
12
  size: {
13
13
  sm: "max-w-sm";
14
14
  md: "max-w-lg";
@@ -74,8 +74,9 @@
74
74
  <Popover.Portal>
75
75
  <Popover.Content
76
76
  sideOffset={6}
77
+ collisionPadding={8}
77
78
  align="end"
78
- class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-80 overflow-hidden rounded-2xl border border-neutral-200 bg-white shadow-2xl dark:border-neutral-800 dark:bg-neutral-900"
79
+ class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-80 max-w-[calc(100vw-1rem)] overflow-hidden rounded-2xl border border-neutral-200 bg-white shadow-2xl dark:border-neutral-800 dark:bg-neutral-900"
79
80
  >
80
81
  <!-- Header -->
81
82
  <div
@@ -34,9 +34,10 @@
34
34
 
35
35
  <Popover.Portal>
36
36
  <Popover.Content
37
+ collisionPadding={8}
37
38
  class={unstyled
38
39
  ? className
39
- : `data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 rounded-xl border border-neutral-200 bg-white p-4 shadow-xl duration-150 outline-none dark:border-neutral-800 dark:bg-neutral-900 ${className}`}
40
+ : `data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 max-w-[calc(100vw-1rem)] rounded-xl border border-neutral-200 bg-white p-4 shadow-xl duration-150 outline-none dark:border-neutral-800 dark:bg-neutral-900 ${className}`}
40
41
  sideOffset={8}
41
42
  >
42
43
  {#if children}
@@ -29,18 +29,18 @@
29
29
  let positionClasses = $derived.by(() => {
30
30
  switch (position) {
31
31
  case 'top-left':
32
- return 'top-4 left-4 items-start';
32
+ return 'top-4 inset-x-4 sm:right-auto sm:left-4 items-center sm:items-start';
33
33
  case 'top-center':
34
- return 'top-4 left-1/2 -translate-x-1/2 items-center';
34
+ return 'top-4 inset-x-4 sm:inset-x-auto sm:left-1/2 sm:-translate-x-1/2 items-center';
35
35
  case 'bottom-right':
36
- return 'bottom-4 right-4 items-end';
36
+ return 'bottom-4 inset-x-4 sm:left-auto sm:right-4 items-center sm:items-end';
37
37
  case 'bottom-left':
38
- return 'bottom-4 left-4 items-start';
38
+ return 'bottom-4 inset-x-4 sm:right-auto sm:left-4 items-center sm:items-start';
39
39
  case 'bottom-center':
40
- return 'bottom-4 left-1/2 -translate-x-1/2 items-center';
40
+ return 'bottom-4 inset-x-4 sm:inset-x-auto sm:left-1/2 sm:-translate-x-1/2 items-center';
41
41
  case 'top-right':
42
42
  default:
43
- return 'top-4 right-4 items-end';
43
+ return 'top-4 inset-x-4 sm:left-auto sm:right-4 items-center sm:items-end';
44
44
  }
45
45
  });
46
46
 
@@ -55,7 +55,7 @@
55
55
 
56
56
  {#if visibleToasts.length > 0}
57
57
  <div
58
- class="yaxa-toaster pointer-events-none fixed z-[9999] flex w-full max-w-md flex-col gap-2.5 p-4 sm:p-0 {positionClasses} {className}"
58
+ class="yaxa-toaster pointer-events-none fixed z-[9999] flex max-w-md flex-col gap-2.5 sm:w-full {positionClasses} {className}"
59
59
  aria-label="Notifications"
60
60
  data-rich-colors={restProps.richColors ?? true}
61
61
  data-expand={restProps.expand ?? false}
@@ -472,7 +472,7 @@
472
472
  <!-- Mode Switchers & Navigation Links -->
473
473
  <div class="flex flex-col items-center gap-2 pt-2 text-xs text-zinc-500 dark:text-zinc-400">
474
474
  {#if currentMode === 'signin'}
475
- <div class="flex w-full items-center justify-between">
475
+ <div class="flex w-full flex-wrap items-center justify-between gap-2">
476
476
  {#if showForgotPassword}
477
477
  <button
478
478
  type="button"
@@ -91,8 +91,9 @@
91
91
  <Popover.Portal>
92
92
  <Popover.Content
93
93
  sideOffset={6}
94
+ collisionPadding={8}
94
95
  align="start"
95
- class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-64 overflow-hidden rounded-xl border border-neutral-200 bg-white p-1.5 shadow-xl dark:border-neutral-800 dark:bg-neutral-900"
96
+ class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 z-50 w-64 max-w-[calc(100vw-1rem)] overflow-hidden rounded-xl border border-neutral-200 bg-white p-1.5 shadow-xl dark:border-neutral-800 dark:bg-neutral-900"
96
97
  >
97
98
  <div
98
99
  class="px-2 py-1.5 text-[10px] font-semibold tracking-wider text-neutral-400 uppercase"
@@ -165,8 +165,8 @@
165
165
  </div>
166
166
  {:else}
167
167
  {#each passkeys as key (key.id)}
168
- <div class="flex items-center justify-between p-3.5 px-4">
169
- <div class="flex items-center gap-3">
168
+ <div class="flex items-center justify-between gap-2 p-3.5 px-4">
169
+ <div class="flex min-w-0 items-center gap-3">
170
170
  <div
171
171
  class="flex h-9 w-9 items-center justify-center rounded-lg bg-primary-100/60 text-primary-700 dark:bg-primary-950/60 dark:text-primary-300"
172
172
  >
@@ -106,7 +106,7 @@
106
106
  </p>
107
107
  </div>
108
108
 
109
- <div class="flex items-center gap-2">
109
+ <div class="flex flex-wrap items-center gap-2">
110
110
  {#if onupgrade}
111
111
  <Button variant="outline" size="sm" onclick={onupgrade}>Change Plan</Button>
112
112
  {/if}
@@ -38,7 +38,7 @@
38
38
  class="relative overflow-hidden rounded-xl border border-neutral-200 shadow-xl dark:border-neutral-800 {className}"
39
39
  >
40
40
  <div
41
- class="flex items-center justify-between border-b border-neutral-200 bg-neutral-100 px-4 py-2.5 text-sm font-medium text-neutral-600 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-400"
41
+ class="flex flex-wrap items-center justify-between gap-2 border-b border-neutral-200 bg-neutral-100 px-3 py-2 text-xs font-medium text-neutral-600 sm:px-4 sm:py-2.5 sm:text-sm dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-400"
42
42
  >
43
43
  <span class="flex items-center gap-1.5">
44
44
  <span class="h-2 w-2 rounded-full bg-emerald-500"></span>
@@ -91,20 +91,23 @@
91
91
  {#if showHeader}
92
92
  <!-- Top Bar -->
93
93
  <div
94
- class="flex flex-wrap items-center justify-between gap-4 rounded-2xl border border-neutral-200 bg-white p-6 shadow-sm dark:border-neutral-800 dark:bg-neutral-900"
94
+ class="flex flex-wrap items-center justify-between gap-4 rounded-2xl border border-neutral-200 bg-white p-4 shadow-sm sm:p-6 dark:border-neutral-800 dark:bg-neutral-900"
95
95
  >
96
- <div>
97
- <h1 class="text-xl font-bold text-neutral-900 dark:text-white">Visual Web Theme Studio</h1>
96
+ <div class="min-w-0 flex-1">
97
+ <h1 class="text-lg font-bold text-neutral-900 sm:text-xl dark:text-white">
98
+ Visual Web Theme Studio
99
+ </h1>
98
100
  <p class="mt-1 text-xs text-neutral-500">
99
101
  Visually customize palettes, harmonic radii, fonts, and scrollbars with instant live
100
102
  preview and zero FOUC.
101
103
  </p>
102
104
  </div>
103
105
 
104
- <div class="flex items-center gap-2.5">
106
+ <div class="flex w-full flex-wrap items-center gap-2 sm:w-auto">
105
107
  <Button
106
108
  variant="outline"
107
109
  size="sm"
110
+ class="flex-1 sm:flex-initial"
108
111
  onclick={() => {
109
112
  const chosen = theme.randomize();
110
113
  toast.info(
@@ -117,22 +120,33 @@
117
120
  Randomize
118
121
  </Button>
119
122
 
120
- <Button variant="outline" size="sm" onclick={() => theme.reset()}>
123
+ <Button
124
+ variant="outline"
125
+ size="sm"
126
+ class="flex-1 sm:flex-initial"
127
+ onclick={() => theme.reset()}
128
+ >
121
129
  <Icon name="rotate-ccw" size="xs" class="mr-1.5" />
122
130
  Reset
123
131
  </Button>
124
132
 
125
- <Button variant="solid" size="sm" onclick={() => (exportModalOpen = true)}>
133
+ <Button
134
+ variant="solid"
135
+ size="sm"
136
+ class="w-full sm:w-auto"
137
+ onclick={() => (exportModalOpen = true)}
138
+ >
126
139
  <Icon name="download" size="xs" class="mr-1.5" />
127
140
  Export Theme Tokens
128
141
  </Button>
129
142
  </div>
130
143
  </div>
131
144
  {:else}
132
- <div class="flex items-center justify-end gap-2.5">
145
+ <div class="flex w-full flex-wrap items-center justify-end gap-2 sm:w-auto">
133
146
  <Button
134
147
  variant="outline"
135
148
  size="sm"
149
+ class="flex-1 sm:flex-initial"
136
150
  onclick={() => {
137
151
  const chosen = theme.randomize();
138
152
  toast.info(
@@ -145,12 +159,22 @@
145
159
  Randomize
146
160
  </Button>
147
161
 
148
- <Button variant="outline" size="sm" onclick={() => theme.reset()}>
162
+ <Button
163
+ variant="outline"
164
+ size="sm"
165
+ class="flex-1 sm:flex-initial"
166
+ onclick={() => theme.reset()}
167
+ >
149
168
  <Icon name="rotate-ccw" size="xs" class="mr-1.5" />
150
169
  Reset
151
170
  </Button>
152
171
 
153
- <Button variant="solid" size="sm" onclick={() => (exportModalOpen = true)}>
172
+ <Button
173
+ variant="solid"
174
+ size="sm"
175
+ class="w-full sm:w-auto"
176
+ onclick={() => (exportModalOpen = true)}
177
+ >
154
178
  <Icon name="download" size="xs" class="mr-1.5" />
155
179
  Export Theme Tokens
156
180
  </Button>
@@ -167,12 +191,12 @@
167
191
  Color Mode
168
192
  </div>
169
193
  <div class="grid grid-cols-3 gap-1.5">
170
- {#each ['light', 'dark', 'system'] as const as mode}
194
+ {#each ['light', 'dark', 'system'] as const as mode (mode)}
171
195
  <Button
172
196
  variant={theme.mode === mode ? 'solid' : 'outline'}
173
197
  size="xs"
174
198
  onclick={() => theme.setMode(mode)}
175
- class="w-full justify-center px-1.5 py-1 text-[11px] capitalize"
199
+ class="min-h-9 w-full justify-center px-1.5 py-1 text-[11px] capitalize"
176
200
  >
177
201
  <Icon
178
202
  name={mode === 'light' ? 'sun' : mode === 'dark' ? 'moon' : 'laptop'}
@@ -199,7 +223,7 @@
199
223
  </span>
200
224
  </div>
201
225
  <div class="grid grid-cols-4 gap-1.5">
202
- {#each Object.entries(ACCENT_PALETTES) as [key, pal]}
226
+ {#each Object.entries(ACCENT_PALETTES) as [key, pal] (key)}
203
227
  <button
204
228
  type="button"
205
229
  onclick={() => theme.setAccent(key as AccentName)}
@@ -234,7 +258,7 @@
234
258
  </span>
235
259
  </div>
236
260
  <div class="grid grid-cols-2 gap-2">
237
- {#each Object.entries(NEUTRAL_PALETTES) as [key, pal]}
261
+ {#each Object.entries(NEUTRAL_PALETTES) as [key, pal] (key)}
238
262
  {@const active = theme.neutral === key}
239
263
  <button
240
264
  type="button"
@@ -284,12 +308,12 @@
284
308
  <span class="text-xs font-semibold text-primary-500 capitalize">{theme.radius}</span>
285
309
  </div>
286
310
  <div class="grid grid-cols-5 gap-1">
287
- {#each Object.entries(RADIUS_PRESETS) as [key, r]}
311
+ {#each Object.entries(RADIUS_PRESETS) as [key, r] (key)}
288
312
  {@const active = theme.radius === key}
289
313
  <button
290
314
  type="button"
291
315
  onclick={() => theme.setRadius(key as RadiusPreset)}
292
- class="border px-1 py-1.5 text-center text-[10px] font-semibold transition-all {active
316
+ class="min-h-9 min-w-0 border px-1 py-1.5 text-center text-[10px] font-semibold transition-all sm:text-xs {active
293
317
  ? 'border-primary-500 bg-primary-500 font-bold text-white shadow-xs'
294
318
  : 'border-neutral-200 bg-white text-neutral-700 hover:bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-800 dark:text-neutral-300 dark:hover:bg-neutral-700'}"
295
319
  style="border-radius: {r.value} !important;"
@@ -306,12 +330,12 @@
306
330
  Typography & Sizing
307
331
  </div>
308
332
  <div class="grid grid-cols-3 gap-1">
309
- {#each Object.entries(FONT_PRESETS) as [key, font]}
333
+ {#each Object.entries(FONT_PRESETS) as [key, font] (key)}
310
334
  {@const active = theme.fontFamily === key}
311
335
  <button
312
336
  type="button"
313
337
  onclick={() => theme.setFontFamily(key as FontFamily)}
314
- class="rounded-md border px-1 py-1.5 text-center text-[11px] font-semibold capitalize transition-all {active
338
+ class="min-h-9 rounded-md border px-1 py-1.5 text-center text-[11px] font-semibold capitalize transition-all {active
315
339
  ? 'border-primary-500 bg-primary-500 text-white shadow-xs'
316
340
  : 'border-neutral-200 bg-white text-neutral-700 hover:bg-neutral-100 dark:border-neutral-800 dark:bg-neutral-800 dark:text-neutral-300 dark:hover:bg-neutral-700'}"
317
341
  style="font-family: {font.value} !important;"
@@ -326,12 +350,12 @@
326
350
  Density / Size Scale
327
351
  </div>
328
352
  <div class="grid grid-cols-3 gap-1">
329
- {#each Object.entries(FONT_SIZE_PRESETS) as [key, sz]}
353
+ {#each Object.entries(FONT_SIZE_PRESETS) as [key, sz] (key)}
330
354
  <Button
331
355
  variant={theme.fontSize === key ? 'solid' : 'outline'}
332
356
  size="xs"
333
357
  onclick={() => theme.setFontSize(key as BaseFontSize)}
334
- class="w-full justify-center px-1 py-1 text-[11px]"
358
+ class="min-h-8.5 w-full min-w-0 justify-center px-0.5 py-1 text-[10px] sm:px-1 sm:text-[11px]"
335
359
  >
336
360
  <span class="truncate">{sz.name}</span>
337
361
  </Button>
@@ -342,14 +366,17 @@
342
366
 
343
367
  <!-- Scrollbar Auto-Alignment -->
344
368
  <Card padding="sm" class="space-y-2">
345
- <div class="flex items-center justify-between">
346
- <div>
369
+ <div class="flex items-center justify-between gap-3">
370
+ <div class="min-w-0 flex-1">
347
371
  <div class="text-xs font-bold text-neutral-900 dark:text-white">Accent Scrollbars</div>
348
- <div class="text-[11px] text-neutral-500">Sync scrollbar thumb to active accent</div>
372
+ <div class="truncate text-[11px] text-neutral-500 sm:whitespace-normal">
373
+ Sync scrollbar thumb to active accent
374
+ </div>
349
375
  </div>
350
376
  <Button
351
377
  variant={theme.accentScrollbar ? 'solid' : 'outline'}
352
378
  size="xs"
379
+ class="shrink-0"
353
380
  onclick={() => theme.setAccentScrollbar(!theme.accentScrollbar)}
354
381
  >
355
382
  {theme.accentScrollbar ? 'Enabled' : 'Disabled'}
@@ -361,10 +388,10 @@
361
388
  <!-- Right Live Sandbox Canvas (7 cols on xl, 8 cols on 2xl) -->
362
389
  <div class="space-y-6 xl:col-span-7 2xl:col-span-8">
363
390
  <!-- Elements Preview Card -->
364
- <Card>
391
+ <Card ui={{ header: 'p-4 sm:p-6', body: 'p-4 sm:p-6 space-y-6' }}>
365
392
  {#snippet header()}
366
393
  <div class="flex items-center justify-between">
367
- <span class="font-semibold text-neutral-900 dark:text-white">
394
+ <span class="text-sm font-semibold text-neutral-900 sm:text-base dark:text-white">
368
395
  Interactive Component Sandbox
369
396
  </span>
370
397
  <Badge variant="soft" color="primary">Live Preview</Badge>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaxa-svelte",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "The Intuitive Svelte UI Library — Nuxt UI v4 equivalent for SvelteKit 2 & 3 & Svelte 5 with automated SEO, dynamic OpenGraph cards, sitemaps, robots.txt, and Tailwind CSS v4.",
5
5
  "license": "MIT",
6
6
  "homepage": "https://yaxa.vercel.app",