@webority/theme 0.17.2 → 0.17.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/theme",
3
- "version": "0.17.2",
3
+ "version": "0.17.4",
4
4
  "description": "Webority shared design system — Bootstrap 5.3 SCSS overrides, --wui-* tokens, and shared component CSS. The single source of brand chrome for React portals and Razor sites.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "publishConfig": {
@@ -407,31 +407,119 @@
407
407
  gap: 6px;
408
408
  }
409
409
 
410
- // Pagination footer — rows-per-page + range + prev/next, sits flush under a
411
- // table.
410
+ // Density (AppDataTable / <app-data-table> `density`). Comfortable is the rules above. An avatar
411
+ // with no size of its own follows the density through --avatar-size; one given a size keeps it.
412
+ .table-ledger {
413
+ --avatar-size: 32px;
414
+ }
415
+ .table-ledger.wui-table-cozy {
416
+ --avatar-size: 28px;
417
+ }
418
+ .table-ledger.wui-table-cozy tbody td {
419
+ height: 48px;
420
+ padding-top: 8px;
421
+ padding-bottom: 8px;
422
+ }
423
+ // Compact is one line per row: the identity subline is hidden and text steps down to 13px.
424
+ .table-ledger.wui-table-compact {
425
+ --avatar-size: 24px;
426
+ }
427
+ .table-ledger.wui-table-compact thead th {
428
+ height: 36px;
429
+ }
430
+ .table-ledger.wui-table-compact tbody td {
431
+ height: 40px;
432
+ padding-top: 6px;
433
+ padding-bottom: 6px;
434
+ font-size: 13px;
435
+ }
436
+ .table-ledger.wui-table-compact .u-meta > .u-email,
437
+ .table-ledger.wui-table-compact .u-meta > .wui-text-xs {
438
+ display: none;
439
+ }
440
+ .table-ledger.wui-table-compact .u-cell {
441
+ gap: 10px;
442
+ }
443
+ // Striped (`striped`): alternate-row shading in place of divider lines, on any density.
444
+ .table-ledger.wui-table-striped tbody td {
445
+ border-bottom: 0;
446
+ }
447
+ .table-ledger.wui-table-striped tbody tr:nth-child(even) {
448
+ --bs-table-bg-state: var(--color-bg-subtle);
449
+ }
450
+ .table-ledger.wui-table-striped tbody tr:hover {
451
+ --bs-table-bg-state: var(--color-bg-muted);
452
+ }
453
+
454
+ // Table toolbar (AppTableToolbar / <app-table-toolbar>): filters left, search and actions right;
455
+ // while rows are selected the bulk bar takes the same strip. Both rows are always in the markup,
456
+ // one `hidden`, so the swap never moves the table.
457
+ .wui-table-toolbar {
458
+ padding: 12px var(--wui-table-pad, 20px) 0;
459
+ }
460
+ .wui-table-toolbar-main,
461
+ .wui-table-toolbar-filters,
462
+ .wui-table-toolbar-actions,
463
+ .wui-table-toolbar-bulk {
464
+ display: flex;
465
+ flex-wrap: wrap;
466
+ align-items: center;
467
+ gap: 12px;
468
+ }
469
+ .wui-table-toolbar-actions {
470
+ margin-left: auto;
471
+ gap: 8px;
472
+ }
473
+ // A filter control wider than a phone (a four-option segmented strip) scrolls inside the toolbar
474
+ // instead of widening the page.
475
+ .wui-table-toolbar-filters {
476
+ min-width: 0;
477
+ max-width: 100%;
478
+ overflow-x: auto;
479
+ scrollbar-width: none;
480
+ }
481
+ // The search box is full-width on its own; in a toolbar it is one control among several.
482
+ .wui-table-toolbar .search-wrap {
483
+ width: min(320px, 100%);
484
+ }
485
+ .wui-table-toolbar-bulk {
486
+ gap: 8px;
487
+ min-height: 40px;
488
+ padding: 4px 8px 4px 16px;
489
+ border-radius: var(--radius);
490
+ background: var(--color-primary-soft);
491
+ }
492
+ .wui-table-toolbar-count {
493
+ margin-right: 8px;
494
+ font-size: 13px;
495
+ font-weight: 600;
496
+ color: var(--color-text);
497
+ }
498
+ .wui-table-toolbar-clear {
499
+ margin-left: auto;
500
+ }
501
+ .wui-table-toolbar-main[hidden],
502
+ .wui-table-toolbar-bulk[hidden] {
503
+ display: none;
504
+ }
505
+
506
+ // Pagination footer: a "10 per page" menu beside the page buttons, flush under a table. No row
507
+ // range and no total; the page buttons say where you are.
412
508
  .table-pagination {
413
509
  display: flex;
414
510
  align-items: center;
415
511
  justify-content: flex-end;
416
- gap: 22px;
512
+ gap: 16px;
417
513
  padding: 12px 16px;
418
514
  border-top: 1px solid var(--color-border);
419
515
  font-size: 13px;
420
516
  color: var(--color-text-muted);
421
517
  flex-wrap: wrap;
422
518
  }
423
- // Rows-per-page sits at the leading edge; range + nav stay grouped on the
424
- // right.
425
519
  .tpg-rows {
426
520
  display: inline-flex;
427
521
  align-items: center;
428
- gap: 8px;
429
522
  white-space: nowrap;
430
- margin-right: auto;
431
- font-size: 12px;
432
- font-weight: 500;
433
- text-transform: uppercase;
434
- color: var(--color-text-subtle);
435
523
  }
436
524
  .select.tpg-select {
437
525
  width: auto;
@@ -617,22 +705,254 @@
617
705
  --bs-table-bg-state: color-mix(in srgb, var(--color-primary) 14%, transparent);
618
706
  }
619
707
 
620
- // Row status affordance — a colour bar on the leading edge, so a row's state is
621
- // legible without relying on a tint alone.
708
+ // Row status affordance (AppDataTable `rowStatus`, or `data-status` on a Razor <tr>): a colour bar
709
+ // on the leading edge, so a row's state is legible without relying on a tint alone. Bootstrap paints
710
+ // a cell's hover/selected tint with its own inset box-shadow, so the bar is LAYERED over that shadow
711
+ // rather than replacing it; replacing it dropped the tint from the first cell of a status row.
712
+ .table-ledger tbody tr[data-status] {
713
+ --wui-row-status: var(--color-border-strong);
714
+ }
715
+ .table-ledger tbody tr[data-status="success"] {
716
+ --wui-row-status: var(--color-success);
717
+ }
718
+ .table-ledger tbody tr[data-status="warning"] {
719
+ --wui-row-status: var(--color-warning);
720
+ }
721
+ .table-ledger tbody tr[data-status="danger"] {
722
+ --wui-row-status: var(--color-danger);
723
+ }
724
+ .table-ledger tbody tr[data-status="info"] {
725
+ --wui-row-status: var(--color-info);
726
+ }
622
727
  .table-ledger tbody tr[data-status] > td:first-child {
623
- box-shadow: inset 3px 0 0 0 var(--color-border-strong);
728
+ box-shadow:
729
+ inset 3px 0 0 0 var(--wui-row-status),
730
+ inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg)));
731
+ }
732
+
733
+ // Columns menu (AppColumnMenu / <app-column-menu>) and the column it hides on Razor.
734
+ .table-ledger .wui-col-hidden {
735
+ display: none;
736
+ }
737
+ .wui-column-menu {
738
+ min-width: 200px;
739
+ padding: 8px 0;
740
+ }
741
+ .wui-column-menu-title {
742
+ padding: 4px 14px 8px;
743
+ font-size: 12px;
744
+ font-weight: 600;
745
+ letter-spacing: 0.04em;
746
+ text-transform: uppercase;
747
+ color: var(--color-text-subtle);
748
+ }
749
+ .wui-column-menu-item {
750
+ display: flex;
751
+ align-items: center;
752
+ gap: 10px;
753
+ padding: 6px 14px;
754
+ font-size: 14px;
755
+ color: var(--color-text);
756
+ cursor: pointer;
757
+ }
758
+ .wui-column-menu-item:hover {
759
+ background: var(--color-bg-subtle);
760
+ }
761
+
762
+ // A row that opens its record (AppDataTable rowHref / onRowClick, Razor data-href).
763
+ .table-ledger tbody tr.wui-row-link {
764
+ cursor: pointer;
765
+ }
766
+ .table-ledger tbody tr.wui-row-link:focus-visible {
767
+ outline: 2px solid var(--color-primary);
768
+ outline-offset: -2px;
769
+ }
770
+
771
+ // Stacked on phones (AppDataTable `stackOnMobile`, Razor `stack-on-mobile`): below sm each row is a
772
+ // card. The first column (after the library's select/expand cells) leads the card at full width,
773
+ // every other cell is a "label: value" line from its data-label, the select checkbox sits in the
774
+ // card's top corner and the row actions close the card. The header row is visually hidden, not
775
+ // removed, so the column names stay available to assistive technology.
776
+ @include media-breakpoint-down(sm) {
777
+ // Cards flow with the page: no sideways scroll, and no maxHeight scroll box (an inline
778
+ // max-height from `maxHeight` has to be overridden, hence !important).
779
+ .table-wrap:has(> .table-ledger.wui-table-stack) {
780
+ overflow: visible;
781
+ max-height: none !important;
782
+ }
783
+ .table-wrap:has(> .table-ledger.wui-table-stack) .table-ledger thead th {
784
+ position: static;
785
+ }
786
+ .table-ledger.wui-table-stack,
787
+ .table-ledger.wui-table-stack > tbody,
788
+ .table-ledger.wui-table-stack > tfoot,
789
+ .table-ledger.wui-table-stack > tbody > tr,
790
+ .table-ledger.wui-table-stack > tfoot > tr {
791
+ display: block;
792
+ width: 100%;
793
+ }
794
+ .table-ledger.wui-table-stack > thead {
795
+ // Block, so overflow:hidden applies; a table-header-group ignores it and its row still
796
+ // widened the page.
797
+ display: block;
798
+ position: absolute;
799
+ width: 1px;
800
+ height: 1px;
801
+ overflow: hidden;
802
+ clip-path: inset(50%);
803
+ white-space: nowrap;
804
+ }
805
+ .table-ledger.wui-table-stack > tbody > tr,
806
+ .table-ledger.wui-table-stack > tfoot > tr {
807
+ position: relative;
808
+ margin-bottom: 12px;
809
+ padding: 12px 16px;
810
+ border: 1px solid var(--color-border);
811
+ border-radius: var(--radius-lg, 12px);
812
+ }
813
+ .table-ledger.wui-table-stack > tbody > tr > td,
814
+ .table-ledger.wui-table-stack > tfoot > tr > td {
815
+ display: flex;
816
+ align-items: center;
817
+ justify-content: space-between;
818
+ gap: 12px;
819
+ height: auto;
820
+ min-height: 0;
821
+ padding: 6px 0;
822
+ border: 0;
823
+ text-align: right;
824
+ // A pinned column has nothing to pin to in a card. The pinned-column selectors outrank this
825
+ // one by design (they must beat every table rule), so the card layout overrides them here.
826
+ position: static !important;
827
+ }
828
+ .table-ledger.wui-table-stack > tbody > tr > td::before,
829
+ .table-ledger.wui-table-stack > tfoot > tr > td::before {
830
+ content: attr(data-label);
831
+ flex: none;
832
+ font-size: 12px;
833
+ font-weight: 500;
834
+ color: var(--color-text-subtle);
835
+ text-align: left;
836
+ }
837
+ // A value takes the space beside its label and wraps inside the card rather than running past
838
+ // its edge (a long email under an avatar did).
839
+ .table-ledger.wui-table-stack > * > tr > td > * {
840
+ min-width: 0;
841
+ overflow-wrap: anywhere;
842
+ }
843
+ .table-ledger.wui-table-stack > tbody > tr > td .u-meta {
844
+ align-items: flex-end;
845
+ }
846
+ // The leading cell is the card's title line: full width, no label.
847
+ .table-ledger.wui-table-stack > * > tr > td:not(.wui-select-cell, .wui-expand-cell):is(:first-child, .wui-select-cell + td, .wui-expand-cell + td) {
848
+ justify-content: flex-start;
849
+ padding: 0 32px 6px 0;
850
+ text-align: left;
851
+ }
852
+ .table-ledger.wui-table-stack > * > tr > td:not(.wui-select-cell, .wui-expand-cell):is(:first-child, .wui-select-cell + td, .wui-expand-cell + td) .u-meta {
853
+ align-items: flex-start;
854
+ }
855
+ .table-ledger.wui-table-stack > * > tr > td:not(.wui-select-cell, .wui-expand-cell):is(:first-child, .wui-select-cell + td, .wui-expand-cell + td)::before {
856
+ content: none;
857
+ }
858
+ // Empty cells (a footer's blank columns) take no line.
859
+ .table-ledger.wui-table-stack > * > tr > td:empty {
860
+ display: none;
861
+ }
862
+ .table-ledger.wui-table-stack > tbody > tr > td.wui-select-cell,
863
+ .table-ledger.wui-table-stack > tbody > tr > td.wui-expand-cell {
864
+ // !important for the same reason as the static rule above: a pinned-column `left` with this
865
+ // `right` would stretch the cell across the card.
866
+ position: absolute !important;
867
+ top: 12px;
868
+ left: auto !important;
869
+ right: 12px;
870
+ width: auto;
871
+ min-width: 0;
872
+ max-width: none;
873
+ padding: 0;
874
+ }
875
+ .table-ledger.wui-table-stack > tbody > tr > td:has(> .u-row-actions) {
876
+ justify-content: flex-end;
877
+ margin-top: 6px;
878
+ padding-top: 10px;
879
+ border-top: 1px solid var(--color-border);
880
+ }
881
+ .table-ledger.wui-table-stack > tbody > tr > td:has(> .u-row-actions)::before {
882
+ content: none;
883
+ }
884
+ // The status bar becomes the card's left edge.
885
+ .table-ledger.wui-table-stack > tbody > tr[data-status] {
886
+ border-left: 3px solid var(--wui-row-status);
887
+ }
888
+ .table-ledger.wui-table-stack > tbody > tr[data-status] > td:first-child {
889
+ box-shadow: none;
890
+ }
891
+ }
892
+
893
+ // Totals row (AppDataTable column `footer`, or an author <tfoot> on Razor).
894
+ .table-ledger tfoot td {
895
+ height: 48px;
896
+ padding: 12px;
897
+ border-top: 1px solid var(--color-border);
898
+ font-size: 13px;
899
+ font-weight: 600;
900
+ color: var(--color-text);
901
+ font-variant-numeric: tabular-nums;
902
+ }
903
+ .table-ledger tfoot td:first-child {
904
+ padding-left: 20px;
624
905
  }
625
- .table-ledger tbody tr[data-status="success"] > td:first-child {
626
- box-shadow: inset 3px 0 0 0 var(--color-success);
906
+ .table-ledger tfoot td:last-child {
907
+ padding-right: 20px;
627
908
  }
628
- .table-ledger tbody tr[data-status="warning"] > td:first-child {
629
- box-shadow: inset 3px 0 0 0 var(--color-warning);
909
+
910
+ // Pinned header (`maxHeight`): the wrap becomes the scroll box and the header sticks to its top.
911
+ .table-wrap.wui-table-scroll {
912
+ overflow-y: auto;
913
+ overscroll-behavior-y: contain;
630
914
  }
631
- .table-ledger tbody tr[data-status="danger"] > td:first-child {
632
- box-shadow: inset 3px 0 0 0 var(--color-danger);
915
+ .table-wrap.wui-table-scroll .table-ledger thead th {
916
+ position: sticky;
917
+ top: 0;
918
+ z-index: 2;
633
919
  }
634
- .table-ledger tbody tr[data-status="info"] > td:first-child {
635
- box-shadow: inset 3px 0 0 0 var(--color-info);
920
+ // The wrap's own top padding would otherwise show rows scrolling behind it, above the header.
921
+ .table-wrap.wui-table-scroll:has(> .table-ledger) {
922
+ padding-top: 0;
923
+ }
924
+
925
+ // Pinned first column (`stickyFirstColumn`): the identity column, and the select/expand columns the
926
+ // library puts before it, hold their place while a wide table scrolls sideways. The identity column
927
+ // is found by position, because Razor authors write plain cells: it is the first cell after the
928
+ // library-owned select (44px) and expand (40px) cells. Pinned cells take the surface colour so
929
+ // scrolled cells do not show through; hover and selection still tint them through the inset shadow.
930
+ .table-ledger.wui-table-sticky-first > * > tr:not(.wui-row-expanded) > .wui-expand-cell,
931
+ .table-ledger.wui-table-sticky-first > * > tr:not(.wui-row-expanded) > .wui-select-cell,
932
+ .table-ledger.wui-table-sticky-first > * > tr:not(.wui-row-expanded) > :not(.wui-expand-cell, .wui-select-cell):is(:first-child, .wui-expand-cell + *, .wui-select-cell + *) {
933
+ position: sticky;
934
+ // The offset is a custom property so the per-layout rules below can set it without having to
935
+ // out-rank this selector's specificity.
936
+ left: var(--wui-sticky-left, 0);
937
+ z-index: 1;
938
+ }
939
+ .table-ledger.wui-table-sticky-first > :is(tbody, tfoot) > tr:not(.wui-row-expanded) > :is(.wui-expand-cell, .wui-select-cell),
940
+ .table-ledger.wui-table-sticky-first > :is(tbody, tfoot) > tr:not(.wui-row-expanded) > :not(.wui-expand-cell, .wui-select-cell):is(:first-child, .wui-expand-cell + *, .wui-select-cell + *) {
941
+ background-color: var(--color-surface);
942
+ }
943
+ .table-ledger.wui-table-sticky-first > * > tr > .wui-expand-cell + .wui-select-cell,
944
+ .table-ledger.wui-table-sticky-first > * > tr > .wui-expand-cell + :not(.wui-select-cell) {
945
+ --wui-sticky-left: 40px;
946
+ }
947
+ .table-ledger.wui-table-sticky-first > * > tr > .wui-select-cell + * {
948
+ --wui-sticky-left: 44px;
949
+ }
950
+ .table-ledger.wui-table-sticky-first > * > tr > .wui-expand-cell + .wui-select-cell + * {
951
+ --wui-sticky-left: 84px;
952
+ }
953
+ // A pinned header cell over a pinned column sits above both.
954
+ .table-wrap.wui-table-scroll .table-ledger.wui-table-sticky-first > thead > tr > :is(.wui-expand-cell, .wui-select-cell, :first-child, .wui-expand-cell + *, .wui-select-cell + *) {
955
+ z-index: 3;
636
956
  }
637
957
 
638
958
  // Row actions — icon-only buttons replacing a kebab dropdown.
@@ -656,7 +976,8 @@
656
976
  height: var(--wui-action-size);
657
977
  padding: 0;
658
978
  border: none;
659
- background: var(--color-bg-subtle);
979
+ // No disc at rest: a row of actions reads as quiet icons, and the tint below appears on hover.
980
+ background: transparent;
660
981
  border-radius: 999px;
661
982
  color: var(--color-text-subtle);
662
983
  cursor: pointer;
@@ -808,6 +1129,12 @@
808
1129
  opacity: 0;
809
1130
  transition: opacity 0.12s ease;
810
1131
  }
1132
+ // On a right-aligned (numeric) header the arrow leads the label, so the label ends flush with
1133
+ // the values under it.
1134
+ .table th.sortable .wui-sort-lead {
1135
+ margin-left: 0;
1136
+ margin-right: 6px;
1137
+ }
811
1138
  .table th.sortable:hover .sort-icon {
812
1139
  opacity: 0.6;
813
1140
  }
@@ -805,6 +805,9 @@ button.step:focus-visible .step-circle {
805
805
  grid-template-columns: repeat(var(--form-cols, 2), minmax(0, 1fr));
806
806
  gap: 16px;
807
807
  align-items: start;
808
+ // In a form row every field fills its column, so a date picker drops its compact standalone
809
+ // width here and lines up with the fields beside it. A .date-w-* class on the field still wins.
810
+ --wui-datepicker-width: 100%;
808
811
  }
809
812
  // Below md, collapse to 1 column.
810
813
  @include media-breakpoint-down(md) {