@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
|
@@ -281,11 +281,11 @@
|
|
|
281
281
|
max-height: var(--cb-max-height, min(16rem, 40vh));
|
|
282
282
|
overflow-y: auto;
|
|
283
283
|
padding: var(--sp-1);
|
|
284
|
-
background: var(--cb-bg, var(--
|
|
284
|
+
background: var(--cb-bg, var(--raised-fill));
|
|
285
285
|
color: var(--text);
|
|
286
|
-
border:
|
|
286
|
+
border: var(--edge-width) solid var(--cb-border, var(--raised-edge));
|
|
287
287
|
border-radius: var(--cb-radius, var(--r-md));
|
|
288
|
-
box-shadow: var(--shadow
|
|
288
|
+
box-shadow: var(--raised-shadow);
|
|
289
289
|
transform-origin: var(--tsu-origin, top left);
|
|
290
290
|
opacity: 1;
|
|
291
291
|
scale: 1;
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
|
|
40
40
|
const labelShown = $derived(showLabel ?? box === undefined);
|
|
41
41
|
let copied = $state(false);
|
|
42
|
+
let run = $state(0);
|
|
42
43
|
let status = $state('');
|
|
43
44
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
44
45
|
|
|
@@ -46,6 +47,7 @@
|
|
|
46
47
|
const ok = await copyToClipboard(text);
|
|
47
48
|
playSound(ok ? 'success' : 'error', sound);
|
|
48
49
|
copied = ok;
|
|
50
|
+
if (ok) run++;
|
|
49
51
|
status = ok ? copiedLabel : 'Copy failed';
|
|
50
52
|
clearTimeout(timer);
|
|
51
53
|
timer = setTimeout(() => {
|
|
@@ -63,26 +65,33 @@
|
|
|
63
65
|
class="copy-btn {labelShown && box !== undefined ? 'copy-labelled' : ''} {klass}"
|
|
64
66
|
sound={false}
|
|
65
67
|
onclick={copy}
|
|
68
|
+
data-copied={copied || undefined}
|
|
66
69
|
aria-label={copied ? copiedLabel : label}
|
|
67
70
|
title={label}
|
|
68
71
|
>
|
|
69
|
-
<span class="glyphs" class:copied aria-hidden="true">
|
|
72
|
+
<span class="glyphs" class:copied class:tinted={variant === 'ghost' || variant === 'default'} aria-hidden="true">
|
|
70
73
|
<span class="glyph copy"><Icon name="copy" /></span>
|
|
71
74
|
<span class="glyph check"><Icon name="check" /></span>
|
|
75
|
+
<svg class="glyph tick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.25" stroke-linecap="round" stroke-linejoin="round">
|
|
76
|
+
<path d="M4.5 12.5l5 5L19.5 7" pathLength="1" />
|
|
77
|
+
</svg>
|
|
78
|
+
{#key run}<span class="sparks"></span>{/key}
|
|
72
79
|
</span>
|
|
73
|
-
{#if labelShown}
|
|
80
|
+
{#if labelShown}
|
|
81
|
+
<span class="words" class:copied aria-hidden="true"><span class="word idle">{label}</span><span class="word done">{copiedLabel}</span></span>
|
|
82
|
+
{/if}
|
|
74
83
|
</Button>
|
|
75
84
|
<span class="sr-only" role="status" aria-live="polite">{status}</span>
|
|
76
85
|
|
|
77
86
|
<style>
|
|
78
|
-
/* Both glyphs share one grid cell
|
|
79
|
-
|
|
80
|
-
glyph fades out fast and back in over the base duration on revert. */
|
|
87
|
+
/* Both glyphs share one grid cell and both words another, so the swap never
|
|
88
|
+
changes the width. */
|
|
81
89
|
:global(.copy-btn.btn-box.copy-labelled) {
|
|
82
90
|
width: auto;
|
|
83
91
|
padding-inline: var(--sp-2);
|
|
84
92
|
}
|
|
85
93
|
.glyphs {
|
|
94
|
+
position: relative;
|
|
86
95
|
display: inline-grid;
|
|
87
96
|
place-items: center;
|
|
88
97
|
}
|
|
@@ -111,4 +120,136 @@
|
|
|
111
120
|
opacity var(--dur-spring-bounce) ease,
|
|
112
121
|
scale var(--dur-spring-bounce) var(--ease-spring-bounce);
|
|
113
122
|
}
|
|
123
|
+
.tick,
|
|
124
|
+
.sparks {
|
|
125
|
+
display: none;
|
|
126
|
+
}
|
|
127
|
+
.words {
|
|
128
|
+
display: inline-grid;
|
|
129
|
+
overflow: hidden;
|
|
130
|
+
text-align: start;
|
|
131
|
+
}
|
|
132
|
+
.word {
|
|
133
|
+
grid-area: 1 / 1;
|
|
134
|
+
}
|
|
135
|
+
.done,
|
|
136
|
+
.copied .idle {
|
|
137
|
+
opacity: 0;
|
|
138
|
+
}
|
|
139
|
+
.copied .done {
|
|
140
|
+
opacity: 1;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/* Morph burst: the check springs in on a turn inside a ring of eight sparks. */
|
|
144
|
+
.glyphs:not(:global([data-fx-copy]:not([data-fx-copy='burst'])) *),
|
|
145
|
+
:global([data-fx-copy='burst']) .glyphs:not(:global([data-fx-copy='burst'] [data-fx-copy]:not([data-fx-copy='burst'])) *) {
|
|
146
|
+
& .check {
|
|
147
|
+
scale: 0.4;
|
|
148
|
+
rotate: 60deg;
|
|
149
|
+
transition-property: opacity, scale, rotate;
|
|
150
|
+
transition-duration: var(--dur-exit-fast);
|
|
151
|
+
transition-timing-function: ease;
|
|
152
|
+
}
|
|
153
|
+
&.copied .check {
|
|
154
|
+
scale: 1;
|
|
155
|
+
rotate: 0deg;
|
|
156
|
+
transition-duration: var(--dur-fast), var(--dur-spring), var(--dur-spring);
|
|
157
|
+
transition-delay: calc(var(--dur-exit-fast) / 2);
|
|
158
|
+
transition-timing-function: ease, var(--ease-spring-bounce), var(--ease-out);
|
|
159
|
+
}
|
|
160
|
+
& .sparks {
|
|
161
|
+
--spark-a: color-mix(in srgb, var(--ok) 85%, transparent);
|
|
162
|
+
--spark-b: color-mix(in srgb, var(--accent) 85%, transparent);
|
|
163
|
+
position: absolute;
|
|
164
|
+
inset: -9px;
|
|
165
|
+
display: block;
|
|
166
|
+
pointer-events: none;
|
|
167
|
+
background:
|
|
168
|
+
radial-gradient(circle at 50% 4%, var(--spark-a) 1.6px, transparent 2.2px),
|
|
169
|
+
radial-gradient(circle at 83% 17%, var(--spark-b) 1.3px, transparent 1.9px),
|
|
170
|
+
radial-gradient(circle at 96% 50%, var(--spark-a) 1.6px, transparent 2.2px),
|
|
171
|
+
radial-gradient(circle at 83% 83%, var(--spark-b) 1.3px, transparent 1.9px),
|
|
172
|
+
radial-gradient(circle at 50% 96%, var(--spark-a) 1.6px, transparent 2.2px),
|
|
173
|
+
radial-gradient(circle at 17% 83%, var(--spark-b) 1.3px, transparent 1.9px),
|
|
174
|
+
radial-gradient(circle at 4% 50%, var(--spark-a) 1.6px, transparent 2.2px),
|
|
175
|
+
radial-gradient(circle at 17% 17%, var(--spark-b) 1.3px, transparent 1.9px);
|
|
176
|
+
opacity: 0;
|
|
177
|
+
}
|
|
178
|
+
&.copied .sparks {
|
|
179
|
+
animation: copy-burst calc(var(--dur-slow) + var(--dur-exit-fast)) var(--ease-out);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
@keyframes copy-burst {
|
|
183
|
+
0% {
|
|
184
|
+
opacity: 0;
|
|
185
|
+
scale: 0.35;
|
|
186
|
+
rotate: -20deg;
|
|
187
|
+
}
|
|
188
|
+
30% {
|
|
189
|
+
opacity: 1;
|
|
190
|
+
}
|
|
191
|
+
100% {
|
|
192
|
+
opacity: 0;
|
|
193
|
+
scale: 1.15;
|
|
194
|
+
rotate: 0deg;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* Tick and slide: the tick draws itself while the label rolls up into its
|
|
199
|
+
copied form. */
|
|
200
|
+
:global([data-fx-copy='tick']) .glyphs:not(:global([data-fx-copy='tick'] [data-fx-copy]:not([data-fx-copy='tick'])) *) {
|
|
201
|
+
& .check {
|
|
202
|
+
display: none;
|
|
203
|
+
}
|
|
204
|
+
& .tick {
|
|
205
|
+
display: block;
|
|
206
|
+
width: 1em;
|
|
207
|
+
height: 1em;
|
|
208
|
+
opacity: 0;
|
|
209
|
+
transition: opacity var(--dur-exit-fast) ease var(--dur-instant);
|
|
210
|
+
}
|
|
211
|
+
&.copied .tick {
|
|
212
|
+
opacity: 1;
|
|
213
|
+
transition-duration: var(--dur-fast);
|
|
214
|
+
}
|
|
215
|
+
& .tick path {
|
|
216
|
+
stroke-dasharray: 1;
|
|
217
|
+
stroke-dashoffset: 1;
|
|
218
|
+
transition: stroke-dashoffset var(--dur-instant) ease var(--dur-exit-fast);
|
|
219
|
+
}
|
|
220
|
+
&.copied .tick path {
|
|
221
|
+
stroke-dashoffset: 0;
|
|
222
|
+
transition: stroke-dashoffset var(--dur-slow) var(--ease-out) calc(var(--dur-fast) / 2);
|
|
223
|
+
}
|
|
224
|
+
& .copy {
|
|
225
|
+
transition-property: opacity, scale;
|
|
226
|
+
transition-duration: var(--dur-base), var(--dur-spring);
|
|
227
|
+
transition-timing-function: ease, var(--ease-spring);
|
|
228
|
+
}
|
|
229
|
+
&.copied .copy {
|
|
230
|
+
scale: 0.6;
|
|
231
|
+
transition-duration: var(--dur-exit-fast), var(--dur-exit);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
:global([data-fx-copy='tick']) .words:not(:global([data-fx-copy='tick'] [data-fx-copy]:not([data-fx-copy='tick'])) *) {
|
|
235
|
+
& .word {
|
|
236
|
+
transition-property: opacity, translate;
|
|
237
|
+
transition-duration: var(--dur-base), var(--dur-spring);
|
|
238
|
+
transition-timing-function: ease, var(--ease-spring);
|
|
239
|
+
}
|
|
240
|
+
& .done {
|
|
241
|
+
translate: 0 100%;
|
|
242
|
+
}
|
|
243
|
+
&.copied .idle {
|
|
244
|
+
translate: 0 -100%;
|
|
245
|
+
transition-duration: var(--dur-exit-fast), var(--dur-exit);
|
|
246
|
+
}
|
|
247
|
+
&.copied .done {
|
|
248
|
+
translate: 0 0;
|
|
249
|
+
transition-delay: calc(var(--dur-exit-fast) / 2);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
.tinted.copied :is(.check, .tick) {
|
|
253
|
+
color: var(--ok);
|
|
254
|
+
}
|
|
114
255
|
</style>
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
'foot';
|
|
216
216
|
width: 100%;
|
|
217
217
|
height: 100%;
|
|
218
|
-
background: var(--
|
|
218
|
+
background: var(--overlay-fill);
|
|
219
219
|
will-change: translate;
|
|
220
220
|
padding-top: var(--safe-top);
|
|
221
221
|
padding-bottom: var(--safe-bottom);
|
|
@@ -242,7 +242,7 @@
|
|
|
242
242
|
align-items: center;
|
|
243
243
|
gap: var(--sp-2);
|
|
244
244
|
padding: var(--sp-4);
|
|
245
|
-
border-bottom:
|
|
245
|
+
border-bottom: var(--edge-width) solid var(--surface-edge);
|
|
246
246
|
}
|
|
247
247
|
.panel-icon {
|
|
248
248
|
display: inline-flex;
|
|
@@ -269,7 +269,7 @@
|
|
|
269
269
|
display: flex;
|
|
270
270
|
gap: var(--sp-1);
|
|
271
271
|
padding: var(--sp-2);
|
|
272
|
-
border-bottom:
|
|
272
|
+
border-bottom: var(--edge-width) solid var(--surface-edge);
|
|
273
273
|
overflow-x: auto;
|
|
274
274
|
scrollbar-width: thin;
|
|
275
275
|
}
|
|
@@ -290,7 +290,7 @@
|
|
|
290
290
|
justify-content: flex-end;
|
|
291
291
|
gap: var(--sp-2);
|
|
292
292
|
padding: var(--sp-4);
|
|
293
|
-
border-top:
|
|
293
|
+
border-top: var(--edge-width) solid var(--surface-edge);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
@media (min-width: 48rem) {
|
|
@@ -298,8 +298,8 @@
|
|
|
298
298
|
width: min(var(--drawer-w), 100%);
|
|
299
299
|
padding-top: 0;
|
|
300
300
|
padding-bottom: 0;
|
|
301
|
-
border-left:
|
|
302
|
-
box-shadow: var(--shadow
|
|
301
|
+
border-left: var(--edge-width) solid var(--border-strong);
|
|
302
|
+
box-shadow: var(--overlay-shadow);
|
|
303
303
|
grid-template-columns: minmax(0, 1fr);
|
|
304
304
|
grid-template-rows: auto minmax(0, 1fr) auto;
|
|
305
305
|
grid-template-areas:
|
|
@@ -309,7 +309,7 @@
|
|
|
309
309
|
}
|
|
310
310
|
.drawer[data-side='left'] .panel {
|
|
311
311
|
border-left: 0;
|
|
312
|
-
border-right:
|
|
312
|
+
border-right: var(--edge-width) solid var(--border-strong);
|
|
313
313
|
}
|
|
314
314
|
.panel.has-nav {
|
|
315
315
|
grid-template-columns: var(--drawer-nav-w) minmax(0, 1fr);
|
|
@@ -325,7 +325,7 @@
|
|
|
325
325
|
overflow-x: hidden;
|
|
326
326
|
overflow-y: auto;
|
|
327
327
|
border-bottom: 0;
|
|
328
|
-
border-right:
|
|
328
|
+
border-right: var(--edge-width) solid var(--surface-edge);
|
|
329
329
|
}
|
|
330
330
|
.panel-nav-mobile {
|
|
331
331
|
display: none;
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
|
|
60
60
|
<div class="field {klass}" class:field-grow={grow} class:field-no-shrink={!shrink} class:field-inline={layout === 'inline'} data-tsu="Field">
|
|
61
61
|
{#if label}
|
|
62
|
-
<label class="label" for={forId} style:width={labelWidth}>{label}</label>
|
|
62
|
+
<label class="label" for={forId} style:width={labelWidth}><span class="label-text">{label}</span></label>
|
|
63
63
|
{/if}
|
|
64
64
|
{@render children?.()}
|
|
65
65
|
{#if hint}
|
|
@@ -103,4 +103,58 @@
|
|
|
103
103
|
font-size: var(--fs-xs);
|
|
104
104
|
color: var(--danger);
|
|
105
105
|
}
|
|
106
|
+
|
|
107
|
+
/* Spring label: over a md text field the label rests inside the control and
|
|
108
|
+
springs into its top edge on focus or once filled. */
|
|
109
|
+
:global([data-fx-focus='label']) .field:not(.field-inline):not(:global([data-fx-focus='label'] [data-fx-focus]:not([data-fx-focus='label'])) *):has(> .label + :global(input.input:not(.input-sm, .input-lg, .grouped)), > .label + :global(.textarea-wrap:not(.grouped) > .textarea:not(.textarea-sm, .textarea-lg))) {
|
|
110
|
+
--float-h: 3.25rem;
|
|
111
|
+
position: relative;
|
|
112
|
+
& > .label {
|
|
113
|
+
position: absolute;
|
|
114
|
+
inset-block-start: 0;
|
|
115
|
+
inset-inline-start: 0;
|
|
116
|
+
z-index: 1;
|
|
117
|
+
display: flex;
|
|
118
|
+
align-items: center;
|
|
119
|
+
max-width: 100%;
|
|
120
|
+
height: var(--float-h);
|
|
121
|
+
padding-inline: calc(var(--control-px-md) + var(--edge-width));
|
|
122
|
+
font-size: var(--control-fs-md);
|
|
123
|
+
pointer-events: none;
|
|
124
|
+
}
|
|
125
|
+
& .label-text {
|
|
126
|
+
overflow: hidden;
|
|
127
|
+
text-overflow: ellipsis;
|
|
128
|
+
white-space: nowrap;
|
|
129
|
+
line-height: var(--lh-none);
|
|
130
|
+
transform-origin: left center;
|
|
131
|
+
transition-property: translate, scale, color;
|
|
132
|
+
transition-duration: var(--dur-spring), var(--dur-spring), var(--dur-fast);
|
|
133
|
+
transition-timing-function: var(--ease-spring-bounce), var(--ease-spring-bounce), ease;
|
|
134
|
+
}
|
|
135
|
+
& :global(:is(.input, .textarea)) {
|
|
136
|
+
min-height: var(--float-h);
|
|
137
|
+
padding-top: 1.35rem;
|
|
138
|
+
padding-bottom: 0.4rem;
|
|
139
|
+
}
|
|
140
|
+
& :global(:is(.input, .textarea):not(:focus)::placeholder) {
|
|
141
|
+
color: transparent;
|
|
142
|
+
}
|
|
143
|
+
& :global(:is(.input, .textarea):focus) {
|
|
144
|
+
animation: none;
|
|
145
|
+
box-shadow:
|
|
146
|
+
var(--control-shadow),
|
|
147
|
+
inset 0 -1px 0 var(--focus-tint);
|
|
148
|
+
}
|
|
149
|
+
&:has(:global(:is(.input, .textarea):is(:focus, :not(:placeholder-shown), :autofill))) .label-text {
|
|
150
|
+
translate: 0 -0.7rem;
|
|
151
|
+
scale: 0.78;
|
|
152
|
+
}
|
|
153
|
+
&:has(:global(:is(.input, .textarea):focus)) > .label {
|
|
154
|
+
color: var(--accent);
|
|
155
|
+
}
|
|
156
|
+
&:has(:global([aria-invalid='true'])) > .label {
|
|
157
|
+
color: var(--danger);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
106
160
|
</style>
|
|
@@ -107,9 +107,12 @@
|
|
|
107
107
|
gap: var(--sp-2);
|
|
108
108
|
padding: var(--sp-2) var(--btn-px-md);
|
|
109
109
|
min-height: var(--control-height-default);
|
|
110
|
-
border:
|
|
110
|
+
border: var(--edge-width) solid var(--btn-edge);
|
|
111
111
|
border-radius: var(--r-md);
|
|
112
|
-
background: var(--
|
|
112
|
+
background: var(--btn-fill);
|
|
113
|
+
--file-shadow-rest: var(--btn-shadow);
|
|
114
|
+
--file-shadow-lift: var(--btn-shadow-hover);
|
|
115
|
+
box-shadow: var(--file-shadow-rest);
|
|
113
116
|
color: var(--text);
|
|
114
117
|
font-weight: var(--fw-medium);
|
|
115
118
|
font-size: var(--control-fs-md);
|
|
@@ -119,16 +122,20 @@
|
|
|
119
122
|
cursor: pointer;
|
|
120
123
|
user-select: none;
|
|
121
124
|
--icon-size: var(--control-icon-md);
|
|
122
|
-
--file-scale-dur: var(--dur-
|
|
125
|
+
--file-scale-dur: var(--dur-release);
|
|
126
|
+
--file-scale-ease: var(--ease-spring-bounce);
|
|
123
127
|
transition:
|
|
124
128
|
background var(--dur-fast) ease,
|
|
125
129
|
border-color var(--dur-fast) ease,
|
|
130
|
+
box-shadow var(--dur-fast) ease,
|
|
126
131
|
color var(--dur-fast) ease,
|
|
127
132
|
opacity var(--dur-fast) ease,
|
|
128
|
-
|
|
133
|
+
translate var(--dur-fast) var(--ease-out),
|
|
134
|
+
scale var(--file-scale-dur) var(--file-scale-ease);
|
|
129
135
|
}
|
|
130
136
|
.file-btn:active:not(.disabled) {
|
|
131
137
|
--file-scale-dur: var(--dur-press);
|
|
138
|
+
--file-scale-ease: var(--ease-out);
|
|
132
139
|
scale: var(--press-scale);
|
|
133
140
|
}
|
|
134
141
|
.file-btn.icon-only:active:not(.disabled),
|
|
@@ -197,20 +204,25 @@
|
|
|
197
204
|
font-size: var(--icon-xl);
|
|
198
205
|
}
|
|
199
206
|
.file-btn.primary {
|
|
200
|
-
background: var(--
|
|
201
|
-
border-color: var(--
|
|
207
|
+
background: var(--btn-primary-fill);
|
|
208
|
+
border-color: var(--btn-primary-edge);
|
|
209
|
+
--file-shadow-rest: var(--btn-primary-shadow);
|
|
210
|
+
--file-shadow-lift: var(--btn-primary-shadow-hover);
|
|
202
211
|
color: var(--text-on-accent);
|
|
203
212
|
font-weight: var(--fw-semibold);
|
|
204
213
|
}
|
|
205
214
|
.file-btn.ghost {
|
|
206
215
|
background: transparent;
|
|
207
216
|
border-color: transparent;
|
|
217
|
+
--file-shadow-rest: none;
|
|
218
|
+
--file-shadow-lift: none;
|
|
208
219
|
}
|
|
209
220
|
/* The hidden input keeps focusability (sr-only, not display:none), so mirror
|
|
210
221
|
its focus onto the label for a visible ring. */
|
|
211
222
|
.file-btn:focus-within {
|
|
212
223
|
outline: var(--focus-ring);
|
|
213
224
|
outline-offset: var(--focus-ring-offset);
|
|
225
|
+
box-shadow: var(--file-shadow-rest), var(--focus-glow);
|
|
214
226
|
}
|
|
215
227
|
.file-btn.disabled {
|
|
216
228
|
opacity: var(--disabled-opacity);
|
|
@@ -219,6 +231,8 @@
|
|
|
219
231
|
@media (hover: hover) and (pointer: fine) {
|
|
220
232
|
.file-btn:hover:not(.disabled) {
|
|
221
233
|
border-color: var(--accent);
|
|
234
|
+
translate: 0 calc(-1 * var(--hover-lift));
|
|
235
|
+
--file-shadow-rest: var(--file-shadow-lift);
|
|
222
236
|
}
|
|
223
237
|
.file-btn.primary:hover:not(.disabled) {
|
|
224
238
|
filter: brightness(1.08);
|
|
@@ -473,7 +473,7 @@
|
|
|
473
473
|
padding: 0 var(--control-px-md);
|
|
474
474
|
min-height: var(--control-height-default);
|
|
475
475
|
background: var(--bg-elevated);
|
|
476
|
-
border:
|
|
476
|
+
border: var(--edge-width) solid var(--control-edge);
|
|
477
477
|
border-radius: var(--r-md);
|
|
478
478
|
transition:
|
|
479
479
|
border-color var(--dur-fast) ease,
|
|
@@ -502,7 +502,7 @@
|
|
|
502
502
|
font-size: var(--fs-xs);
|
|
503
503
|
line-height: var(--lh-none);
|
|
504
504
|
color: var(--text-faint);
|
|
505
|
-
border:
|
|
505
|
+
border: var(--edge-width) solid var(--border);
|
|
506
506
|
border-radius: var(--r-sm);
|
|
507
507
|
padding: var(--sp-0-5) var(--sp-1);
|
|
508
508
|
}
|
|
@@ -600,8 +600,8 @@
|
|
|
600
600
|
left: 0;
|
|
601
601
|
right: 0;
|
|
602
602
|
z-index: var(--z-drawer);
|
|
603
|
-
background: var(--
|
|
604
|
-
border:
|
|
603
|
+
background: var(--raised-fill);
|
|
604
|
+
border: var(--edge-width) solid var(--raised-edge);
|
|
605
605
|
border-radius: var(--r-md);
|
|
606
606
|
box-shadow: var(--shadow-lg);
|
|
607
607
|
padding: var(--sp-1);
|
|
@@ -125,9 +125,9 @@
|
|
|
125
125
|
min-width: 0;
|
|
126
126
|
padding: var(--sp-0-5) var(--sp-2);
|
|
127
127
|
border-radius: var(--r-pill);
|
|
128
|
-
background: var(--
|
|
128
|
+
background: var(--surface-fill-2);
|
|
129
129
|
color: var(--text-muted);
|
|
130
|
-
border:
|
|
130
|
+
border: var(--edge-width) solid var(--surface-edge);
|
|
131
131
|
text-decoration: none;
|
|
132
132
|
}
|
|
133
133
|
.branch {
|
|
@@ -314,6 +314,8 @@
|
|
|
314
314
|
height: 1rem;
|
|
315
315
|
max-width: 1rem;
|
|
316
316
|
max-height: 1rem;
|
|
317
|
+
min-width: 0;
|
|
318
|
+
min-height: 0;
|
|
317
319
|
display: inline-flex;
|
|
318
320
|
align-items: center;
|
|
319
321
|
justify-content: center;
|
|
@@ -321,6 +323,22 @@
|
|
|
321
323
|
font-size: var(--fs-xs);
|
|
322
324
|
line-height: var(--lh-none);
|
|
323
325
|
}
|
|
326
|
+
/* Controls with a minimum box (e.g. a `box="xs"` IconButton) shrink to the cell instead of clipping. */
|
|
327
|
+
.stacked > .cell > :global(*) {
|
|
328
|
+
min-width: 0;
|
|
329
|
+
min-height: 0;
|
|
330
|
+
max-width: 1rem;
|
|
331
|
+
max-height: 1rem;
|
|
332
|
+
}
|
|
333
|
+
/* A labelled Dot folds to its disc; the label stays in the accessibility tree. */
|
|
334
|
+
.stacked :global(.dot-row > :not(.dot)) {
|
|
335
|
+
position: absolute;
|
|
336
|
+
width: 1px;
|
|
337
|
+
height: 1px;
|
|
338
|
+
overflow: hidden;
|
|
339
|
+
clip-path: inset(50%);
|
|
340
|
+
white-space: nowrap;
|
|
341
|
+
}
|
|
324
342
|
.stacked > :global(*) :global(:focus-visible),
|
|
325
343
|
.stacked > :global(:focus-visible) {
|
|
326
344
|
outline-offset: var(--focus-ring-offset-inset);
|
|
@@ -343,11 +361,11 @@
|
|
|
343
361
|
margin: 0;
|
|
344
362
|
padding: var(--sp-1);
|
|
345
363
|
max-width: calc(100vw - 2 * var(--sp-2));
|
|
346
|
-
background: var(--
|
|
364
|
+
background: var(--raised-fill);
|
|
347
365
|
color: var(--text);
|
|
348
|
-
border:
|
|
366
|
+
border: var(--edge-width) solid var(--raised-edge);
|
|
349
367
|
border-radius: var(--r-md);
|
|
350
|
-
box-shadow: var(--shadow
|
|
368
|
+
box-shadow: var(--raised-shadow);
|
|
351
369
|
opacity: 0;
|
|
352
370
|
scale: var(--enter-scale);
|
|
353
371
|
transition:
|
|
@@ -221,8 +221,8 @@
|
|
|
221
221
|
position: absolute;
|
|
222
222
|
inset: auto 0 0 0;
|
|
223
223
|
height: var(--ig-h);
|
|
224
|
-
background: var(--textarea-bg, var(--
|
|
225
|
-
border:
|
|
224
|
+
background: var(--textarea-bg, var(--control-fill));
|
|
225
|
+
border: var(--edge-width) solid var(--textarea-border, var(--control-edge));
|
|
226
226
|
border-top: 0;
|
|
227
227
|
border-radius: 0 0 var(--ig-radius) var(--ig-radius);
|
|
228
228
|
transition: border-color var(--dur-fast) ease;
|
|
@@ -234,7 +234,7 @@
|
|
|
234
234
|
.ig-bar:has(.ig-field :global([aria-invalid='true']))::before {
|
|
235
235
|
border-color: var(--danger);
|
|
236
236
|
}
|
|
237
|
-
.ig:has(:focus-visible)::after {
|
|
237
|
+
.ig:has(:global(:focus-visible))::after {
|
|
238
238
|
content: '';
|
|
239
239
|
position: absolute;
|
|
240
240
|
inset: 0;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { ComponentProps } from 'svelte';
|
|
3
|
+
import SegmentedControl, { type SegmentOption } from './SegmentedControl.svelte';
|
|
4
|
+
import { theme } from '../../stores/theme.svelte';
|
|
5
|
+
import { LOOKS, type LookId } from '../../theme-mode';
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
looks,
|
|
9
|
+
experimental = true,
|
|
10
|
+
experimentalLabel = 'beta',
|
|
11
|
+
label = 'Look',
|
|
12
|
+
size = 'md',
|
|
13
|
+
control,
|
|
14
|
+
block,
|
|
15
|
+
scroll,
|
|
16
|
+
sound,
|
|
17
|
+
onchange,
|
|
18
|
+
class: klass = '',
|
|
19
|
+
}: Pick<ComponentProps<typeof SegmentedControl>, 'size' | 'control' | 'block' | 'scroll' | 'sound'> & {
|
|
20
|
+
/** Restrict and order the offered looks; defaults to every registered look. */
|
|
21
|
+
looks?: readonly LookId[];
|
|
22
|
+
/** Offer looks whose status is `experimental`. */
|
|
23
|
+
experimental?: boolean;
|
|
24
|
+
/** Badge on experimental looks; empty hides it. */
|
|
25
|
+
experimentalLabel?: string;
|
|
26
|
+
label?: string;
|
|
27
|
+
onchange?: (look: LookId) => void;
|
|
28
|
+
class?: string;
|
|
29
|
+
} = $props();
|
|
30
|
+
|
|
31
|
+
const options = $derived(
|
|
32
|
+
(looks ?? LOOKS.map((l) => l.id)).flatMap((id) => {
|
|
33
|
+
const l = LOOKS.find((x) => x.id === id);
|
|
34
|
+
const beta = l?.status === 'experimental';
|
|
35
|
+
if (!l || (beta && !experimental)) return [];
|
|
36
|
+
return [{ value: l.id, label: l.label }];
|
|
37
|
+
})
|
|
38
|
+
);
|
|
39
|
+
const tagged = new Set<string>(LOOKS.filter((l) => l.status === 'experimental').map((l) => l.id));
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
{#snippet option(o: SegmentOption)}
|
|
43
|
+
<span class="look">
|
|
44
|
+
<span class="name">{o.label}</span>
|
|
45
|
+
{#if experimentalLabel && tagged.has(o.value)}<span class="tag">{experimentalLabel}</span>{/if}
|
|
46
|
+
</span>
|
|
47
|
+
{/snippet}
|
|
48
|
+
|
|
49
|
+
<SegmentedControl
|
|
50
|
+
{options}
|
|
51
|
+
value={theme.look}
|
|
52
|
+
onchange={(v) => {
|
|
53
|
+
theme.setLook(v);
|
|
54
|
+
onchange?.(theme.look);
|
|
55
|
+
}}
|
|
56
|
+
{label}
|
|
57
|
+
{size}
|
|
58
|
+
{control}
|
|
59
|
+
{block}
|
|
60
|
+
{scroll}
|
|
61
|
+
{sound}
|
|
62
|
+
class="look-picker {klass}"
|
|
63
|
+
{option}
|
|
64
|
+
/>
|
|
65
|
+
|
|
66
|
+
<style>
|
|
67
|
+
.look {
|
|
68
|
+
display: inline-flex;
|
|
69
|
+
align-items: center;
|
|
70
|
+
gap: var(--sp-1);
|
|
71
|
+
min-width: 0;
|
|
72
|
+
}
|
|
73
|
+
:global(.seg-block) .name {
|
|
74
|
+
min-width: 0;
|
|
75
|
+
overflow: hidden;
|
|
76
|
+
text-overflow: ellipsis;
|
|
77
|
+
}
|
|
78
|
+
.tag {
|
|
79
|
+
flex: none;
|
|
80
|
+
padding: 0 var(--sp-1);
|
|
81
|
+
border: 1px solid currentColor;
|
|
82
|
+
border-radius: var(--r-pill);
|
|
83
|
+
font-size: var(--fs-xs);
|
|
84
|
+
font-weight: var(--fw-semibold);
|
|
85
|
+
line-height: var(--lh-tight);
|
|
86
|
+
}
|
|
87
|
+
</style>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ComponentProps } from 'svelte';
|
|
2
|
+
import SegmentedControl from './SegmentedControl.svelte';
|
|
3
|
+
import { type LookId } from '../../theme-mode';
|
|
4
|
+
type $$ComponentProps = Pick<ComponentProps<typeof SegmentedControl>, 'size' | 'control' | 'block' | 'scroll' | 'sound'> & {
|
|
5
|
+
/** Restrict and order the offered looks; defaults to every registered look. */
|
|
6
|
+
looks?: readonly LookId[];
|
|
7
|
+
/** Offer looks whose status is `experimental`. */
|
|
8
|
+
experimental?: boolean;
|
|
9
|
+
/** Badge on experimental looks; empty hides it. */
|
|
10
|
+
experimentalLabel?: string;
|
|
11
|
+
label?: string;
|
|
12
|
+
onchange?: (look: LookId) => void;
|
|
13
|
+
class?: string;
|
|
14
|
+
};
|
|
15
|
+
declare const LookPicker: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
16
|
+
type LookPicker = ReturnType<typeof LookPicker>;
|
|
17
|
+
export default LookPicker;
|
|
@@ -286,10 +286,10 @@
|
|
|
286
286
|
max-height: calc(100dvh - var(--safe-top) - var(--sp-6));
|
|
287
287
|
display: flex;
|
|
288
288
|
flex-direction: column;
|
|
289
|
-
background: var(--
|
|
290
|
-
border:
|
|
289
|
+
background: var(--overlay-fill);
|
|
290
|
+
border: var(--edge-width) solid var(--overlay-edge);
|
|
291
291
|
border-radius: var(--r-lg) var(--r-lg) 0 0;
|
|
292
|
-
box-shadow: var(--shadow
|
|
292
|
+
box-shadow: var(--overlay-shadow);
|
|
293
293
|
padding-bottom: var(--safe-bottom);
|
|
294
294
|
/* Bottom sheet: slides up on enter, down on exit. */
|
|
295
295
|
opacity: 0;
|
|
@@ -357,7 +357,7 @@
|
|
|
357
357
|
align-items: center;
|
|
358
358
|
gap: var(--sp-2);
|
|
359
359
|
padding: var(--sp-4);
|
|
360
|
-
border-bottom:
|
|
360
|
+
border-bottom: var(--edge-width) solid var(--surface-edge);
|
|
361
361
|
}
|
|
362
362
|
.sheet-toned {
|
|
363
363
|
border-top: 3px solid var(--modal-tone);
|
|
@@ -382,7 +382,7 @@
|
|
|
382
382
|
justify-content: flex-end;
|
|
383
383
|
gap: var(--sp-2);
|
|
384
384
|
padding: var(--sp-4);
|
|
385
|
-
border-top:
|
|
385
|
+
border-top: var(--edge-width) solid var(--surface-edge);
|
|
386
386
|
}
|
|
387
387
|
/* Opt-in: a flex item is shrink-to-fit, so a layout wrapper handed to the
|
|
388
388
|
footer would size to its content and hug the right edge instead of
|