@runwisp/ui 0.13.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/package.json +39 -47
  2. package/src/astro/CTABand.astro +19 -12
  3. package/src/astro/Hero.astro +3 -3
  4. package/src/astro/SiteFooter.astro +4 -4
  5. package/src/astro/SiteHeader.astro +4 -4
  6. package/src/lib/actions/portal.ts +0 -3
  7. package/src/lib/assets/runwisp-logo.svg +1 -8
  8. package/src/lib/components/AccordionItem.svelte +4 -10
  9. package/src/lib/components/Alert.svelte +11 -11
  10. package/src/lib/components/Avatar.svelte +1 -1
  11. package/src/lib/components/Badge.svelte +13 -13
  12. package/src/lib/components/Breadcrumb.svelte +3 -3
  13. package/src/lib/components/Button.svelte +2 -2
  14. package/src/lib/components/Card.svelte +4 -4
  15. package/src/lib/components/Checkbox.svelte +12 -10
  16. package/src/lib/components/CodeBlock.svelte +7 -13
  17. package/src/lib/components/CronInput.svelte +12 -15
  18. package/src/lib/components/DataGrid.svelte +14 -13
  19. package/src/lib/components/Drawer.svelte +9 -9
  20. package/src/lib/components/Dropdown.svelte +12 -20
  21. package/src/lib/components/DurationInput.svelte +10 -14
  22. package/src/lib/components/EmptyState.svelte +1 -1
  23. package/src/lib/components/ErrorState.svelte +4 -4
  24. package/src/lib/components/FeatureCard.svelte +4 -4
  25. package/src/lib/components/FormField.svelte +4 -6
  26. package/src/lib/components/Heading.svelte +4 -1
  27. package/src/lib/components/Input.svelte +10 -12
  28. package/src/lib/components/Kbd.svelte +1 -1
  29. package/src/lib/components/Link.svelte +2 -3
  30. package/src/lib/components/LinkButton.svelte +2 -2
  31. package/src/lib/components/LogConsole.svelte +22 -21
  32. package/src/lib/components/Logo.svelte +3 -1
  33. package/src/lib/components/Modal.svelte +9 -11
  34. package/src/lib/components/PageContainer.svelte +1 -1
  35. package/src/lib/components/PageHeader.svelte +1 -1
  36. package/src/lib/components/Pagination.svelte +4 -4
  37. package/src/lib/components/Popover.svelte +5 -7
  38. package/src/lib/components/Progress.svelte +6 -4
  39. package/src/lib/components/Prose.svelte +4 -4
  40. package/src/lib/components/Radio.svelte +11 -10
  41. package/src/lib/components/RadioGroup.svelte +3 -5
  42. package/src/lib/components/Select.svelte +23 -36
  43. package/src/lib/components/Separator.svelte +4 -1
  44. package/src/lib/components/Skeleton.svelte +6 -6
  45. package/src/lib/components/StatusIndicator.svelte +7 -18
  46. package/src/lib/components/Table.svelte +1 -1
  47. package/src/lib/components/TableCell.svelte +1 -1
  48. package/src/lib/components/TableRow.svelte +1 -5
  49. package/src/lib/components/Tabs.svelte +10 -13
  50. package/src/lib/components/TaskCard.svelte +7 -4
  51. package/src/lib/components/Textarea.svelte +10 -11
  52. package/src/lib/components/ThemeToggle.svelte +1 -1
  53. package/src/lib/components/ToastContainer.svelte +3 -8
  54. package/src/lib/components/Toggle.svelte +2 -3
  55. package/src/lib/components/Tooltip.svelte +12 -10
  56. package/src/lib/components/button-styles.ts +17 -18
  57. package/src/lib/components/dashboard/RunDetailPanel.svelte +160 -249
  58. package/src/lib/components/dashboard/RunFilterPopover.svelte +31 -25
  59. package/src/lib/components/dashboard/RunsList.svelte +55 -50
  60. package/src/lib/components/dashboard/run-filters.test.ts +31 -33
  61. package/src/lib/components/dashboard/run-filters.ts +33 -33
  62. package/src/lib/components/dashboard/run-helpers.test.ts +42 -17
  63. package/src/lib/components/dashboard/run-helpers.ts +53 -17
  64. package/src/lib/components/dashboard/status-config.ts +9 -8
  65. package/src/lib/theme-tokens.css +179 -114
  66. package/src/lib/theme.css +49 -35
  67. package/src/lib/utils/error.ts +0 -3
  68. package/src/lib/utils/format.test.ts +2 -6
  69. package/src/lib/utils/format.ts +28 -3
@@ -42,7 +42,6 @@
42
42
  // but we want to catch it to show "Invalid cron expression".
43
43
  const text = cronstrue.toString(value, { throwExceptionOnParseError: true });
44
44
 
45
- // Calculate next run
46
45
  let next: Date | undefined;
47
46
  try {
48
47
  const interval = CronExpressionParser.parse(value);
@@ -57,16 +56,15 @@
57
56
  }
58
57
  });
59
58
 
60
- // Determine state for styling
61
59
  let isError = $derived(!disabled && value && value.trim() !== "" && !explanation.isValid);
62
60
  </script>
63
61
 
64
62
  <div class="space-y-2">
65
63
  <div class="flex items-center justify-between">
66
- <div class="text-sm font-medium text-on-surface-muted">Cron Schedule</div>
64
+ <div class="font-mono text-xs font-medium text-on-surface-muted">Cron Schedule</div>
67
65
  <button
68
66
  type="button"
69
- class="flex items-center gap-1 text-xs text-primary hover:text-primary-hover"
67
+ class="flex items-center gap-1 font-mono text-xs text-primary hover:text-primary-hover"
70
68
  onclick={() => (examplesModalOpen = true)}
71
69
  >
72
70
  Examples <Info size={12} />
@@ -74,32 +72,31 @@
74
72
  </div>
75
73
  <div class="flex gap-4">
76
74
  <div class="flex-1">
77
- <!-- Pass connection-status-like classes effectively by using the component props or class -->
78
75
  <Input
79
76
  placeholder="* * * * *"
80
77
  bind:value
81
78
  {disabled}
82
- class="font-mono {isError
83
- ? '!focus:border-danger-500 !focus:ring-danger-500 border-danger-300!'
84
- : ''}"
79
+ class="font-mono {isError ? 'border-danger-surface!' : ''}"
85
80
  />
86
81
  </div>
87
82
  </div>
88
- <!-- Live Feedback -->
89
83
  <div
90
- class="flex items-start gap-3 rounded-md border p-3 transition-colors duration-200
91
- {isError ? 'border-danger-soft-border bg-danger-soft' : 'border-outline bg-surface-sunken'}"
84
+ class="flex items-start gap-3 rounded-[4px] border p-3 {isError
85
+ ? 'border-danger-soft-border bg-danger-soft'
86
+ : 'border-outline bg-surface-sunken'}"
92
87
  >
93
88
  <Info size={18} class="mt-0.5 {isError ? 'text-danger-surface' : 'text-primary'}" />
94
89
  <div>
95
90
  <div
96
- class="text-sm font-medium {isError ? 'text-danger-soft-text' : 'text-on-surface'}"
91
+ class="font-sans text-sm font-medium {isError
92
+ ? 'text-danger-soft-text'
93
+ : 'text-on-surface'}"
97
94
  >
98
95
  {explanation.text}
99
96
  </div>
100
97
  {#if explanation.next}
101
- <div class="mt-0.5 text-xs text-on-surface-muted">
102
- Next run: <span class="font-medium text-on-surface"
98
+ <div class="mt-0.5 font-sans text-xs text-on-surface-muted">
99
+ Next run: <span class="font-mono font-medium text-on-surface"
103
100
  >{explanation.next.toLocaleString()}</span
104
101
  >
105
102
  </div>
@@ -118,7 +115,7 @@
118
115
  {#each cronExamples as example (example.cron)}
119
116
  <button
120
117
  type="button"
121
- class="flex w-full items-center justify-between rounded-lg px-3 py-2 text-left transition-colors hover:bg-surface-sunken"
118
+ class="flex w-full items-center justify-between rounded-[3px] px-3 py-2 text-left hover:bg-surface-sunken"
122
119
  onclick={() => selectExample(example.cron)}
123
120
  >
124
121
  <code class="font-mono text-sm text-on-surface">{example.cron}</code>
@@ -55,8 +55,6 @@
55
55
  import Pagination from "./Pagination.svelte";
56
56
  import Fuse from "fuse.js";
57
57
 
58
- import dlv from "dlv";
59
-
60
58
  let {
61
59
  columns,
62
60
  data,
@@ -169,7 +167,7 @@
169
167
  </script>
170
168
 
171
169
  <div
172
- class="relative flex flex-col rounded-xl border border-outline bg-surface-raised shadow-sm {className}"
170
+ class="relative flex flex-col rounded-[4px] border border-outline bg-surface-raised shadow-sm {className}"
173
171
  >
174
172
  {#if filterable}
175
173
  <div class="border-b border-outline p-3">
@@ -188,7 +186,7 @@
188
186
  {/if}
189
187
 
190
188
  <div
191
- class="relative min-h-0 flex-1 rounded-t-xl rounded-b-xl {wrapperClass ??
189
+ class="relative min-h-0 flex-1 rounded-[4px] {wrapperClass ??
192
190
  (stickyHeader ? '' : 'overflow-x-auto')}"
193
191
  >
194
192
  {#if loading}
@@ -196,7 +194,7 @@
196
194
  class="absolute inset-0 z-10 flex items-center justify-center bg-surface-raised/50 backdrop-blur-[1px]"
197
195
  >
198
196
  <div
199
- class="flex items-center gap-3 rounded-full border border-outline-faint bg-surface-raised px-4 py-2 shadow-lg"
197
+ class="flex items-center gap-3 rounded-full border border-outline bg-surface-raised px-4 py-2 shadow-md"
200
198
  >
201
199
  <svg class="h-5 w-5 animate-spin text-primary" viewBox="0 0 24 24" fill="none">
202
200
  <circle
@@ -213,7 +211,7 @@
213
211
  d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
214
212
  ></path>
215
213
  </svg>
216
- <span class="text-sm font-medium text-on-surface-muted">Loading...</span>
214
+ <span class="font-mono text-sm text-on-surface-muted">Loading...</span>
217
215
  </div>
218
216
  </div>
219
217
  {/if}
@@ -239,7 +237,7 @@
239
237
  <th
240
238
  class="
241
239
  group px-4 {compact ? 'py-2' : 'py-3'}
242
- font-medium text-on-surface-muted
240
+ font-mono text-xs tracking-wide text-on-surface-faint uppercase
243
241
  {column.align === 'center' ? 'text-center' : ''}
244
242
  {column.align === 'right' ? 'text-right' : ''}
245
243
  "
@@ -249,7 +247,7 @@
249
247
  <button
250
248
  onclick={() => handleSort(column.key as string)}
251
249
  class="
252
- inline-flex items-center gap-1 transition-colors group-hover:text-on-surface
250
+ inline-flex items-center gap-1 group-hover:text-on-surface
253
251
  {sortKey === column.key ? 'text-on-surface' : ''}
254
252
  "
255
253
  >
@@ -263,7 +261,7 @@
263
261
  {:else}
264
262
  <ArrowUpDown
265
263
  size={14}
266
- class="text-on-surface-faint opacity-0 transition-opacity group-hover:opacity-100"
264
+ class="text-on-surface-faint opacity-0 group-hover:opacity-100"
267
265
  />
268
266
  {/if}
269
267
  </button>
@@ -295,13 +293,12 @@
295
293
  {#each pagedData as row, idx (idx)}
296
294
  <tr
297
295
  class="
298
- border-b border-outline-faint last:border-b-0
296
+ border-b border-outline last:border-b-0
299
297
  {striped && idx % 2 === 1 ? 'bg-surface-sunken/30' : ''}
300
298
  {hoverable ? 'hover:bg-surface-sunken' : ''}
301
299
  {isSelected(row) ? 'bg-primary-soft/30' : ''}
302
300
  {onRowClick ? 'cursor-pointer' : ''}
303
- transition-colors duration-200
304
- "
301
+ "
305
302
  onclick={() => onRowClick?.(row)}
306
303
  >
307
304
  {#if selectable}
@@ -329,7 +326,11 @@
329
326
  {#if column.render}
330
327
  {@render column.render(row)}
331
328
  {:else}
332
- {dlv(row, column.key as string) ?? "-"}
329
+ <!-- Raw field value: a token, so mono. Snippet-rendered
330
+ cells choose their own voice. -->
331
+ <span class="font-mono"
332
+ >{row[column.key as keyof T] ?? "-"}</span
333
+ >
333
334
  {/if}
334
335
  </td>
335
336
  {/each}
@@ -100,7 +100,7 @@
100
100
  <div
101
101
  class="
102
102
  absolute z-10 flex flex-col
103
- bg-surface-overlay shadow-xl
103
+ border border-outline bg-surface-overlay shadow-lg
104
104
  {positionClasses[side]}
105
105
  {className}
106
106
  "
@@ -110,18 +110,20 @@
110
110
  aria-labelledby={title ? "drawer-title" : undefined}
111
111
  transition:fly={{ ...flyParams[side], duration: 250 }}
112
112
  >
113
- <!-- Header -->
114
113
  {#if header}
115
- <div class="border-b border-outline-faint px-6 py-4">
114
+ <div class="border-b border-outline px-6 py-4">
116
115
  {@render header()}
117
116
  </div>
118
117
  {:else if title || closable}
119
118
  <div
120
- class="flex items-start justify-between gap-4 border-b border-outline-faint px-6 py-4"
119
+ class="flex items-start justify-between gap-4 border-b border-outline px-6 py-4"
121
120
  >
122
121
  <div>
123
122
  {#if title}
124
- <h2 id="drawer-title" class="text-lg font-semibold text-on-surface">
123
+ <h2
124
+ id="drawer-title"
125
+ class="font-mono text-lg font-semibold text-on-surface"
126
+ >
125
127
  {title}
126
128
  </h2>
127
129
  {/if}
@@ -129,7 +131,7 @@
129
131
  {#if closable}
130
132
  <button
131
133
  onclick={handleClose}
132
- class="-m-2 shrink-0 rounded-lg p-2 text-on-surface-faint transition-colors hover:bg-surface-sunken hover:text-on-surface-muted"
134
+ class="-m-2 shrink-0 rounded-[3px] p-2 text-on-surface-faint hover:bg-surface-sunken hover:text-on-surface-muted"
133
135
  aria-label="Close"
134
136
  >
135
137
  <X size={20} />
@@ -138,16 +140,14 @@
138
140
  </div>
139
141
  {/if}
140
142
 
141
- <!-- Body -->
142
143
  {#if children}
143
144
  <div class="flex-1 overflow-y-auto px-6 py-4">
144
145
  {@render children()}
145
146
  </div>
146
147
  {/if}
147
148
 
148
- <!-- Footer -->
149
149
  {#if footer}
150
- <div class="border-t border-outline-faint bg-surface-sunken/50 px-6 py-4">
150
+ <div class="border-t border-outline bg-surface-sunken px-6 py-4">
151
151
  {@render footer()}
152
152
  </div>
153
153
  {/if}
@@ -6,8 +6,6 @@
6
6
  import type { Component } from "svelte";
7
7
  import { tick, onDestroy } from "svelte";
8
8
  import { EllipsisVertical, Check } from "@lucide/svelte";
9
- import { scale } from "svelte/transition";
10
- import { quintOut } from "svelte/easing";
11
9
  import { computePosition, autoUpdate, flip, shift, offset } from "@floating-ui/dom";
12
10
 
13
11
  interface MenuItem {
@@ -58,11 +56,7 @@
58
56
 
59
57
  void computePosition(triggerEl, menuEl, {
60
58
  placement,
61
- middleware: [
62
- offset(4), // MENU_OFFSET
63
- flip(),
64
- shift({ padding: 8 }), // VIEWPORT_PADDING
65
- ],
59
+ middleware: [offset(4), flip(), shift({ padding: 8 })],
66
60
  }).then(({ x, y, placement }) => {
67
61
  if (menuEl) {
68
62
  Object.assign(menuEl.style, {
@@ -71,7 +65,7 @@
71
65
  position: "absolute",
72
66
  });
73
67
 
74
- // Simple origin logic based on verify placement
68
+ // Flip transform origin vertically when the menu flips above the trigger
75
69
  const isTop = placement.startsWith("top");
76
70
  transformOrigin = `${isTop ? "bottom" : "top"} ${align}`;
77
71
  }
@@ -127,7 +121,7 @@
127
121
  bind:this={triggerEl}
128
122
  aria-label={triggerLabel}
129
123
  aria-expanded={open}
130
- class="rounded-lg p-2 text-on-surface-muted transition-colors hover:bg-surface-sunken hover:text-on-surface focus:ring-2 focus:ring-outline focus:outline-none active:bg-surface-sunken"
124
+ class="rounded-[3px] p-2 text-on-surface-muted hover:bg-surface-sunken hover:text-on-surface focus:ring-2 focus:ring-outline focus:outline-none active:bg-surface-sunken"
131
125
  >
132
126
  {#if trigger}
133
127
  {@render trigger()}
@@ -141,12 +135,11 @@
141
135
  use:portal
142
136
  bind:this={menuEl}
143
137
  role="menu"
144
- transition:scale={{ duration: 150, start: 0.95, easing: quintOut }}
145
138
  class="
146
139
  z-[9999]
147
140
  min-w-48 overflow-y-auto
148
- rounded-xl
149
- border border-outline bg-surface-overlay py-1 shadow-xl shadow-on-surface/5
141
+ rounded-[4px]
142
+ border border-outline bg-surface-overlay py-1 shadow-lg
150
143
  "
151
144
  style="transform-origin: {transformOrigin};"
152
145
  >
@@ -155,20 +148,19 @@
155
148
  {:else}
156
149
  {#each items as item, idx (idx)}
157
150
  {#if item.divider}
158
- <hr class="my-1 border-outline-faint" />
151
+ <hr class="my-1 border-outline" />
159
152
  {:else if item.href}
160
153
  <a
161
154
  href={item.href}
162
155
  title={item.title}
163
156
  role="menuitem"
164
157
  class="
165
- mx-1 flex items-center gap-3 rounded-lg px-3 py-2 text-sm
158
+ mx-1 flex items-center gap-3 rounded-[3px] px-3 py-2 font-mono text-sm
166
159
  {item.danger
167
160
  ? 'text-danger-soft-text hover:bg-danger-soft'
168
- : 'text-on-surface-muted hover:bg-surface-sunken'}
161
+ : 'text-on-surface-muted hover:bg-surface-sunken hover:text-on-surface'}
169
162
  {item.disabled ? 'cursor-not-allowed opacity-50' : ''}
170
- transition-colors
171
- "
163
+ "
172
164
  >
173
165
  {#if item.icon}
174
166
  {@const Icon = item.icon}
@@ -183,11 +175,11 @@
183
175
  title={item.title}
184
176
  role="menuitem"
185
177
  class="
186
- mx-1 box-border flex w-full max-w-[calc(100%-8px)] items-center gap-3 rounded-lg px-3 py-2 text-left text-sm
178
+ mx-1 box-border flex w-full max-w-[calc(100%-8px)] items-center gap-3 rounded-[3px] px-3 py-2 text-left font-mono text-sm
187
179
  {item.danger
188
180
  ? 'text-danger-soft-text hover:bg-danger-soft'
189
- : 'text-on-surface-muted hover:bg-surface-sunken'}
190
- transition-colors disabled:cursor-not-allowed
181
+ : 'text-on-surface-muted hover:bg-surface-sunken hover:text-on-surface'}
182
+ disabled:cursor-not-allowed
191
183
  disabled:opacity-50
192
184
  "
193
185
  >
@@ -46,15 +46,15 @@
46
46
  };
47
47
 
48
48
  const inputClasses = `
49
- w-full rounded-lg border
49
+ w-full rounded-[3px] border font-mono
50
50
  bg-surface-raised text-on-surface placeholder:text-on-surface-faint
51
- focus:outline-none focus:ring-4 focus:ring-ring/10 focus:border-ring
51
+ focus:outline-none focus:border-ring focus:ring-2 focus:ring-ring focus:ring-offset-2
52
52
  disabled:bg-surface-sunken disabled:text-on-surface-muted disabled:cursor-not-allowed
53
- transition-all duration-normal ease-in-out
54
- `;
53
+ `;
55
54
 
56
55
  const normalBorder = "border-outline hover:border-outline-hover shadow-sm";
57
- const errorBorder = "border-danger-400 focus:border-danger-500 focus:ring-danger-500/20";
56
+ const errorBorder =
57
+ "border-danger-surface focus:border-danger-surface focus:ring-danger-surface";
58
58
 
59
59
  function formatMs(msValue: number): string {
60
60
  if (typeof msValue !== "number" || isNaN(msValue)) return "";
@@ -113,14 +113,14 @@
113
113
 
114
114
  <div class="space-y-1.5 {className}">
115
115
  {#if label}
116
- <label for={inputId} class="block text-sm font-medium text-on-surface-muted">
116
+ <label for={inputId} class="block font-mono text-xs font-medium text-on-surface-muted">
117
117
  {label}
118
118
  </label>
119
119
  {/if}
120
120
 
121
121
  <div class="group relative">
122
122
  <div
123
- class="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-on-surface-faint transition-colors group-focus-within:text-primary"
123
+ class="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-on-surface-faint group-focus-within:text-primary"
124
124
  >
125
125
  <Clock size={16} />
126
126
  </div>
@@ -155,18 +155,14 @@
155
155
  </div>
156
156
 
157
157
  {#if parsedHuman && isFocused}
158
- <p
159
- class="animate-in slide-in-from-top-1 fade-in text-xs font-medium text-primary duration-200"
160
- >
158
+ <p class="font-sans text-xs font-medium text-primary">
161
159
  {parsedHuman}
162
160
  </p>
163
161
  {:else if error}
164
- <p
165
- class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
166
- >
162
+ <p class="font-sans text-xs text-danger-soft-text">
167
163
  {error}
168
164
  </p>
169
165
  {:else if hint}
170
- <p class="text-sm text-on-surface-muted">{hint}</p>
166
+ <p class="font-sans text-xs text-on-surface-muted">{hint}</p>
171
167
  {/if}
172
168
  </div>
@@ -32,7 +32,7 @@
32
32
  <Icon size={iconSize} class="text-on-surface-faint" />
33
33
  </div>
34
34
 
35
- <h3 class="mb-1 text-lg font-semibold text-on-surface">{title}</h3>
35
+ <h3 class="mb-1 font-mono text-lg font-semibold text-on-surface">{title}</h3>
36
36
 
37
37
  {#if description}
38
38
  <p class="mb-6 max-w-sm text-sm text-on-surface-muted">{description}</p>
@@ -41,14 +41,14 @@
41
41
 
42
42
  const IconComponent = $derived(icon ?? (variant === "disconnected" ? WifiOff : TriangleAlert));
43
43
 
44
- const iconBg = $derived(variant === "disconnected" ? "bg-danger-soft" : "bg-warning-soft");
44
+ const iconBg = $derived(variant === "disconnected" ? "bg-warning-soft" : "bg-danger-soft");
45
45
  const iconColor = $derived(
46
- variant === "disconnected" ? "text-danger-surface" : "text-warning-surface",
46
+ variant === "disconnected" ? "text-warning-soft-text" : "text-danger-soft-text",
47
47
  );
48
48
  </script>
49
49
 
50
50
  <div
51
- class="flex flex-col items-center justify-center gap-4 rounded-xl border border-outline bg-surface-raised px-6 py-12 text-center {className}"
51
+ class="flex flex-col items-center justify-center gap-4 rounded-[4px] border border-outline bg-surface-raised px-6 py-12 text-center {className}"
52
52
  role="alert"
53
53
  >
54
54
  <div class="rounded-full {iconBg} p-3">
@@ -56,7 +56,7 @@
56
56
  </div>
57
57
 
58
58
  <div class="space-y-1">
59
- <h3 class="text-lg font-semibold text-on-surface">{defaultTitle}</h3>
59
+ <h3 class="font-mono text-lg font-semibold text-on-surface">{defaultTitle}</h3>
60
60
  <p class="max-w-sm text-sm text-on-surface-muted">{message}</p>
61
61
  </div>
62
62
 
@@ -15,19 +15,19 @@
15
15
  </script>
16
16
 
17
17
  <article
18
- class="overflow-hidden rounded-xl border border-outline bg-surface-raised p-6 shadow-sm transition-colors hover:border-outline-hover {className}"
18
+ class="overflow-hidden rounded-[4px] border border-outline bg-surface-raised p-6 hover:border-outline-hover {className}"
19
19
  >
20
20
  <div class="flex flex-col gap-3">
21
21
  {#if Icon}
22
22
  <span
23
- class="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-primary-soft text-primary-soft-text"
23
+ class="inline-flex h-10 w-10 items-center justify-center rounded-[3px] border border-primary-soft-border bg-primary-soft text-primary-soft-text"
24
24
  >
25
25
  <Icon size={20} />
26
26
  </span>
27
27
  {/if}
28
- <h3 class="text-lg font-semibold text-on-surface">{title}</h3>
28
+ <h3 class="font-mono text-base text-primary">{title}</h3>
29
29
  {#if children}
30
- <div class="text-sm leading-relaxed text-on-surface-muted">
30
+ <div class="font-sans text-sm leading-relaxed text-on-surface-muted">
31
31
  {@render children()}
32
32
  </div>
33
33
  {/if}
@@ -36,7 +36,7 @@
36
36
 
37
37
  <div class="space-y-1.5 {className}">
38
38
  {#if label}
39
- <label for={fieldId} class="block text-sm font-medium text-on-surface-muted">
39
+ <label for={fieldId} class="block font-mono text-xs font-medium text-on-surface-muted">
40
40
  {label}
41
41
  {#if required}
42
42
  <span class="text-danger-soft-text">*</span>
@@ -45,18 +45,16 @@
45
45
  {/if}
46
46
 
47
47
  {#if description}
48
- <p class="text-sm text-on-surface-faint">{description}</p>
48
+ <p class="font-sans text-xs text-on-surface-faint">{description}</p>
49
49
  {/if}
50
50
 
51
51
  {@render children()}
52
52
 
53
53
  {#if error}
54
- <p
55
- class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
56
- >
54
+ <p class="font-sans text-xs text-danger-soft-text">
57
55
  {error}
58
56
  </p>
59
57
  {:else if hint}
60
- <p class="text-sm text-on-surface-muted">{hint}</p>
58
+ <p class="font-sans text-xs text-on-surface-muted">{hint}</p>
61
59
  {/if}
62
60
  </div>
@@ -40,6 +40,9 @@
40
40
  const tag = $derived(`h${level}` as const);
41
41
  </script>
42
42
 
43
- <svelte:element this={tag} class="text-on-surface {sizeClasses[resolvedSize]} {className}">
43
+ <svelte:element
44
+ this={tag}
45
+ class="font-mono tracking-tight text-on-surface {sizeClasses[resolvedSize]} {className}"
46
+ >
44
47
  {@render children()}
45
48
  </svelte:element>
@@ -43,20 +43,20 @@
43
43
  };
44
44
 
45
45
  const inputClasses = `
46
- w-full rounded-lg border
46
+ w-full rounded-[3px] border font-mono
47
47
  bg-surface-raised text-on-surface placeholder:text-on-surface-faint
48
- focus:outline-none focus:ring-4 focus:ring-ring/10 focus:border-ring
48
+ focus:outline-none focus:border-ring focus:ring-2 focus:ring-ring focus:ring-offset-2
49
49
  disabled:bg-surface-sunken disabled:text-on-surface-muted disabled:cursor-not-allowed
50
- transition-all duration-normal ease-in-out
51
- `;
50
+ `;
52
51
 
53
52
  const normalBorder = "border-outline hover:border-outline-hover shadow-sm";
54
- const errorBorder = "border-danger-400 focus:border-danger-500 focus:ring-danger-500/20";
53
+ const errorBorder =
54
+ "border-danger-surface focus:border-danger-surface focus:ring-danger-surface";
55
55
  </script>
56
56
 
57
57
  <div class="space-y-1.5 {className}">
58
58
  {#if label}
59
- <label for={inputId} class="block text-sm font-medium text-on-surface-muted">
59
+ <label for={inputId} class="block font-mono text-xs font-medium text-on-surface-muted">
60
60
  {label}
61
61
  </label>
62
62
  {/if}
@@ -64,7 +64,7 @@
64
64
  <div class="group relative">
65
65
  {#if leadingIcon}
66
66
  <div
67
- class="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-on-surface-faint transition-colors group-focus-within:text-on-surface-muted"
67
+ class="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-on-surface-faint group-focus-within:text-on-surface-muted"
68
68
  >
69
69
  {@render leadingIcon()}
70
70
  </div>
@@ -97,7 +97,7 @@
97
97
  </div>
98
98
  {:else if trailingIcon}
99
99
  <div
100
- class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-on-surface-faint transition-colors group-focus-within:text-on-surface-muted"
100
+ class="pointer-events-none absolute top-1/2 right-3 -translate-y-1/2 text-on-surface-faint group-focus-within:text-on-surface-muted"
101
101
  >
102
102
  {@render trailingIcon()}
103
103
  </div>
@@ -105,12 +105,10 @@
105
105
  </div>
106
106
 
107
107
  {#if error}
108
- <p
109
- class="animate-in slide-in-from-top-1 fade-in text-sm text-danger-soft-text duration-200"
110
- >
108
+ <p class="font-sans text-xs text-danger-soft-text">
111
109
  {error}
112
110
  </p>
113
111
  {:else if hint}
114
- <p class="text-sm text-on-surface-muted">{hint}</p>
112
+ <p class="font-sans text-xs text-on-surface-muted">{hint}</p>
115
113
  {/if}
116
114
  </div>
@@ -16,7 +16,7 @@
16
16
  min-w-[1.5em] px-1.5 py-0.5
17
17
  text-xs font-mono font-medium
18
18
  bg-surface-sunken text-on-surface-muted
19
- border border-outline rounded-md
19
+ border border-outline rounded-[3px]
20
20
  shadow-sm
21
21
  `;
22
22
  </script>
@@ -27,9 +27,8 @@
27
27
 
28
28
  const baseClasses = `
29
29
  inline-flex items-center gap-1
30
- transition-colors duration-normal ease-out
31
- focus-visible:outline-none focus-visible:ring-4 focus-visible:ring-ring/10 focus-visible:border-ring
32
- rounded-sm
30
+ focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
31
+ rounded-[3px]
33
32
  `;
34
33
 
35
34
  const variantClasses: Record<LinkVariant, string> = {
@@ -40,7 +40,7 @@
40
40
  {...restProps}
41
41
  >
42
42
  {#if icon}
43
- <span class="shrink-0 transition-transform group-active:scale-95">
43
+ <span class="shrink-0 group-active:scale-95">
44
44
  {@render icon()}
45
45
  </span>
46
46
  {/if}
@@ -48,7 +48,7 @@
48
48
  {@render children()}
49
49
  {/if}
50
50
  {#if iconRight}
51
- <span class="shrink-0 transition-transform group-active:scale-95">
51
+ <span class="shrink-0 group-active:scale-95">
52
52
  {@render iconRight()}
53
53
  </span>
54
54
  {/if}