@webority/theme 0.6.9 → 0.6.11
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/dist/webority-theme.css +1 -1
- package/package.json +1 -1
- package/scss/wui/_choice.scss +47 -0
- package/scss/wui/_controls.scss +128 -0
- package/scss/wui/_navigation.scss +71 -2
- package/scss/wui/_typography.scss +18 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webority/theme",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.11",
|
|
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": {
|
package/scss/wui/_choice.scss
CHANGED
|
@@ -201,6 +201,24 @@
|
|
|
201
201
|
min-height: var(--wui-control-height-lg);
|
|
202
202
|
font-size: 16px;
|
|
203
203
|
}
|
|
204
|
+
// Multi-select grows with its chips, so a size sets the FLOOR (min-height from the rule
|
|
205
|
+
// above) but never a fixed height — reset height:auto so a second row of chips can appear.
|
|
206
|
+
.search-select-sm .search-select-multi,
|
|
207
|
+
.search-select-lg .search-select-multi {
|
|
208
|
+
height: auto;
|
|
209
|
+
}
|
|
210
|
+
// AppSelectMini is <wui-select compact> (a 32px picker). Its sizes are compact-scaled —
|
|
211
|
+
// the compound selector outranks both the plain size rule and the compact rule.
|
|
212
|
+
.search-select-compact.search-select-sm .search-select-control {
|
|
213
|
+
height: 28px;
|
|
214
|
+
min-height: 28px;
|
|
215
|
+
font-size: 12px;
|
|
216
|
+
}
|
|
217
|
+
.search-select-compact.search-select-lg .search-select-control {
|
|
218
|
+
height: 38px;
|
|
219
|
+
min-height: 38px;
|
|
220
|
+
font-size: 14px;
|
|
221
|
+
}
|
|
204
222
|
|
|
205
223
|
// Clear (✕) — sits between the value and the caret, shown only once a value is
|
|
206
224
|
// chosen. It is a <span role="button"> inside the trigger <button> (a nested
|
|
@@ -227,6 +245,35 @@
|
|
|
227
245
|
color: var(--color-text);
|
|
228
246
|
}
|
|
229
247
|
|
|
248
|
+
// Leading icon + static prefix (e.g. a funnel glyph, or "Sort:") — cluster at the
|
|
249
|
+
// start of the control, before the value. Mainly for the mini toolbar picker.
|
|
250
|
+
.search-select-icon {
|
|
251
|
+
flex-shrink: 0;
|
|
252
|
+
display: inline-flex;
|
|
253
|
+
color: var(--color-text-muted);
|
|
254
|
+
font-size: 15px;
|
|
255
|
+
}
|
|
256
|
+
.search-select-prefix {
|
|
257
|
+
flex-shrink: 0;
|
|
258
|
+
color: var(--color-text-subtle);
|
|
259
|
+
font-weight: 400;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Per-option leading glyph — wraps icon + label as one node (menu row, the selected
|
|
263
|
+
// value in the control, and chips all render through optionLabelHtml). The icon inherits
|
|
264
|
+
// the row's colour so it recolours with the label on hover / when selected.
|
|
265
|
+
.wui-opt-label {
|
|
266
|
+
display: inline-flex;
|
|
267
|
+
align-items: center;
|
|
268
|
+
gap: 8px;
|
|
269
|
+
min-width: 0;
|
|
270
|
+
vertical-align: middle;
|
|
271
|
+
}
|
|
272
|
+
.wui-opt-label > .bi {
|
|
273
|
+
flex-shrink: 0;
|
|
274
|
+
font-size: 15px;
|
|
275
|
+
}
|
|
276
|
+
|
|
230
277
|
// AppSelectMini — the compact single-purpose picker (page size, a range switch). It shares
|
|
231
278
|
// every behaviour with AppSelect and differs only in metrics.
|
|
232
279
|
//
|
package/scss/wui/_controls.scss
CHANGED
|
@@ -172,6 +172,134 @@
|
|
|
172
172
|
vertical-align: middle;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
+
// ── Premium checkbox / radio / switch ──
|
|
176
|
+
// AppCheckbox / AppRadio / AppSwitch render Bootstrap's .form-check markup, whose default
|
|
177
|
+
// input is a plain 16px box. This restyles it to the Webority selection-control language
|
|
178
|
+
// (see .lx-check above): a 20px squircle box, brand fill, a crisp white tick, a soft primary
|
|
179
|
+
// hover ring + focus ring, and a themed switch. One block upgrades all three, both surfaces.
|
|
180
|
+
.form-check {
|
|
181
|
+
display: flex;
|
|
182
|
+
align-items: center;
|
|
183
|
+
gap: 8px;
|
|
184
|
+
min-height: 0;
|
|
185
|
+
margin-bottom: 0;
|
|
186
|
+
padding-left: 0;
|
|
187
|
+
}
|
|
188
|
+
.form-check-inline {
|
|
189
|
+
display: inline-flex;
|
|
190
|
+
margin-right: 16px;
|
|
191
|
+
}
|
|
192
|
+
.form-check-input {
|
|
193
|
+
box-sizing: border-box;
|
|
194
|
+
flex: none;
|
|
195
|
+
float: none;
|
|
196
|
+
width: 20px;
|
|
197
|
+
height: 20px;
|
|
198
|
+
margin: 0;
|
|
199
|
+
appearance: none;
|
|
200
|
+
-webkit-appearance: none;
|
|
201
|
+
border: 1.6px solid var(--color-border-strong);
|
|
202
|
+
background-color: var(--color-surface);
|
|
203
|
+
background-repeat: no-repeat;
|
|
204
|
+
background-position: center;
|
|
205
|
+
background-size: 15px 15px;
|
|
206
|
+
cursor: pointer;
|
|
207
|
+
transition:
|
|
208
|
+
border-color 0.15s ease,
|
|
209
|
+
background-color 0.15s ease,
|
|
210
|
+
box-shadow 0.15s ease,
|
|
211
|
+
background-position 0.15s ease;
|
|
212
|
+
}
|
|
213
|
+
// Bootstrap floats the input into the label's left padding via `.form-check .form-check-input`
|
|
214
|
+
// (specificity 0,2,0 — it outranks the rule above), which drags the box a negative margin
|
|
215
|
+
// left, out of the card. Our flex row places it directly; undo the float and negative margin
|
|
216
|
+
// at matching specificity.
|
|
217
|
+
.form-check .form-check-input {
|
|
218
|
+
float: none;
|
|
219
|
+
margin: 0;
|
|
220
|
+
}
|
|
221
|
+
.form-check-input[type="checkbox"] {
|
|
222
|
+
border-radius: var(--radius-sm);
|
|
223
|
+
corner-shape: squircle;
|
|
224
|
+
}
|
|
225
|
+
.form-check-input[type="radio"] {
|
|
226
|
+
border-radius: 50%;
|
|
227
|
+
}
|
|
228
|
+
.form-check-input:hover:not(:disabled):not(:checked) {
|
|
229
|
+
border-color: var(--color-primary);
|
|
230
|
+
box-shadow: 0 0 0 4px var(--color-primary-soft);
|
|
231
|
+
}
|
|
232
|
+
.form-check-input:focus-visible {
|
|
233
|
+
outline: none;
|
|
234
|
+
border-color: var(--color-primary);
|
|
235
|
+
box-shadow: 0 0 0 3px var(--color-primary-soft);
|
|
236
|
+
}
|
|
237
|
+
.form-check-input:checked {
|
|
238
|
+
border-color: var(--color-primary);
|
|
239
|
+
background-color: var(--color-primary);
|
|
240
|
+
}
|
|
241
|
+
.form-check-input[type="checkbox"]:checked {
|
|
242
|
+
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");
|
|
243
|
+
}
|
|
244
|
+
.form-check-input[type="checkbox"]:indeterminate {
|
|
245
|
+
border-color: var(--color-primary);
|
|
246
|
+
background-color: var(--color-primary);
|
|
247
|
+
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");
|
|
248
|
+
}
|
|
249
|
+
.form-check-input[type="radio"]:checked {
|
|
250
|
+
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");
|
|
251
|
+
background-size: 20px 20px;
|
|
252
|
+
}
|
|
253
|
+
.form-check-input:disabled {
|
|
254
|
+
cursor: not-allowed;
|
|
255
|
+
opacity: 0.55;
|
|
256
|
+
background-color: var(--color-bg-muted);
|
|
257
|
+
border-color: var(--color-border);
|
|
258
|
+
box-shadow: none;
|
|
259
|
+
}
|
|
260
|
+
.form-check-label {
|
|
261
|
+
cursor: pointer;
|
|
262
|
+
font-size: 14px;
|
|
263
|
+
line-height: 1.4;
|
|
264
|
+
color: var(--color-text);
|
|
265
|
+
user-select: none;
|
|
266
|
+
}
|
|
267
|
+
.form-check-input:disabled ~ .form-check-label {
|
|
268
|
+
cursor: not-allowed;
|
|
269
|
+
color: var(--color-text-subtle);
|
|
270
|
+
}
|
|
271
|
+
// Switch — a pill track with a sliding white knob.
|
|
272
|
+
.form-switch {
|
|
273
|
+
padding-left: 0;
|
|
274
|
+
}
|
|
275
|
+
.form-switch .form-check-input {
|
|
276
|
+
width: 40px;
|
|
277
|
+
height: 22px;
|
|
278
|
+
border-radius: 999px;
|
|
279
|
+
border-color: transparent;
|
|
280
|
+
background-color: var(--color-border-strong);
|
|
281
|
+
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");
|
|
282
|
+
background-position: left center;
|
|
283
|
+
background-size: 18px 18px;
|
|
284
|
+
}
|
|
285
|
+
.form-switch .form-check-input:hover:not(:disabled):not(:checked) {
|
|
286
|
+
border-color: transparent;
|
|
287
|
+
box-shadow: 0 0 0 4px var(--color-primary-soft);
|
|
288
|
+
}
|
|
289
|
+
.form-switch .form-check-input:checked {
|
|
290
|
+
border-color: transparent;
|
|
291
|
+
background-color: var(--color-primary);
|
|
292
|
+
background-position: right center;
|
|
293
|
+
// Re-assert the knob — the checkbox :checked tick has equal specificity and would
|
|
294
|
+
// otherwise win, turning the switch into a ticked box.
|
|
295
|
+
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");
|
|
296
|
+
}
|
|
297
|
+
@media (prefers-reduced-motion: reduce) {
|
|
298
|
+
.form-check-input {
|
|
299
|
+
transition: none;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
175
303
|
// ── Field / Input ──
|
|
176
304
|
// Inputs — always-white fill, 16px squircle, soft brand border tint on focus
|
|
177
305
|
// (no glow).
|
|
@@ -268,17 +268,86 @@
|
|
|
268
268
|
background: transparent;
|
|
269
269
|
color: var(--color-text-subtle);
|
|
270
270
|
cursor: pointer;
|
|
271
|
+
// `visibility`, not opacity alone: an opacity-0 button keeps its place in the tab order,
|
|
272
|
+
// so an empty field handed a keyboard user an invisible "Clear search" that cleared
|
|
273
|
+
// nothing. Delayed on the way out so the fade still plays, immediate on the way in.
|
|
271
274
|
opacity: 0;
|
|
275
|
+
visibility: hidden;
|
|
272
276
|
pointer-events: none;
|
|
273
277
|
transition:
|
|
274
278
|
opacity 0.12s ease,
|
|
279
|
+
visibility 0s linear 0.12s,
|
|
275
280
|
color 0.12s ease;
|
|
276
281
|
}
|
|
277
|
-
.
|
|
278
|
-
|
|
282
|
+
// Revealed by CONTENT only. Revealing on :focus-within as well put a ✕ on an empty Razor
|
|
283
|
+
// field while React — which renders the button only when there is a value — showed none:
|
|
284
|
+
// the same component disagreeing across surfaces, which is the one thing this library exists
|
|
285
|
+
// to prevent.
|
|
286
|
+
.search-wrap.has-value .search-clear {
|
|
279
287
|
opacity: 1;
|
|
288
|
+
visibility: visible;
|
|
280
289
|
pointer-events: auto;
|
|
290
|
+
transition:
|
|
291
|
+
opacity 0.12s ease,
|
|
292
|
+
visibility 0s,
|
|
293
|
+
color 0.12s ease;
|
|
281
294
|
}
|
|
282
295
|
.search-clear:hover {
|
|
283
296
|
color: var(--color-text);
|
|
284
297
|
}
|
|
298
|
+
// type=search draws the browser's own cancel ✕; the component ships its own .search-clear,
|
|
299
|
+
// so the native one would double up. Remove it (WebKit/Blink).
|
|
300
|
+
.search-input::-webkit-search-cancel-button,
|
|
301
|
+
.search-input::-webkit-search-decoration {
|
|
302
|
+
-webkit-appearance: none;
|
|
303
|
+
appearance: none;
|
|
304
|
+
display: none;
|
|
305
|
+
}
|
|
306
|
+
// Sizes — sm / lg around the default, mirroring AppInput. The leading icon and clear button
|
|
307
|
+
// track the height so they stay centred and clear of the text.
|
|
308
|
+
.search-wrap.search-sm .search-input {
|
|
309
|
+
height: var(--wui-control-height-sm);
|
|
310
|
+
padding-left: 34px;
|
|
311
|
+
padding-right: 32px;
|
|
312
|
+
font-size: 13px;
|
|
313
|
+
}
|
|
314
|
+
.search-wrap.search-sm > .search-icon,
|
|
315
|
+
.search-wrap.search-sm .search-spinner {
|
|
316
|
+
left: 11px;
|
|
317
|
+
}
|
|
318
|
+
.search-wrap.search-sm .search-clear {
|
|
319
|
+
width: 22px;
|
|
320
|
+
height: 22px;
|
|
321
|
+
right: 6px;
|
|
322
|
+
}
|
|
323
|
+
.search-wrap.search-lg .search-input {
|
|
324
|
+
height: var(--wui-control-height-lg);
|
|
325
|
+
padding-left: 46px;
|
|
326
|
+
padding-right: 40px;
|
|
327
|
+
font-size: 16px;
|
|
328
|
+
}
|
|
329
|
+
.search-wrap.search-lg > .search-icon,
|
|
330
|
+
.search-wrap.search-lg .search-spinner {
|
|
331
|
+
left: 16px;
|
|
332
|
+
}
|
|
333
|
+
// Loading — a spinner replaces the leading glyph while a search is in flight.
|
|
334
|
+
.search-wrap.is-loading > .search-icon {
|
|
335
|
+
display: none;
|
|
336
|
+
}
|
|
337
|
+
.search-spinner {
|
|
338
|
+
position: absolute;
|
|
339
|
+
left: 13px;
|
|
340
|
+
top: 50%;
|
|
341
|
+
margin-top: -9px;
|
|
342
|
+
width: 18px;
|
|
343
|
+
height: 18px;
|
|
344
|
+
border: 2px solid var(--color-border-strong);
|
|
345
|
+
border-top-color: var(--color-primary);
|
|
346
|
+
border-radius: 50%;
|
|
347
|
+
animation: lx-spin 0.7s linear infinite;
|
|
348
|
+
pointer-events: none;
|
|
349
|
+
}
|
|
350
|
+
.search-input:disabled {
|
|
351
|
+
cursor: not-allowed;
|
|
352
|
+
opacity: 0.6;
|
|
353
|
+
}
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
|
|
222
222
|
/* ── Multi-select (chips in the control; check-list dropdown; extends .search-select) ── */
|
|
223
223
|
.search-select-multi {
|
|
224
|
-
align-items:
|
|
224
|
+
align-items: center;
|
|
225
225
|
gap: 6px;
|
|
226
226
|
min-height: 40px;
|
|
227
227
|
height: auto;
|
|
@@ -237,7 +237,21 @@
|
|
|
237
237
|
min-width: 0;
|
|
238
238
|
align-items: center;
|
|
239
239
|
}
|
|
240
|
-
|
|
240
|
+
// Collapse mode (collapse-chips): chips stay on one row; the JS folds the overflow into
|
|
241
|
+
// a "+N" counter (see WuiMultiSelect._collapseChips).
|
|
242
|
+
wui-multiselect[collapse-chips] .ms-chips {
|
|
243
|
+
flex-wrap: nowrap;
|
|
244
|
+
overflow: hidden;
|
|
245
|
+
}
|
|
246
|
+
.chip-more {
|
|
247
|
+
flex-shrink: 0;
|
|
248
|
+
background: var(--color-primary-soft);
|
|
249
|
+
border-color: transparent;
|
|
250
|
+
color: var(--color-primary);
|
|
251
|
+
font-weight: 600;
|
|
252
|
+
}
|
|
253
|
+
.search-select-multi .search-select-caret,
|
|
254
|
+
.search-select-multi .search-select-icon {
|
|
241
255
|
align-self: center;
|
|
242
256
|
}
|
|
243
257
|
.ms-check {
|
|
@@ -253,9 +267,11 @@
|
|
|
253
267
|
opacity: 1;
|
|
254
268
|
}
|
|
255
269
|
.ms-opt-label {
|
|
270
|
+
flex: 1;
|
|
256
271
|
min-width: 0;
|
|
257
272
|
overflow: hidden;
|
|
258
273
|
text-overflow: ellipsis;
|
|
274
|
+
text-align: left;
|
|
259
275
|
}
|
|
260
276
|
.search-select-multi .search-select-option {
|
|
261
277
|
display: flex;
|