@webority/theme 0.7.6 → 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.
@@ -262,6 +262,26 @@
262
262
  width: 0;
263
263
  height: 0;
264
264
  }
265
+ // Between the brand row and the nav — a workspace/tenant switcher, an environment
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).
271
+ .app-sidebar-start {
272
+ flex: none;
273
+ margin-bottom: 8px;
274
+ overflow: hidden;
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
+ }
265
285
  .app-sidebar-foot {
266
286
  flex: none;
267
287
  padding-top: 12px;
@@ -366,12 +386,31 @@
366
386
  gap: 0;
367
387
  padding: 9px 0;
368
388
  }
369
- .app-shell.collapsed .app-nav-text,
370
389
  .app-shell.collapsed .app-nav-badge,
371
390
  .app-shell.collapsed .app-nav-group-label,
372
- .app-shell.collapsed .app-sidebar-brand-text {
391
+ .app-shell.collapsed .app-sidebar-brand-text,
392
+ .app-shell.collapsed .app-sidebar-start {
373
393
  display: none;
374
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
+ }
375
414
 
376
415
  // ── Badge tones ──────────────────────────────────────────────────────────────
377
416
  // A nav badge can carry a status tone so an alert count reads red, not neutral.
@@ -492,7 +531,7 @@
492
531
  // 36px = half the 72px topbar, so the button lines up with the topbar's centre.
493
532
  top: 36px;
494
533
  left: calc(var(--app-sidebar-w) - 12px);
495
- z-index: 30;
534
+ z-index: var(--wui-z-fixed);
496
535
  width: 24px;
497
536
  height: 24px;
498
537
  display: inline-grid;
@@ -597,7 +636,7 @@
597
636
  right: 0;
598
637
  bottom: 0;
599
638
  left: var(--app-sidebar-w);
600
- z-index: 30;
639
+ z-index: var(--wui-z-fixed);
601
640
  background: color-mix(in srgb, var(--color-surface) 90%, transparent);
602
641
  backdrop-filter: blur(50px);
603
642
  -webkit-backdrop-filter: blur(50px);
@@ -638,9 +677,14 @@
638
677
  }
639
678
  .app-shell.app-shell.collapsed .app-nav-text,
640
679
  .app-shell.app-shell.collapsed .app-nav-badge,
641
- .app-shell.app-shell.collapsed .app-nav-group-label {
680
+ .app-shell.app-shell.collapsed .app-nav-group-label,
681
+ .app-shell.app-shell.collapsed .app-sidebar-start {
642
682
  display: revert;
643
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
+ }
644
688
  .app-shell.app-shell .app-sidebar-toggle {
645
689
  display: none;
646
690
  }
@@ -89,7 +89,9 @@
89
89
  bottom: calc(100% + 8px);
90
90
  left: 50%;
91
91
  transform: translateX(-50%);
92
- z-index: 220;
92
+ // Same rung as the .is-portal variant below — an anchored infotip inside a
93
+ // modal must clear the same modal it was previously trapped under at 220.
94
+ z-index: var(--wui-z-menu);
93
95
  display: flex;
94
96
  flex-direction: column;
95
97
  gap: 4px;
@@ -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
+ }