wings-plugin-ui 0.0.0-stage → 0.1.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 (6) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +179 -2
  3. package/package.json +44 -4
  4. package/plugin-ui.css +1292 -0
  5. package/ui.d.ts +276 -0
  6. package/ui.js +578 -0
package/plugin-ui.css ADDED
@@ -0,0 +1,1292 @@
1
+ /* Wings base styles for plugin pages, loaded before the page's own CSS. Use the variables to match Wings,
2
+ and the plain elements (button, input, select) already look native. */
3
+
4
+ /* Everything here sits in cascade layers, below any CSS of a plugin's own: Tailwind, a stylesheet, a style attribute,
5
+ layered or not, wins over Wings' defaults without a fight over specificity. The order is declared here, first, with
6
+ Tailwind's layer names in it: wings-base (the plain elements) is under Tailwind's preflight, which resets them, and
7
+ wings-components (what wings-plugin-ui renders) is over it, so the SDK's components keep their look next to a
8
+ reset. Tailwind's components and utilities, and any unlayered CSS, are over both. */
9
+ @layer wings-base, theme, base, wings-components, components, utilities;
10
+
11
+ @layer wings-base {
12
+ :root {
13
+ color-scheme: dark;
14
+ --wings-bg: transparent;
15
+ --wings-surface: rgb(255 255 255 / 0.04);
16
+ --wings-fg: #ece9e4;
17
+ --wings-muted: #9d978f;
18
+ --wings-hover: rgb(255 255 255 / 0.06);
19
+ --wings-pressed: rgb(255 255 255 / 0.1);
20
+ --wings-hairline: rgb(255 255 255 / 0.07);
21
+ --wings-hairline-strong: rgb(255 255 255 / 0.16);
22
+ --wings-input: rgb(255 255 255 / 0.06);
23
+ --wings-ring: rgb(255 255 255 / 0.32);
24
+ --wings-primary: #ece9e4;
25
+ --wings-primary-fg: #17151a;
26
+ --wings-info: #9cc0ff;
27
+ --wings-success: #7dd3a7;
28
+ --wings-warning: #e9b65f;
29
+ --wings-danger: #f2786a;
30
+ --wings-radius: 10px;
31
+ --wings-font: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, Ubuntu, sans-serif;
32
+ --wings-mono: ui-monospace, "SF Mono", Menlo, monospace;
33
+ }
34
+
35
+ *,
36
+ *::before,
37
+ *::after {
38
+ box-sizing: border-box;
39
+ }
40
+
41
+ html,
42
+ body {
43
+ margin: 0;
44
+ background: var(--wings-bg);
45
+ color: var(--wings-fg);
46
+ }
47
+ /* The text size goes on body, not html: the root stays the browser's 16px, so a rem means what it does everywhere. */
48
+ body {
49
+ font: 13px/1.45 var(--wings-font);
50
+ -webkit-font-smoothing: antialiased;
51
+ }
52
+
53
+ h1,
54
+ h2,
55
+ h3,
56
+ p {
57
+ margin: 0;
58
+ }
59
+
60
+ button,
61
+ input,
62
+ select,
63
+ textarea {
64
+ font: inherit;
65
+ color: inherit;
66
+ }
67
+
68
+ button {
69
+ display: inline-flex;
70
+ align-items: center;
71
+ justify-content: center;
72
+ gap: 6px;
73
+ height: 30px;
74
+ padding: 0 12px;
75
+ border: 1px solid var(--wings-hairline-strong);
76
+ border-radius: 8px;
77
+ background: var(--wings-input);
78
+ font-weight: 500;
79
+ cursor: pointer;
80
+ transition:
81
+ background-color 150ms,
82
+ transform 100ms;
83
+ }
84
+ button:hover {
85
+ background: var(--wings-pressed);
86
+ }
87
+ button:active {
88
+ transform: scale(0.97);
89
+ }
90
+ button:disabled {
91
+ opacity: 0.5;
92
+ cursor: default;
93
+ transform: none;
94
+ }
95
+ button.primary {
96
+ border-color: transparent;
97
+ background: var(--wings-primary);
98
+ color: var(--wings-primary-fg);
99
+ }
100
+ button.primary:hover {
101
+ background: color-mix(in srgb, var(--wings-primary) 85%, transparent);
102
+ }
103
+ button.danger {
104
+ border-color: transparent;
105
+ background: color-mix(in srgb, var(--wings-danger) 85%, transparent);
106
+ color: #1b0f0d;
107
+ }
108
+ button.ghost {
109
+ border-color: transparent;
110
+ background: transparent;
111
+ }
112
+ button.ghost:hover {
113
+ background: var(--wings-hover);
114
+ }
115
+
116
+ input,
117
+ select,
118
+ textarea {
119
+ width: 100%;
120
+ height: 32px;
121
+ padding: 0 10px;
122
+ border: 1px solid var(--wings-hairline-strong);
123
+ border-radius: 8px;
124
+ background: var(--wings-input);
125
+ outline: none;
126
+ }
127
+ textarea {
128
+ height: auto;
129
+ padding: 8px 10px;
130
+ }
131
+ input::placeholder,
132
+ textarea::placeholder {
133
+ color: var(--wings-muted);
134
+ }
135
+
136
+ :focus-visible {
137
+ outline: 2px solid var(--wings-ring);
138
+ outline-offset: 1px;
139
+ }
140
+
141
+ .muted {
142
+ color: var(--wings-muted);
143
+ }
144
+
145
+ .card {
146
+ border: 1px solid var(--wings-hairline);
147
+ border-radius: var(--wings-radius);
148
+ background: var(--wings-surface);
149
+ }
150
+
151
+ @media (prefers-reduced-motion: reduce) {
152
+ * {
153
+ transition: none !important;
154
+ animation: none !important;
155
+ }
156
+ }
157
+ }
158
+
159
+ /* The classes wings-plugin-ui renders, a public contract: the names and data attributes stay, and Wings may
160
+ restyle them. The interactive components are Base UI's, drawn as the app's own shadcn/ui ones. */
161
+
162
+ @layer wings-components {
163
+
164
+ /* Raised layers, like a list of suggestions or a menu, as Wings' own menus draw them, and the controls as the app's
165
+ own shadcn/ui ones do. */
166
+ :root {
167
+ --wings-menu-bg: rgb(32 29 35);
168
+ --wings-menu-ring: rgb(236 233 228 / 0.1);
169
+ --wings-menu-active: rgb(255 255 255 / 0.08);
170
+ --wings-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
171
+ --wings-border: rgb(255 255 255 / 0.08);
172
+ --wings-control-border: rgb(255 255 255 / 0.1);
173
+ --wings-control-bg: rgb(255 255 255 / 0.03);
174
+ --wings-control-bg-hover: rgb(255 255 255 / 0.05);
175
+ --wings-secondary: rgb(255 255 255 / 0.08);
176
+ --wings-ring-soft: rgb(255 255 255 / 0.16);
177
+ --wings-radius-sm: calc(var(--wings-radius) * 0.6);
178
+ --wings-radius-md: calc(var(--wings-radius) * 0.8);
179
+ }
180
+
181
+ /* The page's place: a sidebar, popover, view (modal, window or tab) or settings page. */
182
+ .w-shell {
183
+ display: flex;
184
+ flex-direction: column;
185
+ gap: 10px;
186
+ padding: 14px;
187
+ }
188
+ .w-shell[data-place="popover"] {
189
+ gap: 12px;
190
+ padding: 12px 14px 14px;
191
+ }
192
+ .w-shell[data-place="settings"] {
193
+ gap: 12px;
194
+ }
195
+ /* A page that lays itself out edge to edge, like a canvas, fills the frame and scrolls on its own. */
196
+ .w-shell[data-fill] {
197
+ height: 100vh;
198
+ gap: 0;
199
+ padding: 0;
200
+ overflow: hidden;
201
+ }
202
+
203
+ /* Layout. */
204
+ .w-row {
205
+ display: flex;
206
+ align-items: center;
207
+ gap: 8px;
208
+ min-width: 0;
209
+ }
210
+ .w-stack {
211
+ display: flex;
212
+ flex-direction: column;
213
+ gap: 8px;
214
+ min-width: 0;
215
+ }
216
+ :is(ul, ol):is(.w-row, .w-stack, .w-card) {
217
+ margin: 0;
218
+ list-style: none;
219
+ }
220
+ :is(ul, ol):is(.w-row, .w-stack) {
221
+ padding: 0;
222
+ }
223
+ :is(.w-row, .w-stack)[data-gap="none"] {
224
+ gap: 0;
225
+ }
226
+ :is(.w-row, .w-stack)[data-gap="xs"] {
227
+ gap: 2px;
228
+ }
229
+ :is(.w-row, .w-stack)[data-gap="s"] {
230
+ gap: 4px;
231
+ }
232
+ :is(.w-row, .w-stack)[data-gap="l"] {
233
+ gap: 12px;
234
+ }
235
+ .w-row[data-align="start"] {
236
+ align-items: flex-start;
237
+ }
238
+ .w-row[data-align="baseline"] {
239
+ align-items: baseline;
240
+ }
241
+ .w-row[data-justify="end"] {
242
+ justify-content: flex-end;
243
+ }
244
+ .w-row[data-justify="between"] {
245
+ justify-content: space-between;
246
+ }
247
+ .w-row[data-wrap] {
248
+ flex-wrap: wrap;
249
+ }
250
+ :is(.w-row, .w-stack, .w-text)[data-grow] {
251
+ flex: 1;
252
+ min-width: 0;
253
+ }
254
+
255
+ /* Text. */
256
+ .w-text[data-tone="muted"] {
257
+ color: var(--wings-muted);
258
+ }
259
+ .w-text[data-tone="info"] {
260
+ color: var(--wings-info);
261
+ }
262
+ .w-text[data-tone="success"] {
263
+ color: var(--wings-success);
264
+ }
265
+ .w-text[data-tone="warn"] {
266
+ color: var(--wings-warning);
267
+ }
268
+ .w-text[data-tone="danger"] {
269
+ color: var(--wings-danger);
270
+ }
271
+ .w-text[data-size="small"] {
272
+ font-size: 12px;
273
+ }
274
+ .w-text[data-size="tiny"] {
275
+ font-size: 11px;
276
+ }
277
+ .w-text[data-weight="medium"] {
278
+ font-weight: 500;
279
+ }
280
+ .w-text[data-weight="strong"] {
281
+ font-weight: 600;
282
+ }
283
+ .w-text[data-mono] {
284
+ font-family: var(--wings-mono);
285
+ }
286
+ .w-text[data-numeric] {
287
+ font-variant-numeric: tabular-nums;
288
+ }
289
+ .w-text[data-truncate] {
290
+ display: block;
291
+ overflow: hidden;
292
+ text-overflow: ellipsis;
293
+ white-space: nowrap;
294
+ }
295
+ .w-text[data-clamp] {
296
+ display: -webkit-box;
297
+ -webkit-line-clamp: 3;
298
+ -webkit-box-orient: vertical;
299
+ overflow: hidden;
300
+ }
301
+ :is(.w-row, .w-list-item, .w-list-button) > .w-text[data-truncate] {
302
+ min-width: 0;
303
+ }
304
+
305
+ /* A page's or a card's title. */
306
+ .w-title {
307
+ font-size: 14px;
308
+ font-weight: 600;
309
+ line-height: 1.35;
310
+ overflow-wrap: anywhere;
311
+ }
312
+ .w-title[data-size="large"] {
313
+ font-size: 15px;
314
+ }
315
+ /* A title cut to one line with an ellipsis, like Text truncate. */
316
+ .w-title[data-truncate] {
317
+ overflow: hidden;
318
+ text-overflow: ellipsis;
319
+ white-space: nowrap;
320
+ }
321
+ :is(.w-row, .w-list-item, .w-list-button) > .w-title[data-truncate] {
322
+ min-width: 0;
323
+ }
324
+
325
+ /* A section heading, with its controls on the right. */
326
+ .w-heading {
327
+ margin-top: 6px;
328
+ font-size: 12px;
329
+ font-weight: 500;
330
+ color: var(--wings-muted);
331
+ }
332
+ .w-heading-row {
333
+ display: flex;
334
+ align-items: center;
335
+ gap: 8px;
336
+ min-width: 0;
337
+ }
338
+ .w-heading-row > .w-heading {
339
+ flex: 1;
340
+ min-width: 0;
341
+ }
342
+
343
+ /* A card, and the quieter one for a summary line. */
344
+ .w-card {
345
+ display: flex;
346
+ flex-direction: column;
347
+ gap: 10px;
348
+ min-width: 0;
349
+ padding: 10px 12px;
350
+ border: 1px solid var(--wings-hairline);
351
+ border-radius: var(--wings-radius);
352
+ background: var(--wings-surface);
353
+ }
354
+ .w-card[data-size="compact"] {
355
+ gap: 6px;
356
+ padding: 8px 10px;
357
+ font-size: 12px;
358
+ }
359
+
360
+ /* A labelled bar, and the bar on its own. --w-value is how full it is. */
361
+ .w-gauge {
362
+ display: grid;
363
+ grid-template-columns: 1fr auto;
364
+ gap: 4px 8px;
365
+ font-size: 12px;
366
+ }
367
+ .w-gauge-value {
368
+ font-variant-numeric: tabular-nums;
369
+ font-weight: 500;
370
+ }
371
+ .w-gauge-note {
372
+ grid-column: 1 / -1;
373
+ color: var(--wings-muted);
374
+ }
375
+ .w-meter {
376
+ grid-column: 1 / -1;
377
+ height: 4px;
378
+ border-radius: 2px;
379
+ background: var(--wings-hairline-strong);
380
+ overflow: hidden;
381
+ }
382
+ .w-meter-fill {
383
+ display: block;
384
+ width: var(--w-value, 0%);
385
+ height: 100%;
386
+ border-radius: 2px;
387
+ background: var(--wings-info);
388
+ }
389
+ .w-meter[data-tone="warn"] > .w-meter-fill {
390
+ background: var(--wings-warning);
391
+ }
392
+ .w-meter[data-tone="danger"] > .w-meter-fill {
393
+ background: var(--wings-danger);
394
+ }
395
+
396
+ /* A row of small figures, label over value. */
397
+ .w-stats {
398
+ display: grid;
399
+ grid-auto-flow: column;
400
+ grid-auto-columns: minmax(0, 1fr);
401
+ gap: 8px;
402
+ margin: 0;
403
+ padding: 10px 12px;
404
+ border: 1px solid var(--wings-hairline);
405
+ border-radius: var(--wings-radius);
406
+ background: var(--wings-surface);
407
+ }
408
+ .w-stat-label {
409
+ font-size: 11px;
410
+ color: var(--wings-muted);
411
+ }
412
+ .w-stat-value {
413
+ margin: 2px 0 0;
414
+ font-weight: 500;
415
+ overflow: hidden;
416
+ text-overflow: ellipsis;
417
+ white-space: nowrap;
418
+ }
419
+ .w-stat-value[data-tone="warn"] {
420
+ color: var(--wings-warning);
421
+ }
422
+ .w-stat-value[data-tone="danger"] {
423
+ color: var(--wings-danger);
424
+ }
425
+
426
+ /* Label and value pairs, like a work item's fields. */
427
+ .w-props {
428
+ display: flex;
429
+ flex-direction: column;
430
+ gap: 6px;
431
+ margin: 0;
432
+ }
433
+ .w-prop {
434
+ display: grid;
435
+ grid-template-columns: 92px minmax(0, 1fr);
436
+ gap: 8px;
437
+ align-items: start;
438
+ font-size: 12px;
439
+ line-height: 22px;
440
+ }
441
+ .w-prop-label {
442
+ color: var(--wings-muted);
443
+ }
444
+ .w-prop-value {
445
+ min-width: 0;
446
+ margin: 0;
447
+ overflow-wrap: anywhere;
448
+ }
449
+
450
+ /* Lists: rows that light up under the pointer, or rows that are a button. */
451
+ .w-list {
452
+ display: flex;
453
+ flex-direction: column;
454
+ margin: 0;
455
+ padding: 0;
456
+ list-style: none;
457
+ font-size: 12px;
458
+ }
459
+ .w-list-item {
460
+ display: flex;
461
+ align-items: center;
462
+ gap: 8px;
463
+ min-width: 0;
464
+ padding: 4px 6px;
465
+ border-radius: 6px;
466
+ }
467
+ .w-list[data-density="compact"] > .w-list-item {
468
+ padding: 0 6px;
469
+ }
470
+ .w-list-item[data-align="start"] {
471
+ align-items: flex-start;
472
+ }
473
+ .w-list-item:hover {
474
+ background: var(--wings-hover);
475
+ }
476
+ .w-list-item:has(> .w-list-button) {
477
+ display: block;
478
+ padding: 0;
479
+ }
480
+ .w-list-item:has(> .w-list-button):hover {
481
+ background: none;
482
+ }
483
+ .w-list-button {
484
+ display: flex;
485
+ align-items: center;
486
+ justify-content: flex-start;
487
+ gap: 8px;
488
+ width: 100%;
489
+ min-width: 0;
490
+ height: auto;
491
+ padding: 5px 6px;
492
+ border: 0;
493
+ border-radius: 6px;
494
+ background: none;
495
+ font-size: inherit;
496
+ font-weight: inherit;
497
+ text-align: left;
498
+ }
499
+ .w-list-button[data-align="baseline"] {
500
+ align-items: baseline;
501
+ }
502
+ .w-list-button:hover {
503
+ background: var(--wings-hover);
504
+ }
505
+ .w-list-button:active {
506
+ transform: none;
507
+ }
508
+
509
+ /* Status: a dot, a spinner and a badge. */
510
+ .w-dot {
511
+ flex: none;
512
+ width: 6px;
513
+ height: 6px;
514
+ border-radius: 50%;
515
+ background: var(--wings-success);
516
+ }
517
+ .w-dot[data-tone="muted"] {
518
+ background: var(--wings-muted);
519
+ }
520
+ .w-dot[data-tone="info"] {
521
+ background: var(--wings-info);
522
+ }
523
+ .w-dot[data-tone="warn"] {
524
+ background: var(--wings-warning);
525
+ }
526
+ .w-dot[data-tone="danger"] {
527
+ background: var(--wings-danger);
528
+ }
529
+ /* A colour from the plugin's data, like a work item type's, in --w-color. */
530
+ .w-dot[data-color] {
531
+ background: var(--w-color);
532
+ }
533
+ /* Lucide's loader-circle, as the app's Spinner draws it: in the colour of the text around it. */
534
+ .w-spinner {
535
+ flex: none;
536
+ width: 16px;
537
+ height: 16px;
538
+ background: currentColor;
539
+ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a9 9 0 1 1-6.219-8.56'/%3E%3C/svg%3E") center / contain no-repeat;
540
+ mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12a9 9 0 1 1-6.219-8.56'/%3E%3C/svg%3E") center / contain no-repeat;
541
+ animation: w-spin 1s linear infinite;
542
+ }
543
+ .w-spinner[data-size="large"] {
544
+ width: 24px;
545
+ height: 24px;
546
+ }
547
+ @keyframes w-spin {
548
+ to {
549
+ transform: rotate(360deg);
550
+ }
551
+ }
552
+ /* A badge, as the app draws it. 'data-tone' is Wings' own: a colour that says something. */
553
+ .w-badge {
554
+ display: inline-flex;
555
+ flex: none;
556
+ align-items: center;
557
+ justify-content: center;
558
+ gap: 4px;
559
+ width: fit-content;
560
+ max-width: 100%;
561
+ height: 20px;
562
+ padding: 2px 8px;
563
+ overflow: hidden;
564
+ border: 1px solid transparent;
565
+ border-radius: calc(var(--wings-radius) * 2.6);
566
+ background: var(--wings-primary);
567
+ color: var(--wings-primary-fg);
568
+ font-size: 12px;
569
+ font-weight: 500;
570
+ line-height: 1.33;
571
+ text-overflow: ellipsis;
572
+ white-space: nowrap;
573
+ }
574
+ .w-badge[data-variant="secondary"] {
575
+ background: var(--wings-secondary);
576
+ color: var(--wings-fg);
577
+ }
578
+ .w-badge[data-variant="destructive"] {
579
+ background: color-mix(in srgb, var(--wings-danger) 20%, transparent);
580
+ color: var(--wings-danger);
581
+ }
582
+ .w-badge[data-variant="outline"] {
583
+ border-color: var(--wings-border);
584
+ background: transparent;
585
+ color: var(--wings-fg);
586
+ }
587
+ .w-badge[data-variant="ghost"] {
588
+ background: transparent;
589
+ color: var(--wings-fg);
590
+ }
591
+ .w-badge[data-variant="ghost"]:hover {
592
+ background: var(--wings-hover);
593
+ color: var(--wings-muted);
594
+ }
595
+ .w-badge[data-variant="link"] {
596
+ background: transparent;
597
+ color: var(--wings-primary);
598
+ text-underline-offset: 4px;
599
+ }
600
+ .w-badge[data-variant="link"]:hover {
601
+ text-decoration: underline;
602
+ }
603
+ .w-badge[data-tone="info"] {
604
+ --w-tone: var(--wings-info);
605
+ }
606
+ .w-badge[data-tone="success"] {
607
+ --w-tone: var(--wings-success);
608
+ }
609
+ .w-badge[data-tone="warn"] {
610
+ --w-tone: var(--wings-warning);
611
+ }
612
+ .w-badge[data-tone="danger"] {
613
+ --w-tone: var(--wings-danger);
614
+ }
615
+ .w-badge[data-tone]:not([data-variant="outline"]) {
616
+ background: color-mix(in srgb, var(--w-tone) 20%, transparent);
617
+ color: var(--w-tone);
618
+ }
619
+ .w-badge[data-tone][data-variant="outline"] {
620
+ border-color: color-mix(in srgb, var(--w-tone) 45%, transparent);
621
+ color: var(--w-tone);
622
+ }
623
+
624
+ /* Nothing to show yet, or nothing at all. */
625
+ .w-loading {
626
+ color: var(--wings-muted);
627
+ }
628
+ .w-empty {
629
+ display: flex;
630
+ flex-direction: column;
631
+ align-items: center;
632
+ justify-content: center;
633
+ gap: 16px;
634
+ min-width: 0;
635
+ padding: 24px;
636
+ text-align: center;
637
+ text-wrap: balance;
638
+ }
639
+ .w-empty-header {
640
+ display: flex;
641
+ flex-direction: column;
642
+ align-items: center;
643
+ gap: 8px;
644
+ max-width: 384px;
645
+ }
646
+ .w-empty-title {
647
+ font-size: 14px;
648
+ font-weight: 500;
649
+ letter-spacing: -0.025em;
650
+ }
651
+ .w-empty-description {
652
+ font-size: 14px;
653
+ line-height: 1.625;
654
+ color: var(--wings-muted);
655
+ }
656
+
657
+ /* Buttons, as the app's are: 'data-variant' is default, secondary, outline, ghost, destructive or link; 'data-size' is
658
+ default, xs, sm, lg, icon, icon-xs, icon-sm or icon-lg. */
659
+ .w-button {
660
+ display: inline-flex;
661
+ flex-shrink: 0;
662
+ align-items: center;
663
+ justify-content: center;
664
+ gap: 6px;
665
+ height: 32px;
666
+ padding: 0 10px;
667
+ border: 1px solid transparent;
668
+ border-radius: var(--wings-radius);
669
+ background: var(--wings-primary);
670
+ background-clip: padding-box;
671
+ color: var(--wings-primary-fg);
672
+ font-size: 14px;
673
+ font-weight: 500;
674
+ line-height: 1.43;
675
+ white-space: nowrap;
676
+ user-select: none;
677
+ cursor: pointer;
678
+ outline: none;
679
+ transition:
680
+ background-color 150ms,
681
+ border-color 150ms,
682
+ box-shadow 150ms,
683
+ color 150ms,
684
+ transform 100ms;
685
+ }
686
+ .w-button[data-variant="default"]:hover {
687
+ background: color-mix(in srgb, var(--wings-primary) 80%, transparent);
688
+ }
689
+ .w-button[data-variant="secondary"] {
690
+ background: var(--wings-secondary);
691
+ color: var(--wings-fg);
692
+ }
693
+ .w-button[data-variant="secondary"]:hover {
694
+ background: color-mix(in srgb, var(--wings-secondary), var(--wings-fg) 5%);
695
+ }
696
+ .w-button[data-variant="outline"] {
697
+ border-color: var(--wings-control-border);
698
+ background: var(--wings-control-bg);
699
+ color: var(--wings-fg);
700
+ }
701
+ .w-button[data-variant="outline"]:hover {
702
+ background: var(--wings-control-bg-hover);
703
+ }
704
+ .w-button[data-variant="outline"][aria-expanded="true"] {
705
+ background: var(--wings-hover);
706
+ }
707
+ .w-button[data-variant="ghost"] {
708
+ background: transparent;
709
+ color: var(--wings-fg);
710
+ }
711
+ .w-button[data-variant="ghost"]:hover {
712
+ background: color-mix(in srgb, var(--wings-hover) 50%, transparent);
713
+ }
714
+ .w-button[data-variant="ghost"][aria-expanded="true"] {
715
+ background: var(--wings-hover);
716
+ }
717
+ .w-button[data-variant="destructive"] {
718
+ background: color-mix(in srgb, var(--wings-danger) 20%, transparent);
719
+ color: var(--wings-danger);
720
+ }
721
+ .w-button[data-variant="destructive"]:hover {
722
+ background: color-mix(in srgb, var(--wings-danger) 30%, transparent);
723
+ }
724
+ .w-button[data-variant="link"] {
725
+ background: transparent;
726
+ color: var(--wings-primary);
727
+ text-underline-offset: 4px;
728
+ }
729
+ .w-button[data-variant="link"]:hover {
730
+ background: transparent;
731
+ text-decoration: underline;
732
+ }
733
+ .w-button[data-size="xs"] {
734
+ gap: 4px;
735
+ height: 24px;
736
+ padding: 0 8px;
737
+ border-radius: var(--wings-radius-md);
738
+ font-size: 12px;
739
+ }
740
+ .w-button[data-size="sm"] {
741
+ gap: 4px;
742
+ height: 28px;
743
+ padding: 0 10px;
744
+ border-radius: var(--wings-radius-md);
745
+ font-size: 12.8px;
746
+ }
747
+ .w-button[data-size="lg"] {
748
+ height: 36px;
749
+ }
750
+ .w-button[data-size="icon"],
751
+ .w-button[data-size="icon-xs"],
752
+ .w-button[data-size="icon-sm"],
753
+ .w-button[data-size="icon-lg"] {
754
+ padding: 0;
755
+ width: 32px;
756
+ }
757
+ .w-button[data-size="icon-xs"] {
758
+ width: 24px;
759
+ height: 24px;
760
+ border-radius: var(--wings-radius-md);
761
+ }
762
+ .w-button[data-size="icon-sm"] {
763
+ width: 28px;
764
+ height: 28px;
765
+ border-radius: var(--wings-radius-md);
766
+ }
767
+ .w-button[data-size="icon-lg"] {
768
+ width: 36px;
769
+ height: 36px;
770
+ }
771
+ .w-button:active {
772
+ transform: none;
773
+ }
774
+ .w-button:active:not([aria-haspopup], [aria-disabled="true"]) {
775
+ transform: translateY(1px);
776
+ }
777
+ /* Busy: it keeps focus, unlike a disabled button, and looks like one. */
778
+ .w-button[aria-disabled="true"],
779
+ .w-button:disabled {
780
+ opacity: 0.5;
781
+ }
782
+ .w-button[aria-disabled="true"] {
783
+ cursor: default;
784
+ }
785
+ .w-button:disabled {
786
+ pointer-events: none;
787
+ }
788
+ .w-button[data-size="xs"] .w-spinner {
789
+ width: 12px;
790
+ height: 12px;
791
+ }
792
+ .w-button[data-size="sm"] .w-spinner {
793
+ width: 14px;
794
+ height: 14px;
795
+ }
796
+
797
+ /* Chips, the app's small toggle: a quiet button, and a group of them that filters what shows. */
798
+ .w-chips {
799
+ display: flex;
800
+ align-items: center;
801
+ gap: 8px;
802
+ min-width: 0;
803
+ }
804
+ .w-chip {
805
+ display: inline-flex;
806
+ flex-shrink: 0;
807
+ align-items: center;
808
+ justify-content: center;
809
+ gap: 4px;
810
+ min-width: 28px;
811
+ height: 28px;
812
+ padding: 0 10px;
813
+ border: 1px solid transparent;
814
+ border-radius: var(--wings-radius-md);
815
+ background: transparent;
816
+ color: var(--wings-fg);
817
+ font-size: 12.8px;
818
+ font-weight: 500;
819
+ white-space: nowrap;
820
+ cursor: pointer;
821
+ outline: none;
822
+ transition:
823
+ background-color 150ms,
824
+ border-color 150ms,
825
+ box-shadow 150ms;
826
+ }
827
+ .w-chip:hover,
828
+ .w-chip[aria-pressed="true"] {
829
+ background: var(--wings-hover);
830
+ }
831
+ .w-chip:active {
832
+ transform: none;
833
+ }
834
+ .w-chip:disabled {
835
+ opacity: 0.5;
836
+ pointer-events: none;
837
+ }
838
+
839
+ /* Tabs over a panel. 'data-variant' is default, a pill in a tray, or line, an underline. */
840
+ .w-tabs-root {
841
+ display: flex;
842
+ flex-direction: column;
843
+ gap: 8px;
844
+ min-width: 0;
845
+ }
846
+ .w-tabs {
847
+ position: relative;
848
+ display: inline-flex;
849
+ align-items: center;
850
+ justify-content: center;
851
+ gap: 0;
852
+ width: fit-content;
853
+ height: 32px;
854
+ padding: 3px;
855
+ border: 0;
856
+ border-radius: var(--wings-radius);
857
+ background: var(--wings-hover);
858
+ color: var(--wings-muted);
859
+ }
860
+ .w-tabs[data-variant="line"] {
861
+ gap: 4px;
862
+ border-radius: 0;
863
+ background: transparent;
864
+ }
865
+ .w-tab {
866
+ position: relative;
867
+ display: inline-flex;
868
+ flex: 1;
869
+ align-items: center;
870
+ justify-content: center;
871
+ gap: 6px;
872
+ height: calc(100% - 1px);
873
+ padding: 2px 6px;
874
+ border: 1px solid transparent;
875
+ border-radius: var(--wings-radius-md);
876
+ background: transparent;
877
+ color: var(--wings-muted);
878
+ font-size: 14px;
879
+ font-weight: 500;
880
+ white-space: nowrap;
881
+ cursor: pointer;
882
+ outline: none;
883
+ transition:
884
+ background-color 150ms,
885
+ border-color 150ms,
886
+ box-shadow 150ms,
887
+ color 150ms;
888
+ }
889
+ .w-tab:hover {
890
+ background: transparent;
891
+ color: var(--wings-fg);
892
+ }
893
+ .w-tab:active {
894
+ transform: none;
895
+ }
896
+ .w-tab[data-active] {
897
+ border-color: var(--wings-control-border);
898
+ background: var(--wings-control-bg);
899
+ color: var(--wings-fg);
900
+ box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);
901
+ }
902
+ .w-tabs[data-variant="line"] > .w-tab[data-active] {
903
+ border-color: transparent;
904
+ background: transparent;
905
+ box-shadow: none;
906
+ }
907
+ .w-tab::after {
908
+ content: "";
909
+ position: absolute;
910
+ right: 0;
911
+ bottom: -5px;
912
+ left: 0;
913
+ height: 2px;
914
+ background: var(--wings-fg);
915
+ opacity: 0;
916
+ transition: opacity 150ms;
917
+ }
918
+ .w-tabs[data-variant="line"] > .w-tab[data-active]::after {
919
+ opacity: 1;
920
+ }
921
+ .w-tab-panel {
922
+ font-size: 14px;
923
+ outline: none;
924
+ }
925
+
926
+ /* Form fields, drawn as the app's: a 32px control with a faint fill, and a ring when focused. */
927
+ .w-field {
928
+ display: flex;
929
+ flex-direction: column;
930
+ gap: 8px;
931
+ }
932
+ .w-field-label {
933
+ display: flex;
934
+ align-items: center;
935
+ gap: 8px;
936
+ font-size: 14px;
937
+ font-weight: 500;
938
+ line-height: 1.375;
939
+ user-select: none;
940
+ }
941
+ .w-field[data-layout="inline"] > .w-field-row {
942
+ display: flex;
943
+ align-items: center;
944
+ gap: 8px;
945
+ min-width: 0;
946
+ }
947
+ .w-field-row > .w-field-label {
948
+ flex: none;
949
+ }
950
+ .w-field-group {
951
+ display: flex;
952
+ flex-direction: column;
953
+ gap: 12px;
954
+ min-width: 0;
955
+ margin: 0;
956
+ padding: 0;
957
+ border: 0;
958
+ }
959
+ .w-field-group > legend {
960
+ margin-bottom: 6px;
961
+ padding: 0;
962
+ }
963
+ .w-field-hint {
964
+ font-size: 14px;
965
+ line-height: 1.5;
966
+ color: var(--wings-muted);
967
+ }
968
+ .w-field-hint[data-tone="danger"] {
969
+ color: var(--wings-danger);
970
+ }
971
+ .w-input,
972
+ .w-select {
973
+ display: flex;
974
+ align-items: center;
975
+ width: 100%;
976
+ min-width: 0;
977
+ height: 32px;
978
+ padding: 4px 10px;
979
+ border: 1px solid var(--wings-control-border);
980
+ border-radius: var(--wings-radius);
981
+ background: var(--wings-control-bg);
982
+ color: inherit;
983
+ font-size: 14px;
984
+ font-weight: 400;
985
+ outline: none;
986
+ transition:
987
+ background-color 150ms,
988
+ border-color 150ms,
989
+ box-shadow 150ms;
990
+ }
991
+ .w-input::placeholder {
992
+ color: var(--wings-muted);
993
+ }
994
+ .w-input:disabled {
995
+ background: var(--wings-control-bg-hover);
996
+ opacity: 0.5;
997
+ cursor: not-allowed;
998
+ pointer-events: none;
999
+ }
1000
+ .w-select {
1001
+ justify-content: space-between;
1002
+ gap: 6px;
1003
+ padding: 0 8px 0 10px;
1004
+ text-align: left;
1005
+ white-space: nowrap;
1006
+ user-select: none;
1007
+ cursor: pointer;
1008
+ }
1009
+ .w-select:hover {
1010
+ background: var(--wings-control-bg-hover);
1011
+ }
1012
+ .w-select:active {
1013
+ transform: none;
1014
+ }
1015
+ .w-select[data-disabled] {
1016
+ opacity: 0.5;
1017
+ cursor: not-allowed;
1018
+ }
1019
+ .w-select[data-placeholder] {
1020
+ color: var(--wings-muted);
1021
+ }
1022
+ .w-select-value {
1023
+ flex: 1;
1024
+ min-width: 0;
1025
+ overflow: hidden;
1026
+ text-overflow: ellipsis;
1027
+ white-space: nowrap;
1028
+ }
1029
+ .w-select-icon {
1030
+ flex: none;
1031
+ width: 16px;
1032
+ height: 16px;
1033
+ color: var(--wings-muted);
1034
+ pointer-events: none;
1035
+ }
1036
+ .w-item-check {
1037
+ position: absolute;
1038
+ top: 50%;
1039
+ right: 8px;
1040
+ display: flex;
1041
+ width: 16px;
1042
+ height: 16px;
1043
+ margin-top: -8px;
1044
+ pointer-events: none;
1045
+ }
1046
+ .w-item-check > svg,
1047
+ .w-checkbox-indicator > svg {
1048
+ width: 100%;
1049
+ height: 100%;
1050
+ }
1051
+ .w-check {
1052
+ display: flex;
1053
+ align-items: center;
1054
+ gap: 8px;
1055
+ min-width: 0;
1056
+ font-size: 14px;
1057
+ line-height: 1.43;
1058
+ cursor: pointer;
1059
+ }
1060
+ .w-checkbox {
1061
+ display: inline-flex;
1062
+ flex: none;
1063
+ align-items: center;
1064
+ justify-content: center;
1065
+ width: 16px;
1066
+ height: 16px;
1067
+ margin: 0;
1068
+ padding: 0;
1069
+ border: 1px solid var(--wings-control-border);
1070
+ border-radius: 4px;
1071
+ background: var(--wings-control-bg);
1072
+ color: var(--wings-primary-fg);
1073
+ outline: none;
1074
+ transition:
1075
+ background-color 150ms,
1076
+ border-color 150ms,
1077
+ box-shadow 150ms;
1078
+ }
1079
+ .w-checkbox:hover {
1080
+ background: var(--wings-control-bg);
1081
+ }
1082
+ .w-checkbox:active {
1083
+ transform: none;
1084
+ }
1085
+ .w-checkbox[data-checked],
1086
+ .w-checkbox[data-checked]:hover,
1087
+ .w-checkbox[data-indeterminate],
1088
+ .w-checkbox[data-indeterminate]:hover {
1089
+ border-color: var(--wings-primary);
1090
+ background: var(--wings-primary);
1091
+ }
1092
+ .w-checkbox[data-disabled] {
1093
+ opacity: 0.5;
1094
+ cursor: not-allowed;
1095
+ }
1096
+ .w-checkbox-indicator {
1097
+ display: flex;
1098
+ width: 14px;
1099
+ height: 14px;
1100
+ }
1101
+ /* A control the page marked invalid, as the app marks one. */
1102
+ .w-input[aria-invalid="true"],
1103
+ .w-select[aria-invalid="true"],
1104
+ .w-checkbox[aria-invalid="true"] {
1105
+ border-color: color-mix(in srgb, var(--wings-danger) 50%, transparent);
1106
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--wings-danger) 40%, transparent);
1107
+ }
1108
+ :is(.w-button, .w-chip, .w-tab, .w-input, .w-select, .w-checkbox):focus-visible {
1109
+ border-color: var(--wings-ring);
1110
+ box-shadow: 0 0 0 3px var(--wings-ring-soft);
1111
+ outline: none;
1112
+ }
1113
+ .w-button[data-variant="destructive"]:focus-visible {
1114
+ border-color: color-mix(in srgb, var(--wings-danger) 40%, transparent);
1115
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--wings-danger) 40%, transparent);
1116
+ }
1117
+ .w-tab:focus-visible {
1118
+ outline: 1px solid var(--wings-ring);
1119
+ }
1120
+
1121
+ /* A field with suggestions under it, a select and a menu: raised lists of options, which Base UI puts in the page's
1122
+ body, positioned under what they belong to. */
1123
+ .w-combobox {
1124
+ min-width: 0;
1125
+ }
1126
+ .w-menu-root {
1127
+ display: inline-block;
1128
+ min-width: 0;
1129
+ }
1130
+ .w-positioner {
1131
+ z-index: 50;
1132
+ outline: none;
1133
+ }
1134
+ .w-popup {
1135
+ box-sizing: border-box;
1136
+ max-height: var(--available-height);
1137
+ overflow-x: hidden;
1138
+ overflow-y: auto;
1139
+ padding: 4px;
1140
+ border-radius: var(--wings-radius);
1141
+ background: var(--wings-menu-bg);
1142
+ color: var(--wings-fg);
1143
+ box-shadow:
1144
+ 0 0 0 1px var(--wings-menu-ring),
1145
+ var(--wings-shadow);
1146
+ outline: none;
1147
+ transform-origin: var(--transform-origin);
1148
+ transition:
1149
+ opacity 100ms,
1150
+ transform 100ms;
1151
+ }
1152
+ .w-popup[data-starting-style],
1153
+ .w-popup[data-ending-style] {
1154
+ opacity: 0;
1155
+ transform: scale(0.95);
1156
+ }
1157
+ /* A list that stays shown while it filters has no exit to animate. */
1158
+ .w-select-popup,
1159
+ .w-combobox-popup {
1160
+ transition: none;
1161
+ }
1162
+ .w-select-popup {
1163
+ min-width: max(144px, var(--anchor-width));
1164
+ }
1165
+ .w-combobox-popup {
1166
+ width: var(--anchor-width);
1167
+ max-height: min(var(--available-height), 252px);
1168
+ }
1169
+ .w-menu-popup {
1170
+ width: max-content;
1171
+ min-width: max(128px, var(--anchor-width));
1172
+ max-width: var(--available-width);
1173
+ }
1174
+ .w-select-item,
1175
+ .w-option,
1176
+ .w-menu-item {
1177
+ position: relative;
1178
+ display: flex;
1179
+ align-items: center;
1180
+ gap: 6px;
1181
+ padding: 4px 6px;
1182
+ border-radius: var(--wings-radius-md);
1183
+ font-size: 14px;
1184
+ cursor: default;
1185
+ user-select: none;
1186
+ outline: none;
1187
+ }
1188
+ .w-select-item {
1189
+ padding-right: 32px;
1190
+ }
1191
+ .w-option {
1192
+ align-items: baseline;
1193
+ gap: 8px;
1194
+ }
1195
+ .w-select-item[data-highlighted],
1196
+ .w-option[data-highlighted],
1197
+ .w-menu-item[data-highlighted] {
1198
+ background: var(--wings-menu-active);
1199
+ color: #f4f1ec;
1200
+ }
1201
+ .w-select-item[data-disabled],
1202
+ .w-menu-item[data-disabled] {
1203
+ opacity: 0.5;
1204
+ pointer-events: none;
1205
+ }
1206
+ .w-menu-item[data-variant="destructive"] {
1207
+ color: var(--wings-danger);
1208
+ }
1209
+ .w-menu-item[data-variant="destructive"][data-highlighted] {
1210
+ background: color-mix(in srgb, var(--wings-danger) 20%, transparent);
1211
+ color: var(--wings-danger);
1212
+ }
1213
+ .w-option-label {
1214
+ flex: 1;
1215
+ min-width: 0;
1216
+ overflow: hidden;
1217
+ text-overflow: ellipsis;
1218
+ white-space: nowrap;
1219
+ }
1220
+ .w-option-detail {
1221
+ flex: none;
1222
+ max-width: 45%;
1223
+ overflow: hidden;
1224
+ text-overflow: ellipsis;
1225
+ white-space: nowrap;
1226
+ font-size: 12px;
1227
+ color: var(--wings-muted);
1228
+ }
1229
+ .w-option-empty {
1230
+ padding: 8px 6px;
1231
+ text-align: center;
1232
+ font-size: 14px;
1233
+ color: var(--wings-muted);
1234
+ }
1235
+ .w-option-empty:empty {
1236
+ display: none;
1237
+ }
1238
+
1239
+ /* More behind a line that opens it. */
1240
+ .w-disclosure-trigger {
1241
+ display: flex;
1242
+ align-items: center;
1243
+ justify-content: flex-start;
1244
+ gap: 4px;
1245
+ width: auto;
1246
+ height: auto;
1247
+ padding: 4px 6px;
1248
+ border: 0;
1249
+ border-radius: 6px;
1250
+ background: none;
1251
+ color: var(--wings-muted);
1252
+ font-size: 12px;
1253
+ font-weight: 400;
1254
+ text-align: left;
1255
+ cursor: pointer;
1256
+ }
1257
+ .w-disclosure-trigger:hover {
1258
+ background: none;
1259
+ color: var(--wings-fg);
1260
+ }
1261
+ .w-disclosure-trigger:active {
1262
+ transform: none;
1263
+ }
1264
+ .w-disclosure-trigger::before {
1265
+ content: "";
1266
+ flex: none;
1267
+ width: 12px;
1268
+ height: 12px;
1269
+ background: currentColor;
1270
+ -webkit-mask-repeat: no-repeat;
1271
+ -webkit-mask-position: center;
1272
+ -webkit-mask-size: contain;
1273
+ mask-repeat: no-repeat;
1274
+ mask-position: center;
1275
+ mask-size: contain;
1276
+ -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E");
1277
+ mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9 18 6-6-6-6'/%3E%3C/svg%3E");
1278
+ transition: transform 150ms;
1279
+ }
1280
+ .w-disclosure-trigger[data-panel-open]::before {
1281
+ transform: rotate(90deg);
1282
+ }
1283
+ .w-disclosure-trigger:focus-visible {
1284
+ outline: 2px solid var(--wings-ring);
1285
+ }
1286
+
1287
+ @media (prefers-reduced-motion: reduce) {
1288
+ .w-spinner {
1289
+ animation: none;
1290
+ }
1291
+ }
1292
+ }