@webority/theme 0.7.3 → 0.7.5
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/README.md +108 -6
- package/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/_components.scss +158 -547
- package/scss/_marketing.scss +337 -0
- package/scss/_tokens.scss +119 -108
- package/scss/_variables.scss +126 -132
- package/scss/_wui-aliases.scss +103 -102
- package/scss/wui/_choice.scss +4 -4
- package/scss/wui/_controls.scss +167 -251
- package/scss/wui/_data.scss +39 -0
- package/scss/wui/_display.scss +88 -0
- package/scss/wui/_navigation.scss +27 -0
- package/scss/wui/_overlays.scss +2 -2
- package/scss/wui/_scrollbar.scss +13 -11
- package/scss/wui/_shell.scss +3 -3
- package/scss/wui/_utilities.scss +139 -1
package/scss/wui/_choice.scss
CHANGED
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
// scrollable modal / table card. Sits above the modal layer.
|
|
319
319
|
.search-select-menu-fixed {
|
|
320
320
|
position: fixed;
|
|
321
|
-
z-index:
|
|
321
|
+
z-index: var(--wui-z-menu);
|
|
322
322
|
right: auto;
|
|
323
323
|
top: auto;
|
|
324
324
|
left: auto;
|
|
@@ -483,7 +483,7 @@
|
|
|
483
483
|
// sits above the modal layer.
|
|
484
484
|
.datepicker-pop {
|
|
485
485
|
position: fixed;
|
|
486
|
-
z-index:
|
|
486
|
+
z-index: var(--wui-z-menu);
|
|
487
487
|
width: 312px;
|
|
488
488
|
max-width: calc(100vw - 16px);
|
|
489
489
|
padding: 18px;
|
|
@@ -801,7 +801,7 @@ button.datepicker-month:hover {
|
|
|
801
801
|
}
|
|
802
802
|
.daterange-pop {
|
|
803
803
|
position: fixed;
|
|
804
|
-
z-index:
|
|
804
|
+
z-index: var(--wui-z-menu);
|
|
805
805
|
width: 620px;
|
|
806
806
|
max-width: calc(100vw - 16px);
|
|
807
807
|
padding: 18px;
|
|
@@ -998,7 +998,7 @@ button.datepicker-month:hover {
|
|
|
998
998
|
// modal layer, matching .search-select-menu-fixed and .datepicker-pop.
|
|
999
999
|
.country-dropdown-menu {
|
|
1000
1000
|
position: fixed;
|
|
1001
|
-
z-index:
|
|
1001
|
+
z-index: var(--wui-z-menu);
|
|
1002
1002
|
width: 320px;
|
|
1003
1003
|
max-width: calc(100vw - 16px);
|
|
1004
1004
|
padding: 4px;
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -6,191 +6,56 @@
|
|
|
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
|
-
// ── Checkbox
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
// input via `appearance: none`, so it stays keyboard- and screen-reader-
|
|
15
|
-
// accessible.
|
|
9
|
+
// ── Checkbox / radio / switch ──
|
|
10
|
+
// Bootstrap `.form-check` + `.form-check-input` are the public selection skin
|
|
11
|
+
// (AppCheckbox / AppRadio / AppSwitch). `.wui-check` / `.wui-radio` are aliases
|
|
12
|
+
// for the same glyph (AppDataTable select column + legacy `.wui-control`
|
|
13
|
+
// markup) — never a second look. One 20px brand glyph language.
|
|
16
14
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
// Group rows in a `.wui-control-group` (horizontal, wraps) or stack them.
|
|
15
|
+
// Layout wrappers:
|
|
16
|
+
// .form-check / .form-check-label — App* path
|
|
17
|
+
// .wui-control / .wui-control-label — optional compact label wrap (alias)
|
|
18
|
+
|
|
19
|
+
.form-check,
|
|
23
20
|
.wui-control {
|
|
24
|
-
display:
|
|
21
|
+
display: flex;
|
|
25
22
|
align-items: center;
|
|
26
|
-
gap:
|
|
23
|
+
gap: 8px;
|
|
24
|
+
min-height: 0;
|
|
25
|
+
margin-bottom: 0;
|
|
26
|
+
padding-left: 0;
|
|
27
27
|
cursor: pointer;
|
|
28
28
|
user-select: none;
|
|
29
29
|
font-size: 14px;
|
|
30
30
|
color: var(--color-text);
|
|
31
31
|
line-height: 1.4;
|
|
32
32
|
}
|
|
33
|
+
.wui-control {
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
}
|
|
36
|
+
.form-check-inline {
|
|
37
|
+
display: inline-flex;
|
|
38
|
+
margin-right: 16px;
|
|
39
|
+
}
|
|
33
40
|
.wui-control-group {
|
|
34
41
|
display: flex;
|
|
35
42
|
flex-wrap: wrap;
|
|
36
43
|
align-items: center;
|
|
37
44
|
gap: 8px 20px;
|
|
38
45
|
}
|
|
46
|
+
.form-check-label,
|
|
39
47
|
.wui-control-label {
|
|
40
|
-
|
|
48
|
+
cursor: pointer;
|
|
49
|
+
font-size: 14px;
|
|
50
|
+
line-height: 1.4;
|
|
51
|
+
color: var(--color-text);
|
|
52
|
+
user-select: none;
|
|
41
53
|
}
|
|
42
54
|
|
|
43
|
-
//
|
|
55
|
+
// Glyph — shared by BS form-check-input and the wui-check alias.
|
|
56
|
+
.form-check-input,
|
|
44
57
|
.wui-check,
|
|
45
58
|
.wui-radio {
|
|
46
|
-
appearance: none;
|
|
47
|
-
-webkit-appearance: none;
|
|
48
|
-
flex: none;
|
|
49
|
-
box-sizing: border-box;
|
|
50
|
-
width: 20px;
|
|
51
|
-
height: 20px;
|
|
52
|
-
// 8px transparent ring → 36px hit area + room for the hover ripple
|
|
53
|
-
margin: 8px;
|
|
54
|
-
position: relative;
|
|
55
|
-
border: 1.6px solid var(--color-border-strong);
|
|
56
|
-
background: transparent;
|
|
57
|
-
cursor: pointer;
|
|
58
|
-
transition:
|
|
59
|
-
border-color 0.15s ease,
|
|
60
|
-
background-color 0.15s ease;
|
|
61
|
-
}
|
|
62
|
-
.wui-check {
|
|
63
|
-
border-radius: var(--radius-sm);
|
|
64
|
-
corner-shape: squircle;
|
|
65
|
-
}
|
|
66
|
-
.wui-radio {
|
|
67
|
-
border-radius: 50%;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Soft primary circular ripple behind the glyph on hover (the Minimals halo).
|
|
71
|
-
.wui-check::after,
|
|
72
|
-
.wui-radio::after {
|
|
73
|
-
content: "";
|
|
74
|
-
position: absolute;
|
|
75
|
-
left: 50%;
|
|
76
|
-
top: 50%;
|
|
77
|
-
width: 36px;
|
|
78
|
-
height: 36px;
|
|
79
|
-
border-radius: 50%;
|
|
80
|
-
transform: translate(-50%, -50%) scale(0.5);
|
|
81
|
-
background: var(--color-primary-soft);
|
|
82
|
-
opacity: 0;
|
|
83
|
-
z-index: -1;
|
|
84
|
-
pointer-events: none;
|
|
85
|
-
transition:
|
|
86
|
-
opacity 0.15s ease,
|
|
87
|
-
transform 0.15s ease;
|
|
88
|
-
}
|
|
89
|
-
.wui-control:hover .wui-check::after,
|
|
90
|
-
.wui-control:hover .wui-radio::after,
|
|
91
|
-
.wui-check:hover::after,
|
|
92
|
-
.wui-radio:hover::after {
|
|
93
|
-
opacity: 1;
|
|
94
|
-
transform: translate(-50%, -50%) scale(1);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// Checked — primary fill (checkbox) / primary ring + dot (radio)
|
|
98
|
-
.wui-check:checked {
|
|
99
|
-
border-color: var(--color-primary);
|
|
100
|
-
background: var(--color-primary);
|
|
101
|
-
// white tick drawn with a rotated box-shadow-free pseudo
|
|
102
|
-
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E");
|
|
103
|
-
background-size: 15px 15px;
|
|
104
|
-
background-position: center;
|
|
105
|
-
background-repeat: no-repeat;
|
|
106
|
-
}
|
|
107
|
-
.wui-radio:checked {
|
|
108
|
-
border-color: var(--color-primary);
|
|
109
|
-
}
|
|
110
|
-
.wui-radio:checked::before {
|
|
111
|
-
content: "";
|
|
112
|
-
position: absolute;
|
|
113
|
-
left: 50%;
|
|
114
|
-
top: 50%;
|
|
115
|
-
width: 10px;
|
|
116
|
-
height: 10px;
|
|
117
|
-
border-radius: 50%;
|
|
118
|
-
background: var(--color-primary);
|
|
119
|
-
transform: translate(-50%, -50%);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Keyboard focus ring (only on focus-visible, not mouse)
|
|
123
|
-
.wui-check:focus-visible,
|
|
124
|
-
.wui-radio:focus-visible {
|
|
125
|
-
outline: none;
|
|
126
|
-
box-shadow: 0 0 0 3px var(--color-primary-soft);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Disabled
|
|
130
|
-
.wui-check:disabled,
|
|
131
|
-
.wui-radio:disabled,
|
|
132
|
-
.wui-control:has(:disabled) {
|
|
133
|
-
cursor: not-allowed;
|
|
134
|
-
}
|
|
135
|
-
.wui-check:disabled,
|
|
136
|
-
.wui-radio:disabled {
|
|
137
|
-
border-color: var(--color-border);
|
|
138
|
-
background-color: var(--color-bg-muted);
|
|
139
|
-
}
|
|
140
|
-
.wui-check:disabled:checked,
|
|
141
|
-
.wui-radio:disabled:checked {
|
|
142
|
-
border-color: var(--color-border-strong);
|
|
143
|
-
}
|
|
144
|
-
.wui-check:disabled:checked {
|
|
145
|
-
background-color: var(--color-border-strong);
|
|
146
|
-
}
|
|
147
|
-
.wui-radio:disabled:checked::before {
|
|
148
|
-
background: var(--color-border-strong);
|
|
149
|
-
}
|
|
150
|
-
.wui-control:has(:disabled) {
|
|
151
|
-
color: var(--color-text-subtle);
|
|
152
|
-
}
|
|
153
|
-
.wui-check:disabled::after,
|
|
154
|
-
.wui-radio:disabled::after {
|
|
155
|
-
display: none;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
@media (prefers-reduced-motion: reduce) {
|
|
159
|
-
.wui-check,
|
|
160
|
-
.wui-radio,
|
|
161
|
-
.wui-check::after,
|
|
162
|
-
.wui-radio::after {
|
|
163
|
-
transition: none;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Row-selection checkbox variant used inside a .table-ledger select column.
|
|
168
|
-
.wui-check {
|
|
169
|
-
width: 16px;
|
|
170
|
-
height: 16px;
|
|
171
|
-
accent-color: var(--color-primary);
|
|
172
|
-
cursor: pointer;
|
|
173
|
-
vertical-align: middle;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// ── Premium checkbox / radio / switch ──
|
|
177
|
-
// AppCheckbox / AppRadio / AppSwitch render Bootstrap's .form-check markup, whose default
|
|
178
|
-
// input is a plain 16px box. This restyles it to the Webority selection-control language
|
|
179
|
-
// (see .wui-check above): a 20px squircle box, brand fill, a crisp white tick, a soft primary
|
|
180
|
-
// hover ring + focus ring, and a themed switch. One block upgrades all three, both surfaces.
|
|
181
|
-
.form-check {
|
|
182
|
-
display: flex;
|
|
183
|
-
align-items: center;
|
|
184
|
-
gap: 8px;
|
|
185
|
-
min-height: 0;
|
|
186
|
-
margin-bottom: 0;
|
|
187
|
-
padding-left: 0;
|
|
188
|
-
}
|
|
189
|
-
.form-check-inline {
|
|
190
|
-
display: inline-flex;
|
|
191
|
-
margin-right: 16px;
|
|
192
|
-
}
|
|
193
|
-
.form-check-input {
|
|
194
59
|
box-sizing: border-box;
|
|
195
60
|
flex: none;
|
|
196
61
|
float: none;
|
|
@@ -211,65 +76,82 @@
|
|
|
211
76
|
box-shadow 0.15s ease,
|
|
212
77
|
background-position 0.15s ease;
|
|
213
78
|
}
|
|
214
|
-
// Bootstrap floats the input into the label's left padding via
|
|
215
|
-
// (
|
|
216
|
-
// left, out of the card. Our flex row places it directly; undo the float and negative margin
|
|
217
|
-
// at matching specificity.
|
|
79
|
+
// Bootstrap floats the input into the label's left padding via
|
|
80
|
+
// `.form-check .form-check-input` (0,2,0). Undo at matching specificity.
|
|
218
81
|
.form-check .form-check-input {
|
|
219
82
|
float: none;
|
|
220
83
|
margin: 0;
|
|
221
84
|
}
|
|
222
|
-
.form-check-input[type="checkbox"]
|
|
85
|
+
.form-check-input[type="checkbox"],
|
|
86
|
+
.wui-check {
|
|
223
87
|
border-radius: var(--radius-sm);
|
|
224
88
|
corner-shape: squircle;
|
|
225
89
|
}
|
|
226
|
-
.form-check-input[type="radio"]
|
|
90
|
+
.form-check-input[type="radio"],
|
|
91
|
+
.wui-radio {
|
|
227
92
|
border-radius: 50%;
|
|
228
93
|
}
|
|
229
|
-
.form-check-input:hover:not(:disabled):not(:checked)
|
|
94
|
+
.form-check-input:hover:not(:disabled):not(:checked),
|
|
95
|
+
.wui-check:hover:not(:disabled):not(:checked),
|
|
96
|
+
.wui-radio:hover:not(:disabled):not(:checked) {
|
|
230
97
|
border-color: var(--color-primary);
|
|
231
98
|
box-shadow: 0 0 0 4px var(--color-primary-soft);
|
|
232
99
|
}
|
|
233
|
-
.form-check-input:focus-visible
|
|
100
|
+
.form-check-input:focus-visible,
|
|
101
|
+
.wui-check:focus-visible,
|
|
102
|
+
.wui-radio:focus-visible {
|
|
234
103
|
outline: none;
|
|
235
104
|
border-color: var(--color-primary);
|
|
236
105
|
box-shadow: 0 0 0 3px var(--color-primary-soft);
|
|
237
106
|
}
|
|
238
|
-
.form-check-input:checked
|
|
107
|
+
.form-check-input:checked,
|
|
108
|
+
.wui-check:checked,
|
|
109
|
+
.wui-radio:checked {
|
|
239
110
|
border-color: var(--color-primary);
|
|
240
111
|
background-color: var(--color-primary);
|
|
241
112
|
}
|
|
242
|
-
.form-check-input[type="checkbox"]:checked
|
|
113
|
+
.form-check-input[type="checkbox"]:checked,
|
|
114
|
+
.wui-check:checked {
|
|
243
115
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5l4.5 4.5L19 7'/%3E%3C/svg%3E");
|
|
244
116
|
}
|
|
245
|
-
.form-check-input[type="checkbox"]:indeterminate
|
|
117
|
+
.form-check-input[type="checkbox"]:indeterminate,
|
|
118
|
+
.wui-check:indeterminate {
|
|
246
119
|
border-color: var(--color-primary);
|
|
247
120
|
background-color: var(--color-primary);
|
|
248
121
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='3.4' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E");
|
|
249
122
|
}
|
|
250
|
-
.form-check-input[type="radio"]:checked
|
|
123
|
+
.form-check-input[type="radio"]:checked,
|
|
124
|
+
.wui-radio:checked {
|
|
251
125
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ccircle cx='12' cy='12' r='5.5' fill='white'/%3E%3C/svg%3E");
|
|
252
126
|
background-size: 20px 20px;
|
|
253
127
|
}
|
|
254
|
-
.form-check-input:disabled
|
|
128
|
+
.form-check-input:disabled,
|
|
129
|
+
.wui-check:disabled,
|
|
130
|
+
.wui-radio:disabled {
|
|
255
131
|
cursor: not-allowed;
|
|
256
132
|
opacity: 0.55;
|
|
257
133
|
background-color: var(--color-bg-muted);
|
|
258
134
|
border-color: var(--color-border);
|
|
259
135
|
box-shadow: none;
|
|
260
136
|
}
|
|
261
|
-
.form-check-label
|
|
262
|
-
|
|
263
|
-
font-size: 14px;
|
|
264
|
-
line-height: 1.4;
|
|
265
|
-
color: var(--color-text);
|
|
266
|
-
user-select: none;
|
|
267
|
-
}
|
|
268
|
-
.form-check-input:disabled ~ .form-check-label {
|
|
137
|
+
.form-check-input:disabled ~ .form-check-label,
|
|
138
|
+
.wui-control:has(:disabled) {
|
|
269
139
|
cursor: not-allowed;
|
|
270
140
|
color: var(--color-text-subtle);
|
|
271
141
|
}
|
|
272
|
-
|
|
142
|
+
|
|
143
|
+
// Compact row-select — ONLY inside the select column (AppDataTable / Razor
|
|
144
|
+
// data-table → `.wui-select-cell`). Scope is load-bearing.
|
|
145
|
+
.wui-select-cell .wui-check,
|
|
146
|
+
.wui-select-cell .form-check-input {
|
|
147
|
+
width: 16px;
|
|
148
|
+
height: 16px;
|
|
149
|
+
margin: 0;
|
|
150
|
+
vertical-align: middle;
|
|
151
|
+
background-size: 12px 12px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Switch — pill track + sliding knob (Bootstrap .form-switch only).
|
|
273
155
|
.form-switch {
|
|
274
156
|
padding-left: 0;
|
|
275
157
|
}
|
|
@@ -291,19 +173,28 @@
|
|
|
291
173
|
border-color: transparent;
|
|
292
174
|
background-color: var(--color-primary);
|
|
293
175
|
background-position: right center;
|
|
294
|
-
// Re-assert the knob — the checkbox :checked tick has equal specificity
|
|
295
|
-
// otherwise win, turning the switch into a ticked box.
|
|
176
|
+
// Re-assert the knob — the checkbox :checked tick has equal specificity.
|
|
296
177
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Ccircle cx='10' cy='10' r='7' fill='white'/%3E%3C/svg%3E");
|
|
297
178
|
}
|
|
298
179
|
@media (prefers-reduced-motion: reduce) {
|
|
299
|
-
.form-check-input
|
|
180
|
+
.form-check-input,
|
|
181
|
+
.wui-check,
|
|
182
|
+
.wui-radio {
|
|
300
183
|
transition: none;
|
|
301
184
|
}
|
|
302
185
|
}
|
|
303
186
|
|
|
304
187
|
// ── Field / Input ──
|
|
305
|
-
//
|
|
306
|
-
// (
|
|
188
|
+
// Bootstrap `.form-control` / `.form-select` are the field skin. `.input` /
|
|
189
|
+
// `.select` / `.textarea` are aliases (and App* size/shell hooks) that MUST
|
|
190
|
+
// share the same chrome — never a second look. The only WUI
|
|
191
|
+
// deltas on top of Bootstrap's $input-* vars: shared control height, hover
|
|
192
|
+
// fill, brand-border focus (no glow), soft invalid border.
|
|
193
|
+
//
|
|
194
|
+
// Keep shell utilities (.input-wrap, .input-icon, .input-sm, …) — those are
|
|
195
|
+
// structure, not a parallel visual system.
|
|
196
|
+
.form-control,
|
|
197
|
+
.form-select,
|
|
307
198
|
.input,
|
|
308
199
|
.select,
|
|
309
200
|
.textarea {
|
|
@@ -322,75 +213,87 @@
|
|
|
322
213
|
box-shadow 0.12s;
|
|
323
214
|
font-family: inherit;
|
|
324
215
|
}
|
|
325
|
-
// Size steps — sm/lg opt out of the shared 44px control height
|
|
326
|
-
//
|
|
216
|
+
// Size steps — sm/lg opt out of the shared 44px control height. App* emits
|
|
217
|
+
// .input-sm/.input-lg; Bootstrap's form-control-sm/lg are included so a raw
|
|
218
|
+
// BS field can match without the .input alias.
|
|
219
|
+
.form-control-sm,
|
|
327
220
|
.input-sm {
|
|
328
221
|
height: var(--wui-control-height-sm);
|
|
329
222
|
padding: 6px 10px;
|
|
330
223
|
font-size: 13px;
|
|
331
224
|
}
|
|
225
|
+
.form-control-lg,
|
|
332
226
|
.input-lg {
|
|
333
227
|
height: var(--wui-control-height-lg);
|
|
334
228
|
padding: 14px 16px;
|
|
335
229
|
font-size: 16px;
|
|
336
230
|
}
|
|
337
|
-
.textarea
|
|
231
|
+
.textarea,
|
|
232
|
+
textarea.form-control {
|
|
338
233
|
height: auto;
|
|
339
234
|
min-height: 120px;
|
|
340
235
|
padding: 12px 15px 16px;
|
|
341
236
|
line-height: 1.5;
|
|
342
237
|
resize: vertical;
|
|
343
238
|
}
|
|
344
|
-
// Auto-grow — JS drives the height to fit the content (Razor: data-app-autosize
|
|
345
|
-
//
|
|
346
|
-
|
|
347
|
-
|
|
239
|
+
// Auto-grow — JS drives the height to fit the content (Razor: data-app-autosize;
|
|
240
|
+
// React: autoGrow). Manual grabber and scrollbar off so they can't fight measure.
|
|
241
|
+
.textarea.autosize,
|
|
242
|
+
textarea.form-control.autosize {
|
|
348
243
|
resize: none;
|
|
349
244
|
overflow: hidden;
|
|
350
245
|
}
|
|
351
|
-
//
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
.input:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
246
|
+
// Hover/error guards: without them the hover fill repaints over an invalid border.
|
|
247
|
+
.form-control:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
248
|
+
.form-select:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
249
|
+
.input:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
355
250
|
.select:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error),
|
|
356
|
-
.textarea:hover:not(:focus):not(:disabled):not([aria-invalid="true"]):not(.is-invalid):not(.error) {
|
|
251
|
+
.textarea:hover:not(:focus):not(:disabled):not([readonly]):not([aria-invalid="true"]):not(.is-invalid):not(.error) {
|
|
357
252
|
background-color: var(--color-bg-muted);
|
|
358
253
|
border-color: var(--color-border-strong);
|
|
359
254
|
}
|
|
255
|
+
.form-control:focus,
|
|
256
|
+
.form-select:focus,
|
|
360
257
|
.input:focus,
|
|
361
258
|
.select:focus,
|
|
362
259
|
.textarea:focus {
|
|
363
260
|
outline: none;
|
|
364
261
|
background-color: var(--color-bg-muted);
|
|
365
262
|
color: var(--color-text);
|
|
366
|
-
// Full-strength brand border is the focus cue (no glow) —
|
|
367
|
-
// ~1.5:1, below WCAG 2.2 SC 1.4.11's 3:1 for a non-text focus indicator.
|
|
368
|
-
border-color: var(--color-primary);
|
|
369
|
-
box-shadow: none;
|
|
370
|
-
}
|
|
371
|
-
// Textarea renders as .form-control.textarea (react-bootstrap). Match the
|
|
372
|
-
// shared theme's .form-control:hover specificity so it hovers to
|
|
373
|
-
// --color-border-strong too.
|
|
374
|
-
textarea.textarea.form-control:hover:not(:focus):not(:disabled):not([readonly]) {
|
|
375
|
-
border-color: var(--color-border-strong);
|
|
376
|
-
}
|
|
377
|
-
textarea.textarea:focus {
|
|
378
|
-
outline: none;
|
|
379
|
-
// Full-strength brand border is the focus cue (no glow) — the prior 30% tint read at
|
|
380
|
-
// ~1.5:1, below WCAG 2.2 SC 1.4.11's 3:1 for a non-text focus indicator.
|
|
263
|
+
// Full-strength brand border is the focus cue (no glow) — WCAG 2.2 SC 1.4.11.
|
|
381
264
|
border-color: var(--color-primary);
|
|
382
265
|
box-shadow: none;
|
|
383
266
|
}
|
|
267
|
+
.form-control::placeholder,
|
|
384
268
|
.input::placeholder,
|
|
385
269
|
.textarea::placeholder {
|
|
386
270
|
color: var(--color-text-subtle);
|
|
387
271
|
}
|
|
388
|
-
//
|
|
389
|
-
// the
|
|
272
|
+
// Borderless variant — no border/background, inherits the container's chrome
|
|
273
|
+
// (inline-rename, command-palette fields where the bordered shell is wrong).
|
|
274
|
+
.input-bare,
|
|
275
|
+
.input-bare:hover,
|
|
276
|
+
.input-bare:focus {
|
|
277
|
+
border-color: transparent;
|
|
278
|
+
background: transparent;
|
|
279
|
+
box-shadow: none;
|
|
280
|
+
}
|
|
281
|
+
.input-bare {
|
|
282
|
+
padding-left: 0;
|
|
283
|
+
padding-right: 0;
|
|
284
|
+
}
|
|
285
|
+
.input-bare:focus {
|
|
286
|
+
outline: none;
|
|
287
|
+
}
|
|
288
|
+
// Edge/IE native password reveal — hide so only the app eye toggle shows.
|
|
289
|
+
.form-control::-ms-reveal,
|
|
290
|
+
.form-control::-ms-clear,
|
|
390
291
|
.input::-ms-reveal,
|
|
391
292
|
.input::-ms-clear {
|
|
392
293
|
display: none;
|
|
393
294
|
}
|
|
295
|
+
.form-control:disabled,
|
|
296
|
+
.form-select:disabled,
|
|
394
297
|
.input:disabled,
|
|
395
298
|
.select:disabled,
|
|
396
299
|
.textarea:disabled {
|
|
@@ -399,17 +302,16 @@ textarea.textarea:focus {
|
|
|
399
302
|
color: var(--color-text-subtle);
|
|
400
303
|
cursor: not-allowed;
|
|
401
304
|
}
|
|
305
|
+
.field:has(.form-control:disabled) .label,
|
|
402
306
|
.field:has(.input:disabled) .label {
|
|
403
307
|
color: var(--color-text-subtle);
|
|
404
308
|
}
|
|
405
309
|
|
|
406
|
-
// Read-only is NOT disabled
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
// DISABLED input (and any non-editable element), so a `:read-only` rule silently
|
|
412
|
-
// repaints disabled fields too.
|
|
310
|
+
// Read-only is NOT disabled — still submitted/selectable/copyable, so no
|
|
311
|
+
// not-allowed cursor. Match `[readonly]`, never `:read-only` (that also hits
|
|
312
|
+
// disabled and non-editable controls).
|
|
313
|
+
.form-control[readonly],
|
|
314
|
+
.form-select[readonly],
|
|
413
315
|
.input[readonly],
|
|
414
316
|
.select[readonly],
|
|
415
317
|
.textarea[readonly] {
|
|
@@ -418,26 +320,44 @@ textarea.textarea:focus {
|
|
|
418
320
|
cursor: default;
|
|
419
321
|
}
|
|
420
322
|
|
|
421
|
-
// Invalid
|
|
422
|
-
//
|
|
423
|
-
//
|
|
424
|
-
|
|
425
|
-
|
|
323
|
+
// Invalid — soft danger border (one recipe for aria-invalid, .is-invalid, .error).
|
|
324
|
+
// Soft rather than solid --bs-danger so the field still reads as a surface, not a
|
|
325
|
+
// hard alarm box; text errors use --color-danger-text for AA.
|
|
326
|
+
.form-control.is-invalid,
|
|
327
|
+
.form-select.is-invalid,
|
|
426
328
|
.input.is-invalid,
|
|
427
329
|
.select.is-invalid,
|
|
428
330
|
.textarea.is-invalid,
|
|
331
|
+
.form-control.is-invalid:focus,
|
|
332
|
+
.form-select.is-invalid:focus,
|
|
429
333
|
.input.is-invalid:focus,
|
|
430
334
|
.select.is-invalid:focus,
|
|
431
335
|
.textarea.is-invalid:focus,
|
|
336
|
+
.form-control[aria-invalid="true"],
|
|
337
|
+
.form-select[aria-invalid="true"],
|
|
432
338
|
.input[aria-invalid="true"],
|
|
433
339
|
.select[aria-invalid="true"],
|
|
434
340
|
.textarea[aria-invalid="true"],
|
|
341
|
+
.form-control[aria-invalid="true"]:focus,
|
|
342
|
+
.form-select[aria-invalid="true"]:focus,
|
|
435
343
|
.input[aria-invalid="true"]:focus,
|
|
436
344
|
.select[aria-invalid="true"]:focus,
|
|
437
|
-
.textarea[aria-invalid="true"]:focus
|
|
345
|
+
.textarea[aria-invalid="true"]:focus,
|
|
346
|
+
.form-control.error,
|
|
347
|
+
.input.error,
|
|
348
|
+
.textarea.error,
|
|
349
|
+
.form-control.error:focus,
|
|
350
|
+
.input.error:focus,
|
|
351
|
+
.textarea.error:focus {
|
|
438
352
|
background: var(--color-surface);
|
|
439
353
|
border-color: color-mix(in srgb, var(--color-danger) 45%, var(--color-surface));
|
|
440
354
|
box-shadow: none;
|
|
355
|
+
// Kill Bootstrap's default invalid SVG + extra padding when .is-invalid is used.
|
|
356
|
+
background-image: none;
|
|
357
|
+
}
|
|
358
|
+
.form-control.is-invalid,
|
|
359
|
+
.form-select.is-invalid {
|
|
360
|
+
padding-right: 0.875rem;
|
|
441
361
|
}
|
|
442
362
|
.field-error {
|
|
443
363
|
font-size: 12px;
|
|
@@ -544,20 +464,12 @@ textarea.textarea:focus {
|
|
|
544
464
|
pointer-events: none;
|
|
545
465
|
}
|
|
546
466
|
|
|
547
|
-
//
|
|
548
|
-
.
|
|
549
|
-
.
|
|
550
|
-
.
|
|
551
|
-
.
|
|
552
|
-
|
|
553
|
-
border-color: color-mix(in srgb, var(--color-danger) 45%, var(--color-surface));
|
|
554
|
-
box-shadow: none;
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
// The trailing error glyph. Scoped with the child combinator to a PLAIN field only:
|
|
558
|
-
// a shell-wrapped input (leading icon / unit / password / trailing action) is nested
|
|
559
|
-
// one level deeper, so it keeps its own right edge and never collides with the glyph.
|
|
560
|
-
// Covers both error paths — the `.error` class and `[aria-invalid]` / `.is-invalid`.
|
|
467
|
+
// Trailing error glyph on a PLAIN field only (child combinator). Shell-wrapped
|
|
468
|
+
// inputs sit one level deeper and keep their own right edge.
|
|
469
|
+
// Note: background-image here re-applies after the shared invalid rule zeroed BS's SVG.
|
|
470
|
+
.field > .form-control.error,
|
|
471
|
+
.field > .form-control.is-invalid,
|
|
472
|
+
.field > .form-control[aria-invalid="true"],
|
|
561
473
|
.field > .input.error,
|
|
562
474
|
.field > .input.is-invalid,
|
|
563
475
|
.field > .input[aria-invalid="true"] {
|
|
@@ -568,8 +480,12 @@ textarea.textarea:focus {
|
|
|
568
480
|
background-size: 18px;
|
|
569
481
|
}
|
|
570
482
|
// Dark mode: keep the value in an errored field legible.
|
|
483
|
+
:root[data-theme="dark"] .form-control.error,
|
|
484
|
+
:root[data-theme="dark"] .form-control.error:focus,
|
|
571
485
|
:root[data-theme="dark"] .input.error,
|
|
572
486
|
:root[data-theme="dark"] .input.error:focus,
|
|
487
|
+
:root[data-theme="dark"] .form-control.error:-webkit-autofill,
|
|
488
|
+
:root[data-theme="dark"] .form-control.error:focus:-webkit-autofill,
|
|
573
489
|
:root[data-theme="dark"] .input.error:-webkit-autofill,
|
|
574
490
|
:root[data-theme="dark"] .input.error:focus:-webkit-autofill {
|
|
575
491
|
color: var(--color-text);
|
|
@@ -682,7 +598,7 @@ textarea.textarea:focus {
|
|
|
682
598
|
}
|
|
683
599
|
@keyframes wui-clear-in {
|
|
684
600
|
from { opacity: 0; transform: translateY(-50%) scale(0.72); }
|
|
685
|
-
to
|
|
601
|
+
to { opacity: 1; transform: translateY(-50%) scale(1); }
|
|
686
602
|
}
|
|
687
603
|
|
|
688
604
|
// Prefix / suffix addon — a bordered box before/after the input (₹, https://, .com). The
|
|
@@ -1043,8 +959,8 @@ wui-select[compact] {
|
|
|
1043
959
|
//
|
|
1044
960
|
// Two other vocabularies existed and are accepted as DEPRECATED ALIASES so a
|
|
1045
961
|
// consumer can migrate without a flag day:
|
|
1046
|
-
//
|
|
1047
|
-
//
|
|
962
|
+
// - colour-named .tone-green / -amber / -red / -blue / -grey (was the library's)
|
|
963
|
+
// - short semantic .tone-ok / -warn / -bad (a short-semantic set)
|
|
1048
964
|
// Author new markup with the semantic names; the aliases will be removed.
|
|
1049
965
|
.status-label.success,
|
|
1050
966
|
.status-label.tone-ok,
|