@wtfalch/design 0.8.0 → 0.9.1
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/README.md +18 -0
- package/dist/components/Button.js +8 -7
- package/dist/components/Callout.js +1 -1
- package/dist/components/Card.d.ts +1 -1
- package/dist/components/Card.js +21 -4
- package/dist/components/Command.js +2 -2
- package/dist/components/Empty.js +22 -1
- package/dist/components/Input.js +4 -4
- package/dist/components/Kbd.js +11 -1
- package/dist/components/Menu.js +3 -3
- package/dist/components/Modal.js +2 -2
- package/dist/components/Pagination.js +6 -1
- package/dist/components/Progress.js +8 -1
- package/dist/components/Rows.d.ts +1 -1
- package/dist/components/Rows.js +1 -1
- package/dist/components/Select.js +2 -2
- package/dist/components/Shell.js +11 -1
- package/dist/components/SizeGrid.js +18 -2
- package/dist/components/Stat.js +11 -2
- package/dist/components/Table.js +7 -1
- package/dist/components/Toast.js +1 -1
- package/dist/components/Tooltip.js +16 -2
- package/dist/components/Tour.js +1 -1
- package/dist/styles/index.css +1117 -418
- package/dist/tf.css +1117 -418
- package/dist/valet.css +1117 -418
- package/package.json +3 -1
package/dist/tf.css
CHANGED
|
@@ -278,7 +278,700 @@
|
|
|
278
278
|
*
|
|
279
279
|
* `scripts/copy-css.mjs` flattens these into one file at build time.
|
|
280
280
|
*/
|
|
281
|
-
/* ---- ./
|
|
281
|
+
/* ---- ./_tailwind.built.css ---- */
|
|
282
|
+
/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
283
|
+
@layer properties;
|
|
284
|
+
@layer theme, base, components, utilities;
|
|
285
|
+
@layer theme {
|
|
286
|
+
:root, :host {
|
|
287
|
+
--font-sans: var(--font-sans);
|
|
288
|
+
--font-mono: var(--font-mono);
|
|
289
|
+
--text-xs: var(--text-xs);
|
|
290
|
+
--text-sm: var(--text-sm);
|
|
291
|
+
--text-base: var(--text-base);
|
|
292
|
+
--text-lg: var(--text-lg);
|
|
293
|
+
--text-xl: var(--text-xl);
|
|
294
|
+
--font-weight-normal: var(--weight);
|
|
295
|
+
--tracking-normal: var(--tracking);
|
|
296
|
+
--radius-sm: var(--radius-sm);
|
|
297
|
+
--radius-md: var(--radius-md);
|
|
298
|
+
--radius-lg: var(--radius-lg);
|
|
299
|
+
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
|
300
|
+
--spacing-1: var(--space-1);
|
|
301
|
+
--spacing-2: var(--space-2);
|
|
302
|
+
--spacing-3: var(--space-3);
|
|
303
|
+
--spacing-4: var(--space-4);
|
|
304
|
+
--spacing-5: var(--space-5);
|
|
305
|
+
--spacing-6: var(--space-6);
|
|
306
|
+
--spacing-10: var(--space-10);
|
|
307
|
+
--text-2xs: var(--text-2xs);
|
|
308
|
+
--text-md: var(--text-md);
|
|
309
|
+
--font-weight-strong: var(--weight-strong);
|
|
310
|
+
--radius-pill: var(--radius-pill);
|
|
311
|
+
--color-text: var(--text);
|
|
312
|
+
--color-muted: var(--muted);
|
|
313
|
+
--color-border: var(--border);
|
|
314
|
+
--color-border-strong: var(--border-strong);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
@layer utilities {
|
|
318
|
+
.collapse {
|
|
319
|
+
visibility: collapse;
|
|
320
|
+
}
|
|
321
|
+
.invisible {
|
|
322
|
+
visibility: hidden;
|
|
323
|
+
}
|
|
324
|
+
.visible {
|
|
325
|
+
visibility: visible;
|
|
326
|
+
}
|
|
327
|
+
.sr-only {
|
|
328
|
+
position: absolute;
|
|
329
|
+
width: 1px;
|
|
330
|
+
height: 1px;
|
|
331
|
+
padding: 0;
|
|
332
|
+
margin: -1px;
|
|
333
|
+
overflow: hidden;
|
|
334
|
+
clip-path: inset(50%);
|
|
335
|
+
white-space: nowrap;
|
|
336
|
+
border-width: 0;
|
|
337
|
+
}
|
|
338
|
+
.absolute {
|
|
339
|
+
position: absolute;
|
|
340
|
+
}
|
|
341
|
+
.fixed {
|
|
342
|
+
position: fixed;
|
|
343
|
+
}
|
|
344
|
+
.relative {
|
|
345
|
+
position: relative;
|
|
346
|
+
}
|
|
347
|
+
.sticky {
|
|
348
|
+
position: sticky;
|
|
349
|
+
}
|
|
350
|
+
.z-\[60\] {
|
|
351
|
+
z-index: 60;
|
|
352
|
+
}
|
|
353
|
+
.container {
|
|
354
|
+
width: 100%;
|
|
355
|
+
@media (width >= 40rem) {
|
|
356
|
+
max-width: 40rem;
|
|
357
|
+
}
|
|
358
|
+
@media (width >= 48rem) {
|
|
359
|
+
max-width: 48rem;
|
|
360
|
+
}
|
|
361
|
+
@media (width >= 64rem) {
|
|
362
|
+
max-width: 64rem;
|
|
363
|
+
}
|
|
364
|
+
@media (width >= 80rem) {
|
|
365
|
+
max-width: 80rem;
|
|
366
|
+
}
|
|
367
|
+
@media (width >= 96rem) {
|
|
368
|
+
max-width: 96rem;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
.m-0 {
|
|
372
|
+
margin: 0px;
|
|
373
|
+
}
|
|
374
|
+
.m-auto {
|
|
375
|
+
margin: auto;
|
|
376
|
+
}
|
|
377
|
+
.mx-auto {
|
|
378
|
+
margin-inline: auto;
|
|
379
|
+
}
|
|
380
|
+
.mt-1 {
|
|
381
|
+
margin-top: var(--spacing-1);
|
|
382
|
+
}
|
|
383
|
+
.mb-3 {
|
|
384
|
+
margin-bottom: var(--spacing-3);
|
|
385
|
+
}
|
|
386
|
+
.ml-2 {
|
|
387
|
+
margin-left: var(--spacing-2);
|
|
388
|
+
}
|
|
389
|
+
.block {
|
|
390
|
+
display: block;
|
|
391
|
+
}
|
|
392
|
+
.contents {
|
|
393
|
+
display: contents;
|
|
394
|
+
}
|
|
395
|
+
.flex {
|
|
396
|
+
display: flex;
|
|
397
|
+
}
|
|
398
|
+
.grid {
|
|
399
|
+
display: grid;
|
|
400
|
+
}
|
|
401
|
+
.hidden {
|
|
402
|
+
display: none;
|
|
403
|
+
}
|
|
404
|
+
.inline {
|
|
405
|
+
display: inline;
|
|
406
|
+
}
|
|
407
|
+
.inline-flex {
|
|
408
|
+
display: inline-flex;
|
|
409
|
+
}
|
|
410
|
+
.table {
|
|
411
|
+
display: table;
|
|
412
|
+
}
|
|
413
|
+
.h-\[14px\] {
|
|
414
|
+
height: 14px;
|
|
415
|
+
}
|
|
416
|
+
.h-\[18px\] {
|
|
417
|
+
height: 18px;
|
|
418
|
+
}
|
|
419
|
+
.max-h-\[24rem\] {
|
|
420
|
+
max-height: 24rem;
|
|
421
|
+
}
|
|
422
|
+
.max-h-\[280px\] {
|
|
423
|
+
max-height: 280px;
|
|
424
|
+
}
|
|
425
|
+
.min-h-0 {
|
|
426
|
+
min-height: 0px;
|
|
427
|
+
}
|
|
428
|
+
.min-h-screen {
|
|
429
|
+
min-height: 100vh;
|
|
430
|
+
}
|
|
431
|
+
.w-\[18px\] {
|
|
432
|
+
width: 18px;
|
|
433
|
+
}
|
|
434
|
+
.w-full {
|
|
435
|
+
width: 100%;
|
|
436
|
+
}
|
|
437
|
+
.max-w-\[46ch\] {
|
|
438
|
+
max-width: 46ch;
|
|
439
|
+
}
|
|
440
|
+
.max-w-\[min\(20rem\,calc\(100vw-var\(--space-6\)\)\)\] {
|
|
441
|
+
max-width: min(20rem, calc(100vw - var(--space-6)));
|
|
442
|
+
}
|
|
443
|
+
.max-w-\[var\(--shell-measure\)\] {
|
|
444
|
+
max-width: var(--shell-measure);
|
|
445
|
+
}
|
|
446
|
+
.max-w-\[var\(--shell-measure-wide\)\] {
|
|
447
|
+
max-width: var(--shell-measure-wide);
|
|
448
|
+
}
|
|
449
|
+
.min-w-0 {
|
|
450
|
+
min-width: 0px;
|
|
451
|
+
}
|
|
452
|
+
.min-w-\[12rem\] {
|
|
453
|
+
min-width: 12rem;
|
|
454
|
+
}
|
|
455
|
+
.flex-1 {
|
|
456
|
+
flex: 1;
|
|
457
|
+
}
|
|
458
|
+
.flex-none {
|
|
459
|
+
flex: none;
|
|
460
|
+
}
|
|
461
|
+
.shrink {
|
|
462
|
+
flex-shrink: 1;
|
|
463
|
+
}
|
|
464
|
+
.grow {
|
|
465
|
+
flex-grow: 1;
|
|
466
|
+
}
|
|
467
|
+
.transform {
|
|
468
|
+
transform: var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,);
|
|
469
|
+
}
|
|
470
|
+
.cursor-help {
|
|
471
|
+
cursor: help;
|
|
472
|
+
}
|
|
473
|
+
.cursor-pointer {
|
|
474
|
+
cursor: pointer;
|
|
475
|
+
}
|
|
476
|
+
.resize {
|
|
477
|
+
resize: both;
|
|
478
|
+
}
|
|
479
|
+
.list-none {
|
|
480
|
+
list-style-type: none;
|
|
481
|
+
}
|
|
482
|
+
.flex-col {
|
|
483
|
+
flex-direction: column;
|
|
484
|
+
}
|
|
485
|
+
.flex-wrap {
|
|
486
|
+
flex-wrap: wrap;
|
|
487
|
+
}
|
|
488
|
+
.items-baseline {
|
|
489
|
+
align-items: baseline;
|
|
490
|
+
}
|
|
491
|
+
.items-center {
|
|
492
|
+
align-items: center;
|
|
493
|
+
}
|
|
494
|
+
.items-start {
|
|
495
|
+
align-items: flex-start;
|
|
496
|
+
}
|
|
497
|
+
.justify-between {
|
|
498
|
+
justify-content: space-between;
|
|
499
|
+
}
|
|
500
|
+
.justify-items-center {
|
|
501
|
+
justify-items: center;
|
|
502
|
+
}
|
|
503
|
+
.gap-0 {
|
|
504
|
+
gap: 0px;
|
|
505
|
+
}
|
|
506
|
+
.gap-1 {
|
|
507
|
+
gap: var(--spacing-1);
|
|
508
|
+
}
|
|
509
|
+
.gap-2 {
|
|
510
|
+
gap: var(--spacing-2);
|
|
511
|
+
}
|
|
512
|
+
.gap-3 {
|
|
513
|
+
gap: var(--spacing-3);
|
|
514
|
+
}
|
|
515
|
+
.gap-4 {
|
|
516
|
+
gap: var(--spacing-4);
|
|
517
|
+
}
|
|
518
|
+
.gap-\[var\(--border-width\)\] {
|
|
519
|
+
gap: var(--border-width);
|
|
520
|
+
}
|
|
521
|
+
.self-center {
|
|
522
|
+
align-self: center;
|
|
523
|
+
}
|
|
524
|
+
.truncate {
|
|
525
|
+
overflow: hidden;
|
|
526
|
+
text-overflow: ellipsis;
|
|
527
|
+
white-space: nowrap;
|
|
528
|
+
}
|
|
529
|
+
.overflow-auto {
|
|
530
|
+
overflow: auto;
|
|
531
|
+
}
|
|
532
|
+
.overflow-hidden {
|
|
533
|
+
overflow: hidden;
|
|
534
|
+
}
|
|
535
|
+
.overflow-x-auto {
|
|
536
|
+
overflow-x: auto;
|
|
537
|
+
}
|
|
538
|
+
.overflow-y-auto {
|
|
539
|
+
overflow-y: auto;
|
|
540
|
+
}
|
|
541
|
+
.overscroll-contain {
|
|
542
|
+
overscroll-behavior: contain;
|
|
543
|
+
}
|
|
544
|
+
.rounded {
|
|
545
|
+
border-radius: 0.25rem;
|
|
546
|
+
}
|
|
547
|
+
.rounded-full {
|
|
548
|
+
border-radius: calc(infinity * 1px);
|
|
549
|
+
}
|
|
550
|
+
.rounded-md {
|
|
551
|
+
border-radius: var(--radius-md);
|
|
552
|
+
}
|
|
553
|
+
.rounded-sm {
|
|
554
|
+
border-radius: var(--radius-sm);
|
|
555
|
+
}
|
|
556
|
+
.border {
|
|
557
|
+
border-style: var(--tw-border-style);
|
|
558
|
+
border-width: var(--border-width);
|
|
559
|
+
}
|
|
560
|
+
.border-b {
|
|
561
|
+
border-bottom-style: var(--tw-border-style);
|
|
562
|
+
border-bottom-width: var(--border-width);
|
|
563
|
+
}
|
|
564
|
+
.border-dashed {
|
|
565
|
+
--tw-border-style: dashed;
|
|
566
|
+
border-style: dashed;
|
|
567
|
+
}
|
|
568
|
+
.border-border {
|
|
569
|
+
border-color: var(--color-border);
|
|
570
|
+
}
|
|
571
|
+
.border-border-strong {
|
|
572
|
+
border-color: var(--color-border-strong);
|
|
573
|
+
}
|
|
574
|
+
.p-0 {
|
|
575
|
+
padding: 0px;
|
|
576
|
+
}
|
|
577
|
+
.p-1 {
|
|
578
|
+
padding: var(--spacing-1);
|
|
579
|
+
}
|
|
580
|
+
.p-2 {
|
|
581
|
+
padding: var(--spacing-2);
|
|
582
|
+
}
|
|
583
|
+
.p-4 {
|
|
584
|
+
padding: var(--spacing-4);
|
|
585
|
+
}
|
|
586
|
+
.px-4 {
|
|
587
|
+
padding-inline: var(--spacing-4);
|
|
588
|
+
}
|
|
589
|
+
.py-2 {
|
|
590
|
+
padding-block: var(--spacing-2);
|
|
591
|
+
}
|
|
592
|
+
.py-3 {
|
|
593
|
+
padding-block: var(--spacing-3);
|
|
594
|
+
}
|
|
595
|
+
.py-5 {
|
|
596
|
+
padding-block: var(--spacing-5);
|
|
597
|
+
}
|
|
598
|
+
.py-6 {
|
|
599
|
+
padding-block: var(--spacing-6);
|
|
600
|
+
}
|
|
601
|
+
.py-10 {
|
|
602
|
+
padding-block: var(--spacing-10);
|
|
603
|
+
}
|
|
604
|
+
.pb-2 {
|
|
605
|
+
padding-bottom: var(--spacing-2);
|
|
606
|
+
}
|
|
607
|
+
.pb-3 {
|
|
608
|
+
padding-bottom: var(--spacing-3);
|
|
609
|
+
}
|
|
610
|
+
.text-center {
|
|
611
|
+
text-align: center;
|
|
612
|
+
}
|
|
613
|
+
.font-mono {
|
|
614
|
+
font-family: var(--font-mono);
|
|
615
|
+
}
|
|
616
|
+
.text-2xs {
|
|
617
|
+
font-size: var(--text-2xs);
|
|
618
|
+
}
|
|
619
|
+
.text-lg {
|
|
620
|
+
font-size: var(--text-lg);
|
|
621
|
+
}
|
|
622
|
+
.text-sm {
|
|
623
|
+
font-size: var(--text-sm);
|
|
624
|
+
}
|
|
625
|
+
.text-xs {
|
|
626
|
+
font-size: var(--text-xs);
|
|
627
|
+
}
|
|
628
|
+
.leading-\[0\] {
|
|
629
|
+
--tw-leading: 0;
|
|
630
|
+
line-height: 0;
|
|
631
|
+
}
|
|
632
|
+
.leading-\[1\.5\] {
|
|
633
|
+
--tw-leading: 1.5;
|
|
634
|
+
line-height: 1.5;
|
|
635
|
+
}
|
|
636
|
+
.leading-\[1\.25\] {
|
|
637
|
+
--tw-leading: 1.25;
|
|
638
|
+
line-height: 1.25;
|
|
639
|
+
}
|
|
640
|
+
.leading-\[16px\] {
|
|
641
|
+
--tw-leading: 16px;
|
|
642
|
+
line-height: 16px;
|
|
643
|
+
}
|
|
644
|
+
.font-normal {
|
|
645
|
+
--tw-font-weight: var(--font-weight-normal);
|
|
646
|
+
font-weight: var(--font-weight-normal);
|
|
647
|
+
}
|
|
648
|
+
.font-strong {
|
|
649
|
+
--tw-font-weight: var(--font-weight-strong);
|
|
650
|
+
font-weight: var(--font-weight-strong);
|
|
651
|
+
}
|
|
652
|
+
.tracking-normal {
|
|
653
|
+
--tw-tracking: var(--tracking-normal);
|
|
654
|
+
letter-spacing: var(--tracking-normal);
|
|
655
|
+
}
|
|
656
|
+
.text-ellipsis {
|
|
657
|
+
text-overflow: ellipsis;
|
|
658
|
+
}
|
|
659
|
+
.whitespace-nowrap {
|
|
660
|
+
white-space: nowrap;
|
|
661
|
+
}
|
|
662
|
+
.text-muted {
|
|
663
|
+
color: var(--color-muted);
|
|
664
|
+
}
|
|
665
|
+
.text-text {
|
|
666
|
+
color: var(--color-text);
|
|
667
|
+
}
|
|
668
|
+
.lowercase {
|
|
669
|
+
text-transform: lowercase;
|
|
670
|
+
}
|
|
671
|
+
.normal-case {
|
|
672
|
+
text-transform: none;
|
|
673
|
+
}
|
|
674
|
+
.uppercase {
|
|
675
|
+
text-transform: uppercase;
|
|
676
|
+
}
|
|
677
|
+
.tabular-nums {
|
|
678
|
+
--tw-numeric-spacing: tabular-nums;
|
|
679
|
+
font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
|
|
680
|
+
}
|
|
681
|
+
.opacity-60 {
|
|
682
|
+
opacity: 60%;
|
|
683
|
+
}
|
|
684
|
+
.opacity-\[0\.55\] {
|
|
685
|
+
opacity: 0.55;
|
|
686
|
+
}
|
|
687
|
+
.shadow {
|
|
688
|
+
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
|
689
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
690
|
+
}
|
|
691
|
+
.ring {
|
|
692
|
+
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentcolor);
|
|
693
|
+
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
|
694
|
+
}
|
|
695
|
+
.outline {
|
|
696
|
+
outline-style: var(--tw-outline-style);
|
|
697
|
+
outline-width: 1px;
|
|
698
|
+
}
|
|
699
|
+
.blur {
|
|
700
|
+
--tw-blur: blur(8px);
|
|
701
|
+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
702
|
+
}
|
|
703
|
+
.sepia {
|
|
704
|
+
--tw-sepia: sepia(100%);
|
|
705
|
+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
706
|
+
}
|
|
707
|
+
.filter {
|
|
708
|
+
filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
|
|
709
|
+
}
|
|
710
|
+
.outline-none {
|
|
711
|
+
--tw-outline-style: none;
|
|
712
|
+
outline-style: none;
|
|
713
|
+
}
|
|
714
|
+
.\[font\:inherit\] {
|
|
715
|
+
font: inherit;
|
|
716
|
+
}
|
|
717
|
+
.surface-bg {
|
|
718
|
+
background: var(--bg);
|
|
719
|
+
}
|
|
720
|
+
.surface-panel {
|
|
721
|
+
background: var(--panel);
|
|
722
|
+
}
|
|
723
|
+
.disabled\:cursor-default:disabled {
|
|
724
|
+
cursor: default;
|
|
725
|
+
}
|
|
726
|
+
.disabled\:opacity-50:disabled {
|
|
727
|
+
opacity: 50%;
|
|
728
|
+
}
|
|
729
|
+
.\[\&\>p\]\:m-0 > p {
|
|
730
|
+
margin: 0px;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
@property --tw-rotate-x {
|
|
734
|
+
syntax: "*";
|
|
735
|
+
inherits: false;
|
|
736
|
+
}
|
|
737
|
+
@property --tw-rotate-y {
|
|
738
|
+
syntax: "*";
|
|
739
|
+
inherits: false;
|
|
740
|
+
}
|
|
741
|
+
@property --tw-rotate-z {
|
|
742
|
+
syntax: "*";
|
|
743
|
+
inherits: false;
|
|
744
|
+
}
|
|
745
|
+
@property --tw-skew-x {
|
|
746
|
+
syntax: "*";
|
|
747
|
+
inherits: false;
|
|
748
|
+
}
|
|
749
|
+
@property --tw-skew-y {
|
|
750
|
+
syntax: "*";
|
|
751
|
+
inherits: false;
|
|
752
|
+
}
|
|
753
|
+
@property --tw-border-style {
|
|
754
|
+
syntax: "*";
|
|
755
|
+
inherits: false;
|
|
756
|
+
initial-value: solid;
|
|
757
|
+
}
|
|
758
|
+
@property --tw-leading {
|
|
759
|
+
syntax: "*";
|
|
760
|
+
inherits: false;
|
|
761
|
+
}
|
|
762
|
+
@property --tw-font-weight {
|
|
763
|
+
syntax: "*";
|
|
764
|
+
inherits: false;
|
|
765
|
+
}
|
|
766
|
+
@property --tw-tracking {
|
|
767
|
+
syntax: "*";
|
|
768
|
+
inherits: false;
|
|
769
|
+
}
|
|
770
|
+
@property --tw-ordinal {
|
|
771
|
+
syntax: "*";
|
|
772
|
+
inherits: false;
|
|
773
|
+
}
|
|
774
|
+
@property --tw-slashed-zero {
|
|
775
|
+
syntax: "*";
|
|
776
|
+
inherits: false;
|
|
777
|
+
}
|
|
778
|
+
@property --tw-numeric-figure {
|
|
779
|
+
syntax: "*";
|
|
780
|
+
inherits: false;
|
|
781
|
+
}
|
|
782
|
+
@property --tw-numeric-spacing {
|
|
783
|
+
syntax: "*";
|
|
784
|
+
inherits: false;
|
|
785
|
+
}
|
|
786
|
+
@property --tw-numeric-fraction {
|
|
787
|
+
syntax: "*";
|
|
788
|
+
inherits: false;
|
|
789
|
+
}
|
|
790
|
+
@property --tw-shadow {
|
|
791
|
+
syntax: "*";
|
|
792
|
+
inherits: false;
|
|
793
|
+
initial-value: 0 0 #0000;
|
|
794
|
+
}
|
|
795
|
+
@property --tw-shadow-color {
|
|
796
|
+
syntax: "*";
|
|
797
|
+
inherits: false;
|
|
798
|
+
}
|
|
799
|
+
@property --tw-shadow-alpha {
|
|
800
|
+
syntax: "<percentage>";
|
|
801
|
+
inherits: false;
|
|
802
|
+
initial-value: 100%;
|
|
803
|
+
}
|
|
804
|
+
@property --tw-inset-shadow {
|
|
805
|
+
syntax: "*";
|
|
806
|
+
inherits: false;
|
|
807
|
+
initial-value: 0 0 #0000;
|
|
808
|
+
}
|
|
809
|
+
@property --tw-inset-shadow-color {
|
|
810
|
+
syntax: "*";
|
|
811
|
+
inherits: false;
|
|
812
|
+
}
|
|
813
|
+
@property --tw-inset-shadow-alpha {
|
|
814
|
+
syntax: "<percentage>";
|
|
815
|
+
inherits: false;
|
|
816
|
+
initial-value: 100%;
|
|
817
|
+
}
|
|
818
|
+
@property --tw-ring-color {
|
|
819
|
+
syntax: "*";
|
|
820
|
+
inherits: false;
|
|
821
|
+
}
|
|
822
|
+
@property --tw-ring-shadow {
|
|
823
|
+
syntax: "*";
|
|
824
|
+
inherits: false;
|
|
825
|
+
initial-value: 0 0 #0000;
|
|
826
|
+
}
|
|
827
|
+
@property --tw-inset-ring-color {
|
|
828
|
+
syntax: "*";
|
|
829
|
+
inherits: false;
|
|
830
|
+
}
|
|
831
|
+
@property --tw-inset-ring-shadow {
|
|
832
|
+
syntax: "*";
|
|
833
|
+
inherits: false;
|
|
834
|
+
initial-value: 0 0 #0000;
|
|
835
|
+
}
|
|
836
|
+
@property --tw-ring-inset {
|
|
837
|
+
syntax: "*";
|
|
838
|
+
inherits: false;
|
|
839
|
+
}
|
|
840
|
+
@property --tw-ring-offset-width {
|
|
841
|
+
syntax: "<length>";
|
|
842
|
+
inherits: false;
|
|
843
|
+
initial-value: 0px;
|
|
844
|
+
}
|
|
845
|
+
@property --tw-ring-offset-color {
|
|
846
|
+
syntax: "*";
|
|
847
|
+
inherits: false;
|
|
848
|
+
initial-value: #fff;
|
|
849
|
+
}
|
|
850
|
+
@property --tw-ring-offset-shadow {
|
|
851
|
+
syntax: "*";
|
|
852
|
+
inherits: false;
|
|
853
|
+
initial-value: 0 0 #0000;
|
|
854
|
+
}
|
|
855
|
+
@property --tw-outline-style {
|
|
856
|
+
syntax: "*";
|
|
857
|
+
inherits: false;
|
|
858
|
+
initial-value: solid;
|
|
859
|
+
}
|
|
860
|
+
@property --tw-blur {
|
|
861
|
+
syntax: "*";
|
|
862
|
+
inherits: false;
|
|
863
|
+
}
|
|
864
|
+
@property --tw-brightness {
|
|
865
|
+
syntax: "*";
|
|
866
|
+
inherits: false;
|
|
867
|
+
}
|
|
868
|
+
@property --tw-contrast {
|
|
869
|
+
syntax: "*";
|
|
870
|
+
inherits: false;
|
|
871
|
+
}
|
|
872
|
+
@property --tw-grayscale {
|
|
873
|
+
syntax: "*";
|
|
874
|
+
inherits: false;
|
|
875
|
+
}
|
|
876
|
+
@property --tw-hue-rotate {
|
|
877
|
+
syntax: "*";
|
|
878
|
+
inherits: false;
|
|
879
|
+
}
|
|
880
|
+
@property --tw-invert {
|
|
881
|
+
syntax: "*";
|
|
882
|
+
inherits: false;
|
|
883
|
+
}
|
|
884
|
+
@property --tw-opacity {
|
|
885
|
+
syntax: "*";
|
|
886
|
+
inherits: false;
|
|
887
|
+
}
|
|
888
|
+
@property --tw-saturate {
|
|
889
|
+
syntax: "*";
|
|
890
|
+
inherits: false;
|
|
891
|
+
}
|
|
892
|
+
@property --tw-sepia {
|
|
893
|
+
syntax: "*";
|
|
894
|
+
inherits: false;
|
|
895
|
+
}
|
|
896
|
+
@property --tw-drop-shadow {
|
|
897
|
+
syntax: "*";
|
|
898
|
+
inherits: false;
|
|
899
|
+
}
|
|
900
|
+
@property --tw-drop-shadow-color {
|
|
901
|
+
syntax: "*";
|
|
902
|
+
inherits: false;
|
|
903
|
+
}
|
|
904
|
+
@property --tw-drop-shadow-alpha {
|
|
905
|
+
syntax: "<percentage>";
|
|
906
|
+
inherits: false;
|
|
907
|
+
initial-value: 100%;
|
|
908
|
+
}
|
|
909
|
+
@property --tw-drop-shadow-size {
|
|
910
|
+
syntax: "*";
|
|
911
|
+
inherits: false;
|
|
912
|
+
}
|
|
913
|
+
@layer properties {
|
|
914
|
+
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
|
915
|
+
*, ::before, ::after, ::backdrop {
|
|
916
|
+
--tw-rotate-x: initial;
|
|
917
|
+
--tw-rotate-y: initial;
|
|
918
|
+
--tw-rotate-z: initial;
|
|
919
|
+
--tw-skew-x: initial;
|
|
920
|
+
--tw-skew-y: initial;
|
|
921
|
+
--tw-border-style: solid;
|
|
922
|
+
--tw-leading: initial;
|
|
923
|
+
--tw-font-weight: initial;
|
|
924
|
+
--tw-tracking: initial;
|
|
925
|
+
--tw-ordinal: initial;
|
|
926
|
+
--tw-slashed-zero: initial;
|
|
927
|
+
--tw-numeric-figure: initial;
|
|
928
|
+
--tw-numeric-spacing: initial;
|
|
929
|
+
--tw-numeric-fraction: initial;
|
|
930
|
+
--tw-shadow: 0 0 #0000;
|
|
931
|
+
--tw-shadow-color: initial;
|
|
932
|
+
--tw-shadow-alpha: 100%;
|
|
933
|
+
--tw-inset-shadow: 0 0 #0000;
|
|
934
|
+
--tw-inset-shadow-color: initial;
|
|
935
|
+
--tw-inset-shadow-alpha: 100%;
|
|
936
|
+
--tw-ring-color: initial;
|
|
937
|
+
--tw-ring-shadow: 0 0 #0000;
|
|
938
|
+
--tw-inset-ring-color: initial;
|
|
939
|
+
--tw-inset-ring-shadow: 0 0 #0000;
|
|
940
|
+
--tw-ring-inset: initial;
|
|
941
|
+
--tw-ring-offset-width: 0px;
|
|
942
|
+
--tw-ring-offset-color: #fff;
|
|
943
|
+
--tw-ring-offset-shadow: 0 0 #0000;
|
|
944
|
+
--tw-outline-style: solid;
|
|
945
|
+
--tw-blur: initial;
|
|
946
|
+
--tw-brightness: initial;
|
|
947
|
+
--tw-contrast: initial;
|
|
948
|
+
--tw-grayscale: initial;
|
|
949
|
+
--tw-hue-rotate: initial;
|
|
950
|
+
--tw-invert: initial;
|
|
951
|
+
--tw-opacity: initial;
|
|
952
|
+
--tw-saturate: initial;
|
|
953
|
+
--tw-sepia: initial;
|
|
954
|
+
--tw-drop-shadow: initial;
|
|
955
|
+
--tw-drop-shadow-color: initial;
|
|
956
|
+
--tw-drop-shadow-alpha: 100%;
|
|
957
|
+
--tw-drop-shadow-size: initial;
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/* ---- ./_system-light.css ---- */
|
|
963
|
+
/* The `system` theme's light half.
|
|
964
|
+
*
|
|
965
|
+
* Its own file, and outside the components layer, because it is a *theme*
|
|
966
|
+
* rather than a component rule. `tokens.css` states the dark base on `:root`
|
|
967
|
+
* and is unlayered; the moment this sat inside `@layer components` it lost to
|
|
968
|
+
* that, and every `system` specimen rendered dark on a light-mode machine.
|
|
969
|
+
* Unlayered beats layered whatever the selector says, which is exactly the
|
|
970
|
+
* trap this repository keeps finding in different costumes.
|
|
971
|
+
*
|
|
972
|
+
* The underscore marks it the same way `_tailwind.built.css` is marked:
|
|
973
|
+
* `copy-css.mjs` passes both through without wrapping them.
|
|
974
|
+
*/
|
|
282
975
|
/* Light, only under the `system` theme. This used to override `:root`
|
|
283
976
|
unconditionally, so choosing a dark theme on a light-mode laptop got
|
|
284
977
|
silently repainted. Every other theme states its own palette. */
|
|
@@ -307,6 +1000,8 @@
|
|
|
307
1000
|
}
|
|
308
1001
|
}
|
|
309
1002
|
|
|
1003
|
+
/* ---- ./base.css ---- */
|
|
1004
|
+
@layer components {
|
|
310
1005
|
/* Derived from the two surfaces every theme already defines, so a theme gets a
|
|
311
1006
|
control colour without knowing this exists. */
|
|
312
1007
|
/* Pseudo-elements too. `*` does not match them, and `box-sizing` is not
|
|
@@ -371,23 +1066,31 @@ body::before {
|
|
|
371
1066
|
`[aria-disabled='true']` twin rather than a rewrite, which leaves each
|
|
372
1067
|
`button` selector at the specificity it had and every committed baseline
|
|
373
1068
|
untouched. */
|
|
374
|
-
|
|
1069
|
+
.btn {
|
|
1070
|
+
/* The three an anchor brings that a `<button>` does not. Harmless on the
|
|
1071
|
+
button form -- it has no underline and centres already -- and the reason
|
|
1072
|
+
`Button` puts one class on both elements rather than keeping a twin
|
|
1073
|
+
selector for each. */
|
|
375
1074
|
text-decoration: none;
|
|
376
1075
|
color: var(--text);
|
|
377
1076
|
text-align: center;
|
|
378
1077
|
}
|
|
379
1078
|
|
|
380
|
-
|
|
1079
|
+
.btn[aria-disabled='true'] {
|
|
381
1080
|
/* A disabled anchor still takes the pointer, so say so. The click itself is
|
|
382
1081
|
blocked in the capture phase by the component. */
|
|
383
1082
|
cursor: not-allowed;
|
|
384
1083
|
}
|
|
385
1084
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
input,
|
|
389
|
-
|
|
390
|
-
|
|
1085
|
+
/* `:where`, so this base carries no specificity of its own.
|
|
1086
|
+
*
|
|
1087
|
+
* It was `button, input, textarea, select` -- an element selector, which
|
|
1088
|
+
* `.size-sm` and every other class outranked without anyone thinking about
|
|
1089
|
+
* it. Moving it to `.btn` to stop a tab inheriting a control's look raised it
|
|
1090
|
+
* to a class, so it tied with `.size-sm` and won on source order: every
|
|
1091
|
+
* button came out `8px 12px` whatever size it asked for. `:where` puts it
|
|
1092
|
+
* back to zero, which is what the element selector was silently providing. */
|
|
1093
|
+
:where(.btn, input, textarea, select) {
|
|
391
1094
|
font: inherit;
|
|
392
1095
|
color: var(--text);
|
|
393
1096
|
background: var(--control);
|
|
@@ -408,8 +1111,7 @@ select {
|
|
|
408
1111
|
the icon; the button simply had no opinion about how to arrange two things.
|
|
409
1112
|
`inline-flex` and a gap gives it one, and the gap replaces the space
|
|
410
1113
|
character people were putting between an icon and its label. */
|
|
411
|
-
|
|
412
|
-
a.btn {
|
|
1114
|
+
.btn {
|
|
413
1115
|
display: inline-flex;
|
|
414
1116
|
align-items: center;
|
|
415
1117
|
justify-content: center;
|
|
@@ -425,18 +1127,41 @@ a.btn {
|
|
|
425
1127
|
|
|
426
1128
|
Several places already drew this by hand as `.chat-bar-btn.on`. This is the
|
|
427
1129
|
same look, said once, on a plain button. */
|
|
428
|
-
|
|
1130
|
+
.btn[aria-pressed='true'] {
|
|
429
1131
|
border-color: var(--accent);
|
|
430
1132
|
color: var(--accent);
|
|
431
1133
|
background: color-mix(in srgb, var(--accent) 10%, var(--panel));
|
|
432
1134
|
}
|
|
433
1135
|
|
|
434
|
-
|
|
1136
|
+
.btn[aria-pressed='true']:hover:not(:disabled) { border-color: var(--accent); }
|
|
435
1137
|
|
|
436
1138
|
/* The glyph never shrinks: in a narrow row flexbox would take it from the icon
|
|
437
1139
|
before the text, and half an icon is not a smaller icon. */
|
|
438
|
-
|
|
439
|
-
|
|
1140
|
+
.btn > svg { flex: none; }
|
|
1141
|
+
|
|
1142
|
+
/* `Select`'s control wears the control styling, and says so.
|
|
1143
|
+
*
|
|
1144
|
+
* It renders React Aria's `Button` -- a bare `<button>` -- and used to take
|
|
1145
|
+
* `font`, colour, the surface and the border from the
|
|
1146
|
+
* `button, input, textarea, select` reset without asking. When that reset
|
|
1147
|
+
* moved to `.btn`, so a tab and a row would stop inheriting a control's look
|
|
1148
|
+
* only to undo it, this lost all of it at once.
|
|
1149
|
+
*
|
|
1150
|
+
* Here rather than in `select.css`, and that placement is the rule, not
|
|
1151
|
+
* tidiness: `font` is a shorthand and it resets `font-size`, so declared
|
|
1152
|
+
* after `.size-sm` below it silently makes every small control full size.
|
|
1153
|
+
* `index.css` says the import order is behaviour; this is what that means.
|
|
1154
|
+
*
|
|
1155
|
+
* `:where` for the same reason the reset above uses it: this is a base a
|
|
1156
|
+
* `size` class has to be able to beat, and a bare class ties with one. */
|
|
1157
|
+
:where(.sel-control) {
|
|
1158
|
+
font: inherit;
|
|
1159
|
+
color: var(--text);
|
|
1160
|
+
background: var(--control);
|
|
1161
|
+
border: var(--border-width) solid var(--border-strong);
|
|
1162
|
+
border-radius: var(--radius);
|
|
1163
|
+
cursor: pointer;
|
|
1164
|
+
}
|
|
440
1165
|
|
|
441
1166
|
.size-sm { font-size: var(--text-xs); padding: var(--space-1) var(--space-2); }
|
|
442
1167
|
|
|
@@ -461,8 +1186,7 @@ input, textarea, select { width: 100%; }
|
|
|
461
1186
|
same disagreement written twice. */
|
|
462
1187
|
textarea { resize: none; }
|
|
463
1188
|
|
|
464
|
-
|
|
465
|
-
a.btn:hover:not([aria-disabled='true']),
|
|
1189
|
+
.btn:hover:not(:disabled),
|
|
466
1190
|
input:hover:not(:disabled),
|
|
467
1191
|
textarea:hover:not(:disabled),
|
|
468
1192
|
select:hover:not(:disabled) { border-color: var(--accent); }
|
|
@@ -470,8 +1194,7 @@ select:hover:not(:disabled) { border-color: var(--accent); }
|
|
|
470
1194
|
/* `:focus-visible` on the button so a click does not leave a ring behind it,
|
|
471
1195
|
`:focus` on the fields because a focused field is a place you are typing and
|
|
472
1196
|
should say so however you got there. */
|
|
473
|
-
|
|
474
|
-
a.btn:focus-visible,
|
|
1197
|
+
.btn:focus-visible,
|
|
475
1198
|
input:focus,
|
|
476
1199
|
textarea:focus,
|
|
477
1200
|
select:focus { outline: none; border-color: var(--accent); }
|
|
@@ -564,14 +1287,13 @@ input[type='radio']:focus-visible { outline: none; box-shadow: var(--focus-ring)
|
|
|
564
1287
|
input[type='checkbox']:disabled,
|
|
565
1288
|
input[type='radio']:disabled { cursor: not-allowed; background: var(--panel-2); }
|
|
566
1289
|
|
|
567
|
-
|
|
568
|
-
|
|
1290
|
+
.btn:disabled,
|
|
1291
|
+
.btn[aria-disabled='true'],
|
|
569
1292
|
input:disabled,
|
|
570
1293
|
textarea:disabled,
|
|
571
1294
|
select:disabled { opacity: 0.45; cursor: not-allowed; }
|
|
572
1295
|
|
|
573
|
-
|
|
574
|
-
a.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
|
|
1296
|
+
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
|
|
575
1297
|
|
|
576
1298
|
/* A disabled primary is not a faded primary.
|
|
577
1299
|
`opacity: .45` was the whole of it, and a coloured fill at 45% is still a
|
|
@@ -579,8 +1301,7 @@ a.btn.primary { background: var(--accent); border-color: var(--accent); color: v
|
|
|
579
1301
|
thing on the panel. That got worse when onboarding started holding people at
|
|
580
1302
|
a disabled Next. So it gives up the fill entirely and becomes the shape of a
|
|
581
1303
|
disabled control -- the opacity then has nothing left to do. */
|
|
582
|
-
|
|
583
|
-
a.btn.primary[aria-disabled='true'] {
|
|
1304
|
+
.btn.primary:disabled {
|
|
584
1305
|
background: var(--panel-2);
|
|
585
1306
|
border-color: var(--border);
|
|
586
1307
|
color: var(--muted);
|
|
@@ -594,7 +1315,7 @@ a.btn.primary[aria-disabled='true'] {
|
|
|
594
1315
|
line under it that travels.
|
|
595
1316
|
`aria-busy` rather than a class: the attribute is what a screen reader reads,
|
|
596
1317
|
and styling the thing that carries the meaning is how they stay in step. */
|
|
597
|
-
|
|
1318
|
+
.btn[aria-busy='true'] {
|
|
598
1319
|
position: relative;
|
|
599
1320
|
overflow: hidden;
|
|
600
1321
|
cursor: progress;
|
|
@@ -607,18 +1328,18 @@ button[aria-busy='true'] {
|
|
|
607
1328
|
something that is working, that reads as a control that failed and went
|
|
608
1329
|
grey. So a busy button keeps every colour it had and takes only the loss of
|
|
609
1330
|
the cursor and the press; the travelling line is what says why. */
|
|
610
|
-
|
|
1331
|
+
.btn[aria-busy='true']:disabled {
|
|
611
1332
|
opacity: 1;
|
|
612
1333
|
cursor: progress;
|
|
613
1334
|
}
|
|
614
1335
|
|
|
615
|
-
|
|
1336
|
+
.btn.primary[aria-busy='true']:disabled {
|
|
616
1337
|
background: var(--accent);
|
|
617
1338
|
border-color: var(--accent);
|
|
618
1339
|
color: var(--on-accent);
|
|
619
1340
|
}
|
|
620
1341
|
|
|
621
|
-
|
|
1342
|
+
.btn[aria-busy='true']::after {
|
|
622
1343
|
content: '';
|
|
623
1344
|
position: absolute;
|
|
624
1345
|
left: 0;
|
|
@@ -650,15 +1371,13 @@ button[aria-busy='true']::after {
|
|
|
650
1371
|
|
|
651
1372
|
/* Quiet: no border, no fill. For the action beside the action -- Cancel next to
|
|
652
1373
|
Save -- where a bordered box competes with the thing it is next to. */
|
|
653
|
-
|
|
654
|
-
a.btn.ghost {
|
|
1374
|
+
.btn.ghost {
|
|
655
1375
|
background: transparent;
|
|
656
1376
|
border-color: transparent;
|
|
657
1377
|
color: var(--muted);
|
|
658
1378
|
}
|
|
659
1379
|
|
|
660
|
-
|
|
661
|
-
a.btn.ghost:hover:not([aria-disabled='true']) {
|
|
1380
|
+
.btn.ghost:hover:not(:disabled) {
|
|
662
1381
|
background: var(--panel-2);
|
|
663
1382
|
border-color: transparent;
|
|
664
1383
|
color: var(--text);
|
|
@@ -740,7 +1459,7 @@ a.btn.ghost:hover:not([aria-disabled='true']) {
|
|
|
740
1459
|
header.) */
|
|
741
1460
|
.row { display: flex; align-items: center; gap: var(--space-3); }
|
|
742
1461
|
|
|
743
|
-
.grow { flex: 1; min-width: 0; }
|
|
1462
|
+
.ctl-grow { flex: 1; min-width: 0; }
|
|
744
1463
|
|
|
745
1464
|
.mono { font-family: var(--font-mono); font-size: var(--text-sm); }
|
|
746
1465
|
|
|
@@ -804,10 +1523,17 @@ a.btn.ghost:hover:not([aria-disabled='true']) {
|
|
|
804
1523
|
/* A pill following a name needs the gap outside its own border. Leading
|
|
805
1524
|
whitespace inside the span puts it under the pill's background instead,
|
|
806
1525
|
which reads as the badge touching the word it qualifies. */
|
|
807
|
-
|
|
1526
|
+
/* A pill beside a name, and the one thing that keeps the pair working:
|
|
1527
|
+
`flex: none`, so the pill never shrinks and the name ellipsises instead.
|
|
1528
|
+
That was `rows.css`'s `.named > .pill`, which meant anybody laying out a
|
|
1529
|
+
name and its pills had to know a package class by name to get it. The
|
|
1530
|
+
variant already says "I am in a row"; it may as well say what that costs. */
|
|
1531
|
+
.row-pill { margin-left: var(--space-2); vertical-align: baseline; flex: none; }
|
|
808
1532
|
|
|
809
1533
|
/* The tile's only control. Dim until hovered: it is chrome on someone's app. */
|
|
810
1534
|
.icon-btn {
|
|
1535
|
+
/* Stated rather than inherited. These came from the `button, input, textarea, select` reset, which applied control styling to everything that happened to be a `<button>` -- so this rule spent its opening lines undoing it, and would break silently if that rule ever moved. It did move, to `.btn`. */
|
|
1536
|
+
font: inherit;
|
|
811
1537
|
display: inline-flex; align-items: center; padding: var(--space-1);
|
|
812
1538
|
background: none; border: 0; color: var(--muted); opacity: 0.6; cursor: pointer;
|
|
813
1539
|
}
|
|
@@ -873,19 +1599,17 @@ a.btn.ghost:hover:not([aria-disabled='true']) {
|
|
|
873
1599
|
transition: none;
|
|
874
1600
|
}
|
|
875
1601
|
|
|
876
|
-
|
|
877
|
-
a.btn.primary:active:not([aria-disabled='true']) {
|
|
1602
|
+
.btn.primary:active:not(:disabled) {
|
|
878
1603
|
background: color-mix(in srgb, var(--text) var(--press-ink), var(--accent));
|
|
879
1604
|
border-color: color-mix(in srgb, var(--text) var(--press-ink), var(--accent));
|
|
880
1605
|
}
|
|
881
1606
|
|
|
882
|
-
|
|
883
|
-
a.btn.ghost:active:not([aria-disabled='true']) {
|
|
1607
|
+
.btn.ghost:active:not(:disabled) {
|
|
884
1608
|
background: color-mix(in srgb, var(--text) var(--press-ink), var(--panel-2));
|
|
885
1609
|
border-color: transparent;
|
|
886
1610
|
}
|
|
887
1611
|
|
|
888
|
-
|
|
1612
|
+
.btn[aria-pressed='true']:active:not(:disabled) {
|
|
889
1613
|
background: color-mix(in srgb, var(--accent) 18%, var(--panel));
|
|
890
1614
|
}
|
|
891
1615
|
|
|
@@ -1056,7 +1780,7 @@ input:active, textarea:active, select:active { transform: none; }
|
|
|
1056
1780
|
|
|
1057
1781
|
/* The top bar carries the title, the views and the way into settings. Nothing
|
|
1058
1782
|
that belongs to a conversation lives here any more, so it can breathe. */
|
|
1059
|
-
.topbar .grow { flex: 1; min-width: 0; }
|
|
1783
|
+
.topbar .ctl-grow { flex: 1; min-width: 0; }
|
|
1060
1784
|
|
|
1061
1785
|
/* Back on the left, Next on the right, with the gap between them doing the
|
|
1062
1786
|
separating -- so the button that goes forward is in the same place on both
|
|
@@ -1088,7 +1812,60 @@ input:active, textarea:active, select:active { transform: none; }
|
|
|
1088
1812
|
to { opacity: 1; }
|
|
1089
1813
|
}
|
|
1090
1814
|
|
|
1815
|
+
/* The states the `button` element rule used to hand every bare `<button>` in
|
|
1816
|
+
the package, now asked for by name.
|
|
1817
|
+
*
|
|
1818
|
+
* This is the half of the decoupling that a screenshot cannot show: `:focus-visible`
|
|
1819
|
+
* is not photographed, so nine components would have lost their focus ring in
|
|
1820
|
+
* silence. `keyboard.spec.ts` is what actually holds this.
|
|
1821
|
+
*
|
|
1822
|
+
* `.btn` has its own, in the rules above. These are the ones that are a button
|
|
1823
|
+
* for their behaviour and not for their appearance -- a tab, a row, a card, a
|
|
1824
|
+
* pager step, a grid cell, a remove cross. */
|
|
1825
|
+
.tab:focus-visible,
|
|
1826
|
+
.row-hit:focus-visible,
|
|
1827
|
+
.card:focus-visible,
|
|
1828
|
+
.ident-remove:focus-visible,
|
|
1829
|
+
.size-cell:focus-visible,
|
|
1830
|
+
.pager-step:focus-visible,
|
|
1831
|
+
.pager-page:focus-visible,
|
|
1832
|
+
.sel-control:focus-visible {
|
|
1833
|
+
outline: 2px solid var(--accent);
|
|
1834
|
+
outline-offset: 2px;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
/* `:disabled` came from the same rule. A control that cannot be pressed has to
|
|
1838
|
+
say so whatever element it is. */
|
|
1839
|
+
.tab:disabled,
|
|
1840
|
+
.row-hit:disabled,
|
|
1841
|
+
.card:disabled,
|
|
1842
|
+
.ident-remove:disabled,
|
|
1843
|
+
.size-cell:disabled,
|
|
1844
|
+
.pager-step:disabled,
|
|
1845
|
+
.pager-page:disabled,
|
|
1846
|
+
.sel-control:disabled {
|
|
1847
|
+
opacity: 0.45;
|
|
1848
|
+
cursor: not-allowed;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
/* The anchor form's disabled states, alongside the button's.
|
|
1852
|
+
*
|
|
1853
|
+
* `.btn` is one class on two elements, and an `<a>` has no `:disabled` -- it
|
|
1854
|
+
* carries `aria-disabled` and a capture-phase click block instead. Every
|
|
1855
|
+
* rule above that reads `:disabled` needs the attribute twin, or a disabled
|
|
1856
|
+
* link is a link that looks pressable and refuses silently. Folding the old
|
|
1857
|
+
* `a.btn` twins into `.btn` dropped these, and the baselines showed a
|
|
1858
|
+
* disabled link at full strength. */
|
|
1859
|
+
.btn.primary[aria-disabled='true'] {
|
|
1860
|
+
background: var(--panel-2);
|
|
1861
|
+
border-color: var(--border);
|
|
1862
|
+
color: var(--muted);
|
|
1863
|
+
opacity: 1;
|
|
1864
|
+
}
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1091
1867
|
/* ---- ./select.css ---- */
|
|
1868
|
+
@layer components {
|
|
1092
1869
|
/* How big a control is, asked for rather than inherited.
|
|
1093
1870
|
Four sizes existed and every one of them came from a descendant selector:
|
|
1094
1871
|
`.set-row button` was 11px in 3px of padding, `.approval-actions button` was
|
|
@@ -1103,8 +1880,7 @@ input:active, textarea:active, select:active { transform: none; }
|
|
|
1103
1880
|
/* Full width, for the one action a card or a step is about. Not a size: a
|
|
1104
1881
|
`block` button is the same height as any other, it just stops being a thing
|
|
1105
1882
|
you have to aim at. */
|
|
1106
|
-
|
|
1107
|
-
a.btn.block { display: flex; width: 100%; }
|
|
1883
|
+
.btn.ctl-block { display: flex; width: 100%; }
|
|
1108
1884
|
|
|
1109
1885
|
/* The default is `size-md`, spelled once for every control.
|
|
1110
1886
|
The paragraph above promised one scale for buttons, inputs and selects, and
|
|
@@ -1112,13 +1888,24 @@ a.btn.block { display: flex; width: 100%; }
|
|
|
1112
1888
|
input was eight pixels taller than the button beside it and no row of the two
|
|
1113
1889
|
could line up. The download modal's search box against its Search button was
|
|
1114
1890
|
the version you could see from across the room. */
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
input,
|
|
1118
|
-
|
|
1119
|
-
|
|
1891
|
+
/* `:where`, so this base carries no specificity of its own.
|
|
1892
|
+
*
|
|
1893
|
+
* It was `button, input, textarea, select` -- an element selector, which
|
|
1894
|
+
* `.size-sm` and every other class outranked without anyone thinking about
|
|
1895
|
+
* it. Moving it to `.btn` to stop a tab inheriting a control's look raised it
|
|
1896
|
+
* to a class, so it tied with `.size-sm` and won on source order: every
|
|
1897
|
+
* button came out `8px 12px` whatever size it asked for. `:where` puts it
|
|
1898
|
+
* back to zero, which is what the element selector was silently providing. */
|
|
1899
|
+
:where(.btn, input, textarea, select) { padding: var(--space-2) var(--space-3); }
|
|
1900
|
+
|
|
1901
|
+
/* The select's control keeps its padding at full class weight, and after
|
|
1902
|
+
`.size-sm` in the cascade, because that is where it has always been: this
|
|
1903
|
+
file is imported after `base.css`, so a `size`d select took the size's type
|
|
1904
|
+
and this padding. Folding it into the `:where` above handed the padding
|
|
1905
|
+
back to `.size-sm` and shrank every small select by seven pixels. */
|
|
1120
1906
|
.sel-control { padding: var(--space-2) var(--space-3); }
|
|
1121
1907
|
|
|
1908
|
+
|
|
1122
1909
|
/* Centred on the whole block -- the caption and the sentence together -- rather
|
|
1123
1910
|
than aligned to either end of it. `.field-row` pins to the bottom, which is
|
|
1124
1911
|
right for a field beside a button and wrong beside two lines of prose. */
|
|
@@ -1142,16 +1929,16 @@ select,
|
|
|
1142
1929
|
.sel { position: relative; display: inline-block; }
|
|
1143
1930
|
|
|
1144
1931
|
/* The subject of its pane rather than a field in it: full width and a size up.
|
|
1145
|
-
That was `.select-field.block` on the native element, and it carried the
|
|
1932
|
+
That was `.select-field.ctl-block` on the native element, and it carried the
|
|
1146
1933
|
reason with it -- model ids are long (`qwen2.5-coder:32b-instruct-q4_K_M`) and
|
|
1147
1934
|
at the narrow size they truncated mid-name. */
|
|
1148
|
-
.sel.block { display: block; width: 100%; }
|
|
1935
|
+
.sel.ctl-block { display: block; width: 100%; }
|
|
1149
1936
|
|
|
1150
|
-
.sel.block .sel-control { padding: var(--space-2) var(--space-3); font-weight: 500; }
|
|
1937
|
+
.sel.ctl-block .sel-control { padding: var(--space-2) var(--space-3); font-weight: 500; }
|
|
1151
1938
|
|
|
1152
1939
|
/* Not full width otherwise: a select as wide as its row, holding one short
|
|
1153
1940
|
word, reads as an input somebody forgot to fill in. */
|
|
1154
|
-
.sel:not(.block) .sel-control { width: auto; max-width: 320px; }
|
|
1941
|
+
.sel:not(.ctl-block) .sel-control { width: auto; max-width: 320px; }
|
|
1155
1942
|
|
|
1156
1943
|
.sel-control {
|
|
1157
1944
|
display: flex;
|
|
@@ -1164,9 +1951,7 @@ select,
|
|
|
1164
1951
|
background: var(--control);
|
|
1165
1952
|
}
|
|
1166
1953
|
|
|
1167
|
-
.sel-
|
|
1168
|
-
|
|
1169
|
-
.sel-caret { flex: none; opacity: 0.6; transition: transform var(--dur-fast) var(--ease); }
|
|
1954
|
+
.sel-caret { transition: transform var(--dur-fast) var(--ease); }
|
|
1170
1955
|
|
|
1171
1956
|
.sel[data-open] .sel-caret { transform: rotate(180deg); }
|
|
1172
1957
|
|
|
@@ -1175,45 +1960,22 @@ select,
|
|
|
1175
1960
|
/* State is read off React Aria's attributes: `data-open` on the root,
|
|
1176
1961
|
`data-focused` / `data-selected` / `data-disabled` on an item, where
|
|
1177
1962
|
`.open`, `[data-active]`, `.on` and `.off` used to be. */
|
|
1963
|
+
/* The list keeps two things the utilities on `Select` cannot say.
|
|
1964
|
+
*
|
|
1965
|
+
* `scroll-padding-block`: the same room while the list is scrolled as it has
|
|
1966
|
+
* at rest. Padding at the top of a scroll container scrolls away with the
|
|
1967
|
+
* content, so the row `scrollIntoView({block: 'nearest'})` brings up ended
|
|
1968
|
+
* flush against the border and read as clipped rather than as one of a set --
|
|
1969
|
+
* and this list opens scrolled to whatever is chosen, so that was the
|
|
1970
|
+
* ordinary state.
|
|
1971
|
+
*
|
|
1972
|
+
* `box-shadow`: `--shadow-2` is a token, and a utility would have to inline
|
|
1973
|
+
* the value, which is the one thing the vocabulary exists to stop. */
|
|
1178
1974
|
.sel-list {
|
|
1179
|
-
/* `fixed`, and in `document.body`: an ancestor that scrolls or hides its
|
|
1180
|
-
overflow would otherwise clip it, which in this app means every settings
|
|
1181
|
-
pane. Position comes from the button's measured rectangle. */
|
|
1182
|
-
/* Positioned by React Aria -- inline `top`/`left` on a portalled popover
|
|
1183
|
-
that flips when the edge is near. `position: fixed` and the measured
|
|
1184
|
-
rectangle it was placed by are gone with the hand-rolled version. */
|
|
1185
|
-
z-index: 60;
|
|
1186
|
-
max-height: 280px;
|
|
1187
|
-
overflow-y: auto;
|
|
1188
|
-
margin: 0;
|
|
1189
|
-
padding: var(--space-1);
|
|
1190
|
-
/* The same room while the list is scrolled as it has at rest. Padding at
|
|
1191
|
-
the top of a scroll container scrolls away with the content, so the row
|
|
1192
|
-
`scrollIntoView({block: 'nearest'})` brings up ended flush against the
|
|
1193
|
-
border and read as clipped rather than as one of a set -- and this list
|
|
1194
|
-
opens scrolled to whatever is chosen, so that was the ordinary state.
|
|
1195
|
-
`scroll-padding` insets what counts as "in view", so it stops short by
|
|
1196
|
-
the same four the sides have. */
|
|
1197
1975
|
scroll-padding-block: var(--space-1);
|
|
1198
|
-
list-style: none;
|
|
1199
|
-
border: var(--border-width) solid var(--border-strong);
|
|
1200
|
-
border-radius: var(--radius-md);
|
|
1201
|
-
background: var(--panel);
|
|
1202
1976
|
box-shadow: var(--shadow-2);
|
|
1203
1977
|
}
|
|
1204
1978
|
|
|
1205
|
-
.sel-item {
|
|
1206
|
-
display: flex;
|
|
1207
|
-
align-items: center;
|
|
1208
|
-
gap: var(--space-2);
|
|
1209
|
-
padding: var(--space-2) var(--space-2);
|
|
1210
|
-
border-radius: var(--radius-sm);
|
|
1211
|
-
cursor: pointer;
|
|
1212
|
-
white-space: nowrap;
|
|
1213
|
-
}
|
|
1214
|
-
|
|
1215
|
-
.sel-item-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
|
|
1216
|
-
|
|
1217
1979
|
/* Hover and keyboard land on the same row, because they are the same idea --
|
|
1218
1980
|
two highlights for one cursor is how a list starts lying about where Enter
|
|
1219
1981
|
will go. */
|
|
@@ -1225,7 +1987,18 @@ select,
|
|
|
1225
1987
|
|
|
1226
1988
|
/* The tick marks the chosen row. Colour alone would not: a list where the only
|
|
1227
1989
|
difference is a hue is a list you cannot read past. */
|
|
1228
|
-
|
|
1990
|
+
/* The tick's resting state lives here, beside the state that overrides it.
|
|
1991
|
+
*
|
|
1992
|
+
* It was `opacity-0` on the element and `opacity: 1` here, which worked only
|
|
1993
|
+
* while this sheet was unlayered. A utility is in `@layer utilities` and
|
|
1994
|
+
* beats `@layer components` whatever the selector, so the moment the sheets
|
|
1995
|
+
* moved into a layer the tick stopped appearing on the selected row -- the
|
|
1996
|
+
* resting state won permanently.
|
|
1997
|
+
*
|
|
1998
|
+
* The rule generalises: a utility for a resting state whose active state is a
|
|
1999
|
+
* relationship between elements will always beat that active state. The pair
|
|
2000
|
+
* has to sit in the same layer. */
|
|
2001
|
+
.sel-tick { opacity: 0; }
|
|
1229
2002
|
|
|
1230
2003
|
.sel-item[data-selected] .sel-tick { opacity: 1; }
|
|
1231
2004
|
|
|
@@ -1261,24 +2034,23 @@ select,
|
|
|
1261
2034
|
.field > input,
|
|
1262
2035
|
.field > textarea,
|
|
1263
2036
|
.field > .sel { width: 100%; }
|
|
2037
|
+
}
|
|
1264
2038
|
|
|
1265
2039
|
/* ---- ./dangerzone.css ---- */
|
|
2040
|
+
@layer components {
|
|
1266
2041
|
/* Destructive, and legible as such standing still. */
|
|
1267
|
-
|
|
1268
|
-
a.btn.danger {
|
|
2042
|
+
.btn.danger {
|
|
1269
2043
|
color: var(--bad);
|
|
1270
2044
|
border-color: color-mix(in srgb, var(--bad) 45%, var(--border-strong));
|
|
1271
2045
|
}
|
|
1272
2046
|
|
|
1273
|
-
|
|
1274
|
-
a.btn.danger:hover:not([aria-disabled='true']) {
|
|
2047
|
+
.btn.danger:hover:not(:disabled) {
|
|
1275
2048
|
background: color-mix(in srgb, var(--bad) 12%, var(--panel));
|
|
1276
2049
|
border-color: var(--bad);
|
|
1277
2050
|
color: var(--bad);
|
|
1278
2051
|
}
|
|
1279
2052
|
|
|
1280
|
-
|
|
1281
|
-
a.btn.danger:active:not([aria-disabled='true']) {
|
|
2053
|
+
.btn.danger:active:not(:disabled) {
|
|
1282
2054
|
background: color-mix(in srgb, var(--bad) 20%, var(--panel));
|
|
1283
2055
|
border-color: var(--bad);
|
|
1284
2056
|
}
|
|
@@ -1377,8 +2149,10 @@ a.btn.danger:active:not([aria-disabled='true']) {
|
|
|
1377
2149
|
controls in the same column at the same indent that do not line up read as
|
|
1378
2150
|
an accident rather than as a pair. */
|
|
1379
2151
|
.field-row > button { min-width: 9ch; }
|
|
2152
|
+
}
|
|
1380
2153
|
|
|
1381
2154
|
/* ---- ./callout.css ---- */
|
|
2155
|
+
@layer components {
|
|
1382
2156
|
/* One shape for everything that tells you something.
|
|
1383
2157
|
`.banner` and `.callout` were the same box drawn twice: a bordered, rounded,
|
|
1384
2158
|
12px panel with a tint. They differed in padding by a pixel in each direction
|
|
@@ -1517,35 +2291,50 @@ a.btn.danger:active:not([aria-disabled='true']) {
|
|
|
1517
2291
|
}
|
|
1518
2292
|
|
|
1519
2293
|
.callout button { flex: none; }
|
|
2294
|
+
}
|
|
1520
2295
|
|
|
1521
2296
|
/* ---- ./card.css ---- */
|
|
2297
|
+
@layer components {
|
|
2298
|
+
/* `.card` is public API, not this component's private styling.
|
|
2299
|
+
*
|
|
2300
|
+
* Consumers apply it to their own markup -- the gallery's skeleton specimen
|
|
2301
|
+
* renders `<div className="card">` around three rows, and app-template uses
|
|
2302
|
+
* `card-pick`. Moving the box into `Card`'s own class list took that away
|
|
2303
|
+
* with no type error and no failing test in this package: it broke only
|
|
2304
|
+
* where somebody else had used the class, which is the worst way for an API
|
|
2305
|
+
* to go.
|
|
2306
|
+
*
|
|
2307
|
+
* So the rule stays and `Card` wears it. The lesson generalises to every
|
|
2308
|
+
* class in this package that a consumer can reach: a component may draw
|
|
2309
|
+
* itself with utilities only where its class is genuinely private. */
|
|
1522
2310
|
.card {
|
|
2311
|
+
display: block;
|
|
2312
|
+
width: 100%;
|
|
2313
|
+
box-sizing: border-box;
|
|
2314
|
+
font: inherit;
|
|
2315
|
+
color: var(--text);
|
|
2316
|
+
text-align: left;
|
|
1523
2317
|
background: var(--panel);
|
|
1524
2318
|
border: var(--border-width) solid var(--border);
|
|
1525
2319
|
border-radius: var(--radius-md);
|
|
1526
|
-
padding: var(--space-3)
|
|
2320
|
+
padding: var(--space-3);
|
|
1527
2321
|
margin-bottom: var(--space-3);
|
|
1528
2322
|
}
|
|
1529
2323
|
|
|
2324
|
+
/* What is left once `Card` draws its own box: the tones, the pick and
|
|
2325
|
+
selected states, and the rules that reach from the card into what it
|
|
2326
|
+
holds. `:has(.card-icon) .card-inline` is a relationship, and a tone is a
|
|
2327
|
+
`color-mix`; neither is a property on one element. */
|
|
2328
|
+
|
|
1530
2329
|
/* Header, when there is one. The title and the line under it were retyped at
|
|
1531
2330
|
every call site and no two agreed on the gap between them. */
|
|
1532
|
-
.card-head { display: flex; align-items: flex-start; gap: var(--space-3); }
|
|
1533
|
-
|
|
1534
|
-
.card-head:not(:last-child) { margin-bottom: var(--space-3); }
|
|
1535
|
-
|
|
1536
|
-
.card-headings { min-width: 0; flex: 1; }
|
|
1537
|
-
|
|
1538
2331
|
/* The card owns the space between the things it holds. Without this a callout
|
|
1539
2332
|
and the button under it sit edge to edge, and every caller has to remember a
|
|
1540
2333
|
margin on whichever child happens to be second. */
|
|
1541
|
-
.card-body { display: grid; gap: var(--space-3); }
|
|
1542
|
-
|
|
1543
2334
|
/* The card's own control, on the card's padding rather than indented into its
|
|
1544
2335
|
body. Centred against the whole heading block, not against the title -- a
|
|
1545
2336
|
switch aligned to the first line drifts upward the moment the description
|
|
1546
2337
|
runs to two. */
|
|
1547
|
-
.card-action { flex: none; align-self: center; display: flex; align-items: center; }
|
|
1548
|
-
|
|
1549
2338
|
/* A row inside a card that lines up with the header rather than indenting past
|
|
1550
2339
|
it: same left edge as the title, same right edge as the action. Its label is
|
|
1551
2340
|
a quiet caption, not a second heading -- the card already has one, and two
|
|
@@ -1592,17 +2381,6 @@ a.btn.danger:active:not([aria-disabled='true']) {
|
|
|
1592
2381
|
color: var(--muted);
|
|
1593
2382
|
}
|
|
1594
2383
|
|
|
1595
|
-
/* A card that is itself the choice. Reset first, because it is a `<button>`
|
|
1596
|
-
now and inherits a control's padding and centring. */
|
|
1597
|
-
button.card {
|
|
1598
|
-
display: block;
|
|
1599
|
-
width: 100%;
|
|
1600
|
-
text-align: left;
|
|
1601
|
-
font: inherit;
|
|
1602
|
-
color: inherit;
|
|
1603
|
-
padding: var(--space-3);
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
2384
|
/* Variants, because a card is doing four jobs and looked like one.
|
|
1607
2385
|
A search result you can act on, a thing already installed, something that
|
|
1608
2386
|
went wrong, and a heading with rows under it are not the same object, and
|
|
@@ -1645,7 +2423,23 @@ button.card {
|
|
|
1645
2423
|
|
|
1646
2424
|
.modal-body .card { background: var(--panel-2); }
|
|
1647
2425
|
|
|
2426
|
+
/* Selected, which used to arrive from `button[aria-pressed='true']`.
|
|
2427
|
+
*
|
|
2428
|
+
* A card marks its choice with `aria-pressed`, and the tint came from the
|
|
2429
|
+
* element rule every bare `<button>` in the package was picking up. That rule
|
|
2430
|
+
* belongs to `Button` now, so the card says what being chosen looks like --
|
|
2431
|
+
* which is the right place for it anyway: a chosen card is a card's idea, not
|
|
2432
|
+
* a button's. */
|
|
2433
|
+
.card[aria-pressed='true'] {
|
|
2434
|
+
border-color: var(--accent);
|
|
2435
|
+
background: color-mix(in srgb, var(--accent) 10%, var(--panel));
|
|
2436
|
+
}
|
|
2437
|
+
|
|
2438
|
+
.card[aria-pressed='true']:hover { border-color: var(--accent); }
|
|
2439
|
+
}
|
|
2440
|
+
|
|
1648
2441
|
/* ---- ./toggle.css ---- */
|
|
2442
|
+
@layer components {
|
|
1649
2443
|
.card-action .switch-row { padding: 0; gap: var(--space-2); }
|
|
1650
2444
|
|
|
1651
2445
|
.card-action .switch-row .switch-body { flex: none; }
|
|
@@ -1876,8 +2670,10 @@ input[type='radio']:active:not(:disabled) {
|
|
|
1876
2670
|
@media (prefers-reduced-motion: reduce) {
|
|
1877
2671
|
.toggle-sweep::before { animation: none; background: color-mix(in oklab, var(--accent) 45%, transparent); }
|
|
1878
2672
|
}
|
|
2673
|
+
}
|
|
1879
2674
|
|
|
1880
2675
|
/* ---- ./skeleton.css ---- */
|
|
2676
|
+
@layer components {
|
|
1881
2677
|
.muted { color: var(--muted); }
|
|
1882
2678
|
|
|
1883
2679
|
.pulse-block {
|
|
@@ -1932,8 +2728,10 @@ input[type='radio']:active:not(:disabled) {
|
|
|
1932
2728
|
/* Still distinguishable from a real block of colour, and still still. */
|
|
1933
2729
|
.skel-pulse { animation: none; opacity: 0.8; }
|
|
1934
2730
|
}
|
|
2731
|
+
}
|
|
1935
2732
|
|
|
1936
2733
|
/* ---- ./rows.css ---- */
|
|
2734
|
+
@layer components {
|
|
1937
2735
|
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
1938
2736
|
|
|
1939
2737
|
/* A name and the pills that qualify it, on one line, where the name is the only
|
|
@@ -2194,72 +2992,24 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2194
2992
|
.row-hit:hover .truncate { color: var(--accent); }
|
|
2195
2993
|
|
|
2196
2994
|
.row-hit:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
2197
|
-
|
|
2198
|
-
/* ---- ./empty.css ---- */
|
|
2199
|
-
/* `Empty`: a list with nothing in it.
|
|
2200
|
-
|
|
2201
|
-
Quieter than `.empty`, which is a whole-screen state at `--space-10`. These
|
|
2202
|
-
sit inside a card that already has a heading and a description, so the same
|
|
2203
|
-
padding would leave a card that is mostly air about nothing.
|
|
2204
|
-
|
|
2205
|
-
Dashed rather than solid: a solid border reads as a thing, and the point of
|
|
2206
|
-
this box is that there is no thing. It marks out where the list will be. */
|
|
2207
|
-
.nothing {
|
|
2208
|
-
display: grid;
|
|
2209
|
-
justify-items: center;
|
|
2210
|
-
gap: var(--space-2);
|
|
2211
|
-
padding: var(--space-5) var(--space-4);
|
|
2212
|
-
border: var(--border-width) dashed var(--border-strong);
|
|
2213
|
-
border-radius: var(--radius-md);
|
|
2214
|
-
text-align: center;
|
|
2215
2995
|
}
|
|
2216
2996
|
|
|
2217
|
-
.nothing-mark { color: var(--muted); line-height: 0; }
|
|
2218
|
-
|
|
2219
|
-
/* The same box with a figure in it, for a whole surface rather than a slot in
|
|
2220
|
-
a card. Two things change and both follow from the size: the dashed outline
|
|
2221
|
-
goes, because an outline marks out where a list will be and a window-sized
|
|
2222
|
-
dashed rectangle reads as a layout that failed rather than a view with
|
|
2223
|
-
nothing on it; and the box centres itself in whatever it was dropped into,
|
|
2224
|
-
which is what makes it land in the middle of an empty dashboard rather than
|
|
2225
|
-
under the header. */
|
|
2226
|
-
.nothing-surface {
|
|
2227
|
-
border: none;
|
|
2228
|
-
padding: var(--space-10) var(--space-4);
|
|
2229
|
-
gap: var(--space-3);
|
|
2230
|
-
margin: auto;
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
/* `currentColor` reaches the figure through the two rules the illustrations
|
|
2234
|
-
ship with, so the colour is set here and the drawing follows it -- the same
|
|
2235
|
-
grey as the sentence under it, in every theme.
|
|
2236
|
-
Two classes, not one: `.illo` sets `color: inherit` further down this file,
|
|
2237
|
-
which beats a single-class rule written earlier and drew the figure in full
|
|
2238
|
-
text black beside a muted sentence. */
|
|
2239
|
-
.nothing-surface .nothing-figure { color: var(--muted); }
|
|
2240
|
-
|
|
2241
|
-
/* `--text-sm`, not the `--text-xs` these lines used to be: it is a sentence
|
|
2242
|
-
somebody is meant to read, not a caption qualifying something else. */
|
|
2243
|
-
.nothing-said { margin: 0; color: var(--muted); font-size: var(--text-sm); line-height: 1.5; max-width: 46ch; }
|
|
2244
|
-
|
|
2245
|
-
.nothing-act { margin-top: var(--space-1); }
|
|
2246
|
-
|
|
2247
2997
|
/* ---- ./progress.css ---- */
|
|
2998
|
+
@layer components {
|
|
2248
2999
|
/* progress ----------------------------------------------------------- */
|
|
2249
3000
|
|
|
2250
|
-
/* The bar
|
|
3001
|
+
/* The bar itself: a track with a child it fills, an animation and a
|
|
3002
|
+
reduced-motion answer. A utility is a property on one element, so none of
|
|
3003
|
+
that is one -- `Progress` draws its own wrapper and head with utilities and
|
|
3004
|
+
this is what is left.
|
|
3005
|
+
|
|
3006
|
+
The bar sat 4px under its own label and flush against whatever was above
|
|
2251
3007
|
the whole block, so a download row read as four lines of text with a rule
|
|
2252
3008
|
drawn through the last one. The label is a caption for the bar and stays
|
|
2253
3009
|
close to it; the block as a whole is a separate statement from the row it
|
|
2254
3010
|
follows. */
|
|
2255
|
-
.progress { display: grid; gap: var(--space-1); }
|
|
2256
|
-
|
|
2257
3011
|
.card > .progress { margin-top: var(--space-3); }
|
|
2258
3012
|
|
|
2259
|
-
.progress-head { display: flex; align-items: baseline; gap: var(--space-2); font-size: var(--text-sm); }
|
|
2260
|
-
|
|
2261
|
-
.progress-detail { color: var(--muted); font-size: var(--text-xs); }
|
|
2262
|
-
|
|
2263
3013
|
.bar { height: 6px; background: var(--panel-2); border-radius: var(--radius-pill); overflow: hidden; }
|
|
2264
3014
|
|
|
2265
3015
|
.bar > i {
|
|
@@ -2287,12 +3037,22 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2287
3037
|
/* Still distinguishable from empty, and from full. */
|
|
2288
3038
|
.bar-indeterminate > i { animation: none; width: 100%; opacity: 0.35; }
|
|
2289
3039
|
}
|
|
3040
|
+
}
|
|
2290
3041
|
|
|
2291
3042
|
/* ---- ./tabs.css ---- */
|
|
3043
|
+
@layer components {
|
|
2292
3044
|
/* ---------------------------------------------------------------- tabs */
|
|
2293
3045
|
.tabs { position: relative; display: flex; gap: var(--space-1); }
|
|
2294
3046
|
|
|
2295
3047
|
.tab {
|
|
3048
|
+
/* Layout the `button` element rule used to supply. A tab is a button for its behaviour, not its look, so it now says what it wants instead of taking a control's arrangement and undoing the rest. */
|
|
3049
|
+
display: inline-flex;
|
|
3050
|
+
align-items: center;
|
|
3051
|
+
justify-content: center;
|
|
3052
|
+
gap: var(--space-2);
|
|
3053
|
+
/* Stated rather than inherited. These came from the `button, input, textarea, select` reset, which applied control styling to everything that happened to be a `<button>` -- so this rule spent its opening lines undoing it, and would break silently if that rule ever moved. It did move, to `.btn`. */
|
|
3054
|
+
font: inherit;
|
|
3055
|
+
cursor: pointer;
|
|
2296
3056
|
position: relative;
|
|
2297
3057
|
z-index: 1;
|
|
2298
3058
|
background: transparent;
|
|
@@ -2381,8 +3141,10 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2381
3141
|
}
|
|
2382
3142
|
|
|
2383
3143
|
.tabs-vertical .tab:active:not(:disabled) { border-color: transparent; }
|
|
3144
|
+
}
|
|
2384
3145
|
|
|
2385
3146
|
/* ---- ./toast.css ---- */
|
|
3147
|
+
@layer components {
|
|
2386
3148
|
/* ---------------------------------------------------------------- toasts */
|
|
2387
3149
|
.toasts {
|
|
2388
3150
|
position: fixed;
|
|
@@ -2449,7 +3211,41 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2449
3211
|
.toast[data-exiting] { animation: none; }
|
|
2450
3212
|
}
|
|
2451
3213
|
|
|
3214
|
+
/* The dismiss control, in its own words.
|
|
3215
|
+
*
|
|
3216
|
+
* It was `className="ghost size-sm"` -- `Button`'s vocabulary, borrowed from
|
|
3217
|
+
* a component that does not render a `Button`. That only ever worked because
|
|
3218
|
+
* `.ghost` and `.size-sm` were element-and-class rules any `<button>` could
|
|
3219
|
+
* pick up, which is the coupling this pass is removing. Nothing photographed
|
|
3220
|
+
* it, either: the toast is transient and the close button is not in any
|
|
3221
|
+
* baseline, so this would have gone quiet. */
|
|
3222
|
+
.toast-close {
|
|
3223
|
+
display: inline-flex;
|
|
3224
|
+
align-items: center;
|
|
3225
|
+
justify-content: center;
|
|
3226
|
+
font: inherit;
|
|
3227
|
+
font-size: var(--text-xs);
|
|
3228
|
+
padding: var(--space-1) var(--space-2);
|
|
3229
|
+
background: transparent;
|
|
3230
|
+
border: var(--border-width) solid transparent;
|
|
3231
|
+
border-radius: var(--radius);
|
|
3232
|
+
color: var(--muted);
|
|
3233
|
+
cursor: pointer;
|
|
3234
|
+
}
|
|
3235
|
+
|
|
3236
|
+
.toast-close:hover:not(:disabled) {
|
|
3237
|
+
background: var(--panel-2);
|
|
3238
|
+
color: var(--text);
|
|
3239
|
+
}
|
|
3240
|
+
|
|
3241
|
+
.toast-close:focus-visible {
|
|
3242
|
+
outline: 2px solid var(--accent);
|
|
3243
|
+
outline-offset: 2px;
|
|
3244
|
+
}
|
|
3245
|
+
}
|
|
3246
|
+
|
|
2452
3247
|
/* ---- ./modal.css ---- */
|
|
3248
|
+
@layer components {
|
|
2453
3249
|
/* modal --------------------------------------------------------------- */
|
|
2454
3250
|
|
|
2455
3251
|
/* `hidden` needs saying out loud here. The browser's own `[hidden]` rule is a
|
|
@@ -2489,13 +3285,6 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2489
3285
|
Not `display: contents`, which was the first attempt. An element with no box
|
|
2490
3286
|
cannot take focus, so React Aria's initial focus fell through to the first
|
|
2491
3287
|
input and Escape -- handled on the section -- stopped closing the window. */
|
|
2492
|
-
.modal-dialog {
|
|
2493
|
-
display: flex;
|
|
2494
|
-
flex-direction: column;
|
|
2495
|
-
min-height: 0;
|
|
2496
|
-
flex: 1;
|
|
2497
|
-
}
|
|
2498
|
-
|
|
2499
3288
|
.modal {
|
|
2500
3289
|
background: var(--panel);
|
|
2501
3290
|
border: 1px solid var(--border);
|
|
@@ -2546,18 +3335,9 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2546
3335
|
laid out for the bare `<strong>` it used to hold -- 86px tall with them,
|
|
2547
3336
|
41px without, on a head whose padding is 12 over 8. `font: inherit` so the
|
|
2548
3337
|
`<strong>` rule above is the one that sets the size. */
|
|
2549
|
-
.modal-title { margin: 0; font: inherit; }
|
|
2550
|
-
|
|
2551
3338
|
/* Under the title, on the header's padding rather than the body's, so it reads
|
|
2552
3339
|
as part of the heading rather than as the first line of content. */
|
|
2553
|
-
.modal-desc {
|
|
2554
|
-
margin: 0;
|
|
2555
|
-
padding: 0 var(--space-4) var(--space-3);
|
|
2556
|
-
color: var(--muted);
|
|
2557
|
-
font-size: var(--text-sm);
|
|
2558
|
-
line-height: 1.5;
|
|
2559
|
-
max-width: 62ch;
|
|
2560
|
-
}
|
|
3340
|
+
.modal-desc { line-height: 1.5; }
|
|
2561
3341
|
|
|
2562
3342
|
.modal-body { padding: var(--space-2) var(--space-4) var(--space-3); overflow-y: auto; }
|
|
2563
3343
|
|
|
@@ -2696,8 +3476,10 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2696
3476
|
.modal.sheet[data-entering],
|
|
2697
3477
|
.modal.sheet[data-exiting] { animation: none; }
|
|
2698
3478
|
}
|
|
3479
|
+
}
|
|
2699
3480
|
|
|
2700
3481
|
/* ---- ./slider.css ---- */
|
|
3482
|
+
@layer components {
|
|
2701
3483
|
/* slider ----------------------------------------------------------------- */
|
|
2702
3484
|
/* A number from a range -- `Slider.tsx` says when one is the right control.
|
|
2703
3485
|
The row is laid out like a switch row: words on the left, the control on the
|
|
@@ -2917,9 +3699,12 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
2917
3699
|
buttons only, so a button in a row stood 25px beside a 35px select and no
|
|
2918
3700
|
size class could ask for the select's height. Type from the row, box from
|
|
2919
3701
|
the scale, and the two line up; an explicit `size-*` still wins. */
|
|
2920
|
-
:where(.set-row) button,
|
|
3702
|
+
:where(.set-row) button,
|
|
3703
|
+
:where(.set-row) .slider-box { font-size: var(--text-xs); }
|
|
3704
|
+
}
|
|
2921
3705
|
|
|
2922
3706
|
/* ---- ./checkbox.css ---- */
|
|
3707
|
+
@layer components {
|
|
2923
3708
|
/* A row you choose. There is no checkbox in it.
|
|
2924
3709
|
Every one of the fourteen in this app sits beside a name and a line of
|
|
2925
3710
|
explanation, so the row was always the thing being designed and the 17px
|
|
@@ -3089,36 +3874,36 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3089
3874
|
.choice[data-disabled] .choice-why { opacity: 0.5; }
|
|
3090
3875
|
|
|
3091
3876
|
.choice[data-disabled]::after { opacity: 0.4; }
|
|
3877
|
+
}
|
|
3092
3878
|
|
|
3093
3879
|
/* ---- ./table.css ---- */
|
|
3880
|
+
@layer components {
|
|
3094
3881
|
/* A second line under a row's name, inside the `<th scope="row">` that makes it
|
|
3095
3882
|
one. The cell is bold because it is a heading; what hangs under it is not,
|
|
3096
3883
|
and inherited the weight -- so a pinned version read as loud as the thing it
|
|
3097
3884
|
versions. */
|
|
3098
|
-
.table th .set-hint { font-weight: 400; }
|
|
3885
|
+
.data-table th .set-hint { font-weight: 400; }
|
|
3099
3886
|
|
|
3100
|
-
.table
|
|
3101
|
-
|
|
3102
|
-
.table {
|
|
3887
|
+
.data-table {
|
|
3103
3888
|
width: 100%;
|
|
3104
3889
|
border-collapse: collapse;
|
|
3105
3890
|
font-size: var(--text-sm);
|
|
3106
3891
|
}
|
|
3107
3892
|
|
|
3108
|
-
.table th,
|
|
3109
|
-
.table td {
|
|
3893
|
+
.data-table th,
|
|
3894
|
+
.data-table td {
|
|
3110
3895
|
text-align: left;
|
|
3111
3896
|
padding: var(--space-2) var(--space-3);
|
|
3112
3897
|
border-bottom: var(--border-width) solid var(--border);
|
|
3113
3898
|
}
|
|
3114
3899
|
|
|
3115
|
-
.table th:first-child,
|
|
3116
|
-
.table td:first-child { padding-left: 0; }
|
|
3900
|
+
.data-table th:first-child,
|
|
3901
|
+
.data-table td:first-child { padding-left: 0; }
|
|
3117
3902
|
|
|
3118
|
-
.table th:last-child,
|
|
3119
|
-
.table td:last-child { padding-right: 0; }
|
|
3903
|
+
.data-table th:last-child,
|
|
3904
|
+
.data-table td:last-child { padding-right: 0; }
|
|
3120
3905
|
|
|
3121
|
-
.table thead th {
|
|
3906
|
+
.data-table thead th {
|
|
3122
3907
|
font-size: var(--text-xs);
|
|
3123
3908
|
font-weight: var(--weight-strong);
|
|
3124
3909
|
text-transform: uppercase;
|
|
@@ -3129,78 +3914,69 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3129
3914
|
|
|
3130
3915
|
/* The row's own name: a `<th scope="row">`, so it carries weight rather than
|
|
3131
3916
|
reading as one more cell. */
|
|
3132
|
-
.table tbody th { font-weight: var(--weight-strong); }
|
|
3917
|
+
.data-table tbody th { font-weight: var(--weight-strong); }
|
|
3133
3918
|
|
|
3134
|
-
.table tbody tr:last-child th,
|
|
3135
|
-
.table tbody tr:last-child td { border-bottom: none; }
|
|
3919
|
+
.data-table tbody tr:last-child th,
|
|
3920
|
+
.data-table tbody tr:last-child td { border-bottom: none; }
|
|
3136
3921
|
|
|
3137
3922
|
/* Numbers compare by column, so they align right and hold their tracks --
|
|
3138
3923
|
proportional digits shift the alignment by a pixel per glyph, which is
|
|
3139
3924
|
exactly the comparison the column was for. */
|
|
3140
|
-
.table .num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--font-mono); }
|
|
3925
|
+
.data-table .num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--font-mono); }
|
|
3141
3926
|
|
|
3142
|
-
.table thead th[style*='right'] { text-align: right; }
|
|
3927
|
+
.data-table thead th[style*='right'] { text-align: right; }
|
|
3928
|
+
}
|
|
3143
3929
|
|
|
3144
3930
|
/* ---- ./sizegrid.css ---- */
|
|
3145
|
-
|
|
3146
|
-
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3931
|
+
@layer components {
|
|
3932
|
+
/* The cell's colour, all of it, in one place.
|
|
3933
|
+
*
|
|
3934
|
+
* `SizeGrid` draws the cell's geometry with utilities and leaves every colour
|
|
3935
|
+
* here, because `on` and `chosen` override the resting state -- and a utility
|
|
3936
|
+
* is in `@layer utilities`, which beats `@layer components` whatever the
|
|
3937
|
+
* selector. Stated as `surface-panel-2` and `border-border` on the element,
|
|
3938
|
+
* the resting state won permanently and no cell ever lit up.
|
|
3939
|
+
*
|
|
3940
|
+
* That is the third time this pattern has bitten: `Select`'s tick and
|
|
3941
|
+
* `Empty`'s figure were the other two. The rule: a resting state whose
|
|
3942
|
+
* active state is a CSS rule has to be a CSS rule too. */
|
|
3150
3943
|
.size-cell {
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
background: var(--panel-2); cursor: pointer;
|
|
3944
|
+
border: var(--border-width) solid var(--border);
|
|
3945
|
+
background: var(--panel-2);
|
|
3154
3946
|
}
|
|
3155
3947
|
|
|
3156
3948
|
/* `on` follows the pointer; `chosen` is what is actually saved, shown only
|
|
3157
3949
|
when nothing is hovered so the two can never contradict each other. */
|
|
3158
|
-
.size-cell.on {
|
|
3950
|
+
.size-cell.on {
|
|
3951
|
+
border-color: var(--accent);
|
|
3952
|
+
background: color-mix(in srgb, var(--accent) 45%, transparent);
|
|
3953
|
+
}
|
|
3159
3954
|
|
|
3160
3955
|
.size-cell.chosen { border-color: var(--accent); }
|
|
3161
|
-
|
|
3162
|
-
.size-cell:disabled { cursor: default; opacity: 0.5; }
|
|
3163
|
-
|
|
3164
|
-
.size-ceiling { opacity: 0.55; }
|
|
3956
|
+
}
|
|
3165
3957
|
|
|
3166
3958
|
/* ---- ./tooltip.css ---- */
|
|
3959
|
+
@layer components {
|
|
3167
3960
|
/* A callout: something worth knowing, with nothing to answer. Distinct from a
|
|
3168
3961
|
`?`, which is an offer to read more, and from a banner, which is a problem.
|
|
3169
3962
|
The mark is a round `i` rather than an emoji so it takes the theme's colours
|
|
3170
3963
|
and stays the same size as the text beside it. */
|
|
3171
|
-
/* An info-toned `.callout`, spelled the old way. Identical to it now rather
|
|
3172
|
-
nearly identical to it, which is the whole point of the consolidation
|
|
3173
|
-
|
|
3174
|
-
|
|
3964
|
+
/* An info-toned `.callout`, spelled the old way. Identical to it now rather
|
|
3965
|
+
than nearly identical to it, which is the whole point of the consolidation.
|
|
3966
|
+
(This comment has been unterminated since before the Tailwind branch -- it
|
|
3967
|
+
stops mid-sentence and never closes, so the CSS parser merged it with the
|
|
3968
|
+
one below and recovered. Nothing was lost from the stylesheet; the rest of
|
|
3969
|
+
the sentence was. Closed here rather than guessed at.) */
|
|
3970
|
+
|
|
3971
|
+
/* What is left once `Tooltip` draws its own mark: the hover and focus that
|
|
3972
|
+
reach the mark from its parent, and the tip -- whose closed and open
|
|
3973
|
+
states are three rules the cascade resolves in order, with
|
|
3974
|
+
`data-entering` and `data-exiting` around the transition. None of that is
|
|
3975
|
+
a property on one element.
|
|
3976
|
+
|
|
3977
|
+
The `?` beside a setting, and the box it shows on hover. Built like the
|
|
3175
3978
|
status dot's tooltip and for the same reasons: `title` waits a second and a
|
|
3176
3979
|
half, renders newlines however it likes, and cannot be styled. */
|
|
3177
|
-
.explain {
|
|
3178
|
-
position: relative;
|
|
3179
|
-
flex: none;
|
|
3180
|
-
display: inline-flex;
|
|
3181
|
-
align-items: center;
|
|
3182
|
-
/* The heading it follows is tracked out, which leaves the last letter's
|
|
3183
|
-
spacing sitting between them and reads as none at all. */
|
|
3184
|
-
margin-left: var(--space-2);
|
|
3185
|
-
cursor: help;
|
|
3186
|
-
/* Headings here are uppercase and tracked out, and one of these sits inside
|
|
3187
|
-
a heading. */
|
|
3188
|
-
text-transform: none;
|
|
3189
|
-
letter-spacing: normal;
|
|
3190
|
-
font-weight: 400;
|
|
3191
|
-
}
|
|
3192
|
-
|
|
3193
|
-
.explain-mark {
|
|
3194
|
-
width: 18px;
|
|
3195
|
-
height: 18px;
|
|
3196
|
-
border-radius: 50%;
|
|
3197
|
-
border: 1px solid var(--border);
|
|
3198
|
-
color: var(--muted);
|
|
3199
|
-
font-size: var(--text-xs);
|
|
3200
|
-
line-height: 16px;
|
|
3201
|
-
text-align: center;
|
|
3202
|
-
}
|
|
3203
|
-
|
|
3204
3980
|
.explain:hover .explain-mark,
|
|
3205
3981
|
.explain:focus-visible .explain-mark { color: var(--text); border-color: var(--accent); }
|
|
3206
3982
|
|
|
@@ -3273,8 +4049,10 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3273
4049
|
}
|
|
3274
4050
|
|
|
3275
4051
|
.explain-tip strong { color: var(--text); font-weight: 600; }
|
|
4052
|
+
}
|
|
3276
4053
|
|
|
3277
4054
|
/* ---- ./illustration.css ---- */
|
|
4055
|
+
@layer components {
|
|
3278
4056
|
/* illustrations -------------------------------------------------------- */
|
|
3279
4057
|
|
|
3280
4058
|
/* Open Peeps, two colours, wearing whatever the theme is.
|
|
@@ -3301,7 +4079,21 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3301
4079
|
a shirt that lets the arm behind it show through reads as a hole. */
|
|
3302
4080
|
.illo .paper { fill: var(--illo-paper); }
|
|
3303
4081
|
|
|
4082
|
+
/* The figure in a whole-surface `Empty`, drawn in the muted grey of the
|
|
4083
|
+
sentence under it.
|
|
4084
|
+
*
|
|
4085
|
+
* A utility cannot do this, and the reason is worth keeping: `.illo` sets
|
|
4086
|
+
* `color: inherit` and is unlayered, while every Tailwind utility lives in
|
|
4087
|
+
* `@layer utilities` -- and an unlayered declaration beats a layered one
|
|
4088
|
+
* whatever the specificity. So `text-muted` on the figure lost, silently,
|
|
4089
|
+
* and the shirt came out full text black beside a muted sentence. The
|
|
4090
|
+
* original rule already carried a note about needing two classes to beat
|
|
4091
|
+
* `.illo`; layers make that impossible rather than merely fiddly. */
|
|
4092
|
+
.nothing-figure { color: var(--muted); }
|
|
4093
|
+
}
|
|
4094
|
+
|
|
3304
4095
|
/* ---- ./field.css ---- */
|
|
4096
|
+
@layer components {
|
|
3305
4097
|
/* fields --------------------------------------------------------------- */
|
|
3306
4098
|
|
|
3307
4099
|
/* Label, description, input, error -- in the order they are wanted. The
|
|
@@ -3399,7 +4191,7 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3399
4191
|
.adorned > .adorned-mark,
|
|
3400
4192
|
.adorned > .adorned-end { grid-area: 1 / 1; }
|
|
3401
4193
|
|
|
3402
|
-
.adorned.block { width: 100%; }
|
|
4194
|
+
.adorned.ctl-block { width: 100%; }
|
|
3403
4195
|
|
|
3404
4196
|
.adorned > .adorned-mark {
|
|
3405
4197
|
justify-self: start;
|
|
@@ -3452,7 +4244,34 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3452
4244
|
|
|
3453
4245
|
.adorned-lg .adorned-clear { padding: var(--space-2); }
|
|
3454
4246
|
|
|
4247
|
+
/* The eye and the clear button stop borrowing `ghost`.
|
|
4248
|
+
*
|
|
4249
|
+
* Both were `className="icon-btn ghost …"` -- reaching into `Button`'s class
|
|
4250
|
+
* vocabulary from another component, which worked only because `.ghost` was
|
|
4251
|
+
* written as `button.ghost` and they happen to be `<button>`s. It is `.btn.ghost`
|
|
4252
|
+
* now, so the borrowing stopped matching and `.icon-btn`'s panel background
|
|
4253
|
+
* came through: the baselines showed a filled tile where there had been a bare
|
|
4254
|
+
* glyph.
|
|
4255
|
+
*
|
|
4256
|
+
* Stated here instead. What they actually wanted from `ghost` is two lines,
|
|
4257
|
+
* and saying them is cheaper than either component knowing what a button's
|
|
4258
|
+
* classes are called. */
|
|
4259
|
+
.secret-eye,
|
|
4260
|
+
.adorned-clear {
|
|
4261
|
+
background: transparent;
|
|
4262
|
+
border-color: transparent;
|
|
4263
|
+
color: var(--muted);
|
|
4264
|
+
}
|
|
4265
|
+
|
|
4266
|
+
.secret-eye:hover:not(:disabled),
|
|
4267
|
+
.adorned-clear:hover:not(:disabled) {
|
|
4268
|
+
background: var(--panel-2);
|
|
4269
|
+
color: var(--text);
|
|
4270
|
+
}
|
|
4271
|
+
}
|
|
4272
|
+
|
|
3455
4273
|
/* ---- ./tour.css ---- */
|
|
4274
|
+
@layer components {
|
|
3456
4275
|
/* tour ----------------------------------------------------------------- */
|
|
3457
4276
|
|
|
3458
4277
|
/* The layer sits over everything and catches nothing: the scrim is painted by
|
|
@@ -3514,141 +4333,41 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3514
4333
|
.tour-actions { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); }
|
|
3515
4334
|
|
|
3516
4335
|
.tour-count { color: var(--muted); font-size: var(--text-xs); }
|
|
4336
|
+
}
|
|
3517
4337
|
|
|
3518
4338
|
/* The mail and forum shell, added in 0.4.0. After the rest, because the
|
|
3519
4339
|
anchored surfaces (`.pop`, `.menu-sheet`, `.cmd`) sit over everything and a
|
|
3520
4340
|
rule of equal specificity that came earlier would lose to whatever the
|
|
3521
4341
|
sheets they cover happen to set. */
|
|
3522
|
-
/* ---- ./kbd.css ---- */
|
|
3523
|
-
/* One rule where there were two identical ones, in `menu.css` and
|
|
3524
|
-
`command.css`. Both keep their class as well, so `Menu` and `Command` are
|
|
3525
|
-
`.kbd .menu-key` and `.kbd .cmd-key` now: the declarations are the same, so
|
|
3526
|
-
nothing moves, and the context classes stay available for a rule that ever
|
|
3527
|
-
needs to tell the two apart. */
|
|
3528
|
-
.kbd {
|
|
3529
|
-
flex: none;
|
|
3530
|
-
font-family: var(--font-mono);
|
|
3531
|
-
font-size: var(--text-2xs);
|
|
3532
|
-
color: var(--muted);
|
|
3533
|
-
letter-spacing: var(--tracking);
|
|
3534
|
-
}
|
|
3535
|
-
|
|
3536
4342
|
/* ---- ./stat.css ---- */
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
.
|
|
3540
|
-
|
|
3541
|
-
gap: var(--space-1);
|
|
3542
|
-
padding: var(--space-4);
|
|
3543
|
-
border: var(--border-width) solid var(--border);
|
|
3544
|
-
border-radius: var(--radius-md);
|
|
3545
|
-
background: var(--panel);
|
|
3546
|
-
}
|
|
3547
|
-
|
|
3548
|
-
/* Inside something already bordered. A bordered tile in a bordered row is two
|
|
3549
|
-
boxes saying one thing. */
|
|
3550
|
-
.stat-bare {
|
|
3551
|
-
padding: 0;
|
|
3552
|
-
border: 0;
|
|
3553
|
-
border-radius: 0;
|
|
3554
|
-
background: none;
|
|
3555
|
-
gap: 0;
|
|
3556
|
-
line-height: 1.25;
|
|
3557
|
-
}
|
|
3558
|
-
|
|
3559
|
-
.stat > p { margin: 0; }
|
|
3560
|
-
|
|
3561
|
-
.stat-label {
|
|
3562
|
-
color: var(--muted);
|
|
3563
|
-
font-size: var(--text-xs);
|
|
3564
|
-
}
|
|
3565
|
-
|
|
3566
|
-
/* Tabular figures are the point: a column of numbers is compared by its
|
|
3567
|
-
digits' positions, and proportional ones move them per row. */
|
|
3568
|
-
.stat-value {
|
|
3569
|
-
font-size: var(--text-lg);
|
|
3570
|
-
font-weight: var(--weight-strong);
|
|
3571
|
-
font-variant-numeric: tabular-nums;
|
|
3572
|
-
}
|
|
3573
|
-
|
|
3574
|
-
.stat-bare .stat-value { font-size: var(--text-sm); }
|
|
3575
|
-
|
|
3576
|
-
.stat-note {
|
|
3577
|
-
color: var(--muted);
|
|
3578
|
-
font-size: var(--text-xs);
|
|
3579
|
-
}
|
|
4343
|
+
@layer components {
|
|
4344
|
+
/* What the utilities on `Stat` cannot say: a tone on the tile colouring the
|
|
4345
|
+
figure inside it. An attribute on the parent selecting a child is a
|
|
4346
|
+
relationship; a utility is a property on one element.
|
|
3580
4347
|
|
|
3581
|
-
|
|
3582
|
-
status box,
|
|
4348
|
+
Only the figure takes the colour. Colouring the label too makes the tile a
|
|
4349
|
+
status box, where a tone here is a remark about the number. */
|
|
3583
4350
|
.stat[data-tone='info'] .stat-value { color: var(--info); }
|
|
3584
4351
|
.stat[data-tone='good'] .stat-value { color: var(--good); }
|
|
3585
4352
|
.stat[data-tone='warn'] .stat-value { color: var(--warn); }
|
|
3586
4353
|
.stat[data-tone='bad'] .stat-value { color: var(--bad); }
|
|
3587
|
-
|
|
3588
|
-
/* ---- ./shell.css ---- */
|
|
3589
|
-
/* The frame. Layout only: every colour and every step is a token, and the
|
|
3590
|
-
page inside it owns its own spacing. */
|
|
3591
|
-
.shell {
|
|
3592
|
-
display: flex;
|
|
3593
|
-
flex-direction: column;
|
|
3594
|
-
min-height: 100vh;
|
|
3595
|
-
background: var(--bg);
|
|
3596
|
-
color: var(--text);
|
|
3597
4354
|
}
|
|
3598
4355
|
|
|
3599
|
-
.
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
and `main-wide` as separate classes. */
|
|
4356
|
+
/* ---- ./shell.css ---- */
|
|
4357
|
+
@layer components {
|
|
4358
|
+
/* The measure, and only the measure.
|
|
4359
|
+
*
|
|
4360
|
+
* `Shell` draws itself with utilities -- it is layout and nothing else. What
|
|
4361
|
+
* stays here is the one value the header band, the nav band and the main band
|
|
4362
|
+
* all have to agree on: a custom property, so an app changes it in one place
|
|
4363
|
+
* rather than overriding three rules and missing the fourth. That is how
|
|
4364
|
+
* manage ended up carrying `head-inner-wide` and `main-wide` as separate
|
|
4365
|
+
* classes it had to remember together. */
|
|
3610
4366
|
.shell {
|
|
3611
4367
|
--shell-measure: 64rem;
|
|
3612
4368
|
--shell-measure-wide: 90rem;
|
|
3613
4369
|
}
|
|
3614
4370
|
|
|
3615
|
-
.shell-head-inner,
|
|
3616
|
-
.shell-nav,
|
|
3617
|
-
.shell-main {
|
|
3618
|
-
width: 100%;
|
|
3619
|
-
max-width: var(--shell-measure);
|
|
3620
|
-
margin-inline: auto;
|
|
3621
|
-
padding-inline: var(--space-4);
|
|
3622
|
-
}
|
|
3623
|
-
|
|
3624
|
-
.shell-wide { max-width: var(--shell-measure-wide); }
|
|
3625
|
-
|
|
3626
|
-
.shell-head-inner {
|
|
3627
|
-
display: flex;
|
|
3628
|
-
align-items: center;
|
|
3629
|
-
justify-content: space-between;
|
|
3630
|
-
gap: var(--space-4);
|
|
3631
|
-
padding-block: var(--space-3);
|
|
3632
|
-
min-width: 0;
|
|
3633
|
-
}
|
|
3634
|
-
|
|
3635
|
-
.shell-who {
|
|
3636
|
-
display: flex;
|
|
3637
|
-
align-items: center;
|
|
3638
|
-
gap: var(--space-3);
|
|
3639
|
-
min-width: 0;
|
|
3640
|
-
}
|
|
3641
|
-
|
|
3642
|
-
.shell-nav { padding-bottom: var(--space-2); }
|
|
3643
|
-
|
|
3644
|
-
.shell-main {
|
|
3645
|
-
flex: 1;
|
|
3646
|
-
padding-block: var(--space-6);
|
|
3647
|
-
/* `min-width: 0` because a flex child will not shrink below its content,
|
|
3648
|
-
and one wide table inside then pushes the whole page sideways. */
|
|
3649
|
-
min-width: 0;
|
|
3650
|
-
}
|
|
3651
|
-
|
|
3652
4371
|
/* The label beside the theme control, when it is drawn. */
|
|
3653
4372
|
.theme-switch {
|
|
3654
4373
|
display: flex;
|
|
@@ -3662,8 +4381,10 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3662
4381
|
font-size: var(--text-sm);
|
|
3663
4382
|
white-space: nowrap;
|
|
3664
4383
|
}
|
|
4384
|
+
}
|
|
3665
4385
|
|
|
3666
4386
|
/* ---- ./scrollarea.css ---- */
|
|
4387
|
+
@layer components {
|
|
3667
4388
|
/* A box that scrolls, with its edges drawn.
|
|
3668
4389
|
|
|
3669
4390
|
The fades are `::before`/`::after` on the scroller itself, pinned with
|
|
@@ -3756,8 +4477,10 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3756
4477
|
.scroller.faded::before,
|
|
3757
4478
|
.scroller.faded::after { transition: none; }
|
|
3758
4479
|
}
|
|
4480
|
+
}
|
|
3759
4481
|
|
|
3760
4482
|
/* ---- ./splitpane.css ---- */
|
|
4483
|
+
@layer components {
|
|
3761
4484
|
/* Two panes and the handle between them. */
|
|
3762
4485
|
.split {
|
|
3763
4486
|
display: flex;
|
|
@@ -3857,8 +4580,10 @@ body.splitting * { cursor: inherit !important; }
|
|
|
3857
4580
|
@media (prefers-reduced-motion: reduce) {
|
|
3858
4581
|
.split-grip { transition: none; }
|
|
3859
4582
|
}
|
|
4583
|
+
}
|
|
3860
4584
|
|
|
3861
4585
|
/* ---- ./popover.css ---- */
|
|
4586
|
+
@layer components {
|
|
3862
4587
|
/* A small surface anchored to what opened it. Same sheet as the select's list,
|
|
3863
4588
|
because they are the same object -- something that appeared over the page,
|
|
3864
4589
|
attached to a control. */
|
|
@@ -3912,29 +4637,17 @@ body.splitting * { cursor: inherit !important; }
|
|
|
3912
4637
|
.pop[data-entering],
|
|
3913
4638
|
.pop[data-exiting] { animation: none; }
|
|
3914
4639
|
}
|
|
3915
|
-
|
|
3916
|
-
/* ---- ./menu.css ---- */
|
|
3917
|
-
/* A list of verbs, on the same sheet as every other anchored surface. */
|
|
3918
|
-
.menu-sheet {
|
|
3919
|
-
background: var(--panel);
|
|
3920
|
-
border: var(--border-width) solid var(--border);
|
|
3921
|
-
border-radius: var(--radius-md);
|
|
3922
|
-
box-shadow: var(--shadow-2);
|
|
3923
|
-
min-width: 12rem;
|
|
3924
|
-
max-width: min(20rem, calc(100vw - var(--space-6)));
|
|
3925
|
-
max-height: 24rem;
|
|
3926
|
-
overflow: auto;
|
|
3927
|
-
overscroll-behavior: contain;
|
|
3928
|
-
z-index: 60;
|
|
3929
4640
|
}
|
|
3930
4641
|
|
|
3931
|
-
.
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
4642
|
+
/* ---- ./menu.css ---- */
|
|
4643
|
+
@layer components {
|
|
4644
|
+
/* What is left once `Menu` draws its sheet and its list: the rows, whose
|
|
4645
|
+
`data-focused`, `data-pressed` and danger states React Aria stamps on
|
|
4646
|
+
them, and the rules that reach from a row into what it holds. The
|
|
4647
|
+
`box-shadow` stays on the sheet too -- `--shadow-2` is a token, and a
|
|
4648
|
+
utility would have to inline it. */
|
|
3937
4649
|
|
|
4650
|
+
/* A list of verbs, on the same sheet as every other anchored surface. */
|
|
3938
4651
|
.menu-item {
|
|
3939
4652
|
display: flex;
|
|
3940
4653
|
align-items: center;
|
|
@@ -3973,14 +4686,6 @@ body.splitting * { cursor: inherit !important; }
|
|
|
3973
4686
|
/* The binding, set in the mono face at the size of the small print. Right of
|
|
3974
4687
|
the row and never the reason the row is wide: `flex: none` with the label
|
|
3975
4688
|
taking the slack. */
|
|
3976
|
-
.menu-key {
|
|
3977
|
-
flex: none;
|
|
3978
|
-
font-family: var(--font-mono);
|
|
3979
|
-
font-size: var(--text-2xs);
|
|
3980
|
-
color: var(--muted);
|
|
3981
|
-
letter-spacing: var(--tracking);
|
|
3982
|
-
}
|
|
3983
|
-
|
|
3984
4689
|
.menu-more { flex: none; color: var(--muted); }
|
|
3985
4690
|
|
|
3986
4691
|
.menu-rule {
|
|
@@ -4016,7 +4721,11 @@ body.splitting * { cursor: inherit !important; }
|
|
|
4016
4721
|
.menu-sheet[data-exiting] { animation: none; }
|
|
4017
4722
|
}
|
|
4018
4723
|
|
|
4724
|
+
.menu-sheet { box-shadow: var(--shadow-2); }
|
|
4725
|
+
}
|
|
4726
|
+
|
|
4019
4727
|
/* ---- ./command.css ---- */
|
|
4728
|
+
@layer components {
|
|
4020
4729
|
/* The command palette: a field over the app and a list under it. */
|
|
4021
4730
|
.cmd-scrim {
|
|
4022
4731
|
position: fixed;
|
|
@@ -4133,14 +4842,6 @@ body.splitting * { cursor: inherit !important; }
|
|
|
4133
4842
|
white-space: nowrap;
|
|
4134
4843
|
}
|
|
4135
4844
|
|
|
4136
|
-
.cmd-key {
|
|
4137
|
-
flex: none;
|
|
4138
|
-
font-family: var(--font-mono);
|
|
4139
|
-
font-size: var(--text-2xs);
|
|
4140
|
-
color: var(--muted);
|
|
4141
|
-
letter-spacing: var(--tracking);
|
|
4142
|
-
}
|
|
4143
|
-
|
|
4144
4845
|
.cmd-empty {
|
|
4145
4846
|
margin: 0;
|
|
4146
4847
|
padding: var(--space-6) var(--space-4);
|
|
@@ -4166,25 +4867,18 @@ body.splitting * { cursor: inherit !important; }
|
|
|
4166
4867
|
.cmd-scrim[data-exiting],
|
|
4167
4868
|
.cmd[data-entering] { animation: none; }
|
|
4168
4869
|
}
|
|
4870
|
+
}
|
|
4169
4871
|
|
|
4170
4872
|
/* ---- ./pagination.css ---- */
|
|
4171
|
-
|
|
4172
|
-
|
|
4173
|
-
|
|
4174
|
-
|
|
4175
|
-
|
|
4176
|
-
gap: var(--space-4);
|
|
4177
|
-
flex-wrap: wrap;
|
|
4178
|
-
padding: var(--space-2) 0;
|
|
4179
|
-
font-size: var(--text-xs);
|
|
4180
|
-
color: var(--muted);
|
|
4181
|
-
}
|
|
4873
|
+
@layer components {
|
|
4874
|
+
/* What is left once `Pagination` draws its own frame: the step and page
|
|
4875
|
+
buttons, whose hover, focus-visible and current states are pseudo-classes
|
|
4876
|
+
on two selectors at once, and `.pager-count strong`, which reaches into
|
|
4877
|
+
text the caller passed. */
|
|
4182
4878
|
|
|
4183
|
-
|
|
4879
|
+
/* Moving through a list that does not fit, and saying how far in you are. */
|
|
4184
4880
|
.pager-count strong { color: var(--text); font-weight: var(--weight-strong); }
|
|
4185
4881
|
|
|
4186
|
-
.pager-controls { display: flex; align-items: center; gap: var(--space-1); }
|
|
4187
|
-
|
|
4188
4882
|
.pager-step,
|
|
4189
4883
|
.pager-page {
|
|
4190
4884
|
display: inline-flex;
|
|
@@ -4295,8 +4989,10 @@ body.splitting * { cursor: inherit !important; }
|
|
|
4295
4989
|
.pager-page,
|
|
4296
4990
|
.pager-jump { transition: none; }
|
|
4297
4991
|
}
|
|
4992
|
+
}
|
|
4298
4993
|
|
|
4299
4994
|
/* ---- ./identity.css ---- */
|
|
4995
|
+
@layer components {
|
|
4300
4996
|
/* A person, said in one line: initials, name, sometimes the address. */
|
|
4301
4997
|
.ident {
|
|
4302
4998
|
display: inline-flex;
|
|
@@ -4376,6 +5072,8 @@ body.splitting * { cursor: inherit !important; }
|
|
|
4376
5072
|
}
|
|
4377
5073
|
|
|
4378
5074
|
.ident-remove {
|
|
5075
|
+
/* Stated rather than inherited. These came from the `button, input, textarea, select` reset, which applied control styling to everything that happened to be a `<button>` -- so this rule spent its opening lines undoing it, and would break silently if that rule ever moved. It did move, to `.btn`. */
|
|
5076
|
+
font: inherit;
|
|
4379
5077
|
flex: none;
|
|
4380
5078
|
display: inline-flex;
|
|
4381
5079
|
align-items: center;
|
|
@@ -4395,6 +5093,7 @@ body.splitting * { cursor: inherit !important; }
|
|
|
4395
5093
|
|
|
4396
5094
|
.ident-remove:hover { background: var(--panel); color: var(--text); }
|
|
4397
5095
|
.ident-remove:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
5096
|
+
}
|
|
4398
5097
|
/* ---- tf: the themes ---- */
|
|
4399
5098
|
:root[data-theme='night']{--bg:#0f1115;--panel:#161a21;--panel-2:#1c222b;--border:#262d38;--text:#e6e9ef;--muted:#8b94a4;--accent:#5b9dff;--accent-dim:#2a4877;--app-bg:#0f1115;color-scheme:dark}
|
|
4400
5099
|
:root[data-theme='paper']{--bg:#f6f7f9;--panel:#ffffff;--panel-2:#f0f2f5;--border:#e4e8ec;--border-strong:#8792a1;--text:#191d23;--muted:#5d6773;--accent:#0e7872;--accent-dim:#7fbdb8;--on-accent:#ffffff;--good:#1c7a4a;--warn:#8a6216;--bad:#b3312c;--info:#216bc9;--shadow-1:0 4px 14px rgba(16, 24, 40, 0.08);--shadow-2:0 8px 24px rgba(16, 24, 40, 0.10);--shadow-3:0 12px 32px rgba(16, 24, 40, 0.12);--scrim:rgba(16, 24, 40, 0.32);color-scheme:light}
|