@dorsk/tsumikit 0.78.0 → 0.80.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.
- package/dist/components/atoms/Artwork.svelte +5 -3
- package/dist/components/atoms/Badge.svelte +2 -5
- package/dist/components/atoms/Button.svelte +327 -17
- package/dist/components/atoms/Button.svelte.d.ts +4 -1
- package/dist/components/atoms/Card.svelte +160 -12
- package/dist/components/atoms/Checkbox.svelte +181 -3
- package/dist/components/atoms/Gauge.svelte +2 -2
- package/dist/components/atoms/Input.svelte +41 -3
- package/dist/components/atoms/Kbd.svelte +1 -1
- package/dist/components/atoms/Link.svelte +61 -0
- package/dist/components/atoms/Progress.svelte +75 -2
- package/dist/components/atoms/Progress.svelte.d.ts +1 -0
- package/dist/components/atoms/SegmentedProgress.svelte +2 -2
- package/dist/components/atoms/Select.svelte +39 -3
- package/dist/components/atoms/Skeleton.svelte +54 -3
- package/dist/components/atoms/Skeleton.svelte.d.ts +1 -0
- package/dist/components/atoms/Slider.svelte +8 -6
- package/dist/components/atoms/Spinner.svelte +152 -3
- package/dist/components/atoms/Spinner.svelte.d.ts +1 -1
- package/dist/components/atoms/Swatch.svelte +1 -1
- package/dist/components/atoms/Switch.svelte +138 -2
- package/dist/components/atoms/Textarea.svelte +42 -3
- package/dist/components/atoms/Thinking.svelte +130 -0
- package/dist/components/atoms/Thinking.svelte.d.ts +15 -0
- package/dist/components/layouts/AppShell.svelte +6 -6
- package/dist/components/layouts/MasterDetail.svelte +3 -3
- package/dist/components/layouts/NavBar.svelte +4 -4
- package/dist/components/layouts/NavSection.svelte +1 -1
- package/dist/components/layouts/ResizablePanel.svelte +7 -7
- package/dist/components/layouts/TabBar.svelte +2 -2
- package/dist/components/molecules/Accordion.svelte +2 -2
- package/dist/components/molecules/AttachmentList.svelte +2 -2
- package/dist/components/molecules/CapBar.svelte +2 -2
- package/dist/components/molecules/CodeBlock.svelte +5 -5
- package/dist/components/molecules/Combobox.svelte +3 -3
- package/dist/components/molecules/CopyButton.svelte +146 -5
- package/dist/components/molecules/Drawer.svelte +8 -8
- package/dist/components/molecules/EmojiPicker.svelte +1 -1
- package/dist/components/molecules/Field.svelte +55 -1
- package/dist/components/molecules/FileButton.svelte +20 -6
- package/dist/components/molecules/FilterInput.svelte +4 -4
- package/dist/components/molecules/GitRef.svelte +2 -2
- package/dist/components/molecules/GlyphStack.svelte +21 -3
- package/dist/components/molecules/InputGroup.svelte +3 -3
- package/dist/components/molecules/LookPicker.svelte +87 -0
- package/dist/components/molecules/LookPicker.svelte.d.ts +17 -0
- package/dist/components/molecules/Modal.svelte +5 -5
- package/dist/components/molecules/MotionSettings.svelte +166 -0
- package/dist/components/molecules/MotionSettings.svelte.d.ts +18 -0
- package/dist/components/molecules/OptionButton.svelte +8 -3
- package/dist/components/molecules/Pagination.svelte +3 -1
- package/dist/components/molecules/Popover.svelte +18 -11
- package/dist/components/molecules/Prose.svelte +6 -6
- package/dist/components/molecules/RadialMenu.svelte +3 -3
- package/dist/components/molecules/RadioGroup.svelte +67 -4
- package/dist/components/molecules/RichSelect.svelte +6 -4
- package/dist/components/molecules/SectionHeader.svelte +1 -1
- package/dist/components/molecules/SeekSlider.svelte +6 -6
- package/dist/components/molecules/SegmentedControl.svelte +7 -7
- package/dist/components/molecules/SortableList.svelte +2 -2
- package/dist/components/molecules/SplitButton.svelte +1 -1
- package/dist/components/molecules/Tabs.svelte +160 -7
- package/dist/components/molecules/ThemePicker.svelte +1 -1
- package/dist/components/molecules/Timestamp.svelte +1 -1
- package/dist/components/molecules/Toggle.svelte +5 -3
- package/dist/components/molecules/Tooltip.svelte +2 -2
- package/dist/components/molecules/VolumeSlider.svelte +3 -3
- package/dist/components/organisms/ConversationFeed.svelte +1 -1
- package/dist/components/organisms/ConversationPanel.svelte +3 -3
- package/dist/components/organisms/DataTable.svelte +18 -16
- package/dist/fx.d.ts +149 -0
- package/dist/fx.js +111 -0
- package/dist/fx.md +58 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.js +8 -2
- package/dist/looks/README.md +101 -0
- package/dist/looks/depth.css +939 -0
- package/dist/looks/editorial.css +545 -0
- package/dist/looks/tactile.css +871 -0
- package/dist/pointer.d.ts +3 -0
- package/dist/pointer.js +40 -0
- package/dist/stores/fx.svelte.d.ts +17 -0
- package/dist/stores/fx.svelte.js +64 -0
- package/dist/stores/theme.svelte.d.ts +8 -2
- package/dist/stores/theme.svelte.js +33 -7
- package/dist/styles/themes.css +249 -249
- package/dist/styles/tokens.css +37 -2
- package/dist/theme-mode.d.ts +31 -0
- package/dist/theme-mode.js +19 -0
- package/package.json +3 -2
|
@@ -96,6 +96,45 @@
|
|
|
96
96
|
const framed = $derived(!!(header || footer || title));
|
|
97
97
|
const native = $derived(as === 'button' || as === 'a');
|
|
98
98
|
|
|
99
|
+
let el = $state<HTMLElement>();
|
|
100
|
+
const lit = $derived(tap || interactive);
|
|
101
|
+
|
|
102
|
+
// The spotlight follows the pointer itself unless the app already runs trackPointer().
|
|
103
|
+
$effect(() => {
|
|
104
|
+
const node = el;
|
|
105
|
+
if (!node || !lit) return;
|
|
106
|
+
let frame = 0;
|
|
107
|
+
let x = 0;
|
|
108
|
+
let y = 0;
|
|
109
|
+
const flush = () => {
|
|
110
|
+
frame = 0;
|
|
111
|
+
const r = node.getBoundingClientRect();
|
|
112
|
+
node.style.setProperty('--pointer-x', `${x - r.left}px`);
|
|
113
|
+
node.style.setProperty('--pointer-y', `${y - r.top}px`);
|
|
114
|
+
};
|
|
115
|
+
const move = (e: PointerEvent) => {
|
|
116
|
+
if (e.pointerType !== 'mouse') return;
|
|
117
|
+
x = e.clientX;
|
|
118
|
+
y = e.clientY;
|
|
119
|
+
if (!frame) frame = requestAnimationFrame(flush);
|
|
120
|
+
};
|
|
121
|
+
const enter = (e: PointerEvent) => {
|
|
122
|
+
if (e.pointerType !== 'mouse' || globalThis.matchMedia?.('(prefers-reduced-motion: reduce)').matches) return;
|
|
123
|
+
if (getComputedStyle(node).getPropertyValue('--card-fx').trim() !== 'spotlight') return;
|
|
124
|
+
move(e);
|
|
125
|
+
node.addEventListener('pointermove', move, { passive: true });
|
|
126
|
+
};
|
|
127
|
+
const leave = () => node.removeEventListener('pointermove', move);
|
|
128
|
+
node.addEventListener('pointerenter', enter, { passive: true });
|
|
129
|
+
node.addEventListener('pointerleave', leave, { passive: true });
|
|
130
|
+
return () => {
|
|
131
|
+
leave();
|
|
132
|
+
node.removeEventListener('pointerenter', enter);
|
|
133
|
+
node.removeEventListener('pointerleave', leave);
|
|
134
|
+
if (frame) cancelAnimationFrame(frame);
|
|
135
|
+
};
|
|
136
|
+
});
|
|
137
|
+
|
|
99
138
|
const NESTED = 'button,a,input,select,textarea,[role=button],[contenteditable],[contenteditable=true]';
|
|
100
139
|
function fromNested(e: Event): boolean {
|
|
101
140
|
const target = e.target as Element | null;
|
|
@@ -118,6 +157,7 @@
|
|
|
118
157
|
|
|
119
158
|
<svelte:element
|
|
120
159
|
this={as}
|
|
160
|
+
bind:this={el}
|
|
121
161
|
data-tsu="Card"
|
|
122
162
|
class="card {klass}"
|
|
123
163
|
class:pad-none={padding === 'none'}
|
|
@@ -168,16 +208,19 @@
|
|
|
168
208
|
{:else}
|
|
169
209
|
{@render children?.()}
|
|
170
210
|
{/if}
|
|
211
|
+
{#if lit}<span class="card-fx" aria-hidden="true"></span>{/if}
|
|
171
212
|
</svelte:element>
|
|
172
213
|
|
|
173
214
|
<style>
|
|
174
215
|
.card {
|
|
175
216
|
--card-pad: var(--sp-4);
|
|
176
217
|
min-width: 0;
|
|
177
|
-
background: var(--
|
|
218
|
+
background: var(--surface-fill);
|
|
178
219
|
--card-border-style: solid;
|
|
179
|
-
border:
|
|
220
|
+
border: var(--edge-width) var(--card-border-style) var(--surface-edge);
|
|
180
221
|
border-radius: var(--r-lg);
|
|
222
|
+
--card-shadow-rest: var(--surface-shadow);
|
|
223
|
+
box-shadow: var(--card-shadow-rest);
|
|
181
224
|
padding: var(--card-pad);
|
|
182
225
|
}
|
|
183
226
|
.card-gap {
|
|
@@ -194,10 +237,10 @@
|
|
|
194
237
|
padding: var(--card-pad);
|
|
195
238
|
}
|
|
196
239
|
.card-head {
|
|
197
|
-
border-bottom:
|
|
240
|
+
border-bottom: var(--edge-width) solid var(--surface-edge);
|
|
198
241
|
}
|
|
199
242
|
.card-foot {
|
|
200
|
-
border-top:
|
|
243
|
+
border-top: var(--edge-width) solid var(--surface-edge);
|
|
201
244
|
}
|
|
202
245
|
/* Theme-aware surface shade, selected by prop so it adapts across themes
|
|
203
246
|
instead of being overridden with app-level :global hacks. `base` is the
|
|
@@ -206,7 +249,7 @@
|
|
|
206
249
|
background: var(--surface);
|
|
207
250
|
}
|
|
208
251
|
.surface-sunken {
|
|
209
|
-
background: var(--
|
|
252
|
+
background: var(--surface-fill-2);
|
|
210
253
|
}
|
|
211
254
|
.border-dashed {
|
|
212
255
|
--card-border-style: dashed;
|
|
@@ -237,18 +280,122 @@
|
|
|
237
280
|
border-color var(--dur-fast) var(--ease),
|
|
238
281
|
background var(--dur-fast) var(--ease),
|
|
239
282
|
box-shadow var(--dur-fast) var(--ease),
|
|
283
|
+
translate var(--dur-fast) var(--ease-out),
|
|
240
284
|
scale var(--dur-fast) var(--ease-out);
|
|
241
285
|
}
|
|
242
286
|
.card-tap:active {
|
|
243
|
-
background: var(--
|
|
287
|
+
background: var(--surface-fill-2);
|
|
244
288
|
scale: var(--press-scale-lg);
|
|
245
289
|
transition-duration: var(--dur-press);
|
|
246
290
|
}
|
|
247
291
|
@media (hover: hover) and (pointer: fine) {
|
|
248
292
|
.card-tap:hover {
|
|
249
|
-
border-color: var(--
|
|
293
|
+
border-color: var(--surface-edge-strong);
|
|
294
|
+
translate: 0 calc(-1 * var(--hover-lift));
|
|
295
|
+
--card-shadow-rest: var(--surface-shadow-hover);
|
|
296
|
+
--card-glow: 100%;
|
|
297
|
+
--card-lit: 1;
|
|
298
|
+
--card-fade: var(--dur-fast);
|
|
299
|
+
--card-spin: var(--card-orbit-spin);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
.card-tap:focus-visible {
|
|
303
|
+
--card-glow: 100%;
|
|
304
|
+
--card-lit: 1;
|
|
305
|
+
--card-fade: var(--dur-fast);
|
|
306
|
+
--card-spin: var(--card-orbit-spin);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
@media (hover: hover) and (pointer: fine) {
|
|
310
|
+
.card-tap:hover > .card-fx {
|
|
311
|
+
--card-orbit: 360deg;
|
|
250
312
|
}
|
|
251
313
|
}
|
|
314
|
+
.card-tap:focus-visible > .card-fx {
|
|
315
|
+
--card-orbit: 360deg;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/* Hover light for tappable cards: a wash on the surface plus a ring on the edge. */
|
|
319
|
+
.card-tap {
|
|
320
|
+
position: relative;
|
|
321
|
+
}
|
|
322
|
+
.card-fx {
|
|
323
|
+
position: absolute;
|
|
324
|
+
inset: calc(-1 * var(--edge-width));
|
|
325
|
+
padding: 1.5px;
|
|
326
|
+
border-radius: inherit;
|
|
327
|
+
pointer-events: none;
|
|
328
|
+
opacity: var(--card-lit, 0);
|
|
329
|
+
mask:
|
|
330
|
+
linear-gradient(var(--text), var(--text)) content-box exclude,
|
|
331
|
+
linear-gradient(var(--text), var(--text));
|
|
332
|
+
transition:
|
|
333
|
+
opacity var(--card-fade, var(--dur-exit)) ease,
|
|
334
|
+
--card-orbit var(--card-spin, var(--dur-instant)) var(--ease-out);
|
|
335
|
+
}
|
|
336
|
+
.card-tap:not(:global([data-fx-card='none']) *),
|
|
337
|
+
:global(:is([data-fx-card='spotlight'], [data-fx-card='orbit'])) .card-tap:not(:global(:is([data-fx-card='spotlight'], [data-fx-card='orbit']) [data-fx-card='none']) *) {
|
|
338
|
+
transition:
|
|
339
|
+
border-color var(--dur-fast) var(--ease),
|
|
340
|
+
background var(--dur-fast) var(--ease),
|
|
341
|
+
box-shadow var(--dur-fast) var(--ease),
|
|
342
|
+
translate var(--dur-fast) var(--ease-out),
|
|
343
|
+
scale var(--dur-fast) var(--ease-out),
|
|
344
|
+
--card-glow var(--card-fade, var(--dur-exit)) ease;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* Spotlight: a soft light follows the pointer and lights the nearest edge. */
|
|
348
|
+
.card-tap:not(:global([data-fx-card]:not([data-fx-card='spotlight'])) *),
|
|
349
|
+
:global([data-fx-card='spotlight']) .card-tap:not(:global([data-fx-card='spotlight'] [data-fx-card]:not([data-fx-card='spotlight'])) *) {
|
|
350
|
+
--card-fx: spotlight;
|
|
351
|
+
background-image: radial-gradient(
|
|
352
|
+
circle 11rem at var(--card-spot-x) var(--card-spot-y),
|
|
353
|
+
color-mix(in srgb, var(--accent) calc(var(--card-glow) * 0.2), transparent),
|
|
354
|
+
transparent 70%
|
|
355
|
+
);
|
|
356
|
+
--card-spot-x: var(--pointer-x, 50%);
|
|
357
|
+
--card-spot-y: var(--pointer-y, 33%);
|
|
358
|
+
& .card-fx {
|
|
359
|
+
background: radial-gradient(
|
|
360
|
+
circle 7rem at calc(var(--card-spot-x) + var(--edge-width)) calc(var(--card-spot-y) + var(--edge-width)),
|
|
361
|
+
var(--accent) 15%,
|
|
362
|
+
transparent
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/* Border orbit: on hover-enter a comet makes exactly one revolution of the edge, then rests. */
|
|
368
|
+
:global([data-fx-card='orbit']) .card-tap:not(:global([data-fx-card='orbit'] [data-fx-card]:not([data-fx-card='orbit'])) *) {
|
|
369
|
+
--card-fx: orbit;
|
|
370
|
+
background-image: linear-gradient(
|
|
371
|
+
160deg,
|
|
372
|
+
color-mix(in srgb, var(--accent) calc(var(--card-glow) * 0.09), transparent),
|
|
373
|
+
transparent 55%
|
|
374
|
+
);
|
|
375
|
+
--card-orbit-spin: calc(var(--dur-slow) * 4);
|
|
376
|
+
& .card-fx {
|
|
377
|
+
background:
|
|
378
|
+
conic-gradient(
|
|
379
|
+
from var(--card-orbit),
|
|
380
|
+
transparent 0 62%,
|
|
381
|
+
color-mix(in srgb, var(--accent) 40%, transparent) 80%,
|
|
382
|
+
var(--accent) 92%,
|
|
383
|
+
color-mix(in srgb, var(--text) 85%, var(--accent)) 96%,
|
|
384
|
+
transparent 100%
|
|
385
|
+
),
|
|
386
|
+
linear-gradient(color-mix(in srgb, var(--accent) 22%, transparent), color-mix(in srgb, var(--accent) 22%, transparent));
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
@property --card-glow {
|
|
390
|
+
syntax: '<percentage>';
|
|
391
|
+
inherits: false;
|
|
392
|
+
initial-value: 0%;
|
|
393
|
+
}
|
|
394
|
+
@property --card-orbit {
|
|
395
|
+
syntax: '<angle>';
|
|
396
|
+
inherits: false;
|
|
397
|
+
initial-value: 0deg;
|
|
398
|
+
}
|
|
252
399
|
.card-max {
|
|
253
400
|
width: 100%;
|
|
254
401
|
}
|
|
@@ -275,9 +422,10 @@
|
|
|
275
422
|
.card-warn,
|
|
276
423
|
.card-danger,
|
|
277
424
|
.card-info,
|
|
425
|
+
.card-accent,
|
|
278
426
|
.card-attention {
|
|
279
|
-
border-color: color-mix(in srgb, var(--card-tone) 55%, var(--
|
|
280
|
-
background: color-mix(in srgb, var(--card-tone) 8%, var(--
|
|
427
|
+
border-color: color-mix(in srgb, var(--card-tone) 55%, var(--surface-edge));
|
|
428
|
+
background: color-mix(in srgb, var(--card-tone) 8%, var(--surface-fill));
|
|
281
429
|
}
|
|
282
430
|
.card-attention {
|
|
283
431
|
background: var(--attention-bg);
|
|
@@ -291,8 +439,8 @@
|
|
|
291
439
|
/* Defaults; overridden inline by the stackY/stackX props. */
|
|
292
440
|
--stack-y: 8px;
|
|
293
441
|
--stack-x: 2px;
|
|
294
|
-
--stack-bg: var(--
|
|
295
|
-
--stack-border: var(--
|
|
442
|
+
--stack-bg: var(--surface-fill-2);
|
|
443
|
+
--stack-border: var(--surface-edge);
|
|
296
444
|
/* Reserve room for the two peeking layers so they aren't clipped. */
|
|
297
445
|
margin-right: calc(var(--stack-x) * 2);
|
|
298
446
|
margin-bottom: calc(var(--stack-y) * 2);
|
|
@@ -306,7 +454,7 @@
|
|
|
306
454
|
/* Opaque fill so each layer fully hides the one behind it — only the
|
|
307
455
|
bottom-right peek (and its single border line) stays visible. */
|
|
308
456
|
background: var(--stack-bg);
|
|
309
|
-
border:
|
|
457
|
+
border: var(--edge-width) var(--card-border-style) var(--stack-border);
|
|
310
458
|
}
|
|
311
459
|
/* Nearest back layer — sits just under the front surface. */
|
|
312
460
|
.card-stacked::before {
|
|
@@ -44,8 +44,10 @@
|
|
|
44
44
|
|
|
45
45
|
const field = getFieldContext();
|
|
46
46
|
const isInvalid = $derived(invalid || !!field?.invalid);
|
|
47
|
+
let armed = $state(false);
|
|
47
48
|
|
|
48
49
|
function change(e: Event & { currentTarget: EventTarget & HTMLInputElement }) {
|
|
50
|
+
armed = true;
|
|
49
51
|
onchange?.(e);
|
|
50
52
|
playSound(e.currentTarget.checked ? 'toggle-on' : 'toggle-off', sound);
|
|
51
53
|
}
|
|
@@ -60,6 +62,7 @@
|
|
|
60
62
|
class="checkbox {klass}"
|
|
61
63
|
class:checkbox-sm={size === 'sm'}
|
|
62
64
|
class:checkbox-lg={size === 'lg'}
|
|
65
|
+
class:armed
|
|
63
66
|
data-tsu="Checkbox"
|
|
64
67
|
>
|
|
65
68
|
<input
|
|
@@ -72,7 +75,10 @@
|
|
|
72
75
|
aria-describedby={ariaDescribedby ?? field?.describedBy}
|
|
73
76
|
aria-invalid={ariaInvalid ?? (isInvalid ? 'true' : undefined)}
|
|
74
77
|
/>
|
|
75
|
-
<span class="box" aria-hidden="true"
|
|
78
|
+
<span class="box" aria-hidden="true">
|
|
79
|
+
<svg viewBox="0 0 20 20"><path class="tick" pathLength="1" d="M5.5 10.5l3 3 6-7" /></svg>
|
|
80
|
+
<span class="fx"></span>
|
|
81
|
+
</span>
|
|
76
82
|
<span class="label-text" class:sr-only={labelHidden}>{label}</span>
|
|
77
83
|
</label>
|
|
78
84
|
|
|
@@ -106,9 +112,11 @@
|
|
|
106
112
|
flex: none;
|
|
107
113
|
width: var(--cb-box, var(--chip-sm));
|
|
108
114
|
height: var(--cb-box, var(--chip-sm));
|
|
109
|
-
border:
|
|
115
|
+
border: var(--edge-width) solid var(--control-edge);
|
|
110
116
|
border-radius: var(--r-sm);
|
|
111
|
-
background: var(--
|
|
117
|
+
background: var(--control-fill);
|
|
118
|
+
box-shadow: var(--control-shadow);
|
|
119
|
+
isolation: isolate;
|
|
112
120
|
transition:
|
|
113
121
|
background var(--dur-fast) var(--ease),
|
|
114
122
|
border-color var(--dur-fast) var(--ease),
|
|
@@ -155,9 +163,179 @@
|
|
|
155
163
|
opacity var(--dur-spring) var(--ease-spring),
|
|
156
164
|
scale var(--dur-spring) var(--ease-spring);
|
|
157
165
|
}
|
|
166
|
+
svg {
|
|
167
|
+
position: absolute;
|
|
168
|
+
inset: -1px;
|
|
169
|
+
width: calc(100% + 2px);
|
|
170
|
+
height: calc(100% + 2px);
|
|
171
|
+
overflow: visible;
|
|
172
|
+
fill: none;
|
|
173
|
+
stroke-linecap: round;
|
|
174
|
+
stroke-linejoin: round;
|
|
175
|
+
}
|
|
176
|
+
.tick {
|
|
177
|
+
stroke: var(--text-on-accent);
|
|
178
|
+
stroke-width: 2.25;
|
|
179
|
+
stroke-dasharray: 1 2;
|
|
180
|
+
stroke-dashoffset: 1.05;
|
|
181
|
+
transition: stroke-dashoffset var(--dur-exit-fast) var(--ease-out);
|
|
182
|
+
}
|
|
183
|
+
.fx {
|
|
184
|
+
position: absolute;
|
|
185
|
+
inset: 0;
|
|
186
|
+
z-index: -1;
|
|
187
|
+
font-size: var(--cb-box, var(--chip-sm));
|
|
188
|
+
pointer-events: none;
|
|
189
|
+
}
|
|
190
|
+
.fx::before,
|
|
191
|
+
.fx::after {
|
|
192
|
+
content: '';
|
|
193
|
+
position: absolute;
|
|
194
|
+
opacity: 0;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/* Effects follow the closest data-fx-check ancestor; draw is the default. */
|
|
198
|
+
.checkbox:not(:global([data-fx-check]:not([data-fx-check='draw'])) *),
|
|
199
|
+
:global([data-fx-check='draw']) .checkbox:not(:global([data-fx-check='draw'] [data-fx-check]:not([data-fx-check='draw'])) *),
|
|
200
|
+
:global([data-fx-check='pop']) .checkbox:not(:global([data-fx-check='pop'] [data-fx-check]:not([data-fx-check='pop'])) *),
|
|
201
|
+
:global([data-fx-check='confetti']) .checkbox:not(:global([data-fx-check='confetti'] [data-fx-check]:not([data-fx-check='confetti'])) *) {
|
|
202
|
+
& input:checked:not(:indeterminate) + .box::after {
|
|
203
|
+
opacity: 0;
|
|
204
|
+
transition: none;
|
|
205
|
+
}
|
|
206
|
+
& input:checked:not(:indeterminate) + .box .tick {
|
|
207
|
+
stroke-dashoffset: 0;
|
|
208
|
+
transition: stroke-dashoffset var(--dur-exit-slow) var(--ease-out) calc(var(--dur-fast) / 3);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
:global([data-fx-check='pop']) .checkbox:not(:global([data-fx-check='pop'] [data-fx-check]:not([data-fx-check='pop'])) *) {
|
|
213
|
+
&.armed input:checked + .box {
|
|
214
|
+
animation: cb-pop var(--dur-spring) var(--ease-out);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
@keyframes cb-pop {
|
|
218
|
+
30% {
|
|
219
|
+
scale: 0.8;
|
|
220
|
+
}
|
|
221
|
+
65% {
|
|
222
|
+
scale: 1.14;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
:global([data-fx-check='confetti']) .checkbox:not(:global([data-fx-check='confetti'] [data-fx-check]:not([data-fx-check='confetti'])) *) {
|
|
227
|
+
& .fx::before,
|
|
228
|
+
& .fx::after {
|
|
229
|
+
inset: -0.85em;
|
|
230
|
+
border-radius: var(--r-pill);
|
|
231
|
+
}
|
|
232
|
+
& .fx::before {
|
|
233
|
+
background:
|
|
234
|
+
radial-gradient(circle at 50% 6%, var(--accent) 0 0.06em, transparent 0.08em),
|
|
235
|
+
radial-gradient(circle at 94% 50%, var(--info) 0 0.06em, transparent 0.08em),
|
|
236
|
+
radial-gradient(circle at 50% 94%, var(--accent) 0 0.06em, transparent 0.08em),
|
|
237
|
+
radial-gradient(circle at 6% 50%, var(--ok) 0 0.06em, transparent 0.08em);
|
|
238
|
+
}
|
|
239
|
+
& .fx::after {
|
|
240
|
+
background:
|
|
241
|
+
radial-gradient(circle at 81% 19%, var(--warn) 0 0.045em, transparent 0.065em),
|
|
242
|
+
radial-gradient(circle at 81% 81%, var(--accent) 0 0.045em, transparent 0.065em),
|
|
243
|
+
radial-gradient(circle at 19% 81%, var(--info) 0 0.045em, transparent 0.065em),
|
|
244
|
+
radial-gradient(circle at 19% 19%, var(--accent) 0 0.045em, transparent 0.065em);
|
|
245
|
+
}
|
|
246
|
+
&.armed input:checked:not(:indeterminate) + .box .fx::before {
|
|
247
|
+
animation: cb-burst var(--dur-spring) var(--ease);
|
|
248
|
+
}
|
|
249
|
+
&.armed input:checked:not(:indeterminate) + .box .fx::after {
|
|
250
|
+
animation: cb-burst-late var(--dur-spring) var(--ease);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
@keyframes cb-burst {
|
|
254
|
+
0% {
|
|
255
|
+
opacity: 0;
|
|
256
|
+
scale: 0.35;
|
|
257
|
+
}
|
|
258
|
+
20%,
|
|
259
|
+
60% {
|
|
260
|
+
opacity: 1;
|
|
261
|
+
}
|
|
262
|
+
100% {
|
|
263
|
+
opacity: 0;
|
|
264
|
+
scale: 1.05;
|
|
265
|
+
rotate: 18deg;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
@keyframes cb-burst-late {
|
|
269
|
+
0% {
|
|
270
|
+
opacity: 0;
|
|
271
|
+
scale: 0.3;
|
|
272
|
+
}
|
|
273
|
+
30%,
|
|
274
|
+
70% {
|
|
275
|
+
opacity: 1;
|
|
276
|
+
}
|
|
277
|
+
100% {
|
|
278
|
+
opacity: 0;
|
|
279
|
+
scale: 0.9;
|
|
280
|
+
rotate: -14deg;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* Two bars, the short and long legs of the tick, fold flat into the dash. */
|
|
285
|
+
:global([data-fx-check='morph']) .checkbox:not(:global([data-fx-check='morph'] [data-fx-check]:not([data-fx-check='morph'])) *) {
|
|
286
|
+
& .box::after {
|
|
287
|
+
display: none;
|
|
288
|
+
}
|
|
289
|
+
& .fx {
|
|
290
|
+
z-index: 0;
|
|
291
|
+
}
|
|
292
|
+
& .fx::before,
|
|
293
|
+
& .fx::after {
|
|
294
|
+
left: 0;
|
|
295
|
+
top: 0;
|
|
296
|
+
height: 0.11em;
|
|
297
|
+
border-radius: var(--r-pill);
|
|
298
|
+
background: var(--text-on-accent);
|
|
299
|
+
scale: 0;
|
|
300
|
+
transition:
|
|
301
|
+
opacity var(--dur-fast) var(--ease),
|
|
302
|
+
translate var(--dur-spring) var(--ease-spring),
|
|
303
|
+
rotate var(--dur-spring) var(--ease-spring),
|
|
304
|
+
scale var(--dur-spring) var(--ease-spring);
|
|
305
|
+
}
|
|
306
|
+
& .fx::before {
|
|
307
|
+
width: 0.26em;
|
|
308
|
+
translate: 0.22em 0.545em;
|
|
309
|
+
rotate: 45deg;
|
|
310
|
+
}
|
|
311
|
+
& .fx::after {
|
|
312
|
+
width: 0.5em;
|
|
313
|
+
translate: 0.325em 0.445em;
|
|
314
|
+
rotate: -49deg;
|
|
315
|
+
}
|
|
316
|
+
& input:checked + .box .fx::before,
|
|
317
|
+
& input:checked + .box .fx::after {
|
|
318
|
+
opacity: 1;
|
|
319
|
+
scale: 1;
|
|
320
|
+
}
|
|
321
|
+
& input:indeterminate + .box .fx::before {
|
|
322
|
+
opacity: 1;
|
|
323
|
+
translate: 0.37em 0.445em;
|
|
324
|
+
rotate: 0deg;
|
|
325
|
+
scale: 1;
|
|
326
|
+
}
|
|
327
|
+
& input:indeterminate + .box .fx::after {
|
|
328
|
+
opacity: 1;
|
|
329
|
+
translate: 0.25em 0.445em;
|
|
330
|
+
rotate: 0deg;
|
|
331
|
+
scale: 1;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
158
335
|
input:focus-visible + .box {
|
|
159
336
|
outline: var(--focus-ring);
|
|
160
337
|
outline-offset: var(--focus-ring-offset);
|
|
338
|
+
box-shadow: var(--control-shadow), var(--focus-glow);
|
|
161
339
|
}
|
|
162
340
|
input[aria-invalid='true']:not(:checked):not(:indeterminate) + .box {
|
|
163
341
|
border-color: var(--danger);
|
|
@@ -108,9 +108,9 @@
|
|
|
108
108
|
height: var(--gauge-h, 1.25rem);
|
|
109
109
|
margin: 0;
|
|
110
110
|
padding: 0;
|
|
111
|
-
border:
|
|
111
|
+
border: var(--edge-width) solid var(--track-edge);
|
|
112
112
|
border-radius: var(--r-sm);
|
|
113
|
-
background: var(--
|
|
113
|
+
background: var(--track-fill);
|
|
114
114
|
overflow: visible;
|
|
115
115
|
color: inherit;
|
|
116
116
|
font: inherit;
|
|
@@ -106,6 +106,7 @@
|
|
|
106
106
|
style:width={wrapped ? undefined : width}
|
|
107
107
|
bind:value
|
|
108
108
|
{...rest}
|
|
109
|
+
placeholder={rest.placeholder ?? (field ? ' ' : undefined)}
|
|
109
110
|
disabled={isDisabled}
|
|
110
111
|
onkeydown={onenter || onsubmit || onkeydown ? handleKeydown : undefined}
|
|
111
112
|
id={id ?? field?.id}
|
|
@@ -142,9 +143,10 @@
|
|
|
142
143
|
padding: var(--sp-2) var(--control-px-md);
|
|
143
144
|
font-size: var(--control-fs-md);
|
|
144
145
|
line-height: var(--lh-tight);
|
|
145
|
-
background: var(--input-bg, var(--
|
|
146
|
-
border:
|
|
146
|
+
background: var(--input-bg, var(--control-fill));
|
|
147
|
+
border: var(--edge-width) solid var(--input-border, var(--control-edge));
|
|
147
148
|
border-radius: var(--input-radius, var(--r-md));
|
|
149
|
+
box-shadow: var(--control-shadow);
|
|
148
150
|
color: var(--input-fg, var(--text));
|
|
149
151
|
transition: border-color var(--dur-fast) ease;
|
|
150
152
|
}
|
|
@@ -155,6 +157,11 @@
|
|
|
155
157
|
.input:focus-visible {
|
|
156
158
|
outline: var(--focus-ring);
|
|
157
159
|
outline-offset: var(--focus-ring-offset);
|
|
160
|
+
box-shadow: var(--control-shadow), var(--focus-glow);
|
|
161
|
+
}
|
|
162
|
+
.input::placeholder {
|
|
163
|
+
color: var(--text-faint);
|
|
164
|
+
opacity: 1;
|
|
158
165
|
}
|
|
159
166
|
.input:disabled {
|
|
160
167
|
opacity: var(--disabled-opacity);
|
|
@@ -206,6 +213,36 @@
|
|
|
206
213
|
.input[aria-invalid='true']:focus {
|
|
207
214
|
border-color: var(--danger);
|
|
208
215
|
}
|
|
216
|
+
.input {
|
|
217
|
+
--focus-tint: var(--accent);
|
|
218
|
+
}
|
|
219
|
+
.input[aria-invalid='true'] {
|
|
220
|
+
--focus-tint: var(--danger);
|
|
221
|
+
}
|
|
222
|
+
/* Focus bloom: a solid ring whose soft halo swells, then settles to a quieter glow. */
|
|
223
|
+
.input:not(.grouped):focus:not(:global([data-fx-focus='none']) *),
|
|
224
|
+
:global(:is([data-fx-focus='bloom'], [data-fx-focus='label'])) .input:not(.grouped):focus:not(:global(:is([data-fx-focus='bloom'], [data-fx-focus='label']) [data-fx-focus='none']) *) {
|
|
225
|
+
outline: none;
|
|
226
|
+
box-shadow:
|
|
227
|
+
var(--control-shadow),
|
|
228
|
+
0 0 0 var(--focus-ring-width) var(--focus-tint),
|
|
229
|
+
0 0 16px 2px color-mix(in srgb, var(--focus-tint) 24%, transparent);
|
|
230
|
+
animation: focus-bloom var(--dur-fast) var(--ease-out);
|
|
231
|
+
}
|
|
232
|
+
@keyframes focus-bloom {
|
|
233
|
+
0% {
|
|
234
|
+
box-shadow:
|
|
235
|
+
var(--control-shadow),
|
|
236
|
+
0 0 0 0 transparent,
|
|
237
|
+
0 0 0 0 transparent;
|
|
238
|
+
}
|
|
239
|
+
45% {
|
|
240
|
+
box-shadow:
|
|
241
|
+
var(--control-shadow),
|
|
242
|
+
0 0 0 calc(var(--focus-ring-width) + 1px) var(--focus-tint),
|
|
243
|
+
0 0 20px 4px color-mix(in srgb, var(--focus-tint) 32%, transparent);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
209
246
|
/* Inside an InputGroup the group draws the focus ring, dims the whole group
|
|
210
247
|
and the overlaid adornments report their widths; the text stays clear of them. */
|
|
211
248
|
.input.grouped {
|
|
@@ -216,6 +253,7 @@
|
|
|
216
253
|
}
|
|
217
254
|
.input.grouped:focus-visible {
|
|
218
255
|
outline: none;
|
|
256
|
+
box-shadow: var(--control-shadow);
|
|
219
257
|
}
|
|
220
258
|
.input.grouped:disabled {
|
|
221
259
|
opacity: 1;
|
|
@@ -296,7 +334,7 @@
|
|
|
296
334
|
}
|
|
297
335
|
@media (hover: hover) and (pointer: fine) {
|
|
298
336
|
.input:hover:not(:disabled, :focus, [aria-invalid='true']) {
|
|
299
|
-
border-color: var(--
|
|
337
|
+
border-color: var(--control-edge-hover);
|
|
300
338
|
}
|
|
301
339
|
.input-clear:hover {
|
|
302
340
|
color: var(--text);
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
font-size: inherit;
|
|
109
109
|
line-height: var(--lh-snug);
|
|
110
110
|
padding: 0 var(--sp-1);
|
|
111
|
-
border:
|
|
111
|
+
border: var(--edge-width) solid var(--border-strong);
|
|
112
112
|
border-bottom-width: 2px;
|
|
113
113
|
border-radius: var(--r-sm);
|
|
114
114
|
background: var(--bg-elevated-2);
|
|
@@ -77,10 +77,71 @@
|
|
|
77
77
|
.underline-hover:hover {
|
|
78
78
|
text-decoration: underline;
|
|
79
79
|
}
|
|
80
|
+
.link:hover {
|
|
81
|
+
--link-on: 0%;
|
|
82
|
+
--link-draw: 100%;
|
|
83
|
+
--link-mark: color-mix(in srgb, currentColor 30%, transparent);
|
|
84
|
+
--link-deco: transparent;
|
|
85
|
+
--link-in: var(--dur-press);
|
|
86
|
+
--link-grow: var(--dur-fast);
|
|
87
|
+
--link-hold: var(--dur-instant);
|
|
88
|
+
}
|
|
80
89
|
}
|
|
81
90
|
.align-center {
|
|
82
91
|
text-align: center;
|
|
83
92
|
}
|
|
93
|
+
|
|
94
|
+
/* Hover, focus and press only raise these; each variant decides what they paint. */
|
|
95
|
+
.link:is(:focus-visible, :active) {
|
|
96
|
+
--link-on: 0%;
|
|
97
|
+
--link-draw: 100%;
|
|
98
|
+
--link-mark: color-mix(in srgb, currentColor 30%, transparent);
|
|
99
|
+
--link-deco: transparent;
|
|
100
|
+
--link-in: var(--dur-press);
|
|
101
|
+
--link-grow: var(--dur-fast);
|
|
102
|
+
--link-hold: var(--dur-instant);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Sweep line: the underline fills in from the left and leaves to the right. */
|
|
106
|
+
.link:not(.underline-none):not(:global([data-fx-link]:not([data-fx-link='sweep'])) *),
|
|
107
|
+
:global([data-fx-link='sweep']) .link:not(.underline-none):not(:global([data-fx-link='sweep'] [data-fx-link]:not([data-fx-link='sweep'])) *) {
|
|
108
|
+
--link-base: 0%;
|
|
109
|
+
text-decoration: none;
|
|
110
|
+
background-image:
|
|
111
|
+
linear-gradient(currentColor, currentColor),
|
|
112
|
+
linear-gradient(color-mix(in srgb, currentColor 30%, transparent), color-mix(in srgb, currentColor 30%, transparent));
|
|
113
|
+
background-repeat: no-repeat;
|
|
114
|
+
background-position: var(--link-on, 100%) 100%, 0 100%;
|
|
115
|
+
background-size: var(--link-draw, 0%) 1.5px, var(--link-base) 1px;
|
|
116
|
+
transition:
|
|
117
|
+
color var(--dur-fast) ease,
|
|
118
|
+
background-size var(--dur-fast) var(--ease-out);
|
|
119
|
+
&.underline-always {
|
|
120
|
+
--link-base: 100%;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* Highlighter: a marker swipes in behind the words and fades on leave. */
|
|
125
|
+
:global([data-fx-link='highlight']) .link:not(.underline-none):not(:global([data-fx-link='highlight'] [data-fx-link]:not([data-fx-link='highlight'])) *) {
|
|
126
|
+
margin-inline: -0.18em;
|
|
127
|
+
padding-inline: 0.18em;
|
|
128
|
+
border-radius: 0.2em 0.35em 0.25em 0.4em;
|
|
129
|
+
background-image: linear-gradient(100deg, var(--link-mark), color-mix(in srgb, var(--link-mark) 70%, transparent));
|
|
130
|
+
background-repeat: no-repeat;
|
|
131
|
+
background-size: var(--link-draw, 0%) 100%;
|
|
132
|
+
text-decoration-color: var(--link-deco, color-mix(in srgb, currentColor 45%, transparent));
|
|
133
|
+
text-underline-offset: 0.2em;
|
|
134
|
+
transition:
|
|
135
|
+
color var(--dur-fast) ease,
|
|
136
|
+
text-decoration-color var(--dur-fast) ease,
|
|
137
|
+
--link-mark var(--link-in, var(--dur-exit)) ease,
|
|
138
|
+
background-size var(--link-grow, var(--dur-instant)) var(--ease-out) var(--link-hold, var(--dur-exit));
|
|
139
|
+
}
|
|
140
|
+
@property --link-mark {
|
|
141
|
+
syntax: '<color>';
|
|
142
|
+
inherits: false;
|
|
143
|
+
initial-value: transparent;
|
|
144
|
+
}
|
|
84
145
|
@media (pointer: coarse) {
|
|
85
146
|
.link {
|
|
86
147
|
position: relative;
|