@webority/theme 0.7.7 → 0.7.8
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 +50 -0
- package/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_components.scss +184 -14
- package/scss/_tokens.scss +11 -3
- package/scss/_wui-aliases.scss +35 -5
- package/scss/_wui-components.scss +3 -0
- package/scss/wui/_card.scss +2 -2
- package/scss/wui/_choice.scss +11 -4
- package/scss/wui/_controls.scss +65 -24
- package/scss/wui/_data.scss +20 -0
- package/scss/wui/_media.scss +1 -1
- package/scss/wui/_overlays.scss +254 -25
- package/scss/wui/_shell.scss +37 -3
- package/scss/wui/_workspace.scss +239 -0
package/scss/wui/_data.scss
CHANGED
|
@@ -230,6 +230,10 @@
|
|
|
230
230
|
background: var(--color-bg-muted);
|
|
231
231
|
color: var(--color-text);
|
|
232
232
|
}
|
|
233
|
+
.wui-expand-toggle:focus-visible {
|
|
234
|
+
outline: 2px solid var(--color-primary);
|
|
235
|
+
outline-offset: 2px;
|
|
236
|
+
}
|
|
233
237
|
.wui-expand-toggle .bi {
|
|
234
238
|
transition: transform 0.18s var(--wui-ease-out-quart, ease);
|
|
235
239
|
}
|
|
@@ -429,6 +433,10 @@
|
|
|
429
433
|
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
430
434
|
color: var(--color-primary);
|
|
431
435
|
}
|
|
436
|
+
.tpg-arrow:focus-visible {
|
|
437
|
+
outline: 2px solid var(--color-primary);
|
|
438
|
+
outline-offset: 2px;
|
|
439
|
+
}
|
|
432
440
|
// Text-5 already reads as "unavailable" — no opacity dimming on top of it.
|
|
433
441
|
.tpg-arrow:disabled {
|
|
434
442
|
color: var(--color-border-strong);
|
|
@@ -460,6 +468,10 @@
|
|
|
460
468
|
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
461
469
|
color: var(--color-primary);
|
|
462
470
|
}
|
|
471
|
+
.tpg-page:focus-visible {
|
|
472
|
+
outline: 2px solid var(--color-primary);
|
|
473
|
+
outline-offset: 2px;
|
|
474
|
+
}
|
|
463
475
|
.tpg-page.is-active {
|
|
464
476
|
background: var(--color-primary);
|
|
465
477
|
color: var(--color-primary-text);
|
|
@@ -627,6 +639,10 @@
|
|
|
627
639
|
background: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
|
628
640
|
color: var(--color-primary);
|
|
629
641
|
}
|
|
642
|
+
.u-action:focus-visible {
|
|
643
|
+
outline: 2px solid var(--color-primary);
|
|
644
|
+
outline-offset: 2px;
|
|
645
|
+
}
|
|
630
646
|
.u-action:disabled {
|
|
631
647
|
opacity: 0.4;
|
|
632
648
|
cursor: not-allowed;
|
|
@@ -729,6 +745,10 @@
|
|
|
729
745
|
cursor: pointer;
|
|
730
746
|
user-select: none;
|
|
731
747
|
}
|
|
748
|
+
.table th.sortable:focus-visible {
|
|
749
|
+
outline: 2px solid var(--color-primary);
|
|
750
|
+
outline-offset: -2px;
|
|
751
|
+
}
|
|
732
752
|
.table th.sortable .sort-icon {
|
|
733
753
|
// Reserved space so the header does not jump width when the arrow appears.
|
|
734
754
|
display: inline-block;
|
package/scss/wui/_media.scss
CHANGED
package/scss/wui/_overlays.scss
CHANGED
|
@@ -329,46 +329,275 @@
|
|
|
329
329
|
font-weight: 500;
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
// ── Tooltip ──
|
|
333
|
+
// Both surfaces render Bootstrap's .tooltip / .tooltip-inner (React via react-bootstrap
|
|
334
|
+
// <Tooltip>, Razor via Bootstrap's own tooltip JS). The wrappers tag the tooltip element with
|
|
335
|
+
// `wui-tooltip` (React className, Razor data-bs-custom-class), so this design lands on App
|
|
336
|
+
// tooltips only and never on a raw Bootstrap one. Every colour flows --wui-tooltip-* →
|
|
337
|
+
// Bootstrap's own --bs-tooltip-* vars, so the arrow (which Bootstrap paints from
|
|
338
|
+
// --bs-tooltip-bg) always tracks the body. Customisation seam: override a --wui-tooltip-* var
|
|
339
|
+
// from a caller className for any colour, or add a tone-* class for a preset.
|
|
340
|
+
//
|
|
341
|
+
// The --wui-tooltip-* defaults sit at ZERO specificity (:where) so a caller's own single class
|
|
342
|
+
// (or inline style) overrides them; the tone presets are ordinary (0,3,0) rules that win over
|
|
343
|
+
// the defaults but lose to nothing a caller would reasonably write.
|
|
344
|
+
:where(.tooltip.wui-tooltip) {
|
|
345
|
+
--wui-tooltip-bg: var(--color-surface);
|
|
346
|
+
--wui-tooltip-color: var(--color-text);
|
|
347
|
+
--wui-tooltip-border: var(--color-border-light);
|
|
348
|
+
--wui-tooltip-radius: var(--radius, 10px);
|
|
349
|
+
--wui-tooltip-shadow: var(--wui-shadow-overlay);
|
|
350
|
+
// Size is these four vars — the `md` default. A size-* class swaps them; a caller can
|
|
351
|
+
// override any one from its own className for a fully custom size.
|
|
352
|
+
--wui-tooltip-max-width: 240px;
|
|
353
|
+
--wui-tooltip-font-size: 0.8125rem;
|
|
354
|
+
--wui-tooltip-padding-y: 7px;
|
|
355
|
+
--wui-tooltip-padding-x: 11px;
|
|
356
|
+
}
|
|
357
|
+
.tooltip.wui-tooltip {
|
|
358
|
+
--bs-tooltip-bg: var(--wui-tooltip-bg);
|
|
359
|
+
--bs-tooltip-color: var(--wui-tooltip-color);
|
|
360
|
+
--bs-tooltip-max-width: var(--wui-tooltip-max-width);
|
|
361
|
+
--bs-tooltip-border-radius: var(--wui-tooltip-radius);
|
|
362
|
+
--bs-tooltip-font-size: var(--wui-tooltip-font-size);
|
|
363
|
+
--bs-tooltip-padding-y: var(--wui-tooltip-padding-y);
|
|
364
|
+
--bs-tooltip-padding-x: var(--wui-tooltip-padding-x);
|
|
365
|
+
--bs-tooltip-opacity: 1;
|
|
366
|
+
}
|
|
367
|
+
.tooltip.wui-tooltip .tooltip-inner {
|
|
368
|
+
// Padding + font-size come from the --bs-tooltip-* vars above (driven by --wui-tooltip-*),
|
|
369
|
+
// so a size-* class or a custom var resizes the tip without touching this rule.
|
|
370
|
+
font-weight: 500;
|
|
371
|
+
line-height: 1.4;
|
|
372
|
+
text-align: left;
|
|
373
|
+
border: 1px solid var(--wui-tooltip-border);
|
|
374
|
+
box-shadow: var(--wui-tooltip-shadow);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// Size presets — swap the four size vars. `md` is the :where default (no class).
|
|
378
|
+
.tooltip.wui-tooltip.size-sm {
|
|
379
|
+
--wui-tooltip-max-width: 200px;
|
|
380
|
+
--wui-tooltip-font-size: 0.75rem;
|
|
381
|
+
--wui-tooltip-padding-y: 4px;
|
|
382
|
+
--wui-tooltip-padding-x: 8px;
|
|
383
|
+
}
|
|
384
|
+
.tooltip.wui-tooltip.size-lg {
|
|
385
|
+
--wui-tooltip-max-width: 300px;
|
|
386
|
+
--wui-tooltip-font-size: 0.9375rem;
|
|
387
|
+
--wui-tooltip-padding-y: 10px;
|
|
388
|
+
--wui-tooltip-padding-x: 14px;
|
|
389
|
+
}
|
|
390
|
+
// A hairline shadow so the light default's white arrow still reads on a white page; on a tinted
|
|
391
|
+
// tone the arrow is coloured and this just adds subtle depth.
|
|
392
|
+
.tooltip.wui-tooltip .tooltip-arrow {
|
|
393
|
+
filter: drop-shadow(0 1px 1px color-mix(in srgb, var(--color-text) 14%, transparent));
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Tone presets — swap the three colour vars; the arrow follows --bs-tooltip-bg automatically.
|
|
397
|
+
// Fills that carry white text use the AA-cleared -strong tokens; warning stays dark-on-yellow.
|
|
398
|
+
.tooltip.wui-tooltip.tone-dark {
|
|
399
|
+
--wui-tooltip-bg: var(--color-text);
|
|
400
|
+
--wui-tooltip-color: #fff;
|
|
401
|
+
--wui-tooltip-border: transparent;
|
|
402
|
+
}
|
|
403
|
+
.tooltip.wui-tooltip.tone-primary {
|
|
404
|
+
--wui-tooltip-bg: var(--color-primary);
|
|
405
|
+
--wui-tooltip-color: var(--color-primary-text);
|
|
406
|
+
--wui-tooltip-border: transparent;
|
|
407
|
+
}
|
|
408
|
+
.tooltip.wui-tooltip.tone-success {
|
|
409
|
+
--wui-tooltip-bg: var(--wui-success-strong);
|
|
410
|
+
--wui-tooltip-color: #fff;
|
|
411
|
+
--wui-tooltip-border: transparent;
|
|
412
|
+
}
|
|
413
|
+
.tooltip.wui-tooltip.tone-danger {
|
|
414
|
+
--wui-tooltip-bg: var(--wui-danger-strong);
|
|
415
|
+
--wui-tooltip-color: #fff;
|
|
416
|
+
--wui-tooltip-border: transparent;
|
|
417
|
+
}
|
|
418
|
+
.tooltip.wui-tooltip.tone-warning {
|
|
419
|
+
--wui-tooltip-bg: var(--color-warning);
|
|
420
|
+
--wui-tooltip-color: var(--color-text);
|
|
421
|
+
--wui-tooltip-border: transparent;
|
|
422
|
+
}
|
|
423
|
+
|
|
332
424
|
// ── Toast (notifications) ──
|
|
333
|
-
//
|
|
334
|
-
//
|
|
425
|
+
// Surface-card toasts shared by React <AppToast/> and Razor <app-toast>/WUI.toast: an elevated
|
|
426
|
+
// card with a left accent bar + variant icon, an auto-dismiss progress bar (pauses on hover/focus),
|
|
427
|
+
// and swipe-to-dismiss. The variant tint drives one custom prop so every part stays in step.
|
|
335
428
|
|
|
336
|
-
|
|
337
|
-
|
|
429
|
+
.wui-toast-container {
|
|
430
|
+
z-index: var(--wui-z-toast);
|
|
338
431
|
display: flex;
|
|
339
|
-
|
|
340
|
-
|
|
432
|
+
flex-direction: column;
|
|
433
|
+
gap: 10px;
|
|
434
|
+
max-width: min(92vw, 400px);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
.wui-toast {
|
|
438
|
+
--wui-toast-accent: var(--color-primary);
|
|
439
|
+
position: relative;
|
|
440
|
+
display: flex;
|
|
441
|
+
align-items: flex-start;
|
|
341
442
|
gap: 12px;
|
|
342
|
-
|
|
343
|
-
|
|
443
|
+
width: 100%;
|
|
444
|
+
padding: 14px 14px 14px 18px;
|
|
445
|
+
background: var(--color-surface);
|
|
446
|
+
color: var(--color-text);
|
|
447
|
+
border: 1px solid var(--color-border-light);
|
|
448
|
+
border-radius: var(--radius-lg, 12px);
|
|
449
|
+
box-shadow: var(--wui-shadow-overlay);
|
|
450
|
+
overflow: hidden;
|
|
451
|
+
touch-action: pan-y;
|
|
452
|
+
user-select: none;
|
|
453
|
+
transition:
|
|
454
|
+
transform 0.2s ease,
|
|
455
|
+
opacity 0.2s ease;
|
|
456
|
+
animation: wui-toast-in 0.24s ease;
|
|
344
457
|
}
|
|
345
458
|
|
|
346
|
-
|
|
347
|
-
|
|
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
|
+
// While a swipe is in progress the toast follows the pointer with no transition.
|
|
469
|
+
.wui-toast--dragging {
|
|
470
|
+
transition: none;
|
|
471
|
+
cursor: grabbing;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.wui-toast--success {
|
|
475
|
+
--wui-toast-accent: var(--color-success);
|
|
476
|
+
}
|
|
477
|
+
.wui-toast--info {
|
|
478
|
+
--wui-toast-accent: var(--color-info);
|
|
479
|
+
}
|
|
480
|
+
.wui-toast--warning {
|
|
481
|
+
--wui-toast-accent: var(--color-warning);
|
|
482
|
+
}
|
|
483
|
+
.wui-toast--error,
|
|
484
|
+
.wui-toast--danger {
|
|
485
|
+
--wui-toast-accent: var(--color-danger);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
.wui-toast-icon {
|
|
489
|
+
flex: none;
|
|
490
|
+
display: inline-flex;
|
|
491
|
+
margin-top: 1px;
|
|
492
|
+
color: var(--wui-toast-accent);
|
|
493
|
+
line-height: 1;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.wui-toast-content {
|
|
497
|
+
flex: 1 1 auto;
|
|
498
|
+
min-width: 0;
|
|
499
|
+
display: flex;
|
|
500
|
+
flex-direction: column;
|
|
501
|
+
gap: 2px;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.wui-toast-title {
|
|
505
|
+
font-size: 0.9375rem;
|
|
348
506
|
font-weight: 600;
|
|
349
|
-
color:
|
|
507
|
+
color: var(--color-text);
|
|
350
508
|
}
|
|
351
509
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
510
|
+
.wui-toast-message {
|
|
511
|
+
font-size: 0.875rem;
|
|
512
|
+
color: var(--color-text);
|
|
513
|
+
overflow-wrap: anywhere;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Action button — tinted with the variant accent, scoped to the toast so it never
|
|
517
|
+
// restyles an arbitrary .btn-link a rich body might contain.
|
|
518
|
+
.wui-toast-action {
|
|
519
|
+
flex: none;
|
|
520
|
+
align-self: center;
|
|
521
|
+
color: var(--wui-toast-accent);
|
|
356
522
|
text-decoration: none;
|
|
357
|
-
font-size:
|
|
358
|
-
font-weight:
|
|
523
|
+
font-size: 0.8125rem;
|
|
524
|
+
font-weight: 600;
|
|
359
525
|
padding: 2px 8px;
|
|
360
526
|
white-space: nowrap;
|
|
527
|
+
}
|
|
528
|
+
.wui-toast-action:hover {
|
|
529
|
+
color: var(--wui-toast-accent);
|
|
530
|
+
text-decoration: underline;
|
|
531
|
+
}
|
|
532
|
+
.wui-toast-action:focus-visible {
|
|
533
|
+
outline: 2px solid var(--wui-toast-accent);
|
|
534
|
+
outline-offset: 2px;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
.wui-toast-close {
|
|
361
538
|
flex: none;
|
|
362
|
-
|
|
363
|
-
|
|
539
|
+
margin-top: 1px;
|
|
540
|
+
font-size: 0.65rem;
|
|
364
541
|
}
|
|
365
542
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
543
|
+
// A pure-CSS auto-dismiss countdown: the bar shrinks over the toast's duration; the JS side
|
|
544
|
+
// listens for animationend to remove it. Because :hover/:focus-within pause the animation,
|
|
545
|
+
// animationend cannot fire while the toast is being read — a self-synchronising timer.
|
|
546
|
+
.wui-toast-progress {
|
|
547
|
+
position: absolute;
|
|
548
|
+
left: 0;
|
|
549
|
+
bottom: 0;
|
|
550
|
+
height: 3px;
|
|
551
|
+
width: 100%;
|
|
552
|
+
transform-origin: left center;
|
|
553
|
+
background: var(--wui-toast-accent);
|
|
554
|
+
opacity: 0.5;
|
|
555
|
+
animation-name: wui-toast-countdown;
|
|
556
|
+
animation-timing-function: linear;
|
|
557
|
+
animation-fill-mode: forwards;
|
|
558
|
+
}
|
|
559
|
+
.wui-toast:hover .wui-toast-progress,
|
|
560
|
+
.wui-toast:focus-within .wui-toast-progress {
|
|
561
|
+
animation-play-state: paused;
|
|
369
562
|
}
|
|
370
563
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
564
|
+
// "+N more" chip when the stack exceeds maxVisible.
|
|
565
|
+
.wui-toast-more {
|
|
566
|
+
align-self: center;
|
|
567
|
+
font-size: 0.75rem;
|
|
568
|
+
font-weight: 600;
|
|
569
|
+
color: var(--color-text-subtle);
|
|
570
|
+
background: var(--color-surface);
|
|
571
|
+
border: 1px solid var(--color-border-light);
|
|
572
|
+
border-radius: 999px;
|
|
573
|
+
padding: 2px 12px;
|
|
574
|
+
box-shadow: var(--wui-shadow-overlay);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
@keyframes wui-toast-countdown {
|
|
578
|
+
from {
|
|
579
|
+
transform: scaleX(1);
|
|
580
|
+
}
|
|
581
|
+
to {
|
|
582
|
+
transform: scaleX(0);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
@keyframes wui-toast-in {
|
|
587
|
+
from {
|
|
588
|
+
opacity: 0;
|
|
589
|
+
transform: translateY(8px);
|
|
590
|
+
}
|
|
591
|
+
to {
|
|
592
|
+
opacity: 1;
|
|
593
|
+
transform: translateY(0);
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
@media (prefers-reduced-motion: reduce) {
|
|
598
|
+
// Drop the entrance slide; the countdown bar stays — it is the auto-dismiss timer, not decoration.
|
|
599
|
+
.wui-toast {
|
|
600
|
+
animation: none;
|
|
601
|
+
transition: none;
|
|
602
|
+
}
|
|
374
603
|
}
|
package/scss/wui/_shell.scss
CHANGED
|
@@ -263,13 +263,25 @@
|
|
|
263
263
|
height: 0;
|
|
264
264
|
}
|
|
265
265
|
// Between the brand row and the nav — a workspace/tenant switcher, an environment
|
|
266
|
-
// badge. It must not shrink when the nav overflows
|
|
267
|
-
// 88px rail, so the collapsed state
|
|
266
|
+
// badge. It must not shrink when the nav overflows. The full card has no place on
|
|
267
|
+
// the 88px rail, so the collapsed state hides it (the mobile drawer restores it).
|
|
268
|
+
// The compact twin (`sidebarStartCompact` / `<app-shell-sidebar-start-compact>`) is
|
|
269
|
+
// the inverse: hidden expanded, shown only on the desktop collapsed rail, sized for
|
|
270
|
+
// a 36px monogram control (AppWorkspaceSwitcher compact).
|
|
268
271
|
.app-sidebar-start {
|
|
269
272
|
flex: none;
|
|
270
273
|
margin-bottom: 8px;
|
|
271
274
|
overflow: hidden;
|
|
272
275
|
}
|
|
276
|
+
.app-sidebar-start-compact {
|
|
277
|
+
display: none;
|
|
278
|
+
flex: none;
|
|
279
|
+
margin-bottom: 8px;
|
|
280
|
+
// Centre the fixed square in the 88px rail so it lines up with the icon column.
|
|
281
|
+
justify-content: center;
|
|
282
|
+
align-items: center;
|
|
283
|
+
overflow: hidden;
|
|
284
|
+
}
|
|
273
285
|
.app-sidebar-foot {
|
|
274
286
|
flex: none;
|
|
275
287
|
padding-top: 12px;
|
|
@@ -374,13 +386,31 @@
|
|
|
374
386
|
gap: 0;
|
|
375
387
|
padding: 9px 0;
|
|
376
388
|
}
|
|
377
|
-
.app-shell.collapsed .app-nav-text,
|
|
378
389
|
.app-shell.collapsed .app-nav-badge,
|
|
379
390
|
.app-shell.collapsed .app-nav-group-label,
|
|
380
391
|
.app-shell.collapsed .app-sidebar-brand-text,
|
|
381
392
|
.app-shell.collapsed .app-sidebar-start {
|
|
382
393
|
display: none;
|
|
383
394
|
}
|
|
395
|
+
// The label leaves the VISUAL flow but must stay in the accessibility tree — an
|
|
396
|
+
// icon-only rail link with display:none text has no accessible name (axe
|
|
397
|
+
// link-name, serious). Clip-rect keeps the name; no phantom gap since it takes
|
|
398
|
+
// no space.
|
|
399
|
+
.app-shell.collapsed .app-nav-text {
|
|
400
|
+
position: absolute;
|
|
401
|
+
width: 1px;
|
|
402
|
+
height: 1px;
|
|
403
|
+
margin: -1px;
|
|
404
|
+
padding: 0;
|
|
405
|
+
overflow: hidden;
|
|
406
|
+
clip-path: inset(50%);
|
|
407
|
+
white-space: nowrap;
|
|
408
|
+
border: 0;
|
|
409
|
+
}
|
|
410
|
+
// Swap: expanded slot off, compact monogram on — only when the author filled the compact slot.
|
|
411
|
+
.app-shell.collapsed .app-sidebar-start-compact {
|
|
412
|
+
display: flex;
|
|
413
|
+
}
|
|
384
414
|
|
|
385
415
|
// ── Badge tones ──────────────────────────────────────────────────────────────
|
|
386
416
|
// A nav badge can carry a status tone so an alert count reads red, not neutral.
|
|
@@ -651,6 +681,10 @@
|
|
|
651
681
|
.app-shell.app-shell.collapsed .app-sidebar-start {
|
|
652
682
|
display: revert;
|
|
653
683
|
}
|
|
684
|
+
// Drawer always shows full labels — the compact rail control has no role here.
|
|
685
|
+
.app-shell.app-shell.collapsed .app-sidebar-start-compact {
|
|
686
|
+
display: none;
|
|
687
|
+
}
|
|
654
688
|
.app-shell.app-shell .app-sidebar-toggle {
|
|
655
689
|
display: none;
|
|
656
690
|
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
// Every class here is namespaced `.workspace-switcher*`, and the rules that have to beat
|
|
2
|
+
// Bootstrap's own .dropdown-item/.dropdown-menu specificity are additionally scoped under
|
|
3
|
+
// the .workspace-switcher root — so this file cannot reach anything a product owns unless
|
|
4
|
+
// the product claims that same prefix (see the CHANGELOG's claimed-class-names section).
|
|
5
|
+
// Tenant / workspace switcher — shared by React <AppWorkspaceSwitcher> and Razor
|
|
6
|
+
// <app-workspace-switcher>. Both render a Bootstrap dropdown, so the menu chrome below
|
|
7
|
+
// keys on the shared classes rather than on either surface's own wrapper.
|
|
8
|
+
//
|
|
9
|
+
// Part of the wui-components layer — see _wui-components.scss for the whole picture and the
|
|
10
|
+
// load order. Order within the layer is LOAD-BEARING: these partials are a split of one
|
|
11
|
+
// stylesheet, not independent modules. Never reorder the imports.
|
|
12
|
+
|
|
13
|
+
.workspace-switcher {
|
|
14
|
+
// The switcher fills whatever slot it is dropped into (a sidebar rail, a topbar cell);
|
|
15
|
+
// the compact trigger opts out below because its whole point is a fixed square.
|
|
16
|
+
display: block;
|
|
17
|
+
width: 100%;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── Trigger ──
|
|
21
|
+
.workspace-switcher-trigger {
|
|
22
|
+
display: flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: 10px;
|
|
25
|
+
width: 100%;
|
|
26
|
+
min-height: 48px;
|
|
27
|
+
padding: 6px 10px;
|
|
28
|
+
background: var(--color-bg-subtle);
|
|
29
|
+
border: 1px solid var(--color-border-light);
|
|
30
|
+
border-radius: var(--radius-lg);
|
|
31
|
+
corner-shape: squircle;
|
|
32
|
+
color: var(--color-text);
|
|
33
|
+
text-align: left;
|
|
34
|
+
cursor: pointer;
|
|
35
|
+
transition: background 180ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
36
|
+
}
|
|
37
|
+
.workspace-switcher-trigger:hover {
|
|
38
|
+
background: var(--color-primary-soft);
|
|
39
|
+
}
|
|
40
|
+
.workspace-switcher-trigger:focus-visible {
|
|
41
|
+
outline: 2px solid var(--color-primary);
|
|
42
|
+
outline-offset: 2px;
|
|
43
|
+
}
|
|
44
|
+
.workspace-switcher-trigger.is-compact {
|
|
45
|
+
width: 36px;
|
|
46
|
+
min-height: 36px;
|
|
47
|
+
height: 36px;
|
|
48
|
+
padding: 0;
|
|
49
|
+
justify-content: center;
|
|
50
|
+
gap: 0;
|
|
51
|
+
border-radius: var(--radius);
|
|
52
|
+
}
|
|
53
|
+
.workspace-switcher-trigger-name {
|
|
54
|
+
flex: 1 1 auto;
|
|
55
|
+
min-width: 0;
|
|
56
|
+
font-size: 14px;
|
|
57
|
+
font-weight: 600;
|
|
58
|
+
// A workspace name is user data: truncate rather than let it push the chevron out.
|
|
59
|
+
overflow: hidden;
|
|
60
|
+
text-overflow: ellipsis;
|
|
61
|
+
white-space: nowrap;
|
|
62
|
+
}
|
|
63
|
+
.workspace-switcher-chevron {
|
|
64
|
+
flex: none;
|
|
65
|
+
color: var(--color-text-muted);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ── Monogram / logo ──
|
|
69
|
+
// One class for both so a logo swap never changes the layout box.
|
|
70
|
+
.workspace-switcher-mark {
|
|
71
|
+
flex: none;
|
|
72
|
+
display: inline-flex;
|
|
73
|
+
align-items: center;
|
|
74
|
+
justify-content: center;
|
|
75
|
+
width: 28px;
|
|
76
|
+
height: 28px;
|
|
77
|
+
border-radius: var(--radius);
|
|
78
|
+
corner-shape: squircle;
|
|
79
|
+
background: var(--color-primary-soft);
|
|
80
|
+
color: var(--color-primary);
|
|
81
|
+
font-size: 11px;
|
|
82
|
+
font-weight: 700;
|
|
83
|
+
letter-spacing: 0.02em;
|
|
84
|
+
line-height: 1;
|
|
85
|
+
object-fit: cover;
|
|
86
|
+
overflow: hidden;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ── Menu ──
|
|
90
|
+
.workspace-switcher .workspace-switcher-menu {
|
|
91
|
+
width: 280px;
|
|
92
|
+
max-width: 280px;
|
|
93
|
+
padding: 6px;
|
|
94
|
+
background: var(--color-surface);
|
|
95
|
+
border: 1px solid var(--color-border-light);
|
|
96
|
+
border-radius: var(--radius-lg);
|
|
97
|
+
corner-shape: squircle;
|
|
98
|
+
box-shadow: var(--wui-shadow-overlay);
|
|
99
|
+
}
|
|
100
|
+
.workspace-switcher-menu-label {
|
|
101
|
+
padding: 6px 10px 4px;
|
|
102
|
+
font-size: 11px;
|
|
103
|
+
font-weight: 700;
|
|
104
|
+
letter-spacing: 0.06em;
|
|
105
|
+
text-transform: uppercase;
|
|
106
|
+
color: var(--color-text-subtle);
|
|
107
|
+
}
|
|
108
|
+
.workspace-switcher-search {
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
gap: 8px;
|
|
112
|
+
margin: 2px 4px 6px;
|
|
113
|
+
padding: 6px 10px;
|
|
114
|
+
background: var(--color-bg-subtle);
|
|
115
|
+
border: 1px solid var(--color-border-light);
|
|
116
|
+
border-radius: var(--radius);
|
|
117
|
+
corner-shape: squircle;
|
|
118
|
+
color: var(--color-text-muted);
|
|
119
|
+
}
|
|
120
|
+
.workspace-switcher-search-input {
|
|
121
|
+
flex: 1 1 auto;
|
|
122
|
+
min-width: 0;
|
|
123
|
+
border: none;
|
|
124
|
+
background: transparent;
|
|
125
|
+
color: var(--color-text);
|
|
126
|
+
font-size: 13px;
|
|
127
|
+
outline: none;
|
|
128
|
+
}
|
|
129
|
+
.workspace-switcher-list {
|
|
130
|
+
display: flex;
|
|
131
|
+
flex-direction: column;
|
|
132
|
+
gap: 2px;
|
|
133
|
+
max-height: 264px;
|
|
134
|
+
overflow-y: auto;
|
|
135
|
+
scrollbar-width: thin;
|
|
136
|
+
scrollbar-color: var(--color-border-strong) transparent;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// The rows are Bootstrap dropdown items on both surfaces, so the shared item chrome has to
|
|
140
|
+
// be re-stated here — an identity row is a three-column grid, not a text line with a glyph.
|
|
141
|
+
.workspace-switcher .workspace-switcher-option,
|
|
142
|
+
.workspace-switcher .workspace-switcher-create {
|
|
143
|
+
display: flex;
|
|
144
|
+
align-items: center;
|
|
145
|
+
gap: 10px;
|
|
146
|
+
width: 100%;
|
|
147
|
+
padding: 7px 10px;
|
|
148
|
+
border: none;
|
|
149
|
+
border-radius: var(--radius);
|
|
150
|
+
corner-shape: squircle;
|
|
151
|
+
background: transparent;
|
|
152
|
+
color: var(--color-text);
|
|
153
|
+
font-size: 14px;
|
|
154
|
+
text-align: left;
|
|
155
|
+
text-decoration: none;
|
|
156
|
+
cursor: pointer;
|
|
157
|
+
}
|
|
158
|
+
.workspace-switcher .workspace-switcher-option:hover,
|
|
159
|
+
.workspace-switcher .workspace-switcher-create:hover {
|
|
160
|
+
background: var(--color-bg-subtle);
|
|
161
|
+
color: var(--color-text);
|
|
162
|
+
}
|
|
163
|
+
.workspace-switcher .workspace-switcher-option:focus-visible,
|
|
164
|
+
.workspace-switcher .workspace-switcher-create:focus-visible,
|
|
165
|
+
.workspace-switcher .workspace-switcher-footer-action:focus-visible {
|
|
166
|
+
outline: 2px solid var(--color-primary);
|
|
167
|
+
outline-offset: 2px;
|
|
168
|
+
}
|
|
169
|
+
// The active workspace keeps its tint through hover, so the tick is never the only cue.
|
|
170
|
+
.workspace-switcher .workspace-switcher-option.is-active,
|
|
171
|
+
.workspace-switcher .workspace-switcher-option.is-active:hover {
|
|
172
|
+
background: var(--color-primary-soft);
|
|
173
|
+
color: var(--color-primary);
|
|
174
|
+
}
|
|
175
|
+
.workspace-switcher-option-name {
|
|
176
|
+
flex: 1 1 auto;
|
|
177
|
+
min-width: 0;
|
|
178
|
+
overflow: hidden;
|
|
179
|
+
text-overflow: ellipsis;
|
|
180
|
+
white-space: nowrap;
|
|
181
|
+
}
|
|
182
|
+
.workspace-switcher-badge {
|
|
183
|
+
flex: none;
|
|
184
|
+
padding: 1px 7px;
|
|
185
|
+
border-radius: 999px;
|
|
186
|
+
background: var(--color-bg-subtle);
|
|
187
|
+
color: var(--color-text-muted);
|
|
188
|
+
font-size: 11px;
|
|
189
|
+
font-weight: 600;
|
|
190
|
+
}
|
|
191
|
+
.workspace-switcher-tick {
|
|
192
|
+
flex: none;
|
|
193
|
+
color: var(--color-primary);
|
|
194
|
+
}
|
|
195
|
+
.workspace-switcher-empty {
|
|
196
|
+
padding: 14px 10px;
|
|
197
|
+
text-align: center;
|
|
198
|
+
font-size: 13px;
|
|
199
|
+
color: var(--color-text-muted);
|
|
200
|
+
}
|
|
201
|
+
.workspace-switcher .workspace-switcher-create {
|
|
202
|
+
color: var(--color-primary);
|
|
203
|
+
font-weight: 600;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ── Footer ──
|
|
207
|
+
// A single workspace still gets a menu: current row + this footer. The divider is what makes
|
|
208
|
+
// that read as a complete panel rather than a truncated list.
|
|
209
|
+
.workspace-switcher-footer {
|
|
210
|
+
margin-top: 6px;
|
|
211
|
+
padding-top: 6px;
|
|
212
|
+
border-top: 1px solid var(--color-border-light);
|
|
213
|
+
}
|
|
214
|
+
.workspace-switcher .workspace-switcher-footer-action {
|
|
215
|
+
display: flex;
|
|
216
|
+
align-items: center;
|
|
217
|
+
gap: 10px;
|
|
218
|
+
width: 100%;
|
|
219
|
+
padding: 7px 10px;
|
|
220
|
+
border: none;
|
|
221
|
+
border-radius: var(--radius);
|
|
222
|
+
corner-shape: squircle;
|
|
223
|
+
background: transparent;
|
|
224
|
+
color: var(--color-text-muted);
|
|
225
|
+
font-size: 13px;
|
|
226
|
+
text-align: left;
|
|
227
|
+
text-decoration: none;
|
|
228
|
+
cursor: pointer;
|
|
229
|
+
}
|
|
230
|
+
.workspace-switcher .workspace-switcher-footer-action:hover {
|
|
231
|
+
background: var(--color-bg-subtle);
|
|
232
|
+
color: var(--color-text);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@media (prefers-reduced-motion: reduce) {
|
|
236
|
+
.workspace-switcher-trigger {
|
|
237
|
+
transition: none;
|
|
238
|
+
}
|
|
239
|
+
}
|