@wtfalch/design 0.3.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/Command.d.ts +57 -0
- package/dist/components/Command.js +36 -0
- package/dist/components/Icon.d.ts +17 -4
- package/dist/components/Identity.d.ts +40 -0
- package/dist/components/Identity.js +36 -0
- package/dist/components/Menu.d.ts +62 -0
- package/dist/components/Menu.js +20 -0
- package/dist/components/Modal.d.ts +20 -1
- package/dist/components/Modal.js +11 -3
- package/dist/components/Pagination.d.ts +52 -0
- package/dist/components/Pagination.js +101 -0
- package/dist/components/Popover.d.ts +46 -0
- package/dist/components/Popover.js +5 -0
- package/dist/components/ScrollArea.d.ts +66 -0
- package/dist/components/ScrollArea.js +47 -0
- package/dist/components/SplitPane.d.ts +57 -0
- package/dist/components/SplitPane.js +103 -0
- package/dist/components/Toggle.js +26 -11
- package/dist/components/iconNames.d.ts +1 -1
- package/dist/components/iconNames.js +25 -0
- package/dist/components/icons.js +97 -0
- package/dist/components/initials.d.ts +36 -0
- package/dist/components/initials.js +51 -0
- package/dist/components/pageWindow.d.ts +21 -0
- package/dist/components/pageWindow.js +46 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +22 -0
- package/dist/styles/index.css +792 -0
- package/dist/tf.css +792 -0
- package/dist/valet.css +792 -0
- package/package.json +10 -13
package/dist/valet.css
CHANGED
|
@@ -2608,6 +2608,61 @@ input[type='radio']:active:not(:disabled) {
|
|
|
2608
2608
|
the package shipped a `tone="bad"` prop that changed nothing. */
|
|
2609
2609
|
.dialog-bad { border-color: color-mix(in oklab, var(--bad) 45%, var(--border)); }
|
|
2610
2610
|
|
|
2611
|
+
/* The sheet: the same window, anchored to an edge.
|
|
2612
|
+
|
|
2613
|
+
Only placement and entry differ. The trap, the restore, the scrim and the
|
|
2614
|
+
header/body/footer are the modal's, which is the point of it being a prop
|
|
2615
|
+
rather than a second component. */
|
|
2616
|
+
.backdrop.sheeted { padding: 0; }
|
|
2617
|
+
.backdrop.sheeted.from-right { justify-content: flex-end; align-items: stretch; }
|
|
2618
|
+
.backdrop.sheeted.from-left { justify-content: flex-start; align-items: stretch; }
|
|
2619
|
+
.backdrop.sheeted.from-bottom { align-items: flex-end; justify-content: stretch; }
|
|
2620
|
+
|
|
2621
|
+
.modal.sheet {
|
|
2622
|
+
max-height: 100%;
|
|
2623
|
+
border-radius: 0;
|
|
2624
|
+
box-shadow: var(--shadow-3);
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
.modal.sheet.from-right,
|
|
2628
|
+
.modal.sheet.from-left {
|
|
2629
|
+
height: 100%;
|
|
2630
|
+
/* A default that is a panel rather than a window. `width` overrides it. */
|
|
2631
|
+
width: min(26rem, 100vw);
|
|
2632
|
+
max-width: 100vw;
|
|
2633
|
+
}
|
|
2634
|
+
|
|
2635
|
+
.modal.sheet.from-right { border-left: var(--border-width) solid var(--border); }
|
|
2636
|
+
.modal.sheet.from-left { border-right: var(--border-width) solid var(--border); }
|
|
2637
|
+
|
|
2638
|
+
.modal.sheet.from-bottom {
|
|
2639
|
+
width: 100%;
|
|
2640
|
+
max-width: 100%;
|
|
2641
|
+
max-height: 85vh;
|
|
2642
|
+
border-top: var(--border-width) solid var(--border);
|
|
2643
|
+
/* Rounded at the top only: the bottom edge is the screen's. */
|
|
2644
|
+
border-radius: var(--radius-lg) var(--radius-lg) 0 0;
|
|
2645
|
+
}
|
|
2646
|
+
|
|
2647
|
+
/* It comes from the edge it is anchored to. A sheet that fades in place is a
|
|
2648
|
+
modal wearing a sheet's shape -- the movement is what says which way it
|
|
2649
|
+
goes and where it will go back to. */
|
|
2650
|
+
.modal.sheet.from-right[data-entering] { animation: sheet-right var(--dur-md) var(--ease-out); }
|
|
2651
|
+
.modal.sheet.from-right[data-exiting] { animation: sheet-right var(--dur-fast) var(--ease) reverse; }
|
|
2652
|
+
.modal.sheet.from-left[data-entering] { animation: sheet-left var(--dur-md) var(--ease-out); }
|
|
2653
|
+
.modal.sheet.from-left[data-exiting] { animation: sheet-left var(--dur-fast) var(--ease) reverse; }
|
|
2654
|
+
.modal.sheet.from-bottom[data-entering] { animation: sheet-bottom var(--dur-md) var(--ease-out); }
|
|
2655
|
+
.modal.sheet.from-bottom[data-exiting] { animation: sheet-bottom var(--dur-fast) var(--ease) reverse; }
|
|
2656
|
+
|
|
2657
|
+
@keyframes sheet-right { from { transform: translateX(100%); } to { transform: none; } }
|
|
2658
|
+
@keyframes sheet-left { from { transform: translateX(-100%); } to { transform: none; } }
|
|
2659
|
+
@keyframes sheet-bottom { from { transform: translateY(100%); } to { transform: none; } }
|
|
2660
|
+
|
|
2661
|
+
@media (prefers-reduced-motion: reduce) {
|
|
2662
|
+
.modal.sheet[data-entering],
|
|
2663
|
+
.modal.sheet[data-exiting] { animation: none; }
|
|
2664
|
+
}
|
|
2665
|
+
|
|
2611
2666
|
/* ---- ./slider.css ---- */
|
|
2612
2667
|
/* slider ----------------------------------------------------------------- */
|
|
2613
2668
|
/* A number from a range -- `Slider.tsx` says when one is the right control.
|
|
@@ -3361,6 +3416,743 @@ input[type='range'].slider:disabled::-moz-range-thumb { opacity: 0; }
|
|
|
3361
3416
|
.tour-actions { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); }
|
|
3362
3417
|
|
|
3363
3418
|
.tour-count { color: var(--muted); font-size: var(--text-xs); }
|
|
3419
|
+
|
|
3420
|
+
/* The mail and forum shell, added in 0.4.0. After the rest, because the
|
|
3421
|
+
anchored surfaces (`.pop`, `.menu-sheet`, `.cmd`) sit over everything and a
|
|
3422
|
+
rule of equal specificity that came earlier would lose to whatever the
|
|
3423
|
+
sheets they cover happen to set. */
|
|
3424
|
+
/* ---- ./scrollarea.css ---- */
|
|
3425
|
+
/* A box that scrolls, with its edges drawn.
|
|
3426
|
+
|
|
3427
|
+
The fades are `::before`/`::after` on the scroller itself, pinned with
|
|
3428
|
+
`position: sticky` rather than `absolute`: an absolutely positioned overlay
|
|
3429
|
+
inside a scrolling box scrolls with the content, so the gradient slides off
|
|
3430
|
+
the top the moment anybody moves. Sticky keeps it against the padding edge
|
|
3431
|
+
while the content runs underneath. */
|
|
3432
|
+
.scroller {
|
|
3433
|
+
position: relative;
|
|
3434
|
+
min-height: 0;
|
|
3435
|
+
min-width: 0;
|
|
3436
|
+
/* A scroll that reaches the end and carries on into the page behind it is
|
|
3437
|
+
how a pane scrolls the whole app away. */
|
|
3438
|
+
overscroll-behavior: contain;
|
|
3439
|
+
scrollbar-color: var(--border-strong) transparent;
|
|
3440
|
+
scrollbar-width: thin;
|
|
3441
|
+
}
|
|
3442
|
+
|
|
3443
|
+
/* A labelled scroller is focusable, so it owes a visible focus indicator --
|
|
3444
|
+
the same obligation the pager's buttons have. `--focus-ring` is a
|
|
3445
|
+
box-shadow value, not an outline shorthand. */
|
|
3446
|
+
.scroller:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
3447
|
+
|
|
3448
|
+
.scroller.axis-y { overflow-y: auto; overflow-x: hidden; }
|
|
3449
|
+
.scroller.axis-x { overflow-x: auto; overflow-y: hidden; }
|
|
3450
|
+
.scroller.axis-both { overflow: auto; }
|
|
3451
|
+
|
|
3452
|
+
.scroller::-webkit-scrollbar { width: var(--space-3); height: var(--space-3); }
|
|
3453
|
+
.scroller::-webkit-scrollbar-track { background: transparent; }
|
|
3454
|
+
.scroller::-webkit-scrollbar-thumb {
|
|
3455
|
+
background: var(--border-strong);
|
|
3456
|
+
/* Inset by a transparent border rather than by width, so the thumb is thin
|
|
3457
|
+
while the grab target stays the full `--space-3`. A bar drawn at the
|
|
3458
|
+
thumb's width looks right and cannot be hit. */
|
|
3459
|
+
border: var(--space-1) solid transparent;
|
|
3460
|
+
background-clip: padding-box;
|
|
3461
|
+
border-radius: var(--radius-pill);
|
|
3462
|
+
}
|
|
3463
|
+
.scroller::-webkit-scrollbar-thumb:hover { background: var(--muted); background-clip: padding-box; }
|
|
3464
|
+
.scroller::-webkit-scrollbar-corner { background: transparent; }
|
|
3465
|
+
|
|
3466
|
+
.scroller.barless { scrollbar-width: none; }
|
|
3467
|
+
.scroller.barless::-webkit-scrollbar { width: 0; height: 0; }
|
|
3468
|
+
|
|
3469
|
+
/* The fades.
|
|
3470
|
+
|
|
3471
|
+
Vertical only, and that is the whole point rather than a shortcut: the
|
|
3472
|
+
misreading this exists to prevent is "the list ended", which is a claim
|
|
3473
|
+
about the bottom edge. Content cut off at the right edge of a box already
|
|
3474
|
+
reads as cut off -- a word sliced in half is its own affordance -- so a
|
|
3475
|
+
horizontal scroller gets the scrollbar and nothing else.
|
|
3476
|
+
|
|
3477
|
+
`--panel` because that is what a scroller is nearly always sitting on;
|
|
3478
|
+
`fade={false}` is the escape for when it is not. */
|
|
3479
|
+
.scroller.faded::before,
|
|
3480
|
+
.scroller.faded::after {
|
|
3481
|
+
content: '';
|
|
3482
|
+
position: sticky;
|
|
3483
|
+
left: 0;
|
|
3484
|
+
display: block;
|
|
3485
|
+
height: var(--space-5);
|
|
3486
|
+
pointer-events: none;
|
|
3487
|
+
opacity: 0;
|
|
3488
|
+
transition: opacity var(--dur-fast) var(--ease);
|
|
3489
|
+
z-index: 1;
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
/* Negative margins on the far side, so neither strip takes a row of height
|
|
3493
|
+
from the content it is drawn over. */
|
|
3494
|
+
.scroller.faded::before {
|
|
3495
|
+
top: 0;
|
|
3496
|
+
margin-bottom: calc(var(--space-5) * -1);
|
|
3497
|
+
background: linear-gradient(to bottom, var(--panel), transparent);
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
.scroller.faded::after {
|
|
3501
|
+
bottom: 0;
|
|
3502
|
+
margin-top: calc(var(--space-5) * -1);
|
|
3503
|
+
background: linear-gradient(to top, var(--panel), transparent);
|
|
3504
|
+
}
|
|
3505
|
+
|
|
3506
|
+
.scroller.faded[data-top='true']::before { opacity: 1; }
|
|
3507
|
+
.scroller.faded[data-bottom='true']::after { opacity: 1; }
|
|
3508
|
+
|
|
3509
|
+
/* A scroller that only goes sideways has no vertical edge to mark. */
|
|
3510
|
+
.scroller.faded.axis-x::before,
|
|
3511
|
+
.scroller.faded.axis-x::after { content: none; }
|
|
3512
|
+
|
|
3513
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3514
|
+
.scroller.faded::before,
|
|
3515
|
+
.scroller.faded::after { transition: none; }
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
/* ---- ./splitpane.css ---- */
|
|
3519
|
+
/* Two panes and the handle between them. */
|
|
3520
|
+
.split {
|
|
3521
|
+
display: flex;
|
|
3522
|
+
min-width: 0;
|
|
3523
|
+
min-height: 0;
|
|
3524
|
+
width: 100%;
|
|
3525
|
+
height: 100%;
|
|
3526
|
+
}
|
|
3527
|
+
|
|
3528
|
+
.split.dir-row { flex-direction: row; }
|
|
3529
|
+
.split.dir-column { flex-direction: column; }
|
|
3530
|
+
|
|
3531
|
+
/* `min-width: 0` is the whole reason this file exists.
|
|
3532
|
+
|
|
3533
|
+
A flex child will not shrink below the intrinsic width of its content, and
|
|
3534
|
+
the intrinsic width of a mail list is the longest unbroken subject line in
|
|
3535
|
+
it. Without this the handle stops partway across and nothing on screen says
|
|
3536
|
+
why -- it just refuses, differently for every mailbox. */
|
|
3537
|
+
.split-pane {
|
|
3538
|
+
min-width: 0;
|
|
3539
|
+
min-height: 0;
|
|
3540
|
+
overflow: hidden;
|
|
3541
|
+
display: flex;
|
|
3542
|
+
flex-direction: column;
|
|
3543
|
+
}
|
|
3544
|
+
|
|
3545
|
+
.split-first { flex: 0 0 var(--split); }
|
|
3546
|
+
.split-second { flex: 1 1 0; }
|
|
3547
|
+
|
|
3548
|
+
/* The handle is wider than it looks: a hairline is a correct-looking divider
|
|
3549
|
+
and an impossible pointer target. The grip is the line, drawn at
|
|
3550
|
+
`--border-width` down the middle of a `--space-3` strip, and the strip is
|
|
3551
|
+
the thing the pointer has to hit. */
|
|
3552
|
+
.split-handle {
|
|
3553
|
+
flex: none;
|
|
3554
|
+
position: relative;
|
|
3555
|
+
background: transparent;
|
|
3556
|
+
border: none;
|
|
3557
|
+
padding: 0;
|
|
3558
|
+
touch-action: none;
|
|
3559
|
+
}
|
|
3560
|
+
|
|
3561
|
+
.split.dir-row > .split-handle {
|
|
3562
|
+
width: var(--space-3);
|
|
3563
|
+
margin: 0 calc(var(--space-3) / -2);
|
|
3564
|
+
cursor: col-resize;
|
|
3565
|
+
z-index: 1;
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
.split.dir-column > .split-handle {
|
|
3569
|
+
height: var(--space-3);
|
|
3570
|
+
margin: calc(var(--space-3) / -2) 0;
|
|
3571
|
+
cursor: row-resize;
|
|
3572
|
+
z-index: 1;
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3575
|
+
/* The line itself, drawn in the middle of the target. */
|
|
3576
|
+
.split-grip {
|
|
3577
|
+
position: absolute;
|
|
3578
|
+
background: var(--border);
|
|
3579
|
+
transition: background var(--dur-fast) var(--ease);
|
|
3580
|
+
}
|
|
3581
|
+
|
|
3582
|
+
.split.dir-row .split-grip {
|
|
3583
|
+
top: 0;
|
|
3584
|
+
bottom: 0;
|
|
3585
|
+
left: 50%;
|
|
3586
|
+
width: var(--border-width);
|
|
3587
|
+
transform: translateX(-50%);
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
.split.dir-column .split-grip {
|
|
3591
|
+
left: 0;
|
|
3592
|
+
right: 0;
|
|
3593
|
+
top: 50%;
|
|
3594
|
+
height: var(--border-width);
|
|
3595
|
+
transform: translateY(-50%);
|
|
3596
|
+
}
|
|
3597
|
+
|
|
3598
|
+
.split-handle:hover .split-grip,
|
|
3599
|
+
.split-handle:active .split-grip { background: var(--accent); }
|
|
3600
|
+
|
|
3601
|
+
/* Focus is on the grip rather than the padded target: a ring around a 12px
|
|
3602
|
+
transparent strip reads as a ring around nothing. */
|
|
3603
|
+
.split-handle:focus-visible { outline: none; }
|
|
3604
|
+
.split-handle:focus-visible .split-grip {
|
|
3605
|
+
background: var(--accent);
|
|
3606
|
+
outline: none;
|
|
3607
|
+
box-shadow: var(--focus-ring);
|
|
3608
|
+
}
|
|
3609
|
+
|
|
3610
|
+
/* While dragging. On the body, because the pointer crosses both panes and the
|
|
3611
|
+
text in them would otherwise flip the cursor to an I-beam the whole way. */
|
|
3612
|
+
body.splitting { cursor: col-resize; user-select: none; }
|
|
3613
|
+
body.splitting * { cursor: inherit !important; }
|
|
3614
|
+
|
|
3615
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3616
|
+
.split-grip { transition: none; }
|
|
3617
|
+
}
|
|
3618
|
+
|
|
3619
|
+
/* ---- ./popover.css ---- */
|
|
3620
|
+
/* A small surface anchored to what opened it. Same sheet as the select's list,
|
|
3621
|
+
because they are the same object -- something that appeared over the page,
|
|
3622
|
+
attached to a control. */
|
|
3623
|
+
.pop {
|
|
3624
|
+
background: var(--panel);
|
|
3625
|
+
border: var(--border-width) solid var(--border);
|
|
3626
|
+
border-radius: var(--radius-md);
|
|
3627
|
+
box-shadow: var(--shadow-2);
|
|
3628
|
+
max-width: min(22rem, calc(100vw - var(--space-6)));
|
|
3629
|
+
/* React Aria writes `max-height` inline, to the space left in the viewport.
|
|
3630
|
+
A surface that runs to the bottom of the screen is not a popover any more,
|
|
3631
|
+
so the sheet caps it and scrolls instead. Same argument as the select
|
|
3632
|
+
list's 280px cap, and the same number is not required -- a popover holds
|
|
3633
|
+
controls rather than a list, and stops being one sooner. */
|
|
3634
|
+
max-height: 24rem;
|
|
3635
|
+
overflow: auto;
|
|
3636
|
+
overscroll-behavior: contain;
|
|
3637
|
+
/* React Aria positions this element inline; nothing here may set `position`,
|
|
3638
|
+
`top` or `left` or it fights the measurement and lands in the corner. */
|
|
3639
|
+
z-index: 60;
|
|
3640
|
+
}
|
|
3641
|
+
|
|
3642
|
+
.pop-body {
|
|
3643
|
+
padding: var(--space-3);
|
|
3644
|
+
outline: none;
|
|
3645
|
+
color: var(--text);
|
|
3646
|
+
font-size: var(--text-sm);
|
|
3647
|
+
}
|
|
3648
|
+
|
|
3649
|
+
/* Entry, and the direction it comes from. `data-placement` is React Aria's,
|
|
3650
|
+
set to where the surface actually went rather than where it was asked to go,
|
|
3651
|
+
so the movement agrees with the flip. */
|
|
3652
|
+
.pop[data-entering] { animation: pop-in var(--dur-fast) var(--ease-out); }
|
|
3653
|
+
.pop[data-exiting] { animation: pop-in var(--dur-fast) var(--ease) reverse; }
|
|
3654
|
+
|
|
3655
|
+
@keyframes pop-in {
|
|
3656
|
+
from { opacity: 0; transform: translateY(calc(var(--nudge) * -1)); }
|
|
3657
|
+
to { opacity: 1; transform: none; }
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3660
|
+
.pop[data-placement='top'][data-entering],
|
|
3661
|
+
.pop[data-placement='top left'][data-entering],
|
|
3662
|
+
.pop[data-placement='top right'][data-entering] { animation-name: pop-in-up; }
|
|
3663
|
+
|
|
3664
|
+
@keyframes pop-in-up {
|
|
3665
|
+
from { opacity: 0; transform: translateY(var(--nudge)); }
|
|
3666
|
+
to { opacity: 1; transform: none; }
|
|
3667
|
+
}
|
|
3668
|
+
|
|
3669
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3670
|
+
.pop[data-entering],
|
|
3671
|
+
.pop[data-exiting] { animation: none; }
|
|
3672
|
+
}
|
|
3673
|
+
|
|
3674
|
+
/* ---- ./menu.css ---- */
|
|
3675
|
+
/* A list of verbs, on the same sheet as every other anchored surface. */
|
|
3676
|
+
.menu-sheet {
|
|
3677
|
+
background: var(--panel);
|
|
3678
|
+
border: var(--border-width) solid var(--border);
|
|
3679
|
+
border-radius: var(--radius-md);
|
|
3680
|
+
box-shadow: var(--shadow-2);
|
|
3681
|
+
min-width: 12rem;
|
|
3682
|
+
max-width: min(20rem, calc(100vw - var(--space-6)));
|
|
3683
|
+
max-height: 24rem;
|
|
3684
|
+
overflow: auto;
|
|
3685
|
+
overscroll-behavior: contain;
|
|
3686
|
+
z-index: 60;
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
.menu-list {
|
|
3690
|
+
padding: var(--space-1);
|
|
3691
|
+
outline: none;
|
|
3692
|
+
display: grid;
|
|
3693
|
+
gap: var(--border-width);
|
|
3694
|
+
}
|
|
3695
|
+
|
|
3696
|
+
.menu-item {
|
|
3697
|
+
display: flex;
|
|
3698
|
+
align-items: center;
|
|
3699
|
+
gap: var(--space-2);
|
|
3700
|
+
padding: var(--space-2) var(--space-2);
|
|
3701
|
+
border-radius: var(--radius-sm);
|
|
3702
|
+
font-size: var(--text-sm);
|
|
3703
|
+
color: var(--text);
|
|
3704
|
+
cursor: default;
|
|
3705
|
+
outline: none;
|
|
3706
|
+
}
|
|
3707
|
+
|
|
3708
|
+
/* One highlight for pointer and keyboard.
|
|
3709
|
+
|
|
3710
|
+
React Aria sets `data-focused` for whichever is driving, so the row under
|
|
3711
|
+
the cursor and the row the arrows are on are the same row drawn the same
|
|
3712
|
+
way. Two separate treatments -- `:hover` plus a `.active` class -- is how a
|
|
3713
|
+
menu ends up showing two highlighted rows at once, which is the state where
|
|
3714
|
+
pressing Enter does something other than what it looks like it will do. */
|
|
3715
|
+
.menu-item[data-focused] { background: var(--panel-2); }
|
|
3716
|
+
.menu-item[data-disabled] { color: var(--muted); opacity: 0.6; }
|
|
3717
|
+
|
|
3718
|
+
.menu-icon { flex: none; width: var(--space-4); height: var(--space-4); color: var(--muted); }
|
|
3719
|
+
.menu-item[data-focused] .menu-icon { color: var(--text); }
|
|
3720
|
+
|
|
3721
|
+
.menu-text { flex: 1 1 auto; min-width: 0; display: grid; }
|
|
3722
|
+
.menu-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
3723
|
+
.menu-desc {
|
|
3724
|
+
font-size: var(--text-xs);
|
|
3725
|
+
color: var(--muted);
|
|
3726
|
+
overflow: hidden;
|
|
3727
|
+
text-overflow: ellipsis;
|
|
3728
|
+
white-space: nowrap;
|
|
3729
|
+
}
|
|
3730
|
+
|
|
3731
|
+
/* The binding, set in the mono face at the size of the small print. Right of
|
|
3732
|
+
the row and never the reason the row is wide: `flex: none` with the label
|
|
3733
|
+
taking the slack. */
|
|
3734
|
+
.menu-key {
|
|
3735
|
+
flex: none;
|
|
3736
|
+
font-family: var(--font-mono);
|
|
3737
|
+
font-size: var(--text-2xs);
|
|
3738
|
+
color: var(--muted);
|
|
3739
|
+
letter-spacing: var(--tracking);
|
|
3740
|
+
}
|
|
3741
|
+
|
|
3742
|
+
.menu-more { flex: none; color: var(--muted); }
|
|
3743
|
+
|
|
3744
|
+
.menu-rule {
|
|
3745
|
+
height: var(--border-width);
|
|
3746
|
+
background: var(--border);
|
|
3747
|
+
border: none;
|
|
3748
|
+
margin: var(--space-1) 0;
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3751
|
+
.menu-section-title {
|
|
3752
|
+
padding: var(--space-2) var(--space-2) var(--space-1);
|
|
3753
|
+
font-size: var(--text-2xs);
|
|
3754
|
+
text-transform: uppercase;
|
|
3755
|
+
letter-spacing: var(--tracking);
|
|
3756
|
+
color: var(--muted);
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
/* Destructive.
|
|
3760
|
+
|
|
3761
|
+
Tinted into `--panel` at 10%, the same mix `Pill` uses, and for the same
|
|
3762
|
+
measured reason: mixing a mid tone into the darker surface drags the
|
|
3763
|
+
background towards the text and drops the row's contrast on a light theme.
|
|
3764
|
+
Colour is never the only signal -- the row is also last, also below a rule. */
|
|
3765
|
+
.menu-item.danger { color: var(--bad); }
|
|
3766
|
+
.menu-item.danger .menu-icon { color: var(--bad); }
|
|
3767
|
+
.menu-item.danger[data-focused] { background: color-mix(in srgb, var(--bad) 10%, var(--panel)); }
|
|
3768
|
+
|
|
3769
|
+
.menu-sheet[data-entering] { animation: pop-in var(--dur-fast) var(--ease-out); }
|
|
3770
|
+
.menu-sheet[data-exiting] { animation: pop-in var(--dur-fast) var(--ease) reverse; }
|
|
3771
|
+
|
|
3772
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3773
|
+
.menu-sheet[data-entering],
|
|
3774
|
+
.menu-sheet[data-exiting] { animation: none; }
|
|
3775
|
+
}
|
|
3776
|
+
|
|
3777
|
+
/* ---- ./command.css ---- */
|
|
3778
|
+
/* The command palette: a field over the app and a list under it. */
|
|
3779
|
+
.cmd-scrim {
|
|
3780
|
+
position: fixed;
|
|
3781
|
+
inset: 0;
|
|
3782
|
+
z-index: 80;
|
|
3783
|
+
background: var(--scrim);
|
|
3784
|
+
display: flex;
|
|
3785
|
+
justify-content: center;
|
|
3786
|
+
/* Near the top rather than centred. The list grows downwards as you type,
|
|
3787
|
+
and a centred palette moves its own first row every keystroke -- which is
|
|
3788
|
+
the row you are aiming at. */
|
|
3789
|
+
align-items: flex-start;
|
|
3790
|
+
padding: 10vh var(--space-4) var(--space-4);
|
|
3791
|
+
}
|
|
3792
|
+
|
|
3793
|
+
.cmd {
|
|
3794
|
+
width: min(34rem, 100%);
|
|
3795
|
+
background: var(--panel);
|
|
3796
|
+
border: var(--border-width) solid var(--border);
|
|
3797
|
+
border-radius: var(--radius-lg);
|
|
3798
|
+
box-shadow: var(--shadow-3);
|
|
3799
|
+
overflow: hidden;
|
|
3800
|
+
max-height: 70vh;
|
|
3801
|
+
display: flex;
|
|
3802
|
+
flex-direction: column;
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
.cmd-body { outline: none; display: flex; flex-direction: column; min-height: 0; }
|
|
3806
|
+
|
|
3807
|
+
.cmd-field {
|
|
3808
|
+
display: flex;
|
|
3809
|
+
align-items: center;
|
|
3810
|
+
gap: var(--space-2);
|
|
3811
|
+
padding: var(--space-3) var(--space-4);
|
|
3812
|
+
border-bottom: var(--border-width) solid var(--border);
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3815
|
+
.cmd-mark { flex: none; width: var(--space-4); height: var(--space-4); color: var(--muted); }
|
|
3816
|
+
|
|
3817
|
+
/* No border, no focus ring on the input itself: the whole surface is the
|
|
3818
|
+
field, focus is already in it when it opens, and a ring around a box that
|
|
3819
|
+
fills the top of the palette is a ring around the palette. */
|
|
3820
|
+
.cmd-input {
|
|
3821
|
+
flex: 1 1 auto;
|
|
3822
|
+
min-width: 0;
|
|
3823
|
+
border: none;
|
|
3824
|
+
background: transparent;
|
|
3825
|
+
outline: none;
|
|
3826
|
+
color: var(--text);
|
|
3827
|
+
font: inherit;
|
|
3828
|
+
font-size: var(--text-md);
|
|
3829
|
+
}
|
|
3830
|
+
|
|
3831
|
+
.cmd-input::placeholder { color: var(--muted); }
|
|
3832
|
+
|
|
3833
|
+
.cmd-list {
|
|
3834
|
+
padding: var(--space-2);
|
|
3835
|
+
overflow: auto;
|
|
3836
|
+
overscroll-behavior: contain;
|
|
3837
|
+
outline: none;
|
|
3838
|
+
min-height: 0;
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
.cmd-group-title {
|
|
3842
|
+
padding: var(--space-2) var(--space-2) var(--space-1);
|
|
3843
|
+
font-size: var(--text-2xs);
|
|
3844
|
+
text-transform: uppercase;
|
|
3845
|
+
letter-spacing: var(--tracking);
|
|
3846
|
+
color: var(--muted);
|
|
3847
|
+
}
|
|
3848
|
+
|
|
3849
|
+
.cmd-item {
|
|
3850
|
+
display: flex;
|
|
3851
|
+
align-items: center;
|
|
3852
|
+
gap: var(--space-2);
|
|
3853
|
+
padding: var(--space-2);
|
|
3854
|
+
border-radius: var(--radius-sm);
|
|
3855
|
+
font-size: var(--text-sm);
|
|
3856
|
+
color: var(--text);
|
|
3857
|
+
cursor: default;
|
|
3858
|
+
outline: none;
|
|
3859
|
+
}
|
|
3860
|
+
|
|
3861
|
+
/* Two pointers into the list, and they can disagree.
|
|
3862
|
+
|
|
3863
|
+
React Aria's `Menu` unifies these -- hovering a row moves the keyboard's
|
|
3864
|
+
focus to it -- but `ListBox` in 1.21 has no `shouldFocusOnHover` to turn
|
|
3865
|
+
that on, so a hover sets `data-hovered` and the arrows set `data-focused`
|
|
3866
|
+
independently. Styling only `data-focused`, which is what this did first,
|
|
3867
|
+
means the pointer gets no feedback at all until somebody presses an arrow
|
|
3868
|
+
key.
|
|
3869
|
+
|
|
3870
|
+
Both are drawn, and the keyboard's row is marked as well, because when both
|
|
3871
|
+
are showing they are two different answers to "what does Enter run" -- and
|
|
3872
|
+
it is the focused one. The bar is the tell. */
|
|
3873
|
+
.cmd-item[data-hovered],
|
|
3874
|
+
.cmd-item[data-focused] { background: var(--panel-2); }
|
|
3875
|
+
|
|
3876
|
+
.cmd-item[data-focused] {
|
|
3877
|
+
box-shadow: inset calc(var(--border-width) * 2) 0 0 var(--accent);
|
|
3878
|
+
}
|
|
3879
|
+
.cmd-item[data-disabled] { color: var(--muted); opacity: 0.6; }
|
|
3880
|
+
|
|
3881
|
+
.cmd-icon { flex: none; width: var(--space-4); height: var(--space-4); color: var(--muted); }
|
|
3882
|
+
.cmd-item[data-focused] .cmd-icon { color: var(--text); }
|
|
3883
|
+
|
|
3884
|
+
.cmd-text { flex: 1 1 auto; min-width: 0; display: grid; }
|
|
3885
|
+
.cmd-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
3886
|
+
.cmd-desc {
|
|
3887
|
+
font-size: var(--text-xs);
|
|
3888
|
+
color: var(--muted);
|
|
3889
|
+
overflow: hidden;
|
|
3890
|
+
text-overflow: ellipsis;
|
|
3891
|
+
white-space: nowrap;
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
.cmd-key {
|
|
3895
|
+
flex: none;
|
|
3896
|
+
font-family: var(--font-mono);
|
|
3897
|
+
font-size: var(--text-2xs);
|
|
3898
|
+
color: var(--muted);
|
|
3899
|
+
letter-spacing: var(--tracking);
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3902
|
+
.cmd-empty {
|
|
3903
|
+
margin: 0;
|
|
3904
|
+
padding: var(--space-6) var(--space-4);
|
|
3905
|
+
text-align: center;
|
|
3906
|
+
color: var(--muted);
|
|
3907
|
+
font-size: var(--text-sm);
|
|
3908
|
+
}
|
|
3909
|
+
|
|
3910
|
+
.cmd-empty strong { color: var(--text); font-weight: var(--weight-strong); }
|
|
3911
|
+
|
|
3912
|
+
.cmd-scrim[data-entering] { animation: cmd-scrim-in var(--dur-fast) var(--ease-out); }
|
|
3913
|
+
.cmd-scrim[data-exiting] { animation: cmd-scrim-in var(--dur-fast) var(--ease) reverse; }
|
|
3914
|
+
.cmd[data-entering] { animation: cmd-in var(--dur-md) var(--ease-out); }
|
|
3915
|
+
|
|
3916
|
+
@keyframes cmd-scrim-in { from { opacity: 0; } to { opacity: 1; } }
|
|
3917
|
+
@keyframes cmd-in {
|
|
3918
|
+
from { opacity: 0; transform: translateY(calc(var(--nudge) * -1)) scale(0.98); }
|
|
3919
|
+
to { opacity: 1; transform: none; }
|
|
3920
|
+
}
|
|
3921
|
+
|
|
3922
|
+
@media (prefers-reduced-motion: reduce) {
|
|
3923
|
+
.cmd-scrim[data-entering],
|
|
3924
|
+
.cmd-scrim[data-exiting],
|
|
3925
|
+
.cmd[data-entering] { animation: none; }
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3928
|
+
/* ---- ./pagination.css ---- */
|
|
3929
|
+
/* Moving through a list that does not fit, and saying how far in you are. */
|
|
3930
|
+
.pager {
|
|
3931
|
+
display: flex;
|
|
3932
|
+
align-items: center;
|
|
3933
|
+
justify-content: space-between;
|
|
3934
|
+
gap: var(--space-4);
|
|
3935
|
+
flex-wrap: wrap;
|
|
3936
|
+
padding: var(--space-2) 0;
|
|
3937
|
+
font-size: var(--text-xs);
|
|
3938
|
+
color: var(--muted);
|
|
3939
|
+
}
|
|
3940
|
+
|
|
3941
|
+
.pager-count { margin: 0; }
|
|
3942
|
+
.pager-count strong { color: var(--text); font-weight: var(--weight-strong); }
|
|
3943
|
+
|
|
3944
|
+
.pager-controls { display: flex; align-items: center; gap: var(--space-1); }
|
|
3945
|
+
|
|
3946
|
+
.pager-step,
|
|
3947
|
+
.pager-page {
|
|
3948
|
+
display: inline-flex;
|
|
3949
|
+
align-items: center;
|
|
3950
|
+
justify-content: center;
|
|
3951
|
+
/* Square, and 32px rather than the text's height: these are hit repeatedly
|
|
3952
|
+
and in sequence, and a target the size of its glyph is a target that gets
|
|
3953
|
+
missed on the fourth press. */
|
|
3954
|
+
min-width: var(--space-8);
|
|
3955
|
+
height: var(--space-8);
|
|
3956
|
+
padding: 0 var(--space-2);
|
|
3957
|
+
border: var(--border-width) solid transparent;
|
|
3958
|
+
border-radius: var(--radius-sm);
|
|
3959
|
+
background: transparent;
|
|
3960
|
+
color: var(--muted);
|
|
3961
|
+
font: inherit;
|
|
3962
|
+
font-variant-numeric: tabular-nums;
|
|
3963
|
+
cursor: pointer;
|
|
3964
|
+
transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
|
|
3965
|
+
}
|
|
3966
|
+
|
|
3967
|
+
.pager-step:hover:not(:disabled),
|
|
3968
|
+
.pager-page:hover:not(.on) { background: var(--panel-2); color: var(--text); }
|
|
3969
|
+
|
|
3970
|
+
/* `--focus-ring` is a box-shadow value (`0 0 0 2px var(--accent)`), not an
|
|
3971
|
+
outline shorthand — `outline: var(--focus-ring)` is invalid and drops
|
|
3972
|
+
silently, which is how these shipped with no visible focus at all. Every
|
|
3973
|
+
other stylesheet in the package uses `box-shadow`. */
|
|
3974
|
+
.pager-step:focus-visible,
|
|
3975
|
+
.pager-page:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
3976
|
+
|
|
3977
|
+
/* Disabled rather than hidden. An arrow that disappears at the first page
|
|
3978
|
+
moves every other control one step left, so the button under your cursor
|
|
3979
|
+
changes identity between clicks. */
|
|
3980
|
+
.pager-step:disabled { opacity: 0.35; cursor: default; }
|
|
3981
|
+
|
|
3982
|
+
.pager-pages {
|
|
3983
|
+
display: flex;
|
|
3984
|
+
align-items: center;
|
|
3985
|
+
gap: var(--space-1);
|
|
3986
|
+
margin: 0;
|
|
3987
|
+
padding: 0;
|
|
3988
|
+
list-style: none;
|
|
3989
|
+
}
|
|
3990
|
+
|
|
3991
|
+
/* The current page. Border as well as tint, because tint alone is a colour
|
|
3992
|
+
doing the work on its own -- and `aria-current` says it to a reader that
|
|
3993
|
+
sees neither. */
|
|
3994
|
+
.pager-page.on {
|
|
3995
|
+
color: var(--text);
|
|
3996
|
+
background: var(--panel-2);
|
|
3997
|
+
border-color: var(--border-strong);
|
|
3998
|
+
font-weight: var(--weight-strong);
|
|
3999
|
+
}
|
|
4000
|
+
|
|
4001
|
+
/* The gap is a field, drawn as a ghost.
|
|
4002
|
+
|
|
4003
|
+
At rest it is the elision it replaces: no border, no background, the `…` as
|
|
4004
|
+
its placeholder, sized and aligned like the page buttons beside it. Touch it
|
|
4005
|
+
and it becomes a control. That order matters -- a visible "go to page" box
|
|
4006
|
+
in a pager is a control almost nobody uses taking permanent space beside the
|
|
4007
|
+
ones everybody does, and a control that looks like punctuation is one nobody
|
|
4008
|
+
presses. This is punctuation until you press it.
|
|
4009
|
+
|
|
4010
|
+
The width comes from how many digits the largest page has, set by the
|
|
4011
|
+
component, so the row does not change size when the field is typed into --
|
|
4012
|
+
the same reason `pageWindow` holds seven slots at the ends. */
|
|
4013
|
+
.pager-jump {
|
|
4014
|
+
width: max(var(--space-8), calc(var(--digits, 2) * 0.62em + var(--space-5)));
|
|
4015
|
+
height: var(--space-8);
|
|
4016
|
+
padding: 0 var(--space-1);
|
|
4017
|
+
border: var(--border-width) solid transparent;
|
|
4018
|
+
border-radius: var(--radius-sm);
|
|
4019
|
+
background: transparent;
|
|
4020
|
+
color: var(--muted);
|
|
4021
|
+
font: inherit;
|
|
4022
|
+
font-variant-numeric: tabular-nums;
|
|
4023
|
+
text-align: center;
|
|
4024
|
+
cursor: pointer;
|
|
4025
|
+
transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease),
|
|
4026
|
+
color var(--dur-fast) var(--ease);
|
|
4027
|
+
}
|
|
4028
|
+
|
|
4029
|
+
/* Full strength, so the `…` is as legible as the numbers it sits between. */
|
|
4030
|
+
.pager-jump::placeholder { color: var(--muted); opacity: 1; }
|
|
4031
|
+
|
|
4032
|
+
.pager-jump:hover { background: var(--panel-2); color: var(--text); }
|
|
4033
|
+
|
|
4034
|
+
/* Typed into: it stops being an elision and becomes the control it always was.
|
|
4035
|
+
The placeholder goes because the field now has a value to show. */
|
|
4036
|
+
.pager-jump:focus {
|
|
4037
|
+
outline: none;
|
|
4038
|
+
box-shadow: var(--focus-ring);
|
|
4039
|
+
background: var(--control);
|
|
4040
|
+
border-color: var(--border-strong);
|
|
4041
|
+
color: var(--text);
|
|
4042
|
+
cursor: text;
|
|
4043
|
+
}
|
|
4044
|
+
|
|
4045
|
+
/* The spinners are two 10px targets stacked inside a 32px box, and pressing
|
|
4046
|
+
one by accident moves the page. The arrow keys still step the value. */
|
|
4047
|
+
.pager-jump { appearance: textfield; -moz-appearance: textfield; }
|
|
4048
|
+
.pager-jump::-webkit-outer-spin-button,
|
|
4049
|
+
.pager-jump::-webkit-inner-spin-button { appearance: none; margin: 0; }
|
|
4050
|
+
|
|
4051
|
+
@media (prefers-reduced-motion: reduce) {
|
|
4052
|
+
.pager-step,
|
|
4053
|
+
.pager-page,
|
|
4054
|
+
.pager-jump { transition: none; }
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
/* ---- ./identity.css ---- */
|
|
4058
|
+
/* A person, said in one line: initials, name, sometimes the address. */
|
|
4059
|
+
.ident {
|
|
4060
|
+
display: inline-flex;
|
|
4061
|
+
align-items: center;
|
|
4062
|
+
gap: var(--space-2);
|
|
4063
|
+
min-width: 0;
|
|
4064
|
+
font-size: var(--text-sm);
|
|
4065
|
+
color: var(--text);
|
|
4066
|
+
}
|
|
4067
|
+
|
|
4068
|
+
/* The disc.
|
|
4069
|
+
|
|
4070
|
+
`--ident-hue` comes from the address and is the only thing the component
|
|
4071
|
+
sets inline; everything that decides how saturated and how light it is
|
|
4072
|
+
lives here, so the discs stay in the theme rather than staying the same
|
|
4073
|
+
colour on every theme. Mixed towards `--panel` for the same measured reason
|
|
4074
|
+
`Pill` mixes: a full-strength hue behind text drops the contrast on the
|
|
4075
|
+
light themes, and these carry two letters that have to be legible. */
|
|
4076
|
+
.ident-disc {
|
|
4077
|
+
flex: none;
|
|
4078
|
+
display: inline-flex;
|
|
4079
|
+
align-items: center;
|
|
4080
|
+
justify-content: center;
|
|
4081
|
+
width: var(--space-8);
|
|
4082
|
+
height: var(--space-8);
|
|
4083
|
+
border-radius: var(--radius-pill);
|
|
4084
|
+
background: color-mix(in srgb, hsl(var(--ident-hue) 60% 50%) 22%, var(--panel));
|
|
4085
|
+
border: var(--border-width) solid color-mix(in srgb, hsl(var(--ident-hue) 60% 50%) 45%, transparent);
|
|
4086
|
+
color: var(--text);
|
|
4087
|
+
font-size: var(--text-2xs);
|
|
4088
|
+
font-weight: var(--weight-strong);
|
|
4089
|
+
letter-spacing: normal;
|
|
4090
|
+
text-transform: uppercase;
|
|
4091
|
+
user-select: none;
|
|
4092
|
+
}
|
|
4093
|
+
|
|
4094
|
+
.ident.size-sm .ident-disc {
|
|
4095
|
+
width: var(--space-6);
|
|
4096
|
+
height: var(--space-6);
|
|
4097
|
+
font-size: var(--text-2xs);
|
|
4098
|
+
}
|
|
4099
|
+
|
|
4100
|
+
.ident-text { display: grid; min-width: 0; }
|
|
4101
|
+
|
|
4102
|
+
.ident-name {
|
|
4103
|
+
overflow: hidden;
|
|
4104
|
+
text-overflow: ellipsis;
|
|
4105
|
+
white-space: nowrap;
|
|
4106
|
+
font-weight: var(--weight);
|
|
4107
|
+
}
|
|
4108
|
+
|
|
4109
|
+
.ident-address {
|
|
4110
|
+
overflow: hidden;
|
|
4111
|
+
text-overflow: ellipsis;
|
|
4112
|
+
white-space: nowrap;
|
|
4113
|
+
font-size: var(--text-xs);
|
|
4114
|
+
color: var(--muted);
|
|
4115
|
+
}
|
|
4116
|
+
|
|
4117
|
+
.ident-aside { flex: none; color: var(--muted); font-size: var(--text-xs); }
|
|
4118
|
+
|
|
4119
|
+
/* A chip in a composer's To field: boxed, tighter, removable. */
|
|
4120
|
+
.ident.kind-chip {
|
|
4121
|
+
gap: var(--space-1);
|
|
4122
|
+
padding: var(--space-1);
|
|
4123
|
+
border: var(--border-width) solid var(--border);
|
|
4124
|
+
border-radius: var(--radius-pill);
|
|
4125
|
+
background: var(--panel-2);
|
|
4126
|
+
font-size: var(--text-xs);
|
|
4127
|
+
max-width: 100%;
|
|
4128
|
+
}
|
|
4129
|
+
|
|
4130
|
+
.ident.kind-chip .ident-disc {
|
|
4131
|
+
width: var(--space-5);
|
|
4132
|
+
height: var(--space-5);
|
|
4133
|
+
font-size: var(--text-2xs);
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
.ident-remove {
|
|
4137
|
+
flex: none;
|
|
4138
|
+
display: inline-flex;
|
|
4139
|
+
align-items: center;
|
|
4140
|
+
justify-content: center;
|
|
4141
|
+
/* `--space-5` of button around a `--space-3` glyph. The cross is the
|
|
4142
|
+
smallest thing on a composer and it sits next to text somebody is typing,
|
|
4143
|
+
so it needs the target even though it does not look like it does. */
|
|
4144
|
+
width: var(--space-5);
|
|
4145
|
+
height: var(--space-5);
|
|
4146
|
+
padding: 0;
|
|
4147
|
+
border: none;
|
|
4148
|
+
border-radius: var(--radius-pill);
|
|
4149
|
+
background: transparent;
|
|
4150
|
+
color: var(--muted);
|
|
4151
|
+
cursor: pointer;
|
|
4152
|
+
}
|
|
4153
|
+
|
|
4154
|
+
.ident-remove:hover { background: var(--panel); color: var(--text); }
|
|
4155
|
+
.ident-remove:focus-visible { outline: none; box-shadow: var(--focus-ring); }
|
|
3364
4156
|
/* ---- valet: the themes ---- */
|
|
3365
4157
|
:root[data-theme='valet']{--bg:#f4f5f8;--panel:#ffffff;--panel-2:#eceef3;--border:#dcdfe7;--border-strong:#7b8597;--text:#171a21;--muted:#5b6474;--accent:#4f46e5;--accent-dim:#a9a4f0;--on-accent:#ffffff;--good:#1b7f4b;--warn:#8a5f0a;--bad:#bf3a31;--info:#0e6f8e;--app-bg:#f4f5f8;--shadow-1:0 4px 14px rgba(23, 26, 33, 0.08);--shadow-2:0 8px 24px rgba(23, 26, 33, 0.10);--shadow-3:0 12px 32px rgba(23, 26, 33, 0.12);--scrim:rgba(23, 26, 33, 0.32);color-scheme:light}
|
|
3366
4158
|
:root[data-theme='valet-night']{--bg:#0c0f14;--panel:#141820;--panel-2:#1b2029;--border:#262c37;--border-strong:#616b7d;--text:#e8eaf0;--muted:#9ba4b5;--accent:#8f88ff;--accent-dim:#3f3a8f;--on-accent:#0d0b2e;--good:#5fcb8f;--warn:#e2ae58;--bad:#f28b84;--info:#57c4e8;--app-bg:#0c0f14;--shadow-1:0 6px 20px rgba(0, 0, 0, 0.28);--shadow-2:0 8px 28px rgba(0, 0, 0, 0.34);--shadow-3:0 10px 34px rgba(0, 0, 0, 0.38);--scrim:rgba(0, 0, 0, 0.5);color-scheme:dark}
|