@webority/theme 0.7.1 → 0.7.2
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/_components.scss +54 -1
- package/scss/_tokens.scss +41 -0
- package/scss/_wui-aliases.scss +3 -0
- package/scss/_wui-components.scss +1 -0
- package/scss/wui/_choice.scss +8 -3
- package/scss/wui/_controls.scss +6 -3
- package/scss/wui/_data.scss +40 -0
- package/scss/wui/_display.scss +41 -4
- package/scss/wui/_overlays.scss +36 -52
- package/scss/wui/_scrollbar.scss +32 -0
- package/scss/wui/_shell.scss +222 -10
package/scss/wui/_shell.scss
CHANGED
|
@@ -25,31 +25,47 @@
|
|
|
25
25
|
align-items: center;
|
|
26
26
|
gap: 18px;
|
|
27
27
|
text-align: center;
|
|
28
|
+
// Ease in on mount so the boot screen settles rather than snapping into place.
|
|
29
|
+
animation: wui-splash-enter 0.5s var(--wui-ease-ui) both;
|
|
28
30
|
}
|
|
31
|
+
// One diameter drives the mark, its initials, the logo cap, and the bar width — the `size`
|
|
32
|
+
// preset or a custom pixel value resolves to `--wui-splash-size` (set inline by App*/<app-*>).
|
|
29
33
|
.splash-logo {
|
|
30
|
-
max-width:
|
|
34
|
+
max-width: calc(var(--wui-splash-size, 56px) * 3.2);
|
|
31
35
|
height: auto;
|
|
36
|
+
// A slow breath keeps the brand mark feeling alive through the wait.
|
|
37
|
+
animation: wui-splash-breathe 2.6s ease-in-out infinite;
|
|
32
38
|
}
|
|
33
39
|
.splash-mark {
|
|
34
|
-
width: 56px;
|
|
35
|
-
height: 56px;
|
|
40
|
+
width: var(--wui-splash-size, 56px);
|
|
41
|
+
height: var(--wui-splash-size, 56px);
|
|
36
42
|
display: inline-grid;
|
|
37
43
|
place-items: center;
|
|
38
44
|
border-radius: var(--radius-lg);
|
|
39
45
|
background: var(--color-primary);
|
|
40
46
|
color: var(--color-on-scrim);
|
|
41
|
-
font-size:
|
|
47
|
+
font-size: calc(var(--wui-splash-size, 56px) * 0.39);
|
|
42
48
|
font-weight: 700;
|
|
49
|
+
animation: wui-splash-breathe 2.6s ease-in-out infinite;
|
|
43
50
|
}
|
|
44
51
|
.splash-text {
|
|
45
52
|
font-size: 14px;
|
|
46
53
|
color: var(--color-text-muted);
|
|
47
54
|
}
|
|
48
|
-
// A
|
|
49
|
-
//
|
|
55
|
+
// A quieter second line — a reassurance under the main status. Uses --color-text-muted (not
|
|
56
|
+
// -subtle): -subtle is ~4.54:1 on the splash bg and flakes the AA contrast check at 12px; the
|
|
57
|
+
// smaller size (vs .splash-text) already sets the hierarchy without dropping below AA.
|
|
58
|
+
.splash-subtext {
|
|
59
|
+
margin-top: -12px;
|
|
60
|
+
font-size: 12px;
|
|
61
|
+
color: var(--color-text-muted);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ── Boot indicators — one shows, chosen by `variant`. A determinate-looking value would lie
|
|
65
|
+
// about how far along boot is, so every variant is indeterminate. ──
|
|
50
66
|
.splash-bar {
|
|
51
67
|
position: relative;
|
|
52
|
-
width:
|
|
68
|
+
width: calc(var(--wui-splash-size, 56px) * 3.2);
|
|
53
69
|
height: 3px;
|
|
54
70
|
overflow: hidden;
|
|
55
71
|
border-radius: 999px;
|
|
@@ -62,9 +78,62 @@
|
|
|
62
78
|
bottom: 0;
|
|
63
79
|
width: 40%;
|
|
64
80
|
border-radius: 999px;
|
|
65
|
-
|
|
81
|
+
// Feathered edges read smoother than a hard block sliding across.
|
|
82
|
+
background: linear-gradient(
|
|
83
|
+
90deg,
|
|
84
|
+
transparent,
|
|
85
|
+
var(--color-primary) 35%,
|
|
86
|
+
var(--color-primary) 65%,
|
|
87
|
+
transparent
|
|
88
|
+
);
|
|
66
89
|
animation: wui-splash-sweep 1.1s var(--wui-ease-ui) infinite;
|
|
67
90
|
}
|
|
91
|
+
// A spinning arc — the circular take on the same "working, no percentage" idea.
|
|
92
|
+
.splash-ring {
|
|
93
|
+
width: 30px;
|
|
94
|
+
height: 30px;
|
|
95
|
+
border-radius: 50%;
|
|
96
|
+
border: 3px solid var(--color-bg-muted);
|
|
97
|
+
border-top-color: var(--color-primary);
|
|
98
|
+
animation: wui-splash-spin 0.8s linear infinite;
|
|
99
|
+
}
|
|
100
|
+
// Three dots taking turns rising — a lighter, friendlier wait.
|
|
101
|
+
.splash-dots {
|
|
102
|
+
display: inline-flex;
|
|
103
|
+
gap: 7px;
|
|
104
|
+
}
|
|
105
|
+
.splash-dots i {
|
|
106
|
+
width: 9px;
|
|
107
|
+
height: 9px;
|
|
108
|
+
border-radius: 50%;
|
|
109
|
+
background: var(--color-primary);
|
|
110
|
+
animation: wui-splash-dot 1.2s ease-in-out infinite;
|
|
111
|
+
}
|
|
112
|
+
.splash-dots i:nth-child(2) {
|
|
113
|
+
animation-delay: 0.16s;
|
|
114
|
+
}
|
|
115
|
+
.splash-dots i:nth-child(3) {
|
|
116
|
+
animation-delay: 0.32s;
|
|
117
|
+
}
|
|
118
|
+
@keyframes wui-splash-enter {
|
|
119
|
+
from {
|
|
120
|
+
opacity: 0;
|
|
121
|
+
transform: translateY(8px);
|
|
122
|
+
}
|
|
123
|
+
to {
|
|
124
|
+
opacity: 1;
|
|
125
|
+
transform: translateY(0);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
@keyframes wui-splash-breathe {
|
|
129
|
+
0%,
|
|
130
|
+
100% {
|
|
131
|
+
transform: scale(1);
|
|
132
|
+
}
|
|
133
|
+
50% {
|
|
134
|
+
transform: scale(1.045);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
68
137
|
@keyframes wui-splash-sweep {
|
|
69
138
|
from {
|
|
70
139
|
transform: translateX(-100%);
|
|
@@ -73,9 +142,37 @@
|
|
|
73
142
|
transform: translateX(350%);
|
|
74
143
|
}
|
|
75
144
|
}
|
|
145
|
+
@keyframes wui-splash-spin {
|
|
146
|
+
to {
|
|
147
|
+
transform: rotate(360deg);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
@keyframes wui-splash-dot {
|
|
151
|
+
0%,
|
|
152
|
+
60%,
|
|
153
|
+
100% {
|
|
154
|
+
transform: translateY(0);
|
|
155
|
+
opacity: 0.45;
|
|
156
|
+
}
|
|
157
|
+
30% {
|
|
158
|
+
transform: translateY(-7px);
|
|
159
|
+
opacity: 1;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
76
162
|
@media (prefers-reduced-motion: reduce) {
|
|
163
|
+
// Motion off: settle instantly, drop breathing and the spinning/bouncing indicators.
|
|
164
|
+
.splash-inner,
|
|
165
|
+
.splash-logo,
|
|
166
|
+
.splash-mark,
|
|
167
|
+
.splash-ring,
|
|
168
|
+
.splash-dots i {
|
|
169
|
+
animation: none;
|
|
170
|
+
}
|
|
171
|
+
.splash-dots i {
|
|
172
|
+
opacity: 0.7;
|
|
173
|
+
}
|
|
77
174
|
.splash-bar::after {
|
|
78
|
-
//
|
|
175
|
+
// Keep a visible bar — an empty track would read as stalled.
|
|
79
176
|
animation: none;
|
|
80
177
|
width: 100%;
|
|
81
178
|
opacity: 0.55;
|
|
@@ -237,13 +334,17 @@
|
|
|
237
334
|
color: var(--color-primary);
|
|
238
335
|
}
|
|
239
336
|
.app-nav-text {
|
|
337
|
+
// Grows to fill the row so every trailing element — badge, trailing marker,
|
|
338
|
+
// expand caret — lines up at the end in source order without each needing its
|
|
339
|
+
// own `margin-left:auto` (which would fight when more than one is present).
|
|
340
|
+
flex: 1 1 auto;
|
|
240
341
|
min-width: 0;
|
|
241
342
|
overflow: hidden;
|
|
242
343
|
text-overflow: ellipsis;
|
|
243
344
|
white-space: nowrap;
|
|
244
345
|
}
|
|
245
346
|
.app-nav-badge {
|
|
246
|
-
|
|
347
|
+
flex: none;
|
|
247
348
|
padding: 1px 7px;
|
|
248
349
|
border-radius: 999px;
|
|
249
350
|
background: var(--color-bg-muted);
|
|
@@ -272,6 +373,117 @@
|
|
|
272
373
|
display: none;
|
|
273
374
|
}
|
|
274
375
|
|
|
376
|
+
// ── Badge tones ──────────────────────────────────────────────────────────────
|
|
377
|
+
// A nav badge can carry a status tone so an alert count reads red, not neutral.
|
|
378
|
+
.app-nav-badge.tone-danger {
|
|
379
|
+
background: var(--color-danger-soft);
|
|
380
|
+
color: var(--color-danger);
|
|
381
|
+
}
|
|
382
|
+
.app-nav-badge.tone-success {
|
|
383
|
+
background: var(--color-success-soft);
|
|
384
|
+
color: var(--color-success);
|
|
385
|
+
}
|
|
386
|
+
.app-nav-badge.tone-warning {
|
|
387
|
+
background: var(--color-warning-soft);
|
|
388
|
+
color: var(--color-warning);
|
|
389
|
+
}
|
|
390
|
+
// The existing `.app-nav-item.active .app-nav-badge` inverts a tinted badge on an
|
|
391
|
+
// active row (higher specificity), so an active row's badge stays legible.
|
|
392
|
+
|
|
393
|
+
// ── Trailing marker ──────────────────────────────────────────────────────────
|
|
394
|
+
// A right-aligned secondary glyph after the label (a lock, an external arrow, a
|
|
395
|
+
// "new" dot) — distinct from the count badge, and may sit beside one.
|
|
396
|
+
.app-nav-trailing {
|
|
397
|
+
flex: none;
|
|
398
|
+
display: inline-grid;
|
|
399
|
+
place-items: center;
|
|
400
|
+
color: var(--color-text-subtle);
|
|
401
|
+
}
|
|
402
|
+
.app-nav-item.active .app-nav-trailing {
|
|
403
|
+
color: var(--color-primary);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// ── Expandable sub-items ─────────────────────────────────────────────────────
|
|
407
|
+
// A parent row that toggles a nested list. It reuses the .app-nav-item chrome but
|
|
408
|
+
// is a <button>; the caret rotates open. The nested list animates its height via
|
|
409
|
+
// grid-template-rows 0fr→1fr, so there is no fixed max-height to guess.
|
|
410
|
+
.app-nav-item.has-children {
|
|
411
|
+
width: 100%;
|
|
412
|
+
border: 0;
|
|
413
|
+
background: transparent;
|
|
414
|
+
font: inherit;
|
|
415
|
+
text-align: left;
|
|
416
|
+
cursor: pointer;
|
|
417
|
+
}
|
|
418
|
+
.app-nav-caret {
|
|
419
|
+
flex: none;
|
|
420
|
+
display: inline-grid;
|
|
421
|
+
place-items: center;
|
|
422
|
+
color: var(--color-text-subtle);
|
|
423
|
+
transition: transform 0.2s var(--wui-ease-ui);
|
|
424
|
+
}
|
|
425
|
+
.app-nav-item.has-children[aria-expanded="true"] .app-nav-caret {
|
|
426
|
+
transform: rotate(90deg);
|
|
427
|
+
}
|
|
428
|
+
.app-nav-sub {
|
|
429
|
+
display: grid;
|
|
430
|
+
grid-template-rows: 0fr;
|
|
431
|
+
transition: grid-template-rows 0.24s var(--wui-ease-ui);
|
|
432
|
+
}
|
|
433
|
+
.app-nav-sub-inner {
|
|
434
|
+
overflow: hidden;
|
|
435
|
+
display: flex;
|
|
436
|
+
flex-direction: column;
|
|
437
|
+
gap: 2px;
|
|
438
|
+
padding-top: 2px;
|
|
439
|
+
}
|
|
440
|
+
.app-nav-sub.expanded {
|
|
441
|
+
grid-template-rows: 1fr;
|
|
442
|
+
}
|
|
443
|
+
// Child link indented under the parent label (icon slot + gap), smaller, no icon.
|
|
444
|
+
.app-nav-subitem {
|
|
445
|
+
min-height: 38px;
|
|
446
|
+
padding-left: 46px;
|
|
447
|
+
font-size: 13px;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// ── Collapsed rail: label tooltip ────────────────────────────────────────────
|
|
451
|
+
// The rail hides labels, so surface each on hover/focus as a chip to the right —
|
|
452
|
+
// otherwise the icons are unlabelled. Sub-lists and carets have no place in a rail.
|
|
453
|
+
.app-shell.collapsed .app-nav-sub,
|
|
454
|
+
.app-shell.collapsed .app-nav-caret {
|
|
455
|
+
display: none;
|
|
456
|
+
}
|
|
457
|
+
@include media-breakpoint-up(lg) {
|
|
458
|
+
.app-shell.collapsed .app-nav-item[data-label]::after {
|
|
459
|
+
content: attr(data-label);
|
|
460
|
+
position: absolute;
|
|
461
|
+
left: calc(100% + 12px);
|
|
462
|
+
top: 50%;
|
|
463
|
+
z-index: 40;
|
|
464
|
+
padding: 5px 10px;
|
|
465
|
+
border-radius: var(--radius-lg);
|
|
466
|
+
background: var(--color-text);
|
|
467
|
+
color: var(--color-surface);
|
|
468
|
+
font-size: 12px;
|
|
469
|
+
font-weight: 500;
|
|
470
|
+
white-space: nowrap;
|
|
471
|
+
box-shadow: var(--shadow-md);
|
|
472
|
+
opacity: 0;
|
|
473
|
+
transform: translateY(-50%) scale(0.96);
|
|
474
|
+
transform-origin: left center;
|
|
475
|
+
pointer-events: none;
|
|
476
|
+
transition:
|
|
477
|
+
opacity 0.12s ease,
|
|
478
|
+
transform 0.12s ease;
|
|
479
|
+
}
|
|
480
|
+
.app-shell.collapsed .app-nav-item[data-label]:hover::after,
|
|
481
|
+
.app-shell.collapsed .app-nav-item[data-label]:focus-visible::after {
|
|
482
|
+
opacity: 1;
|
|
483
|
+
transform: translateY(-50%) scale(1);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
275
487
|
// A circular button centred on the sidebar's right edge. It is fixed to the
|
|
276
488
|
// viewport, not a sidebar child, because the sidebar clips its own overflow and
|
|
277
489
|
// would cut the button in half.
|