@webority/theme 0.29.0 → 0.30.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.29.0",
3
+ "version": "0.30.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": {
@@ -579,8 +579,7 @@
579
579
  // shut, the trigger keeps the focus look only for keyboard focus (:focus-visible); a pick or
580
580
  // close by pointer returns it to rest. An errored field (aria-invalid on the host) keeps its red.
581
581
  wui-datepicker:not([aria-invalid="true"]) .datepicker:not(.open) .datepicker-trigger:focus:not(:focus-visible),
582
- wui-date-range:not([aria-invalid="true"]) .daterange:not(.open) .daterange-trigger:focus:not(:focus-visible),
583
- wui-timepicker:not([aria-invalid="true"]) .timepicker:not(.open) .timepicker-trigger:focus:not(:focus-visible) {
582
+ wui-date-range:not([aria-invalid="true"]) .daterange:not(.open) .daterange-trigger:focus:not(:focus-visible) {
584
583
  background-color: var(--color-field-bg);
585
584
  border-color: var(--color-border);
586
585
  }
@@ -1350,37 +1349,56 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1350
1349
  font-size: 14px;
1351
1350
  }
1352
1351
 
1353
- // timepicker — time-of-day field with listbox popover (min/max bounds, step granularity, 12/24 display).
1352
+ // timepicker — time-of-day text field (type "7:45 pm") with a clock button that opens hour /
1353
+ // minute / AM-PM columns (min/max bounds, step granularity, 12/24 display).
1354
1354
  .timepicker {
1355
1355
  position: relative;
1356
1356
  width: var(--wui-timepicker-width, 12rem);
1357
1357
  max-width: 100%;
1358
1358
  }
1359
+ .wui-timepicker-field {
1360
+ position: relative;
1361
+ }
1362
+ // The input leaves room on the right for the clock button laid over it.
1359
1363
  .timepicker-trigger {
1360
- display: flex;
1361
- align-items: center;
1362
- justify-content: space-between;
1363
- gap: 8px;
1364
- text-align: left;
1365
- cursor: pointer;
1364
+ padding-right: 44px;
1366
1365
  }
1367
- .timepicker-trigger [data-trigger-label] {
1368
- overflow: hidden;
1369
- white-space: nowrap;
1370
- text-overflow: ellipsis;
1366
+ .timepicker-trigger.input-sm {
1367
+ padding-right: 36px;
1371
1368
  }
1372
- .timepicker-trigger > .wui-icon {
1373
- flex-shrink: 0;
1369
+ .wui-timepicker-toggle {
1370
+ position: absolute;
1371
+ top: 0;
1372
+ right: 0;
1373
+ bottom: 0;
1374
+ display: grid;
1375
+ place-items: center;
1376
+ width: 44px;
1377
+ padding: 0;
1378
+ border: none;
1379
+ border-radius: 0 var(--wui-field-radius) var(--wui-field-radius) 0;
1380
+ background: transparent;
1374
1381
  color: var(--color-text-subtle);
1382
+ cursor: pointer;
1383
+ transition: color 0.12s ease;
1375
1384
  }
1376
- // The label span carries this while nothing is selected — same dim ink the trigger's
1377
- // :not(.has-value) fallback uses, kept as its own rule so the span reads as placeholder
1378
- // even when the trigger classes are toggled independently.
1379
- .timepicker-placeholder {
1380
- color: var(--color-text-subtle);
1385
+ .timepicker-trigger.input-sm + .wui-timepicker-toggle {
1386
+ width: 36px;
1381
1387
  }
1382
- .timepicker-trigger:not(.has-value) {
1383
- color: var(--color-text-subtle);
1388
+ .wui-timepicker-toggle:hover:not(:disabled),
1389
+ .timepicker.open .wui-timepicker-toggle {
1390
+ color: var(--color-primary);
1391
+ }
1392
+ .wui-timepicker-toggle:focus-visible {
1393
+ outline: 2px solid var(--color-primary);
1394
+ outline-offset: -4px;
1395
+ }
1396
+ .wui-timepicker-toggle:disabled {
1397
+ cursor: not-allowed;
1398
+ }
1399
+ // The control's own message for typed text it refuses; the shell's .field-error sits below it.
1400
+ .wui-timepicker-error {
1401
+ margin-top: 4px;
1384
1402
  }
1385
1403
  // Width presets.
1386
1404
  .time-w-sm {
@@ -1396,18 +1414,21 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1396
1414
  --wui-timepicker-width: 100%;
1397
1415
  }
1398
1416
  .timepicker.open .timepicker-trigger {
1399
- // Stay white while the time list is open — see the note on .datepicker.open above.
1417
+ // Stay white while the columns are open — see the note on .datepicker.open above.
1400
1418
  background-color: var(--color-surface);
1401
1419
  border-color: var(--color-focus-border);
1402
1420
  box-shadow: none;
1403
1421
  }
1422
+ // Refused typed text keeps the soft danger border while the columns are open, as the shell's
1423
+ // host-level aria-invalid already does.
1424
+ .timepicker.open .timepicker-trigger[aria-invalid="true"] {
1425
+ border-color: color-mix(in srgb, var(--color-danger) 30%, transparent);
1426
+ }
1404
1427
  .timepicker-pop {
1405
1428
  position: fixed;
1406
1429
  z-index: var(--wui-z-menu);
1407
- width: 180px;
1430
+ width: max-content;
1408
1431
  max-width: calc(100vw - 16px);
1409
- max-height: 360px;
1410
- overflow-y: auto;
1411
1432
  padding: 8px;
1412
1433
  background: var(--color-surface);
1413
1434
  border: 1px solid var(--color-border);
@@ -1439,14 +1460,37 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1439
1460
  transform: translateY(0);
1440
1461
  }
1441
1462
  }
1463
+ .wui-timepicker-cols {
1464
+ display: flex;
1465
+ gap: 4px;
1466
+ }
1467
+ // Each column scrolls on its own; overscroll stays inside so a flick never scrolls the page.
1468
+ .wui-timepicker-col {
1469
+ position: relative;
1470
+ display: flex;
1471
+ flex-direction: column;
1472
+ gap: 2px;
1473
+ min-width: 56px;
1474
+ max-height: 264px;
1475
+ overflow-y: auto;
1476
+ overscroll-behavior: contain;
1477
+ scrollbar-width: thin;
1478
+ }
1479
+ .wui-timepicker-col + .wui-timepicker-col {
1480
+ padding-left: 4px;
1481
+ border-left: 1px solid var(--color-border);
1482
+ }
1442
1483
  .timepicker-option {
1443
1484
  display: block;
1485
+ flex-shrink: 0;
1444
1486
  width: 100%;
1445
- padding: 8px 12px;
1487
+ min-height: 36px;
1488
+ padding: 6px 12px;
1446
1489
  border: none;
1447
1490
  background: transparent;
1448
- text-align: left;
1491
+ text-align: center;
1449
1492
  font-size: 14px;
1493
+ font-variant-numeric: tabular-nums;
1450
1494
  color: var(--color-text);
1451
1495
  cursor: pointer;
1452
1496
  border-radius: var(--radius-sm);
@@ -1454,7 +1498,13 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1454
1498
  background 0.1s,
1455
1499
  color 0.1s;
1456
1500
  }
1457
- .timepicker-option:hover,
1501
+ // Touch screens get full 44px rows.
1502
+ @media (pointer: coarse) {
1503
+ .timepicker-option {
1504
+ min-height: 44px;
1505
+ }
1506
+ }
1507
+ .timepicker-option:hover:not(:disabled),
1458
1508
  .timepicker-option.is-active {
1459
1509
  background: var(--color-primary-soft, color-mix(in srgb, var(--color-primary) 8%, transparent));
1460
1510
  color: var(--color-primary);
@@ -1464,6 +1514,11 @@ wui-timepicker[aria-invalid="true"] .timepicker.open .timepicker-trigger {
1464
1514
  color: var(--color-primary-text);
1465
1515
  font-weight: 600;
1466
1516
  }
1517
+ .timepicker-option:disabled {
1518
+ color: var(--color-text-subtle);
1519
+ opacity: 0.5;
1520
+ cursor: not-allowed;
1521
+ }
1467
1522
  .timepicker-option:focus-visible {
1468
1523
  outline: none;
1469
1524
  box-shadow: inset 0 0 0 2px var(--color-primary);
@@ -7,12 +7,20 @@
7
7
  // Below md the board is one column per screen: a scroll-snapped strip with a column switcher
8
8
  // above it. The board scrolls inside itself, so the page never scrolls sideways.
9
9
  //
10
+ // Sizing variables, set on .wui-kanban (or any ancestor) to override:
11
+ // --wui-kanban-column-width basis width of an open column (288px)
12
+ // --wui-kanban-collapsed-width width of a collapsed column strip (56px)
13
+ // --wui-kanban-gap space between columns (16px)
14
+ // --wui-kanban-column-min-height least height of a column, so short columns still read as
15
+ // lanes (560px; not applied in the phone one-column mode)
16
+ //
10
17
  // Part of the wui-components layer, see _wui-components.scss for load order.
11
18
 
12
19
  .wui-kanban {
13
20
  --wui-kanban-column-width: 288px;
14
21
  --wui-kanban-collapsed-width: 56px;
15
22
  --wui-kanban-gap: 16px;
23
+ --wui-kanban-column-min-height: 560px;
16
24
  display: block;
17
25
  position: relative;
18
26
  min-width: 0;
@@ -34,8 +42,9 @@
34
42
  flex: 1 0 var(--wui-kanban-column-width);
35
43
  min-width: 0;
36
44
  max-width: 100%;
37
- background: var(--color-bg-subtle);
38
- border: 1px solid var(--color-border-light);
45
+ min-height: var(--wui-kanban-column-min-height);
46
+ background: var(--color-bg-muted);
47
+ border: 1px solid var(--color-border);
39
48
  border-radius: var(--wui-radius-lg);
40
49
  transition:
41
50
  background-color 160ms var(--wui-ease-ui),
@@ -74,12 +83,14 @@
74
83
  background: var(--color-danger);
75
84
  }
76
85
 
86
+ // The title takes its own width so the count sits right beside it; the collapse button keeps to
87
+ // the far edge through its own auto margin.
77
88
  .wui-kanban-title {
78
- flex: 1 1 auto;
89
+ flex: 0 1 auto;
79
90
  min-width: 0;
80
91
  margin: 0;
81
- font-size: 0.9375rem;
82
- font-weight: 600;
92
+ font-size: 0.875rem;
93
+ font-weight: 700;
83
94
  line-height: 1.3;
84
95
  color: var(--color-text);
85
96
  overflow-wrap: anywhere;
@@ -117,6 +128,7 @@
117
128
  flex: none;
118
129
  width: 32px;
119
130
  height: 32px;
131
+ margin-inline-start: auto;
120
132
  padding: 0;
121
133
  border: 0;
122
134
  border-radius: var(--wui-radius);
@@ -128,8 +140,9 @@
128
140
  transition: transform 160ms var(--wui-ease-ui);
129
141
  }
130
142
  }
143
+ // The column itself is the muted fill, so hover lifts to the surface.
131
144
  .wui-kanban-collapse:hover {
132
- background: var(--color-bg-muted);
145
+ background: var(--color-surface);
133
146
  color: var(--color-text);
134
147
  }
135
148
  .wui-kanban-collapse:focus-visible,
@@ -247,7 +260,11 @@
247
260
  margin: 0;
248
261
  pointer-events: none;
249
262
  cursor: grabbing;
250
- box-shadow: var(--wui-shadow-overlay);
263
+ // A 2px primary edge drawn as border plus ring, so the clone keeps the card's exact box.
264
+ border-color: var(--color-primary);
265
+ box-shadow:
266
+ 0 0 0 1px var(--color-primary),
267
+ var(--wui-shadow-overlay-lg);
251
268
  rotate: 2deg;
252
269
  }
253
270
  .wui-kanban[data-wui-dragging] {
@@ -267,17 +284,30 @@
267
284
  .wui-kanban-column[data-wui-drop="ok"] {
268
285
  border: 1px dashed var(--color-primary);
269
286
  }
287
+ // The column under the card: a 2px dashed primary edge (an inset outline over a hidden border,
288
+ // so nothing inside shifts) on the soft primary fill, and the note becomes a filled label bar.
270
289
  .wui-kanban-column[data-wui-drop="ok"][data-wui-over],
271
290
  .wui-kanban-column[data-wui-drop="ok"][data-wui-target] {
272
291
  background: var(--color-primary-soft);
273
- border-style: solid;
292
+ border-color: transparent;
293
+ outline: 2px dashed var(--color-primary);
294
+ outline-offset: -2px;
274
295
  }
275
296
  .wui-kanban-column[data-wui-drop="ok"] .wui-kanban-drop-note {
276
297
  color: var(--color-text);
277
298
  }
278
- // Refused: hatched and locked, with the reason in the note.
299
+ .wui-kanban-column[data-wui-drop="ok"][data-wui-over] .wui-kanban-drop-note,
300
+ .wui-kanban-column[data-wui-drop="ok"][data-wui-target] .wui-kanban-drop-note {
301
+ border-radius: var(--wui-radius-sm);
302
+ background: var(--color-primary);
303
+ color: var(--color-primary-text);
304
+ font-weight: 600;
305
+ }
306
+ // Refused: hatched and locked, with the reason in the note. The hatch is drawn over the subtle
307
+ // fill, since its stripes are the muted fill an open column already has.
279
308
  .wui-kanban-column[data-wui-drop="refused"] {
280
309
  border-color: var(--color-border);
310
+ background-color: var(--color-bg-subtle);
281
311
  background-image: repeating-linear-gradient(
282
312
  135deg,
283
313
  var(--color-bg-muted) 0 8px,
@@ -317,6 +347,7 @@
317
347
  }
318
348
  .wui-kanban-collapse {
319
349
  order: -1;
350
+ margin-inline-start: 0;
320
351
  }
321
352
  .wui-kanban-collapse svg {
322
353
  transform: rotate(180deg);
@@ -389,6 +420,7 @@
389
420
  .wui-kanban-column,
390
421
  .wui-kanban-column[data-collapsed] {
391
422
  flex: 0 0 100%;
423
+ min-height: 0;
392
424
  scroll-snap-align: start;
393
425
  align-self: flex-start;
394
426
 
@@ -399,7 +431,7 @@
399
431
  }
400
432
  .wui-kanban-title {
401
433
  writing-mode: horizontal-tb;
402
- flex: 1 1 auto;
434
+ flex: 0 1 auto;
403
435
  white-space: normal;
404
436
  }
405
437
  .wui-kanban-collapse {