@webority/theme 0.7.9 → 0.8.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/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_wui-components.scss +4 -0
- package/scss/wui/_card.scss +20 -1
- package/scss/wui/_controls.scss +33 -3
- package/scss/wui/_data.scss +4 -1
- package/scss/wui/_dock.scss +102 -0
- package/scss/wui/_flow.scss +33 -20
- package/scss/wui/_menu-motion.scss +153 -0
- package/scss/wui/_overlays.scss +221 -25
- package/scss/wui/_toolbar.scss +138 -0
package/scss/wui/_overlays.scss
CHANGED
|
@@ -430,10 +430,16 @@
|
|
|
430
430
|
z-index: var(--wui-z-toast);
|
|
431
431
|
display: flex;
|
|
432
432
|
flex-direction: column;
|
|
433
|
-
gap:
|
|
433
|
+
gap: 8px;
|
|
434
434
|
max-width: min(92vw, 400px);
|
|
435
435
|
}
|
|
436
436
|
|
|
437
|
+
// Bootstrap's .toast-container stacks children with margin-bottom: $toast-spacing (1.5rem),
|
|
438
|
+
// which ADDED to the flex gap above — the stack spacing is the gap alone.
|
|
439
|
+
.wui-toast-container > :not(:last-child) {
|
|
440
|
+
margin-bottom: 0;
|
|
441
|
+
}
|
|
442
|
+
|
|
437
443
|
.wui-toast {
|
|
438
444
|
--wui-toast-accent: var(--color-primary);
|
|
439
445
|
position: relative;
|
|
@@ -441,10 +447,12 @@
|
|
|
441
447
|
align-items: flex-start;
|
|
442
448
|
gap: 12px;
|
|
443
449
|
width: 100%;
|
|
444
|
-
padding: 14px
|
|
450
|
+
padding: 14px 16px;
|
|
445
451
|
background: var(--color-surface);
|
|
446
452
|
color: var(--color-text);
|
|
447
|
-
border:
|
|
453
|
+
// The full-strength border, not border-light: on a white page the surface-on-white
|
|
454
|
+
// card otherwise merges into the background (the overlay shadow alone is too soft).
|
|
455
|
+
border: 1px solid var(--color-border);
|
|
448
456
|
border-radius: var(--radius-lg, 12px);
|
|
449
457
|
box-shadow: var(--wui-shadow-overlay);
|
|
450
458
|
overflow: hidden;
|
|
@@ -456,15 +464,6 @@
|
|
|
456
464
|
animation: wui-toast-in 0.24s ease;
|
|
457
465
|
}
|
|
458
466
|
|
|
459
|
-
// The left accent bar reads the variant tint.
|
|
460
|
-
.wui-toast::before {
|
|
461
|
-
content: "";
|
|
462
|
-
position: absolute;
|
|
463
|
-
inset: 0 auto 0 0;
|
|
464
|
-
width: 4px;
|
|
465
|
-
background: var(--wui-toast-accent);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
467
|
// While a swipe is in progress the toast follows the pointer with no transition.
|
|
469
468
|
.wui-toast--dragging {
|
|
470
469
|
transition: none;
|
|
@@ -513,21 +512,81 @@
|
|
|
513
512
|
overflow-wrap: anywhere;
|
|
514
513
|
}
|
|
515
514
|
|
|
515
|
+
// Hierarchy: with a title present the message reads as the SUBTITLE and drops to muted —
|
|
516
|
+
// title (ink, 600) > message (muted) > action (compact ink, hover reveals the variant tint).
|
|
517
|
+
// A title-less toast keeps full ink, or a bare "Saved" would look washed out.
|
|
518
|
+
.wui-toast-title + .wui-toast-message {
|
|
519
|
+
color: var(--color-text-muted);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Determinate task progress (toast `progress` 0–100) — work advancing, not time running out.
|
|
523
|
+
.wui-toast-task {
|
|
524
|
+
display: flex;
|
|
525
|
+
align-items: center;
|
|
526
|
+
gap: 8px;
|
|
527
|
+
margin-top: 7px;
|
|
528
|
+
}
|
|
529
|
+
.wui-toast-track {
|
|
530
|
+
flex: 1 1 auto;
|
|
531
|
+
height: 6px;
|
|
532
|
+
border-radius: 999px;
|
|
533
|
+
background: var(--color-bg-muted);
|
|
534
|
+
overflow: hidden;
|
|
535
|
+
}
|
|
536
|
+
.wui-toast-track-fill {
|
|
537
|
+
height: 100%;
|
|
538
|
+
border-radius: 999px;
|
|
539
|
+
background: var(--wui-toast-accent);
|
|
540
|
+
transition: width 200ms var(--wui-ease-ui);
|
|
541
|
+
}
|
|
542
|
+
.wui-toast-track-pct {
|
|
543
|
+
flex: none;
|
|
544
|
+
font-size: 0.75rem;
|
|
545
|
+
font-weight: 600;
|
|
546
|
+
color: var(--color-text-muted);
|
|
547
|
+
}
|
|
548
|
+
|
|
516
549
|
// Action button — tinted with the variant accent, scoped to the toast so it never
|
|
517
550
|
// restyles an arbitrary .btn-link a rich body might contain.
|
|
551
|
+
// Lives in the content column, below the message/progress (never beside them).
|
|
552
|
+
// Deliberately NOT a .btn — Bootstrap's button chrome (padding, hover lift, transitions)
|
|
553
|
+
// has no business on a text-line action, so the element carries only this class and the
|
|
554
|
+
// styling is self-contained. Neutral bold ink like the title; underline on hover, no motion.
|
|
555
|
+
// The 3px/8px padding exists for the hover pill; the negative margins give it back, so the
|
|
556
|
+
// resting text sits exactly where the padding-less version did and the approved rhythm holds.
|
|
518
557
|
.wui-toast-action {
|
|
519
558
|
flex: none;
|
|
520
|
-
align-self:
|
|
521
|
-
|
|
522
|
-
|
|
559
|
+
align-self: flex-start;
|
|
560
|
+
background: none;
|
|
561
|
+
border: 0;
|
|
562
|
+
padding: 3px 8px;
|
|
563
|
+
margin: -1px 0 -3px -8px;
|
|
564
|
+
border-radius: 6px;
|
|
565
|
+
cursor: pointer;
|
|
566
|
+
color: var(--color-text);
|
|
567
|
+
font-family: inherit;
|
|
523
568
|
font-size: 0.8125rem;
|
|
524
569
|
font-weight: 600;
|
|
525
|
-
|
|
570
|
+
line-height: 1.4;
|
|
571
|
+
text-decoration: none;
|
|
526
572
|
white-space: nowrap;
|
|
573
|
+
transition: background-color 120ms ease, color 120ms ease;
|
|
527
574
|
}
|
|
575
|
+
// Hover: a soft pill in the toast's variant tint — highlight, not underline, no motion.
|
|
528
576
|
.wui-toast-action:hover {
|
|
577
|
+
background: color-mix(in srgb, var(--wui-toast-accent) 14%, transparent);
|
|
529
578
|
color: var(--wui-toast-accent);
|
|
530
|
-
|
|
579
|
+
}
|
|
580
|
+
.wui-toast-action:focus-visible {
|
|
581
|
+
outline: 2px solid var(--wui-toast-accent);
|
|
582
|
+
outline-offset: 1px;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// Geometry: optical space above the action = message half-leading (3.5px) + column gap
|
|
586
|
+
// (2px) + 2px margin + action half-leading (2px) ≈ 9.5px; below = action half-leading
|
|
587
|
+
// (2px) + this padding (7px) ≈ 9px. Equal by construction — don't re-balance one side alone.
|
|
588
|
+
.wui-toast:has(.wui-toast-action) {
|
|
589
|
+
padding-bottom: 7px;
|
|
531
590
|
}
|
|
532
591
|
.wui-toast-action:focus-visible {
|
|
533
592
|
outline: 2px solid var(--wui-toast-accent);
|
|
@@ -540,18 +599,18 @@
|
|
|
540
599
|
font-size: 0.65rem;
|
|
541
600
|
}
|
|
542
601
|
|
|
543
|
-
//
|
|
544
|
-
//
|
|
545
|
-
//
|
|
602
|
+
// The auto-dismiss TIMER, deliberately invisible (height 0): the element still runs the
|
|
603
|
+
// countdown animation because its animationend is what dismisses the toast on both surfaces,
|
|
604
|
+
// and :hover/:focus-within pausing it is what keeps a toast open while being read. Do not
|
|
605
|
+
// remove the element or the animation — only its visibility is retired (a visible countdown
|
|
606
|
+
// reads as pressure).
|
|
546
607
|
.wui-toast-progress {
|
|
547
608
|
position: absolute;
|
|
548
609
|
left: 0;
|
|
549
610
|
bottom: 0;
|
|
550
|
-
height:
|
|
611
|
+
height: 0;
|
|
551
612
|
width: 100%;
|
|
552
613
|
transform-origin: left center;
|
|
553
|
-
background: var(--wui-toast-accent);
|
|
554
|
-
opacity: 0.5;
|
|
555
614
|
animation-name: wui-toast-countdown;
|
|
556
615
|
animation-timing-function: linear;
|
|
557
616
|
animation-fill-mode: forwards;
|
|
@@ -594,9 +653,146 @@
|
|
|
594
653
|
}
|
|
595
654
|
}
|
|
596
655
|
|
|
656
|
+
// ── Entrance variants (toast/WUI.toast `animation`; host default via AppToast/app-toast) ──
|
|
657
|
+
// The base .wui-toast entrance above is `rise`. The variants below animate the STANDALONE
|
|
658
|
+
// scale/translate properties so they never fight the swipe-to-dismiss inline transform.
|
|
659
|
+
|
|
660
|
+
// `organic` — studied frame-by-frame from Ivan Bjelajac's notification: the surface
|
|
661
|
+
// materializes softly while the status icon grows from a dot with a slight overshoot,
|
|
662
|
+
// and the message trails in last.
|
|
663
|
+
.wui-toast--anim-organic {
|
|
664
|
+
animation: wui-toast-materialize 360ms var(--wui-ease-out-quart);
|
|
665
|
+
}
|
|
666
|
+
.wui-toast--anim-organic .wui-toast-icon {
|
|
667
|
+
animation: wui-toast-icon-grow 420ms 60ms cubic-bezier(0.34, 1.45, 0.64, 1) backwards;
|
|
668
|
+
}
|
|
669
|
+
.wui-toast--anim-organic .wui-toast-content {
|
|
670
|
+
animation: wui-toast-content-in 300ms 190ms var(--wui-ease-out-quart) backwards;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
// `jelly` — drops in with a squash-and-stretch landing.
|
|
674
|
+
.wui-toast--anim-jelly {
|
|
675
|
+
animation: wui-toast-jelly 500ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
// `spring` — slides up from below with an elastic settle.
|
|
679
|
+
.wui-toast--anim-spring {
|
|
680
|
+
animation: wui-toast-spring 550ms cubic-bezier(0.34, 1.56, 0.64, 1);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
@keyframes wui-toast-materialize {
|
|
684
|
+
from {
|
|
685
|
+
opacity: 0;
|
|
686
|
+
scale: 0.97;
|
|
687
|
+
}
|
|
688
|
+
to {
|
|
689
|
+
opacity: 1;
|
|
690
|
+
scale: 1;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
@keyframes wui-toast-icon-grow {
|
|
694
|
+
0% {
|
|
695
|
+
scale: 0;
|
|
696
|
+
}
|
|
697
|
+
70% {
|
|
698
|
+
scale: 1.18;
|
|
699
|
+
}
|
|
700
|
+
100% {
|
|
701
|
+
scale: 1;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
@keyframes wui-toast-content-in {
|
|
705
|
+
from {
|
|
706
|
+
opacity: 0;
|
|
707
|
+
translate: 6px 0;
|
|
708
|
+
}
|
|
709
|
+
to {
|
|
710
|
+
opacity: 1;
|
|
711
|
+
translate: 0 0;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
@keyframes wui-toast-jelly {
|
|
715
|
+
0% {
|
|
716
|
+
opacity: 0;
|
|
717
|
+
translate: 0 -46px;
|
|
718
|
+
scale: 0.6 0.6;
|
|
719
|
+
}
|
|
720
|
+
55% {
|
|
721
|
+
opacity: 1;
|
|
722
|
+
translate: 0 6px;
|
|
723
|
+
scale: 1.05 0.85;
|
|
724
|
+
}
|
|
725
|
+
75% {
|
|
726
|
+
translate: 0 -2px;
|
|
727
|
+
scale: 0.97 1.05;
|
|
728
|
+
}
|
|
729
|
+
100% {
|
|
730
|
+
opacity: 1;
|
|
731
|
+
translate: 0 0;
|
|
732
|
+
scale: 1 1;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
@keyframes wui-toast-spring {
|
|
736
|
+
from {
|
|
737
|
+
opacity: 0;
|
|
738
|
+
translate: 0 64px;
|
|
739
|
+
scale: 0.92;
|
|
740
|
+
}
|
|
741
|
+
to {
|
|
742
|
+
opacity: 1;
|
|
743
|
+
translate: 0 0;
|
|
744
|
+
scale: 1;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// ── Inverse surface (toast/WUI.toast `surface: 'inverse'`) ──
|
|
749
|
+
// The toast renders on the OPPOSITE surface of the active mode — a dark card on a light
|
|
750
|
+
// page, a light card on a dark page — so the stack always contrasts with the content.
|
|
751
|
+
// Everything inside consumes --color-* vars, so re-pointing them here restyles the whole
|
|
752
|
+
// card (icon, text hierarchy, action pill, progress track) in one place.
|
|
753
|
+
.wui-toast--surface-inverse {
|
|
754
|
+
--color-surface: var(--wui-dark-bg-surface);
|
|
755
|
+
--color-border: var(--wui-dark-border);
|
|
756
|
+
--color-text: var(--wui-dark-text);
|
|
757
|
+
--color-text-muted: var(--wui-dark-text-muted);
|
|
758
|
+
--color-bg-muted: var(--wui-dark-bg-muted);
|
|
759
|
+
}
|
|
760
|
+
// Bootstrap's .btn-close glyph is ink-dark; on the dark card it needs the white filter.
|
|
761
|
+
.wui-toast--surface-inverse .wui-toast-close {
|
|
762
|
+
filter: invert(1) grayscale(100%) brightness(200%);
|
|
763
|
+
}
|
|
764
|
+
:root[data-theme="dark"] .wui-toast--surface-inverse,
|
|
765
|
+
:root[data-bs-theme="dark"] .wui-toast--surface-inverse {
|
|
766
|
+
--color-surface: var(--wui-bg-light);
|
|
767
|
+
--color-border: var(--wui-border-light);
|
|
768
|
+
--color-text: var(--wui-text-heading);
|
|
769
|
+
--color-text-muted: var(--wui-text-2);
|
|
770
|
+
--color-bg-muted: var(--wui-bg-muted);
|
|
771
|
+
}
|
|
772
|
+
:root[data-theme="dark"] .wui-toast--surface-inverse .wui-toast-close,
|
|
773
|
+
:root[data-bs-theme="dark"] .wui-toast--surface-inverse .wui-toast-close {
|
|
774
|
+
filter: none;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// ── Corner treatments (toast/WUI.toast `shape`) — `rounded` is the base radius above ──
|
|
778
|
+
.wui-toast--shape-pill {
|
|
779
|
+
border-radius: 999px;
|
|
780
|
+
align-items: center;
|
|
781
|
+
padding-block: 12px;
|
|
782
|
+
}
|
|
783
|
+
.wui-toast--shape-pill .wui-toast-icon,
|
|
784
|
+
.wui-toast--shape-pill .wui-toast-close {
|
|
785
|
+
margin-top: 0;
|
|
786
|
+
}
|
|
787
|
+
.wui-toast--shape-sharp {
|
|
788
|
+
border-radius: 4px;
|
|
789
|
+
}
|
|
790
|
+
|
|
597
791
|
@media (prefers-reduced-motion: reduce) {
|
|
598
|
-
// Drop the entrance
|
|
599
|
-
.wui-toast
|
|
792
|
+
// Drop the entrance motion; the countdown bar stays — it is the auto-dismiss timer, not decoration.
|
|
793
|
+
.wui-toast,
|
|
794
|
+
.wui-toast--anim-organic .wui-toast-icon,
|
|
795
|
+
.wui-toast--anim-organic .wui-toast-content {
|
|
600
796
|
animation: none;
|
|
601
797
|
transition: none;
|
|
602
798
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// ── Toolbar ──
|
|
2
|
+
// AppToolbar / <app-toolbar> — a Figma-style tool strip: a pill of icon tools where
|
|
3
|
+
// the active tool expands to reveal its label. Radio-backed (no JS needed on Razor);
|
|
4
|
+
// the expand/collapse is pure CSS off :checked.
|
|
5
|
+
//
|
|
6
|
+
// Part of the wui-components layer — see _wui-components.scss for load order.
|
|
7
|
+
|
|
8
|
+
.toolbar {
|
|
9
|
+
display: inline-flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 4px;
|
|
12
|
+
padding: 6px;
|
|
13
|
+
background: var(--color-surface);
|
|
14
|
+
border: 1px solid var(--color-border);
|
|
15
|
+
border-radius: var(--wui-radius-lg);
|
|
16
|
+
corner-shape: squircle;
|
|
17
|
+
box-shadow: var(--wui-shadow-overlay);
|
|
18
|
+
}
|
|
19
|
+
.toolbar.is-disabled {
|
|
20
|
+
opacity: 0.6;
|
|
21
|
+
pointer-events: none;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.toolbar-item {
|
|
25
|
+
display: inline-flex;
|
|
26
|
+
align-items: center;
|
|
27
|
+
height: 36px;
|
|
28
|
+
padding-inline: 9px;
|
|
29
|
+
border-radius: var(--wui-radius);
|
|
30
|
+
color: var(--color-text-muted);
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
margin-bottom: 0;
|
|
33
|
+
transition:
|
|
34
|
+
background-color 160ms var(--wui-ease-ui),
|
|
35
|
+
color 160ms var(--wui-ease-ui);
|
|
36
|
+
}
|
|
37
|
+
.toolbar-item:hover {
|
|
38
|
+
background: var(--color-bg-muted);
|
|
39
|
+
color: var(--color-text);
|
|
40
|
+
}
|
|
41
|
+
// The active tool: brand fill + the label slides open beside the icon.
|
|
42
|
+
.toolbar-item:has(.toolbar-input:checked) {
|
|
43
|
+
background: var(--color-primary);
|
|
44
|
+
color: var(--color-primary-text);
|
|
45
|
+
}
|
|
46
|
+
.toolbar-item:has(.toolbar-input:disabled) {
|
|
47
|
+
opacity: 0.5;
|
|
48
|
+
cursor: default;
|
|
49
|
+
}
|
|
50
|
+
.toolbar-item:has(.toolbar-input:focus-visible) {
|
|
51
|
+
outline: 2px solid var(--color-primary);
|
|
52
|
+
outline-offset: 2px;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Visually hidden but focusable/announceable — same trick as .segmented-input.
|
|
56
|
+
.toolbar-input {
|
|
57
|
+
position: absolute;
|
|
58
|
+
width: 1px;
|
|
59
|
+
height: 1px;
|
|
60
|
+
margin: 0;
|
|
61
|
+
padding: 0;
|
|
62
|
+
border: 0;
|
|
63
|
+
clip-path: inset(50%);
|
|
64
|
+
overflow: hidden;
|
|
65
|
+
white-space: nowrap;
|
|
66
|
+
appearance: none;
|
|
67
|
+
opacity: 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.toolbar-item-icon {
|
|
71
|
+
display: grid;
|
|
72
|
+
place-items: center;
|
|
73
|
+
line-height: 1;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.toolbar-item-label {
|
|
77
|
+
max-width: 0;
|
|
78
|
+
margin-left: 0;
|
|
79
|
+
opacity: 0;
|
|
80
|
+
overflow: hidden;
|
|
81
|
+
white-space: nowrap;
|
|
82
|
+
font-size: 0.875rem;
|
|
83
|
+
font-weight: 500;
|
|
84
|
+
transition:
|
|
85
|
+
max-width 240ms var(--wui-ease-ui),
|
|
86
|
+
margin-left 240ms var(--wui-ease-ui),
|
|
87
|
+
opacity 180ms var(--wui-ease-ui);
|
|
88
|
+
}
|
|
89
|
+
.toolbar-item:has(.toolbar-input:checked) .toolbar-item-label {
|
|
90
|
+
max-width: 10rem;
|
|
91
|
+
margin-left: 7px;
|
|
92
|
+
opacity: 1;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.toolbar-sep {
|
|
96
|
+
width: 1px;
|
|
97
|
+
align-self: stretch;
|
|
98
|
+
margin-block: 6px;
|
|
99
|
+
margin-inline: 3px;
|
|
100
|
+
background: var(--color-border);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.toolbar-action {
|
|
104
|
+
display: inline-flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
justify-content: center;
|
|
107
|
+
width: 36px;
|
|
108
|
+
height: 36px;
|
|
109
|
+
padding: 0;
|
|
110
|
+
border: none;
|
|
111
|
+
border-radius: var(--wui-radius);
|
|
112
|
+
background: transparent;
|
|
113
|
+
color: var(--color-text-muted);
|
|
114
|
+
cursor: pointer;
|
|
115
|
+
transition:
|
|
116
|
+
background-color 160ms var(--wui-ease-ui),
|
|
117
|
+
color 160ms var(--wui-ease-ui);
|
|
118
|
+
}
|
|
119
|
+
.toolbar-action:hover:not(:disabled) {
|
|
120
|
+
background: var(--color-bg-muted);
|
|
121
|
+
color: var(--color-text);
|
|
122
|
+
}
|
|
123
|
+
.toolbar-action:focus-visible {
|
|
124
|
+
outline: 2px solid var(--color-primary);
|
|
125
|
+
outline-offset: 2px;
|
|
126
|
+
}
|
|
127
|
+
.toolbar-action:disabled {
|
|
128
|
+
opacity: 0.5;
|
|
129
|
+
cursor: default;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@media (prefers-reduced-motion: reduce) {
|
|
133
|
+
.toolbar-item,
|
|
134
|
+
.toolbar-item-label,
|
|
135
|
+
.toolbar-action {
|
|
136
|
+
transition: none;
|
|
137
|
+
}
|
|
138
|
+
}
|