@webority/theme 0.7.4 → 0.7.6

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.
@@ -6,26 +6,55 @@
6
6
  // here may be overridden by a later partial. Never reorder the imports.
7
7
 
8
8
  // ── Avatar ──
9
- // A photo fills the avatar disc edge to edge; the circle is the frame, so
10
- // the image is cropped to it rather than letterboxed.
9
+ // The initials/icon face is the base layer; a photo overlays it edge to edge, cropped
10
+ // to the disc. While the photo loads — or if it breaks — the face shows through.
11
+ .avatar-face {
12
+ display: inline-flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ line-height: 1;
16
+ }
11
17
  .avatar > .avatar-img {
18
+ position: absolute;
19
+ inset: 0;
12
20
  width: 100%;
13
21
  height: 100%;
14
22
  border-radius: inherit;
15
23
  object-fit: cover;
16
24
  display: block;
25
+ // Photo fades + scales in so a late-arriving blob doesn't pop.
26
+ animation: wui-avatar-in 0.28s var(--wui-ease-out-quart, ease) both;
17
27
  }
18
28
  .avatar {
19
- width: 32px;
20
- height: 32px;
29
+ // `--avatar-size` drives the default (md, its 32px fallback) and any custom size. It is
30
+ // read via `var(… , 32px)` rather than declared here, so an avatar with no size INHERITS
31
+ // one from an AppAvatarGroup that sets it — while a preset class (explicit width below) or
32
+ // an avatar's own inline `--avatar-size` still wins. Never re-declare it on `.avatar`.
33
+ position: relative;
34
+ width: var(--avatar-size, 32px);
35
+ height: var(--avatar-size, 32px);
21
36
  border-radius: 999px;
22
37
  background: color-mix(in srgb, var(--color-primary) 15%, var(--color-surface));
23
38
  color: var(--color-primary);
24
- font-size: 12px;
39
+ font-size: calc(var(--avatar-size, 32px) * 0.375);
25
40
  font-weight: 600;
26
41
  display: inline-flex;
27
42
  align-items: center;
28
43
  justify-content: center;
44
+ flex: none;
45
+ user-select: none;
46
+ }
47
+
48
+ // ── Size scale ──
49
+ .avatar-xs {
50
+ width: 20px;
51
+ height: 20px;
52
+ font-size: 8px;
53
+ }
54
+ .avatar-sm {
55
+ width: 24px;
56
+ height: 24px;
57
+ font-size: 10px;
29
58
  }
30
59
  .avatar-lg {
31
60
  width: 48px;
@@ -37,10 +66,230 @@
37
66
  height: 64px;
38
67
  font-size: 22px;
39
68
  }
40
- .avatar-sm {
41
- width: 24px;
42
- height: 24px;
69
+ .avatar-2xl {
70
+ width: 96px;
71
+ height: 96px;
72
+ font-size: 34px;
73
+ }
74
+
75
+ // ── Shape ── (`shape`): the disc is the default; rounded/square are card-like.
76
+ .avatar-rounded {
77
+ border-radius: 25%;
78
+ }
79
+ .avatar-square {
80
+ border-radius: 12%;
81
+ }
82
+
83
+ // ── Tone ── (`tone`): the initials fill. `auto` derives a stable hue from the name.
84
+ .avatar-neutral {
85
+ background: color-mix(in srgb, var(--color-text-muted) 16%, var(--color-surface));
86
+ color: var(--color-text-muted);
87
+ }
88
+ .avatar-success {
89
+ background: var(--color-success-soft);
90
+ color: var(--color-success-text);
91
+ }
92
+ .avatar-warning {
93
+ background: var(--color-warning-soft);
94
+ color: var(--color-warning-text);
95
+ }
96
+ .avatar-danger {
97
+ background: var(--color-danger-soft);
98
+ color: var(--color-danger-text);
99
+ }
100
+ .avatar-info {
101
+ background: var(--color-info-soft);
102
+ color: var(--color-info-text);
103
+ }
104
+ .avatar-auto {
105
+ background: hsl(var(--avatar-hue, 0) 60% 90%);
106
+ color: hsl(var(--avatar-hue, 0) 42% 32%);
107
+ }
108
+
109
+ // ── Ring ── (`ring`): a tone-coloured ring with a surface gap, separating stacked
110
+ // avatars or marking an active one.
111
+ .avatar-ring {
112
+ box-shadow:
113
+ 0 0 0 2px var(--color-surface),
114
+ 0 0 0 4px currentColor;
115
+ }
116
+
117
+ // ── Corner badge ── (`badge`): a small count/marker at the top-right.
118
+ .avatar-badge {
119
+ position: absolute;
120
+ top: -2px;
121
+ right: -2px;
122
+ display: inline-flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ min-width: 16px;
126
+ height: 16px;
127
+ padding: 0 4px;
128
+ border-radius: 999px;
129
+ background: var(--color-danger);
130
+ color: #fff;
43
131
  font-size: 10px;
132
+ font-weight: 700;
133
+ line-height: 1;
134
+ box-shadow: 0 0 0 2px var(--color-surface);
135
+ }
136
+
137
+ // ── Clickable ── (`href` / `onClick`): the whole disc is one control.
138
+ .avatar-clickable {
139
+ padding: 0;
140
+ border: 0;
141
+ font: inherit;
142
+ text-decoration: none;
143
+ cursor: pointer;
144
+ -webkit-appearance: none;
145
+ appearance: none;
146
+ transition:
147
+ transform 0.15s var(--wui-ease-out-quart, ease),
148
+ box-shadow 0.15s ease;
149
+ }
150
+ .avatar-clickable:hover {
151
+ transform: scale(1.06);
152
+ }
153
+ .avatar-clickable:focus-visible {
154
+ outline: 2px solid var(--color-primary);
155
+ outline-offset: 2px;
156
+ }
157
+
158
+ // ── Presence status ── (`status`): a dot at the lower-right, ringed in the surface
159
+ // colour so it reads on a photo. Online quietly pulses.
160
+ .avatar-status {
161
+ position: absolute;
162
+ right: 2%;
163
+ bottom: 2%;
164
+ width: 28%;
165
+ height: 28%;
166
+ min-width: 8px;
167
+ min-height: 8px;
168
+ max-width: 16px;
169
+ max-height: 16px;
170
+ border-radius: 999px;
171
+ box-shadow: 0 0 0 2px var(--color-surface);
172
+ }
173
+ .avatar-status-online {
174
+ background: var(--color-success);
175
+ }
176
+ .avatar-status-away {
177
+ background: var(--color-warning);
178
+ }
179
+ .avatar-status-busy {
180
+ background: var(--color-danger);
181
+ }
182
+ .avatar-status-offline {
183
+ background: color-mix(in srgb, var(--color-text-muted) 55%, var(--color-surface));
184
+ }
185
+ .avatar-status-online::after {
186
+ content: "";
187
+ position: absolute;
188
+ inset: 0;
189
+ border-radius: inherit;
190
+ background: var(--color-success);
191
+ animation: wui-avatar-ping 1.8s cubic-bezier(0, 0, 0.2, 1) infinite;
192
+ }
193
+
194
+ // ── Hover label ── (`label`): a tooltip above the avatar, revealed on hover/focus.
195
+ .avatar-label {
196
+ position: absolute;
197
+ bottom: calc(100% + 8px);
198
+ left: 50%;
199
+ transform: translateX(-50%) translateY(4px);
200
+ padding: 4px 8px;
201
+ border-radius: var(--radius-sm);
202
+ background: var(--color-text);
203
+ color: var(--color-surface);
204
+ font-size: 12px;
205
+ font-weight: 500;
206
+ line-height: 1.2;
207
+ white-space: nowrap;
208
+ opacity: 0;
209
+ pointer-events: none;
210
+ z-index: 30;
211
+ transition:
212
+ opacity 0.16s ease,
213
+ transform 0.16s var(--wui-ease-out-quart, ease);
214
+ }
215
+ .avatar-label::after {
216
+ content: "";
217
+ position: absolute;
218
+ top: 100%;
219
+ left: 50%;
220
+ transform: translateX(-50%);
221
+ border: 4px solid transparent;
222
+ border-top-color: var(--color-text);
223
+ }
224
+ .avatar:hover > .avatar-label,
225
+ .avatar:focus-visible > .avatar-label,
226
+ .avatar:focus-within > .avatar-label {
227
+ opacity: 1;
228
+ transform: translateX(-50%) translateY(0);
229
+ }
230
+
231
+ // ── Avatar group ── (AppAvatarGroup): a row of overlapping avatars, each ringed in the
232
+ // surface colour so it reads against its neighbour. Hover raises one to the front.
233
+ .avatar-group {
234
+ display: inline-flex;
235
+ align-items: center;
236
+ }
237
+ .avatar-group > .avatar {
238
+ margin-left: -10px;
239
+ box-shadow: 0 0 0 2px var(--color-surface);
240
+ transition: transform 0.15s var(--wui-ease-out-quart, ease);
241
+ }
242
+ .avatar-group > .avatar:first-child {
243
+ margin-left: 0;
244
+ }
245
+ .avatar-group > .avatar:hover {
246
+ z-index: 1;
247
+ transform: translateY(-2px);
248
+ }
249
+ .avatar-group-tight > .avatar {
250
+ margin-left: -14px;
251
+ }
252
+ .avatar-group-loose > .avatar {
253
+ margin-left: -4px;
254
+ }
255
+ // The "+N" overflow chip — a muted avatar-shaped tail.
256
+ .avatar-more {
257
+ background: color-mix(in srgb, var(--color-text-muted) 16%, var(--color-surface));
258
+ color: var(--color-text-muted);
259
+ font-weight: 600;
260
+ }
261
+
262
+ @keyframes wui-avatar-in {
263
+ from {
264
+ opacity: 0;
265
+ transform: scale(0.92);
266
+ }
267
+ to {
268
+ opacity: 1;
269
+ transform: scale(1);
270
+ }
271
+ }
272
+ @keyframes wui-avatar-ping {
273
+ 0% {
274
+ opacity: 0.5;
275
+ transform: scale(1);
276
+ }
277
+ 70%,
278
+ 100% {
279
+ opacity: 0;
280
+ transform: scale(2.2);
281
+ }
282
+ }
283
+ @media (prefers-reduced-motion: reduce) {
284
+ .avatar > .avatar-img {
285
+ animation: none;
286
+ }
287
+ .avatar-status-online::after {
288
+ animation: none;
289
+ }
290
+ .avatar-label {
291
+ transition: opacity 0.16s ease;
292
+ }
44
293
  }
45
294
 
46
295
  // ── File attach ──
@@ -161,7 +410,7 @@
161
410
  width: 28px;
162
411
  height: 28px;
163
412
  border: 0;
164
- background: rgba(0, 0, 0, 0.62);
413
+ background: var(--color-scrim-strong);
165
414
  backdrop-filter: blur(10px);
166
415
  -webkit-backdrop-filter: blur(10px);
167
416
  color: #fff;
@@ -524,8 +773,9 @@
524
773
  display: flex;
525
774
  align-items: center;
526
775
  justify-content: center;
527
- // Subtle dim so the glassy CTA reads on any image, without hiding it.
528
- background: rgba(16, 24, 40, 0.22);
776
+ // Subtle dim so the glassy CTA reads on any image, without hiding it —
777
+ // half the standard scrim strength.
778
+ background: color-mix(in srgb, var(--color-scrim) 50%, transparent);
529
779
  opacity: 0;
530
780
  transition: opacity 0.15s ease;
531
781
  }
@@ -560,7 +810,7 @@
560
810
  padding: 0;
561
811
  border: 2px solid transparent;
562
812
  border-radius: 50%;
563
- background: rgba(16, 24, 40, 0.55);
813
+ background: var(--color-scrim-strong);
564
814
  color: #fff;
565
815
  cursor: pointer;
566
816
  opacity: 0;
@@ -646,7 +896,7 @@
646
896
  align-items: center;
647
897
  justify-content: center;
648
898
  gap: 4px;
649
- background: rgba(20, 26, 33, 0.62);
899
+ background: var(--color-scrim-strong);
650
900
  color: #fff;
651
901
  font-size: 11px;
652
902
  font-weight: 600;
@@ -794,7 +1044,7 @@
794
1044
  align-items: center;
795
1045
  justify-content: center;
796
1046
  gap: 4px;
797
- background: var(--color-scrim);
1047
+ background: var(--color-scrim-strong);
798
1048
  color: var(--color-on-scrim);
799
1049
  font-size: 12px;
800
1050
  font-weight: 600;
@@ -54,7 +54,7 @@
54
54
  .drawer-backdrop {
55
55
  position: fixed;
56
56
  inset: 0;
57
- background: rgba(15, 23, 42, 0.4);
57
+ background: var(--color-scrim);
58
58
  opacity: 0;
59
59
  pointer-events: none;
60
60
  transition: opacity 0.2s;
@@ -549,3 +549,30 @@
549
549
  cursor: not-allowed;
550
550
  opacity: 0.6;
551
551
  }
552
+
553
+ // Breadcrumbs — Bootstrap's .breadcrumb with brand tokens (AppBreadcrumbs / <app-breadcrumbs>).
554
+ // The divider is a CSS ::before glyph, so React (react-bootstrap Breadcrumb) and Razor share it.
555
+ .breadcrumb {
556
+ --bs-breadcrumb-divider: "›";
557
+ --bs-breadcrumb-divider-color: var(--color-text-subtle);
558
+ margin-bottom: 0;
559
+ font-size: 13px;
560
+ }
561
+ .breadcrumb-item a {
562
+ display: inline-flex;
563
+ align-items: center;
564
+ gap: 4px;
565
+ color: var(--color-text-muted);
566
+ text-decoration: none;
567
+ }
568
+ .breadcrumb-item a:hover {
569
+ color: var(--color-primary);
570
+ text-decoration: underline;
571
+ }
572
+ .breadcrumb-item.active {
573
+ display: inline-flex;
574
+ align-items: center;
575
+ gap: 4px;
576
+ color: var(--color-text);
577
+ font-weight: 500;
578
+ }
@@ -15,7 +15,7 @@
15
15
  // Do NOT put layout on backdropClassName — that targets a sibling, not
16
16
  // .modal.
17
17
  .modal-backdrop.wui-modal-dim {
18
- background: rgba(15, 23, 42, 0.45);
18
+ background: var(--color-scrim);
19
19
  z-index: var(--wui-z-modal-backdrop);
20
20
  }
21
21
  .modal.wui-modal-shell {
@@ -328,3 +328,47 @@
328
328
  font-size: 14px;
329
329
  font-weight: 500;
330
330
  }
331
+
332
+ // ── Toast (notifications) ──
333
+ // Bootstrap toast styling + wui extensions for title/action buttons.
334
+ // Used by both React AppToastContainer and Razor WUI.toast.
335
+
336
+ // Toast header with title
337
+ .wui-toast-header {
338
+ display: flex;
339
+ align-items: center;
340
+ justify-content: space-between;
341
+ gap: 12px;
342
+ padding: 10px 12px;
343
+ border: none;
344
+ }
345
+
346
+ .wui-toast-header strong {
347
+ font-size: 14px;
348
+ font-weight: 600;
349
+ color: inherit;
350
+ }
351
+
352
+ // Toast action button — inherits text color from the variant tone. Scoped to the
353
+ // component class, not every .btn-link a consumer might place in a toast body.
354
+ .toast .wui-toast-action {
355
+ color: inherit;
356
+ text-decoration: none;
357
+ font-size: 13px;
358
+ font-weight: 500;
359
+ padding: 2px 8px;
360
+ white-space: nowrap;
361
+ flex: none;
362
+ opacity: 0.9;
363
+ transition: opacity 150ms;
364
+ }
365
+
366
+ .toast .wui-toast-action:hover {
367
+ opacity: 1;
368
+ text-decoration: underline;
369
+ }
370
+
371
+ .toast .wui-toast-action:focus-visible {
372
+ outline: 2px solid currentColor;
373
+ outline-offset: 2px;
374
+ }
@@ -279,6 +279,22 @@ wui-multiselect[collapse-chips] .ms-chips {
279
279
  align-items: center;
280
280
  gap: 8px;
281
281
  }
282
+ .ms-input {
283
+ flex: 1;
284
+ min-width: 60px;
285
+ border: none;
286
+ background: transparent;
287
+ font: inherit;
288
+ color: inherit;
289
+ padding: 0;
290
+ margin: 0;
291
+ }
292
+ .ms-input::placeholder {
293
+ color: var(--color-text-subtle);
294
+ }
295
+ .ms-input:focus {
296
+ outline: none;
297
+ }
282
298
 
283
299
  /* ── Choice card: image / custom-template variant ── */
284
300
  .struct-card-img-card {