@webority/theme 0.23.1 → 0.25.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.
Files changed (40) hide show
  1. package/README.md +16 -2
  2. package/dist/purge-safelist.json +126 -2
  3. package/dist/webority-brand-signal.css +1 -0
  4. package/dist/webority-theme.css +2 -2
  5. package/fonts/sans/OFL.txt +92 -0
  6. package/fonts/sans/WeboritySans-Bold.woff2 +0 -0
  7. package/fonts/sans/WeboritySans-BoldItalic.woff2 +0 -0
  8. package/fonts/sans/WeboritySans-Medium.woff2 +0 -0
  9. package/fonts/sans/WeboritySans-Regular.woff2 +0 -0
  10. package/fonts/sans/WeboritySans-RegularItalic.woff2 +0 -0
  11. package/fonts/sans/WeboritySans-SemiBold.woff2 +0 -0
  12. package/fonts/serif/WeboritySerif-Bold.woff2 +0 -0
  13. package/fonts/serif/WeboritySerif-BoldItalic.woff2 +0 -0
  14. package/fonts/serif/WeboritySerif-DemiBold.woff2 +0 -0
  15. package/fonts/serif/WeboritySerif-ExtraBold.woff2 +0 -0
  16. package/fonts/serif/WeboritySerif-Medium.woff2 +0 -0
  17. package/fonts/serif/WeboritySerif-Regular.woff2 +0 -0
  18. package/fonts/serif/WeboritySerif-RegularItalic.woff2 +0 -0
  19. package/index.scss +2 -1
  20. package/package.json +4 -2
  21. package/scss/_brand-signal.scss +202 -0
  22. package/scss/_fonts.scss +95 -0
  23. package/scss/_tokens.scss +55 -2
  24. package/scss/_variables.scss +2 -1
  25. package/scss/_wui-aliases.scss +21 -0
  26. package/scss/_wui-components.scss +11 -0
  27. package/scss/wui/_calendar.scss +256 -0
  28. package/scss/wui/_chart-card.scss +86 -0
  29. package/scss/wui/_chart.scss +524 -0
  30. package/scss/wui/_command-palette.scss +5 -0
  31. package/scss/wui/_context-menu.scss +53 -0
  32. package/scss/wui/_data.scss +7 -0
  33. package/scss/wui/_display.scss +1 -1
  34. package/scss/wui/_image-crop.scss +146 -0
  35. package/scss/wui/_modal-remote.scss +3 -0
  36. package/scss/wui/_rich-text.scss +281 -0
  37. package/scss/wui/_shell-parts.scss +3 -0
  38. package/scss/wui/_sortable.scss +135 -0
  39. package/scss/wui/_table-server.scss +3 -0
  40. package/scss/wui/_tree.scss +124 -0
@@ -0,0 +1,146 @@
1
+ // ── Image crop ──
2
+ // <wui-image-crop>: the crop surface AppLogoPicker / <app-logo-picker> shows in a dialog before
3
+ // upload. The image is fitted into the stage; the frame is positioned by the element (inline
4
+ // left/top/width/height) and dims everything outside it with a very large box-shadow, which the
5
+ // stage clips. Every colour is a theme token, so dark mode follows the token flip.
6
+ //
7
+ // Part of the wui-components layer, see _wui-components.scss for load order.
8
+
9
+ wui-image-crop {
10
+ display: block;
11
+ }
12
+
13
+ .wui-crop-stage {
14
+ position: relative;
15
+ height: min(360px, 55vh);
16
+ overflow: hidden;
17
+ border-radius: var(--radius-lg);
18
+ background: var(--color-bg-muted);
19
+ // The frame is dragged with a finger, so the browser must not scroll or zoom the page instead.
20
+ touch-action: none;
21
+ user-select: none;
22
+ cursor: grab;
23
+ }
24
+ .wui-crop-stage:active {
25
+ cursor: grabbing;
26
+ }
27
+ .wui-crop-stage:focus-visible {
28
+ outline: 2px solid var(--color-primary);
29
+ outline-offset: 2px;
30
+ }
31
+
32
+ .wui-crop-img {
33
+ position: absolute;
34
+ max-width: none;
35
+ pointer-events: none;
36
+ }
37
+ .wui-crop-img[hidden],
38
+ .wui-crop-frame[hidden] {
39
+ display: none;
40
+ }
41
+
42
+ .wui-crop-frame {
43
+ position: absolute;
44
+ border: 2px solid #fff;
45
+ box-shadow:
46
+ 0 0 0 9999px rgb(0 0 0 / 55%),
47
+ 0 0 0 1px rgb(0 0 0 / 35%);
48
+ // Thirds guide, so a person lining up a face or a logo has something to hold against.
49
+ background-image:
50
+ linear-gradient(to right, transparent 33.2%, rgb(255 255 255 / 35%) 33.3%, transparent 33.5%, transparent 66.5%, rgb(255 255 255 / 35%) 66.7%, transparent 66.8%),
51
+ linear-gradient(to bottom, transparent 33.2%, rgb(255 255 255 / 35%) 33.3%, transparent 33.5%, transparent 66.5%, rgb(255 255 255 / 35%) 66.7%, transparent 66.8%);
52
+ }
53
+ .wui-crop-frame--round {
54
+ border-radius: 50%;
55
+ background-image: none;
56
+ }
57
+
58
+ // A corner handle is a 44px tap target drawn as a small square at its corner.
59
+ .wui-crop-handle {
60
+ position: absolute;
61
+ width: 44px;
62
+ height: 44px;
63
+ padding: 0;
64
+ border: 0;
65
+ background: transparent;
66
+ cursor: nwse-resize;
67
+ }
68
+ .wui-crop-handle::after {
69
+ content: "";
70
+ position: absolute;
71
+ width: 14px;
72
+ height: 14px;
73
+ border-radius: 3px;
74
+ background: #fff;
75
+ box-shadow: 0 0 0 1px rgb(0 0 0 / 45%);
76
+ }
77
+ .wui-crop-handle:focus-visible::after {
78
+ background: var(--color-primary);
79
+ }
80
+ .wui-crop-handle--nw {
81
+ top: -22px;
82
+ left: -22px;
83
+ }
84
+ .wui-crop-handle--ne {
85
+ top: -22px;
86
+ right: -22px;
87
+ cursor: nesw-resize;
88
+ }
89
+ .wui-crop-handle--sw {
90
+ bottom: -22px;
91
+ left: -22px;
92
+ cursor: nesw-resize;
93
+ }
94
+ .wui-crop-handle--se {
95
+ right: -22px;
96
+ bottom: -22px;
97
+ }
98
+ .wui-crop-handle--nw::after {
99
+ top: 15px;
100
+ left: 15px;
101
+ }
102
+ .wui-crop-handle--ne::after {
103
+ top: 15px;
104
+ right: 15px;
105
+ }
106
+ .wui-crop-handle--sw::after {
107
+ bottom: 15px;
108
+ left: 15px;
109
+ }
110
+ .wui-crop-handle--se::after {
111
+ right: 15px;
112
+ bottom: 15px;
113
+ }
114
+
115
+ .wui-crop-controls {
116
+ display: flex;
117
+ align-items: center;
118
+ gap: 12px;
119
+ margin-top: 16px;
120
+ }
121
+ .wui-crop-zoom {
122
+ flex: 1 1 auto;
123
+ min-width: 0;
124
+ margin: 0;
125
+ }
126
+ .wui-crop-zoombtn {
127
+ display: inline-flex;
128
+ flex: 0 0 auto;
129
+ align-items: center;
130
+ justify-content: center;
131
+ width: 36px;
132
+ height: 36px;
133
+ padding: 0;
134
+ border: 1px solid var(--color-border);
135
+ border-radius: 50%;
136
+ background: var(--color-surface);
137
+ color: var(--color-text);
138
+ cursor: pointer;
139
+ }
140
+ .wui-crop-zoombtn:hover {
141
+ border-color: var(--color-border-strong);
142
+ }
143
+ .wui-crop-zoombtn:focus-visible {
144
+ outline: 2px solid var(--color-primary);
145
+ outline-offset: 2px;
146
+ }
@@ -0,0 +1,3 @@
1
+ // Server-loaded modal: loading skeleton, error state and the fullscreen size.
2
+ //
3
+ // Part of the wui-components layer; imported after its base partial (see _wui-components.scss).
@@ -0,0 +1,281 @@
1
+ // Rich text: the <wui-rich-text> element behind AppRichText / <app-rich-text>.
2
+ //
3
+ // Part of the wui-components layer; see _wui-components.scss for the load order. The shell wears the
4
+ // shared field chrome (muted fill at rest, white surface and brand border on focus, soft danger
5
+ // border when invalid) around a toolbar and the editable surface; the content takes the page's
6
+ // type. Every colour is a token the dark remap in _wui-aliases.scss flips with the rest of the portal.
7
+
8
+ wui-rich-text {
9
+ display: block;
10
+ min-width: 0;
11
+ }
12
+ // Until the editor's own script has run, the field shows its starting value in a plain box.
13
+ wui-rich-text:not(:defined) > textarea {
14
+ display: block;
15
+ width: 100%;
16
+ min-height: 180px;
17
+ }
18
+
19
+ .wui-rich-text-shell {
20
+ box-sizing: border-box;
21
+ display: flex;
22
+ flex-direction: column;
23
+ width: 100%;
24
+ border: 1px solid var(--color-border);
25
+ border-radius: var(--wui-field-radius);
26
+ background: var(--color-field-bg);
27
+ color: var(--color-text);
28
+ font-size: 14px;
29
+ line-height: 1.5;
30
+ overflow: hidden;
31
+ transition:
32
+ border-color 0.12s,
33
+ background-color 0.12s;
34
+ }
35
+ .wui-rich-text-shell:hover {
36
+ border-color: var(--color-border-strong);
37
+ }
38
+ .wui-rich-text-shell:focus-within {
39
+ background: var(--color-surface);
40
+ border-color: var(--color-focus-border);
41
+ }
42
+ wui-rich-text[aria-invalid="true"] .wui-rich-text-shell,
43
+ wui-rich-text[aria-invalid="true"] .wui-rich-text-shell:focus-within {
44
+ background: var(--color-surface);
45
+ border-color: color-mix(in srgb, var(--color-danger) 30%, transparent);
46
+ }
47
+ wui-rich-text[disabled] .wui-rich-text-shell {
48
+ background: var(--color-border);
49
+ border-color: transparent;
50
+ color: var(--color-text-subtle);
51
+ cursor: not-allowed;
52
+ }
53
+ wui-rich-text[readonly] .wui-rich-text-shell {
54
+ background: var(--color-bg-subtle);
55
+ color: var(--color-text-muted);
56
+ }
57
+
58
+ // ── Toolbar ──
59
+ // Wraps on a phone rather than scrolling sideways, so every tool stays reachable at 375px.
60
+ .wui-rich-text-toolbar {
61
+ display: flex;
62
+ flex-wrap: wrap;
63
+ align-items: center;
64
+ gap: 2px 8px;
65
+ padding: 6px;
66
+ border-bottom: 1px solid var(--color-border);
67
+ }
68
+ .wui-rich-text-group {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 2px;
72
+ }
73
+ .wui-rich-text-group + .wui-rich-text-group {
74
+ padding-left: 8px;
75
+ border-left: 1px solid var(--color-border);
76
+ }
77
+ .wui-rich-text-button {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ min-width: 32px;
82
+ height: 32px;
83
+ padding: 0 6px;
84
+ border: 0;
85
+ border-radius: var(--wui-radius);
86
+ background: transparent;
87
+ color: var(--color-text-muted);
88
+ font: inherit;
89
+ font-size: 13px;
90
+ cursor: pointer;
91
+ transition:
92
+ background-color 0.12s,
93
+ color 0.12s;
94
+ }
95
+ .wui-rich-text-button:hover:not(:disabled) {
96
+ background: var(--color-bg-muted);
97
+ color: var(--color-text);
98
+ }
99
+ .wui-rich-text-button[aria-pressed="true"] {
100
+ background: var(--color-primary-soft);
101
+ color: var(--color-text);
102
+ }
103
+ .wui-rich-text-button:focus-visible {
104
+ outline: 2px solid var(--color-focus-border);
105
+ outline-offset: 1px;
106
+ }
107
+ .wui-rich-text-button:disabled {
108
+ color: var(--color-text-subtle);
109
+ opacity: 0.5;
110
+ cursor: not-allowed;
111
+ }
112
+ .wui-rich-text-glyph {
113
+ font-size: 13px;
114
+ font-weight: 600;
115
+ line-height: 1;
116
+ }
117
+
118
+ // ── Link box ──
119
+ .wui-rich-text-linkbar {
120
+ display: flex;
121
+ flex-wrap: wrap;
122
+ align-items: center;
123
+ gap: 6px;
124
+ padding: 6px;
125
+ border-bottom: 1px solid var(--color-border);
126
+ background: var(--color-bg-subtle);
127
+ }
128
+ .wui-rich-text-linkbar[hidden],
129
+ .wui-rich-text-link-error[hidden],
130
+ .wui-rich-text-link-remove[hidden] {
131
+ display: none;
132
+ }
133
+ .wui-rich-text-link-input {
134
+ flex: 1 1 200px;
135
+ min-width: 0;
136
+ height: 32px;
137
+ padding: 0 10px;
138
+ border: 1px solid var(--color-border);
139
+ border-radius: var(--wui-field-radius-sm);
140
+ background: var(--color-surface);
141
+ color: var(--color-text);
142
+ font: inherit;
143
+ font-size: 13px;
144
+ }
145
+ .wui-rich-text-link-input:focus {
146
+ outline: none;
147
+ border-color: var(--color-focus-border);
148
+ }
149
+ .wui-rich-text-link-input[aria-invalid="true"] {
150
+ border-color: color-mix(in srgb, var(--color-danger) 30%, transparent);
151
+ }
152
+ .wui-rich-text-link-apply,
153
+ .wui-rich-text-link-remove,
154
+ .wui-rich-text-link-cancel {
155
+ height: 32px;
156
+ padding: 0 10px;
157
+ border: 0;
158
+ border-radius: var(--wui-radius);
159
+ background: transparent;
160
+ color: var(--color-text-muted);
161
+ font: inherit;
162
+ font-size: 13px;
163
+ cursor: pointer;
164
+ }
165
+ .wui-rich-text-link-apply {
166
+ background: var(--color-primary);
167
+ color: var(--color-primary-text);
168
+ }
169
+ .wui-rich-text-link-apply:hover {
170
+ background: var(--color-primary-hover);
171
+ }
172
+ .wui-rich-text-link-remove:hover,
173
+ .wui-rich-text-link-cancel:hover {
174
+ background: var(--color-bg-muted);
175
+ color: var(--color-text);
176
+ }
177
+ .wui-rich-text-link-apply:focus-visible,
178
+ .wui-rich-text-link-remove:focus-visible,
179
+ .wui-rich-text-link-cancel:focus-visible {
180
+ outline: 2px solid var(--color-focus-border);
181
+ outline-offset: 1px;
182
+ }
183
+ .wui-rich-text-link-error {
184
+ flex: 1 0 100%;
185
+ font-size: 12px;
186
+ color: var(--color-danger-text);
187
+ }
188
+
189
+ // ── Editable surface ──
190
+ .wui-rich-text-content {
191
+ min-width: 0;
192
+ }
193
+ .wui-rich-text-prose {
194
+ box-sizing: border-box;
195
+ min-height: 140px;
196
+ max-height: 480px;
197
+ overflow-y: auto;
198
+ padding: 12px 15px;
199
+ outline: none;
200
+ overflow-wrap: anywhere;
201
+ cursor: text;
202
+ }
203
+ wui-rich-text[disabled] .wui-rich-text-prose {
204
+ cursor: not-allowed;
205
+ }
206
+ wui-rich-text[readonly] .wui-rich-text-prose {
207
+ cursor: default;
208
+ }
209
+ .wui-rich-text-prose > :first-child {
210
+ margin-top: 0;
211
+ }
212
+ .wui-rich-text-prose > :last-child {
213
+ margin-bottom: 0;
214
+ }
215
+ .wui-rich-text-prose p {
216
+ margin: 0 0 0.75em;
217
+ }
218
+ .wui-rich-text-prose h2,
219
+ .wui-rich-text-prose h3 {
220
+ margin: 1em 0 0.5em;
221
+ font-weight: 600;
222
+ line-height: 1.3;
223
+ color: var(--color-text);
224
+ }
225
+ .wui-rich-text-prose h2 {
226
+ font-size: 1.25em;
227
+ }
228
+ .wui-rich-text-prose h3 {
229
+ font-size: 1.1em;
230
+ }
231
+ .wui-rich-text-prose ul,
232
+ .wui-rich-text-prose ol {
233
+ margin: 0 0 0.75em;
234
+ padding-left: 1.5em;
235
+ }
236
+ .wui-rich-text-prose li > p {
237
+ margin-bottom: 0.25em;
238
+ }
239
+ .wui-rich-text-prose blockquote {
240
+ margin: 0 0 0.75em;
241
+ padding-left: 12px;
242
+ border-left: 3px solid var(--color-border-strong);
243
+ color: var(--color-text-muted);
244
+ }
245
+ .wui-rich-text-prose a {
246
+ color: var(--bs-link-color);
247
+ text-decoration: underline;
248
+ cursor: pointer;
249
+ }
250
+ .wui-rich-text-prose code {
251
+ padding: 0.1em 0.35em;
252
+ border-radius: var(--wui-radius-sm);
253
+ background: var(--color-bg-muted);
254
+ font-size: 0.9em;
255
+ }
256
+ .wui-rich-text-prose pre {
257
+ margin: 0 0 0.75em;
258
+ padding: 10px 12px;
259
+ border-radius: var(--wui-radius);
260
+ background: var(--color-bg-muted);
261
+ overflow-x: auto;
262
+ }
263
+ .wui-rich-text-prose pre code {
264
+ padding: 0;
265
+ background: transparent;
266
+ font-size: 0.9em;
267
+ }
268
+ // The engine marks the empty first paragraph; the placeholder text is its data attribute.
269
+ .wui-rich-text-prose p.wui-rich-text-empty:first-child::before {
270
+ content: attr(data-placeholder);
271
+ float: left;
272
+ height: 0;
273
+ color: var(--color-text-subtle);
274
+ pointer-events: none;
275
+ }
276
+ @media (prefers-reduced-motion: reduce) {
277
+ .wui-rich-text-shell,
278
+ .wui-rich-text-button {
279
+ transition: none;
280
+ }
281
+ }
@@ -0,0 +1,3 @@
1
+ // Shell parts: notification bell, topbar search, phone tab bar, user menu.
2
+ //
3
+ // Part of the wui-components layer; imported after its base partial (see _wui-components.scss).
@@ -0,0 +1,135 @@
1
+ // ── Sortable list ──
2
+ // AppSortableList / <app-sortable-list> / <wui-sortable>. The element moves the real items
3
+ // while the person drags and shows a `.wui-sortable-ghost` copy under the pointer; everything
4
+ // visual lives here.
5
+ //
6
+ // Part of the wui-components layer — see _wui-components.scss for load order.
7
+
8
+ wui-sortable {
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: 8px;
12
+ // An empty list is still a drop target.
13
+ min-height: 44px;
14
+ }
15
+ wui-sortable[orientation="horizontal"] {
16
+ flex-direction: row;
17
+ flex-wrap: wrap;
18
+ }
19
+
20
+ .wui-sortable-item {
21
+ display: flex;
22
+ align-items: center;
23
+ gap: 8px;
24
+ padding: 6px 12px 6px 6px;
25
+ background: var(--color-surface);
26
+ border: 1px solid var(--color-border);
27
+ border-radius: var(--wui-radius);
28
+ corner-shape: squircle;
29
+ color: var(--color-text);
30
+ // Dragging a body text selection is never what a drag on an item means.
31
+ -webkit-user-select: none;
32
+ user-select: none;
33
+ }
34
+ // A pointer drag from the body starts on the item itself; touch scrolling stays on until the
35
+ // press-and-hold promotes it, so only the handle opts out of the browser's panning.
36
+ wui-sortable:not([handle]):not([disabled]) .wui-sortable-item:not([data-disabled]) {
37
+ cursor: grab;
38
+ }
39
+ wui-sortable[disabled] .wui-sortable-item,
40
+ .wui-sortable-item[data-disabled] {
41
+ color: var(--color-text-muted);
42
+ }
43
+
44
+ .wui-sortable-handle {
45
+ display: grid;
46
+ place-items: center;
47
+ flex: none;
48
+ // 44px is the touch target; the glyph stays small inside it.
49
+ width: 44px;
50
+ height: 44px;
51
+ padding: 0;
52
+ border: none;
53
+ border-radius: var(--wui-radius-sm);
54
+ background: transparent;
55
+ color: var(--color-text-subtle);
56
+ cursor: grab;
57
+ touch-action: none;
58
+ transition:
59
+ background-color 160ms var(--wui-ease-ui),
60
+ color 160ms var(--wui-ease-ui);
61
+ }
62
+ .wui-sortable-handle:hover {
63
+ background: var(--color-bg-muted);
64
+ color: var(--color-text);
65
+ }
66
+ .wui-sortable-handle:focus-visible {
67
+ outline: 2px solid var(--color-primary);
68
+ outline-offset: 2px;
69
+ }
70
+ // Lifted by the keyboard.
71
+ .wui-sortable-handle[aria-pressed="true"] {
72
+ background: var(--color-primary-soft);
73
+ color: var(--color-primary);
74
+ }
75
+ .wui-sortable-handle:disabled {
76
+ cursor: not-allowed;
77
+ opacity: 0.45;
78
+ background: transparent;
79
+ color: var(--color-text-subtle);
80
+ }
81
+
82
+ .wui-sortable-body {
83
+ flex: 1 1 auto;
84
+ // Lets long content wrap instead of widening the list past a 375px phone.
85
+ min-width: 0;
86
+ overflow-wrap: anywhere;
87
+ }
88
+
89
+ // The real item, while its ghost is under the pointer: it marks where the drop would land.
90
+ .wui-sortable-item.wui-sortable-dragging {
91
+ background: var(--color-primary-soft);
92
+ border-style: dashed;
93
+ border-color: var(--color-primary);
94
+ > * {
95
+ opacity: 0.35;
96
+ }
97
+ }
98
+
99
+ .wui-sortable-ghost {
100
+ position: fixed;
101
+ top: 0;
102
+ left: 0;
103
+ z-index: var(--wui-z-tooltip, 1650);
104
+ margin: 0;
105
+ box-shadow: var(--wui-shadow-overlay);
106
+ pointer-events: none;
107
+ cursor: grabbing;
108
+ }
109
+
110
+ // While any drag runs, the whole page shows the grabbing cursor and does not select text.
111
+ html.wui-sortable-active,
112
+ html.wui-sortable-active * {
113
+ cursor: grabbing !important;
114
+ -webkit-user-select: none;
115
+ user-select: none;
116
+ }
117
+
118
+ // The aria-live announcement region: read by screen readers, never seen.
119
+ .wui-sortable-live {
120
+ position: absolute;
121
+ width: 1px;
122
+ height: 1px;
123
+ margin: -1px;
124
+ padding: 0;
125
+ overflow: hidden;
126
+ clip-path: inset(50%);
127
+ white-space: nowrap;
128
+ border: 0;
129
+ }
130
+
131
+ @media (prefers-reduced-motion: reduce) {
132
+ .wui-sortable-handle {
133
+ transition: none;
134
+ }
135
+ }
@@ -0,0 +1,3 @@
1
+ // Server-paged data table: loading, empty and error states, the busy dim and the server sort affordance.
2
+ //
3
+ // Part of the wui-components layer; imported after its base partial (see _wui-components.scss).
@@ -0,0 +1,124 @@
1
+ // ── Tree view ──
2
+ // AppTreeView / <app-tree-view> / <wui-tree>: a hierarchy in the WAI-ARIA tree pattern. The element
3
+ // draws the markup and keeps the state on ARIA attributes (aria-expanded on an item,
4
+ // aria-selected / aria-checked for the selection), which are the state hooks below. Each row is
5
+ // indented by --wui-tree-level (set by the element), so the nesting needs no per-depth rule.
6
+ // Every colour 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
+ wui-tree {
11
+ display: block;
12
+ }
13
+
14
+ .wui-tree,
15
+ .wui-tree-group {
16
+ margin: 0;
17
+ padding: 0;
18
+ list-style: none;
19
+ }
20
+
21
+ .wui-tree-item {
22
+ outline: none;
23
+ }
24
+
25
+ .wui-tree-row {
26
+ display: flex;
27
+ align-items: center;
28
+ gap: 8px;
29
+ min-height: 36px;
30
+ padding: 4px 8px;
31
+ padding-inline-start: calc(8px + var(--wui-tree-level, 0) * 20px);
32
+ border-radius: var(--radius-sm);
33
+ color: var(--color-text);
34
+ cursor: pointer;
35
+ user-select: none;
36
+ }
37
+ .wui-tree-row:hover {
38
+ background: var(--color-bg-muted);
39
+ }
40
+ .wui-tree-row.wui-tree-row-selected {
41
+ background: var(--color-primary-soft);
42
+ color: var(--color-primary);
43
+ }
44
+ .wui-tree-item[aria-disabled="true"] > .wui-tree-row {
45
+ color: var(--color-text-subtle);
46
+ cursor: not-allowed;
47
+ }
48
+ .wui-tree-item[aria-disabled="true"] > .wui-tree-row:hover {
49
+ background: transparent;
50
+ }
51
+ // The item holds the focus, but it also holds its subtree, so the ring goes on its own row.
52
+ .wui-tree-item:focus-visible > .wui-tree-row {
53
+ outline: 2px solid var(--color-primary);
54
+ outline-offset: -2px;
55
+ }
56
+ .wui-tree-row-error .wui-tree-label {
57
+ color: var(--color-danger);
58
+ }
59
+
60
+ .wui-tree-toggle {
61
+ display: inline-flex;
62
+ flex: 0 0 auto;
63
+ align-items: center;
64
+ justify-content: center;
65
+ width: 20px;
66
+ height: 20px;
67
+ color: var(--color-text-muted);
68
+ }
69
+ .wui-tree-toggle .wui-icon {
70
+ transition: transform 0.15s ease;
71
+ }
72
+ .wui-tree-item[aria-expanded="true"] > .wui-tree-row .wui-tree-toggle .wui-icon {
73
+ transform: rotate(90deg);
74
+ }
75
+
76
+ .wui-tree-check {
77
+ flex: 0 0 auto;
78
+ margin: 0;
79
+ }
80
+
81
+ .wui-tree-icon {
82
+ display: inline-flex;
83
+ flex: 0 0 auto;
84
+ color: var(--color-text-muted);
85
+ }
86
+ .wui-tree-row-selected .wui-tree-icon {
87
+ color: inherit;
88
+ }
89
+
90
+ .wui-tree-label {
91
+ flex: 1 1 auto;
92
+ min-width: 0;
93
+ overflow: hidden;
94
+ text-overflow: ellipsis;
95
+ white-space: nowrap;
96
+ }
97
+
98
+ .wui-tree-badge {
99
+ flex: 0 0 auto;
100
+ }
101
+
102
+ .wui-tree-spinner {
103
+ flex: 0 0 auto;
104
+ width: 14px;
105
+ height: 14px;
106
+ border: 2px solid var(--color-border-strong);
107
+ border-top-color: var(--color-primary);
108
+ border-radius: 50%;
109
+ animation: wui-tree-spin 0.7s linear infinite;
110
+ }
111
+ @keyframes wui-tree-spin {
112
+ to {
113
+ transform: rotate(360deg);
114
+ }
115
+ }
116
+
117
+ @media (prefers-reduced-motion: reduce) {
118
+ .wui-tree-toggle .wui-icon {
119
+ transition: none;
120
+ }
121
+ .wui-tree-spinner {
122
+ animation-duration: 2s;
123
+ }
124
+ }