@webority/theme 0.14.0 → 0.15.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webority/theme",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
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": {
@@ -51,5 +51,7 @@
51
51
  @import "wui/dock";
52
52
  @import "wui/consent-banner";
53
53
  @import "wui/toolbar";
54
+ // After `overlays`: anchors the shared modal shell to the top when it holds the palette card.
55
+ @import "wui/command-palette";
54
56
  // Last: restates the settled state of menus other partials style, so it must win.
55
57
  @import "wui/menu-motion";
@@ -0,0 +1,195 @@
1
+ // ── Command palette ──
2
+ // AppCommandPalette / <app-command-palette> / <wui-command-palette>: app-wide "search and jump".
3
+ // The overlay is the shared modal (.wui-modal-shell, .wui-modal), with .wui-cmd-card on the
4
+ // card; this partial styles only what sits inside it, plus the top anchoring. The element marks
5
+ // the active result with aria-selected="true", which is the one state hook below. Every colour
6
+ // is a theme token, so dark mode follows the token flip with no rules of its own.
7
+ //
8
+ // Part of the wui-components layer, see _wui-components.scss for load order.
9
+
10
+ // Anchored near the top rather than centred, so the input stays put while results grow and
11
+ // shrink underneath it.
12
+ .modal.wui-modal-shell:has(.wui-cmd-card) {
13
+ align-items: flex-start;
14
+ padding-top: max(20px, min(12vh, 120px));
15
+ }
16
+ .wui-modal.wui-cmd-card {
17
+ max-height: min(560px, calc(100vh - 2 * max(20px, min(12vh, 120px))));
18
+ }
19
+ // The list scrolls, not the body: the input and the footer stay pinned.
20
+ .wui-cmd-card > .wui-modal-body {
21
+ display: flex;
22
+ flex-direction: column;
23
+ padding: 0;
24
+ overflow: hidden;
25
+ }
26
+
27
+ wui-command-palette {
28
+ display: flex;
29
+ flex: 1 1 auto;
30
+ flex-direction: column;
31
+ min-height: 0;
32
+ }
33
+
34
+ .wui-cmd-search {
35
+ display: flex;
36
+ flex: 0 0 auto;
37
+ align-items: center;
38
+ gap: 10px;
39
+ padding: 0 16px;
40
+ border-bottom: 1px solid var(--color-border);
41
+ }
42
+ .wui-cmd-search-icon {
43
+ display: inline-flex;
44
+ flex: 0 0 auto;
45
+ color: var(--color-text-muted);
46
+ }
47
+ .wui-cmd-input {
48
+ flex: 1 1 auto;
49
+ min-width: 0;
50
+ height: 52px;
51
+ padding: 0;
52
+ border: none;
53
+ outline: none;
54
+ background: transparent;
55
+ color: var(--color-text);
56
+ font-size: 16px;
57
+ }
58
+ .wui-cmd-input::placeholder {
59
+ color: var(--color-text-subtle);
60
+ }
61
+ .wui-cmd-spinner {
62
+ flex: 0 0 auto;
63
+ color: var(--color-primary);
64
+ }
65
+
66
+ .wui-cmd-list {
67
+ flex: 1 1 auto;
68
+ min-height: 0;
69
+ padding: 6px;
70
+ overflow-y: auto;
71
+ overscroll-behavior: contain;
72
+ scrollbar-width: thin;
73
+ scrollbar-color: var(--color-border-strong) transparent;
74
+ }
75
+ .wui-cmd-list[hidden],
76
+ .wui-cmd-group[hidden],
77
+ .wui-cmd-item[hidden] {
78
+ display: none;
79
+ }
80
+ .wui-cmd-group + .wui-cmd-group {
81
+ margin-top: 4px;
82
+ }
83
+ .wui-cmd-group-label {
84
+ padding: 8px 10px 4px;
85
+ color: var(--color-text-muted);
86
+ font-size: 11px;
87
+ font-weight: 600;
88
+ letter-spacing: 0.04em;
89
+ text-transform: uppercase;
90
+ }
91
+
92
+ .wui-cmd-item {
93
+ display: flex;
94
+ align-items: center;
95
+ gap: 12px;
96
+ padding: 8px 10px;
97
+ border-radius: var(--radius);
98
+ corner-shape: squircle;
99
+ color: var(--color-text);
100
+ cursor: pointer;
101
+ // Keeps a little of the next row visible when the keyboard scrolls the active one in.
102
+ scroll-margin: 6px;
103
+ }
104
+ .wui-cmd-item[aria-selected="true"] {
105
+ background: var(--color-primary-soft);
106
+ color: var(--color-primary);
107
+ }
108
+ .wui-cmd-item-icon {
109
+ display: inline-grid;
110
+ flex: 0 0 auto;
111
+ place-items: center;
112
+ width: 32px;
113
+ height: 32px;
114
+ border-radius: var(--radius);
115
+ corner-shape: squircle;
116
+ background: var(--color-bg-muted);
117
+ color: var(--color-text-muted);
118
+ }
119
+ .wui-cmd-item[aria-selected="true"] .wui-cmd-item-icon {
120
+ background: var(--color-surface);
121
+ color: var(--color-primary);
122
+ }
123
+ .wui-cmd-item-text {
124
+ display: flex;
125
+ flex: 1 1 auto;
126
+ flex-direction: column;
127
+ min-width: 0;
128
+ }
129
+ .wui-cmd-item-label,
130
+ .wui-cmd-item-desc {
131
+ overflow: hidden;
132
+ text-overflow: ellipsis;
133
+ white-space: nowrap;
134
+ line-height: 1.4;
135
+ }
136
+ .wui-cmd-item-label {
137
+ font-size: 14px;
138
+ }
139
+ .wui-cmd-item-desc {
140
+ color: var(--color-text-muted);
141
+ font-size: 12px;
142
+ }
143
+ .wui-cmd-item-meta {
144
+ flex: 0 0 auto;
145
+ color: var(--color-text-muted);
146
+ font-size: 12px;
147
+ }
148
+ // The query inside a label: bolder, never a highlighter block.
149
+ .wui-cmd-mark {
150
+ padding: 0;
151
+ background: transparent;
152
+ color: inherit;
153
+ font-weight: 700;
154
+ }
155
+
156
+ .wui-cmd-empty {
157
+ padding: 20px 16px;
158
+ color: var(--color-text-muted);
159
+ font-size: 13px;
160
+ text-align: center;
161
+ }
162
+ // The status region is always in the page, so a screen reader hears the empty text arrive;
163
+ // with nothing to say it takes no room.
164
+ .wui-cmd-empty:empty {
165
+ display: none;
166
+ }
167
+
168
+ .wui-cmd-footer {
169
+ display: flex;
170
+ flex: 0 0 auto;
171
+ flex-wrap: wrap;
172
+ align-items: center;
173
+ gap: 6px 16px;
174
+ padding: 8px 16px;
175
+ border-top: 1px solid var(--color-border);
176
+ color: var(--color-text-muted);
177
+ font-size: 12px;
178
+ }
179
+ .wui-cmd-footer kbd {
180
+ padding: 1px 6px;
181
+ border: 1px solid var(--color-border);
182
+ border-radius: var(--wui-radius-sm);
183
+ background: var(--color-bg-muted);
184
+ color: var(--color-text);
185
+ font-family: inherit;
186
+ font-size: 11px;
187
+ }
188
+
189
+ @media (prefers-reduced-motion: no-preference) {
190
+ .wui-cmd-item {
191
+ transition:
192
+ background-color 0.12s ease,
193
+ color 0.12s ease;
194
+ }
195
+ }
@@ -412,8 +412,14 @@ a.info-row:hover .info-value {
412
412
  // label in primary. The tint is 8% so the label clears AA on it in both themes (light 5.6:1,
413
413
  // dark 4.5:1); a stronger tint drops the dark label under 4.5:1.
414
414
  // ============================================================================
415
+ // Each size sets one option height, icon size and note size, so a text, icon or note option in
416
+ // the same control lines up, and the four sizes step evenly: 28 / 32 / 38 / 44px tall overall
417
+ // (the track adds 8px), xl matching the 44px field height. A note option grows past the height.
415
418
  .segmented {
416
419
  --segmented-selected-bg: color-mix(in srgb, var(--color-primary) 8%, var(--color-surface));
420
+ --segmented-option-h: 24px;
421
+ --segmented-icon: 13px;
422
+ --segmented-note: 10px;
417
423
  position: relative;
418
424
  display: inline-flex;
419
425
  gap: 2px;
@@ -428,7 +434,10 @@ a.info-row:hover .info-value {
428
434
  display: inline-flex;
429
435
  align-items: center;
430
436
  gap: 5px;
431
- padding: 5px 12px;
437
+ min-height: var(--segmented-option-h);
438
+ // A small fixed vertical padding: the min-height sets the height, so an icon cannot push
439
+ // one option taller than its text-only siblings. Only a two-line note option grows.
440
+ padding: 4px 12px;
432
441
  border: 0;
433
442
  border-radius: 999px;
434
443
  background: transparent;
@@ -478,7 +487,7 @@ a.info-row:hover .info-value {
478
487
  color: inherit;
479
488
  }
480
489
  .segmented-option-note {
481
- font-size: 10px;
490
+ font-size: var(--segmented-note);
482
491
  font-weight: 400;
483
492
  // Inherits the option's current text colour (plain, hover, or primary-on-checked) rather
484
493
  // than a fixed token, so it always matches without a separate state per variant. No opacity
@@ -529,25 +538,61 @@ a.info-row:hover .info-value {
529
538
  background: transparent;
530
539
  box-shadow: none;
531
540
  }
541
+ // A two-line note option keeps a little more air inside the ring than a one-line option needs.
542
+ .segmented-option:has(.segmented-option-note) {
543
+ padding-top: 6px;
544
+ padding-bottom: 6px;
545
+ }
546
+ .segmented-option > .wui-icon {
547
+ flex: none;
548
+ width: var(--segmented-icon);
549
+ height: var(--segmented-icon);
550
+ }
551
+ // Borderless (bordered={false} / bordered="false") — the selected pill keeps its tint and primary
552
+ // label but drops the ring, on both the sliding thumb and the no-JS per-option pill.
553
+ .segmented-borderless .segmented-thumb,
554
+ .segmented-borderless .segmented-option:has(.segmented-input:checked) {
555
+ box-shadow: none;
556
+ }
532
557
  // Icon pop — the selected segment's glyph springs up a touch as it becomes active.
533
558
  .segmented-option:has(.segmented-input:checked) .wui-icon {
534
559
  transform: scale(1.14);
535
560
  transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
536
561
  }
537
562
  .segmented-sm .segmented-thumb,
538
- .segmented-lg .segmented-thumb {
563
+ .segmented-lg .segmented-thumb,
564
+ .segmented-xl .segmented-thumb {
539
565
  top: 3px;
540
566
  bottom: 3px;
541
567
  }
568
+ .segmented-sm {
569
+ --segmented-option-h: 20px;
570
+ --segmented-icon: 12px;
571
+ }
572
+ .segmented-lg {
573
+ --segmented-option-h: 30px;
574
+ --segmented-icon: 15px;
575
+ --segmented-note: 11px;
576
+ }
577
+ .segmented-xl {
578
+ --segmented-option-h: 36px;
579
+ --segmented-icon: 17px;
580
+ --segmented-note: 12px;
581
+ }
542
582
  .segmented-sm .segmented-option {
543
583
  padding: 4px 9px;
544
584
  font-size: 11px;
545
585
  }
546
586
  .segmented-lg .segmented-option {
547
587
  gap: 6px;
548
- padding: 8px 16px;
588
+ padding: 4px 16px;
549
589
  font-size: 13px;
550
590
  }
591
+ .segmented-xl .segmented-option {
592
+ gap: 7px;
593
+ padding: 4px 20px;
594
+ font-size: 15px;
595
+ }
551
596
  // Full width — segments share the row equally (a toggle spanning a card or toolbar).
552
597
  .segmented-full {
553
598
  display: flex;
@@ -316,23 +316,23 @@
316
316
  // keeps a non-square source whole rather than cropping it.
317
317
  .wui-confirm-icon img {
318
318
  display: block;
319
- width: var(--wui-confirm-icon-size, 80px);
320
- height: var(--wui-confirm-icon-size, 80px);
319
+ width: var(--wui-confirm-icon-size, 120px);
320
+ height: var(--wui-confirm-icon-size, 120px);
321
321
  object-fit: contain;
322
322
  }
323
323
  .wui-confirm-icon svg {
324
- width: var(--wui-confirm-icon-size, 80px);
325
- height: var(--wui-confirm-icon-size, 80px);
324
+ width: var(--wui-confirm-icon-size, 120px);
325
+ height: var(--wui-confirm-icon-size, 120px);
326
326
  }
327
- // Icon size tracks the copy: a title+message dialog keeps the full 80px badge, but a lone
327
+ // Icon size tracks the copy: a title+message dialog keeps the full 120px badge, but a lone
328
328
  // title (no message) is one line and gets a smaller badge. `.wui-confirm-msg` is a real
329
329
  // element rendered only when a message exists, so `:has()` is reliable here; CSS width/height
330
330
  // override the SVG's own width/height attributes. An explicit iconSize / icon-size sets
331
331
  // --wui-confirm-icon-size on the badge and wins in both cases.
332
332
  .wui-confirm-content:not(:has(.wui-confirm-msg)) .wui-confirm-icon svg,
333
333
  .wui-confirm-content:not(:has(.wui-confirm-msg)) .wui-confirm-icon img {
334
- width: var(--wui-confirm-icon-size, 56px);
335
- height: var(--wui-confirm-icon-size, 56px);
334
+ width: var(--wui-confirm-icon-size, 84px);
335
+ height: var(--wui-confirm-icon-size, 84px);
336
336
  }
337
337
  .wui-confirm-title {
338
338
  margin: 0 0 10px;
@@ -194,8 +194,12 @@
194
194
  .app-shell {
195
195
  --app-sidebar-w: 260px;
196
196
  --app-sidebar-rail-w: 88px;
197
+ // The sidebar's bottom band, from the rule above the collapse toggle to the sidebar's
198
+ // bottom edge, is the height of a one-line footer. The rule then continues the footer's
199
+ // top edge across the shell. The toggle fills what is left after the rule and two gaps.
200
+ --app-shell-foot-h: 39px;
197
201
  // The equal space above (below the rule) and below the collapse toggle.
198
- --app-sidebar-toggle-gap: 12px;
202
+ --app-sidebar-toggle-gap: 4px;
199
203
 
200
204
  position: relative;
201
205
  display: grid;
@@ -552,7 +556,9 @@
552
556
  justify-content: center;
553
557
  width: 100%;
554
558
  position: relative;
555
- min-height: var(--wui-control-height);
559
+ // The band height less the rule and the gaps above and below the toggle, so rule + toggle
560
+ // + bottom inset add up to exactly --app-shell-foot-h.
561
+ height: calc(var(--app-shell-foot-h) - 1px - 2 * var(--app-sidebar-toggle-gap));
556
562
  // 8px above the rule, the 1px rule, then the same gap below it as under the row.
557
563
  margin-top: calc(9px + var(--app-sidebar-toggle-gap));
558
564
  padding: 0;