@webority/theme 0.107.0 → 0.108.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.
@@ -1,146 +1,11 @@
1
1
  // Prefer .wui-* for new chrome; legacy bare names here are frozen until a BREAKING rename.
2
- // Empty state, base table, ledger table + pagination.
2
+ // Base table, ledger table, toolbar and filter bar. The empty state is in _empty-state.scss, the pager in _pagination.scss.
3
3
  //
4
4
  // Part of the wui-components layer — see _wui-components.scss for the whole
5
5
  // picture and the load order. Order within the layer is LOAD-BEARING: these
6
6
  // partials are a split of one stylesheet, not independent modules, so a rule
7
7
  // here may be overridden by a later partial. Never reorder the imports.
8
8
 
9
- // ── Empty state ──
10
- .empty-state {
11
- text-align: center;
12
- padding: 48px 24px;
13
- color: var(--color-text-subtle);
14
- }
15
- .empty-state-title {
16
- // Matches .tbl-empty-title, both in the sans face under every brand. A table's header hides
17
- // in any .table-wrap whose empty block is visible.
18
- font-size: 17px;
19
- font-weight: 600;
20
- line-height: 1.35;
21
- color: var(--color-text);
22
- }
23
- .empty-state-message {
24
- margin: 6px 0 0;
25
- // Matches .tbl-empty-text.
26
- font-size: 16px;
27
- line-height: 1.5;
28
- color: var(--color-text-muted);
29
- }
30
- .empty-state-action {
31
- margin-top: 16px;
32
- }
33
- // Tighter scale for a widget/tile, as opposed to a full card or page.
34
- .empty-state-sm {
35
- padding: 28px 16px;
36
- }
37
- .empty-state-sm .empty-state-title {
38
- font-size: 14px;
39
- }
40
- .empty-state-sm .empty-state-message {
41
- font-size: 12px;
42
- }
43
- .empty-state-icon {
44
- width: 48px;
45
- height: 48px;
46
- border-radius: var(--wui-radius-lg);
47
- background: var(--color-primary-soft);
48
- color: var(--color-primary-text);
49
- display: inline-flex;
50
- align-items: center;
51
- justify-content: center;
52
- margin-bottom: 14px;
53
- }
54
-
55
- // The empty state a DataTable renders in place of its rows. Distinct from
56
- // the card-level .empty-state above: a 50px primary-tinted disc, then a 32px
57
- // gap to the title and 12px to the subtitle.
58
- .tbl-empty {
59
- display: flex;
60
- flex-direction: column;
61
- align-items: center;
62
- text-align: center;
63
- padding: 40px 24px;
64
- }
65
- .tbl-empty-icon {
66
- display: inline-grid;
67
- place-items: center;
68
- flex: none;
69
- width: 50px;
70
- height: 50px;
71
- border-radius: 50%;
72
- background: color-mix(in srgb, var(--color-primary) 10%, transparent);
73
- color: var(--color-primary-text);
74
- margin-bottom: 16px;
75
- }
76
- .tbl-empty-title {
77
- font-size: 17px;
78
- font-weight: 600;
79
- line-height: 1.35;
80
- color: var(--color-text);
81
- }
82
- .tbl-empty-text {
83
- margin-top: 8px;
84
- font-size: 16px;
85
- font-weight: 400;
86
- line-height: 1.5;
87
- color: var(--color-text-muted);
88
- }
89
- // An action (e.g. "New assessment") sits under the subtitle when the caller
90
- // supplies one.
91
- .tbl-empty-action {
92
- margin-top: 20px;
93
- }
94
-
95
- .wui-table-empty {
96
- padding: 36px 24px;
97
- text-align: center;
98
- color: var(--color-text-muted);
99
- }
100
- // The empty state owns its own padding and is not a row you can act on — no
101
- // cell padding, no border, and no hover fill from .table / .table-ledger.
102
- .table td.wui-table-empty:has(> .tbl-empty),
103
- .table td.wui-table-empty:has(> .tbl-empty-plain) {
104
- padding: 0;
105
- border-bottom: 0;
106
- }
107
- // A plain-string empty (no `.tbl-empty` wrapper) — carries the padding the cell
108
- // used to own.
109
- .tbl-empty-plain {
110
- padding: 36px 24px;
111
- text-align: center;
112
- color: var(--color-text-muted);
113
- }
114
- .table tbody tr:hover td.wui-table-empty:has(> .tbl-empty),
115
- .table-ledger tbody tr:hover td.wui-table-empty:has(> .tbl-empty) {
116
- background: transparent;
117
- }
118
- // The React AppDataTable renders its empty state as a SIBLING of the table, INSIDE `.table-wrap`
119
- // (the horizontal scroller), in this `.tbl-empty-block`. Two things fall out of that placement:
120
- // 1. the horizontal scrollbar sits at the BOTTOM of the wrap — below this message — instead of
121
- // being wedged between the header band and the message (which is what an outside-the-wrap
122
- // block produced), and
123
- // 2. `position: sticky; left: 0` + `width: 100%` pins the message to the wrap's VISIBLE width and
124
- // holds it there while a wide header scrolls behind it: `width: 100%` on a child of an
125
- // overflow container resolves to the container's CONTENT (visible) width, not its scroll width,
126
- // so the message never grows the scroll region and never slides off on a narrow screen.
127
- //
128
- // This deliberately does NOT use `container-type` + `100cqw` — the query container collapses
129
- // `.table-wrap` to a sliver when it is a flex/grid item (see the `.table-wrap` note below). Sticky
130
- // sidesteps that entirely. (The `.wui-table-empty` cell rules above remain for Razor, where the
131
- // empty state is author-placed inside a `<td colSpan>`.)
132
- .tbl-empty-block {
133
- position: sticky;
134
- left: 0;
135
- // = the wrap's visible width (content box of the overflow container), never its scroll width.
136
- width: 100%;
137
- // `.tbl-empty` / `.tbl-empty-plain` inside own the centring and padding.
138
- }
139
- // An empty list shows its empty state with no column headers above it.
140
- .table-wrap:has(> .tbl-empty-block:not([hidden])) > table > thead {
141
- display: none;
142
- }
143
-
144
9
  // ── Table (base) ──
145
10
  // The shared data-table look, on the plain `.table` class Bootstrap already
146
11
  // gives every table. Without this a product's tables fall back to stock
@@ -231,7 +96,7 @@
231
96
  text-align: right;
232
97
  }
233
98
 
234
- // ── Table + pagination ──
99
+ // ── Table ──
235
100
  // DataTable cell helpers
236
101
  .table-ledger .wui-cell-end,
237
102
  .table-ledger thead th.wui-cell-end {
@@ -681,171 +546,6 @@
681
546
  }
682
547
  }
683
548
 
684
- // Pagination footer: a "10 per page" menu beside the page buttons, flush under a table. No row
685
- // range and no total; the page buttons say where you are.
686
- .table-pagination {
687
- display: flex;
688
- align-items: center;
689
- justify-content: flex-end;
690
- gap: 16px;
691
- padding: 12px 16px;
692
- border-top: 1px solid var(--color-border);
693
- font-size: 13px;
694
- color: var(--color-text-muted);
695
- flex-wrap: wrap;
696
- }
697
- .tpg-rows {
698
- display: inline-flex;
699
- align-items: center;
700
- white-space: nowrap;
701
- }
702
- .select.tpg-select {
703
- width: auto;
704
- min-width: 64px;
705
- height: var(--wui-control-sm);
706
- min-height: var(--wui-control-sm);
707
- padding: 0 28px 0 12px;
708
- font-size: 13px;
709
- font-weight: 600;
710
- border-radius: var(--wui-radius-md);
711
- background-position: right 8px center;
712
- background-size: 14px;
713
- }
714
- .tpg-range {
715
- font-variant-numeric: tabular-nums;
716
- white-space: nowrap;
717
- font-size: 14px;
718
- font-weight: 500;
719
- color: var(--color-text-muted);
720
- }
721
- .tpg-nav {
722
- display: inline-flex;
723
- flex-wrap: wrap;
724
- justify-content: flex-end;
725
- align-items: center;
726
- gap: 4px;
727
- }
728
- .tpg-arrow {
729
- display: grid;
730
- place-items: center;
731
- width: var(--wui-control-sm);
732
- height: var(--wui-control-sm);
733
- border: none;
734
- background: transparent;
735
- color: var(--color-text-muted);
736
- border-radius: 50%;
737
- cursor: pointer;
738
- transition:
739
- background 0.14s ease,
740
- color 0.14s ease;
741
- }
742
- .tpg-arrow:hover:not(:disabled) {
743
- background: color-mix(in srgb, var(--color-primary) 10%, transparent);
744
- color: var(--color-primary-text);
745
- }
746
- .tpg-arrow:focus-visible {
747
- outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
748
- outline-offset: var(--wui-focus-ring-offset);
749
- }
750
- // Text-5 already reads as "unavailable" — no opacity dimming on top of it.
751
- .tpg-arrow:disabled {
752
- color: var(--color-border-strong);
753
- cursor: not-allowed;
754
- }
755
-
756
- // Numbered mode (mode="numbered") — windowed page buttons sitting between the prev/next
757
- // arrows in .tpg-nav, same 32px circular hit target as .tpg-arrow.
758
- .tpg-page {
759
- display: grid;
760
- place-items: center;
761
- min-width: var(--wui-control-sm);
762
- height: var(--wui-control-sm);
763
- padding: 0 6px;
764
- border: none;
765
- background: transparent;
766
- color: var(--color-text-muted);
767
- border-radius: 50%;
768
- font-size: 13px;
769
- font-weight: 600;
770
- font-variant-numeric: tabular-nums;
771
- text-decoration: none;
772
- cursor: pointer;
773
- transition:
774
- background 0.14s ease,
775
- color 0.14s ease;
776
- }
777
- .tpg-page:hover:not(.is-active) {
778
- background: color-mix(in srgb, var(--color-primary) 10%, transparent);
779
- color: var(--color-primary-text);
780
- }
781
- .tpg-page:focus-visible {
782
- outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
783
- outline-offset: var(--wui-focus-ring-offset);
784
- }
785
- .tpg-page.is-active {
786
- background: var(--color-primary);
787
- color: var(--color-on-primary);
788
- cursor: default;
789
- }
790
- .tpg-ellipsis {
791
- display: grid;
792
- place-items: center;
793
- min-width: 24px;
794
- height: var(--wui-control-sm);
795
- color: var(--color-text-subtle);
796
- font-size: 13px;
797
- user-select: none;
798
- }
799
- // A finger needs a 44px target: the pager's controls grow to the control height on a phone.
800
- @media (max-width: 575.98px) {
801
- .select.tpg-select {
802
- height: var(--wui-control-md);
803
- min-height: var(--wui-control-md);
804
- }
805
- .tpg-arrow {
806
- width: var(--wui-control-md);
807
- height: var(--wui-control-md);
808
- }
809
- .tpg-page {
810
- min-width: var(--wui-control-md);
811
- height: var(--wui-control-md);
812
- }
813
- .tpg-ellipsis {
814
- height: var(--wui-control-md);
815
- }
816
- }
817
-
818
- // ── Standalone pagination (AppPagination) ──
819
- // Bootstrap's .pagination is already styled; these rules refine it for the common case
820
- // of a nav wrapper and ellipsis representation.
821
- .pagination-nav {
822
- display: flex;
823
- align-items: center;
824
- justify-content: center;
825
- max-width: 100%;
826
- }
827
- // Many pages wrap onto a second row rather than push past a phone screen.
828
- .pagination-nav .pagination {
829
- flex-wrap: wrap;
830
- justify-content: center;
831
- row-gap: 4px;
832
- }
833
- .pagination-md {
834
- // Default Bootstrap pagination size (no-op, but explicit for parallelism with sm)
835
- }
836
- .pagination-sm .page-link {
837
- padding: 0.25rem 0.5rem;
838
- font-size: 12px;
839
- }
840
- .page-ellipsis {
841
- display: grid;
842
- place-items: center;
843
- min-width: 24px;
844
- color: var(--color-text-subtle);
845
- font-size: 13px;
846
- user-select: none;
847
- }
848
-
849
549
  // Horizontal scroll guard + flush-fit inside a card. Wrapping the table here
850
550
  // also stops `.card > .table` header-corner rounding from applying mid-card.
851
551
  .table-wrap {
@@ -0,0 +1,34 @@
1
+ // ── Dialog open motion ──
2
+ // Every Bootstrap modal (AppModal, the Ctrl+K palette and search, Razor <app-modal>, confirm
3
+ // dialogs) opens with the same soft zoom the menus use: a fade in from 95% size, no bounce.
4
+ // Like the menu motion it is a PRODUCT-LEVEL choice, set once on the root element:
5
+ //
6
+ // <html> soft zoom (default)
7
+ // <html data-wui-dialog-motion="slide"> Bootstrap's fade and slide down from the top
8
+ //
9
+ // The zoom animates the standalone `scale` property and drops Bootstrap's transform transition,
10
+ // so the slide never runs underneath it. Close stays Bootstrap's own quick fade of the modal.
11
+ // Side panels (offcanvas, drawers) keep their slide: they come from an edge by nature.
12
+ //
13
+ // Imported after menu-motion, whose wui-menu-zoom keyframes it reuses.
14
+
15
+ :root {
16
+ --wui-dialog-anim: wui-menu-zoom;
17
+ --wui-dialog-anim-duration: 160ms;
18
+ --wui-dialog-anim-ease: var(--wui-ease-out);
19
+ }
20
+
21
+ :root:not([data-wui-dialog-motion="slide"]) .modal.fade .modal-dialog {
22
+ transform: none;
23
+ transition: none;
24
+ }
25
+
26
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show .modal-dialog {
27
+ animation: var(--wui-dialog-anim) var(--wui-dialog-anim-duration) var(--wui-dialog-anim-ease);
28
+ }
29
+
30
+ @media (prefers-reduced-motion: reduce) {
31
+ :root:not([data-wui-dialog-motion="slide"]) .modal.show .modal-dialog {
32
+ animation: none;
33
+ }
34
+ }
@@ -11,31 +11,31 @@
11
11
  // thing script does is toggle `.open`.
12
12
  //
13
13
  // Markup (identical on both surfaces):
14
- // <div class="faq-list">
15
- // <div class="faq-item"> (+ .open when expanded)
16
- // <button class="faq-q" aria-expanded>…<span class="faq-q-icon">…</span></button>
17
- // <div class="faq-a-wrap"><div class="faq-a-inner">…</div></div>
14
+ // <div class="wui-accordion">
15
+ // <div class="wui-accordion-item"> (+ .open when expanded)
16
+ // <button class="wui-accordion-trigger" aria-expanded>…<span class="wui-accordion-icon">…</span></button>
17
+ // <div class="wui-accordion-panel"><div class="wui-accordion-body">…</div></div>
18
18
  // </div>
19
19
  // </div>
20
- .faq-list {
20
+ .wui-accordion {
21
21
  display: flex;
22
22
  flex-direction: column;
23
23
  gap: 8px;
24
24
  }
25
- .faq-item {
25
+ .wui-accordion-item {
26
26
  border: 1px solid var(--color-border);
27
27
  background: var(--color-surface);
28
28
  overflow: hidden;
29
29
  transition: border-color 0.15s ease;
30
30
  border-radius: var(--wui-field-md-radius);
31
31
  }
32
- .faq-item:hover:not(.open) {
32
+ .wui-accordion-item:hover:not(.open) {
33
33
  border-color: var(--color-border-strong);
34
34
  }
35
- .faq-item.open {
35
+ .wui-accordion-item.open {
36
36
  border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
37
37
  }
38
- .faq-q {
38
+ .wui-accordion-trigger {
39
39
  display: flex;
40
40
  align-items: center;
41
41
  justify-content: space-between;
@@ -53,25 +53,25 @@
53
53
  cursor: pointer;
54
54
  transition: color 0.12s ease;
55
55
  }
56
- .faq-q:hover {
56
+ .wui-accordion-trigger:hover {
57
57
  color: var(--color-primary-text);
58
58
  }
59
- .faq-q:focus-visible {
59
+ .wui-accordion-trigger:focus-visible {
60
60
  outline: var(--wui-focus-ring-width) solid var(--color-focus-ring);
61
61
  outline-offset: var(--wui-focus-ring-inset);
62
62
  }
63
- .faq-item.open .faq-q {
63
+ .wui-accordion-item.open .wui-accordion-trigger {
64
64
  color: var(--color-primary-text);
65
65
  }
66
66
  // Chevron rotates to point up when the pane is open.
67
- .faq-q-icon {
67
+ .wui-accordion-icon {
68
68
  flex: none;
69
69
  display: grid;
70
70
  place-items: center;
71
71
  color: var(--color-text-subtle);
72
72
  transition: transform 0.2s var(--wui-ease-out);
73
73
  }
74
- .faq-item.open .faq-q-icon {
74
+ .wui-accordion-item.open .wui-accordion-icon {
75
75
  transform: rotate(180deg);
76
76
  color: var(--color-primary-text);
77
77
  }
@@ -79,15 +79,15 @@
79
79
  // The animating track. `min-height: 0` on the inner element is required — a grid
80
80
  // item's default `min-height: auto` refuses to shrink below its content, which
81
81
  // would defeat the 0fr row entirely.
82
- .faq-a-wrap {
82
+ .wui-accordion-panel {
83
83
  display: grid;
84
84
  grid-template-rows: 0fr;
85
85
  transition: grid-template-rows 0.28s var(--wui-ease-out);
86
86
  }
87
- .faq-item.open .faq-a-wrap {
87
+ .wui-accordion-item.open .wui-accordion-panel {
88
88
  grid-template-rows: 1fr;
89
89
  }
90
- .faq-a-inner {
90
+ .wui-accordion-body {
91
91
  min-height: 0;
92
92
  overflow: hidden;
93
93
  // Horizontal padding is CONSTANT in both states. Toggling it with `.open` made the content
@@ -100,23 +100,23 @@
100
100
  line-height: 1.6;
101
101
  color: var(--color-text-muted);
102
102
  }
103
- .faq-a-inner > :first-child {
103
+ .wui-accordion-body > :first-child {
104
104
  margin-top: 0;
105
105
  }
106
- .faq-item.open .faq-a-inner {
106
+ .wui-accordion-item.open .wui-accordion-body {
107
107
  padding-bottom: 16px;
108
108
  }
109
109
 
110
110
  // Flat: a text toggle with a leading chevron, for a section header rather than a card.
111
- .faq-list.wui-accordion-flat {
111
+ .wui-accordion.wui-accordion-flat {
112
112
  gap: 0;
113
113
  }
114
- .faq-list.wui-accordion-flat .faq-item {
114
+ .wui-accordion.wui-accordion-flat .wui-accordion-item {
115
115
  border: 0;
116
116
  background: none;
117
117
  box-shadow: none;
118
118
  }
119
- .faq-list.wui-accordion-flat .faq-q {
119
+ .wui-accordion.wui-accordion-flat .wui-accordion-trigger {
120
120
  display: flex;
121
121
  align-items: center;
122
122
  justify-content: flex-start;
@@ -128,10 +128,10 @@
128
128
  color: var(--color-text-muted);
129
129
  background: none;
130
130
  }
131
- .faq-list.wui-accordion-flat .faq-q-icon {
131
+ .wui-accordion.wui-accordion-flat .wui-accordion-icon {
132
132
  transform: rotate(-90deg);
133
133
  }
134
- .faq-list.wui-accordion-flat .faq-item.open .faq-q-icon {
134
+ .wui-accordion.wui-accordion-flat .wui-accordion-item.open .wui-accordion-icon {
135
135
  transform: none;
136
136
  }
137
137
 
@@ -147,9 +147,9 @@
147
147
  }
148
148
 
149
149
  @media (prefers-reduced-motion: reduce) {
150
- .faq-a-wrap,
151
- .faq-q-icon,
152
- .faq-item {
150
+ .wui-accordion-panel,
151
+ .wui-accordion-icon,
152
+ .wui-accordion-item {
153
153
  transition: none;
154
154
  }
155
155
  }