@webority/theme 0.6.11 → 0.6.13

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.6.11",
3
+ "version": "0.6.13",
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": {
@@ -427,14 +427,47 @@
427
427
  // styled).
428
428
  .datepicker {
429
429
  position: relative;
430
+ // A date is one short value, so the field defaults to a fixed width instead of filling its
431
+ // column. Override per use with a .date-w-* class or the --wui-datepicker-width variable;
432
+ // 100% restores the old full-width, form-aligned behaviour.
433
+ width: var(--wui-datepicker-width, 15rem);
434
+ max-width: 100%;
430
435
  }
431
436
  .datepicker-trigger {
437
+ display: flex;
438
+ align-items: center;
439
+ justify-content: space-between;
440
+ gap: 8px;
432
441
  text-align: left;
433
442
  cursor: pointer;
434
443
  }
444
+ // The label takes the room; the calendar glyph is pinned right and never shrinks.
445
+ .datepicker-trigger [data-trigger-label] {
446
+ overflow: hidden;
447
+ white-space: nowrap;
448
+ text-overflow: ellipsis;
449
+ }
450
+ .datepicker-trigger > .bi {
451
+ flex-shrink: 0;
452
+ color: var(--color-text-subtle);
453
+ }
435
454
  .datepicker-trigger:not(.has-value) {
436
455
  color: var(--color-text-subtle);
437
456
  }
457
+ // Width presets — set the variable on the field (or the bare control) and it cascades down
458
+ // to .datepicker. Consumers apply these through the component's className / class.
459
+ .date-w-sm {
460
+ --wui-datepicker-width: 11rem;
461
+ }
462
+ .date-w-md {
463
+ --wui-datepicker-width: 15rem;
464
+ }
465
+ .date-w-lg {
466
+ --wui-datepicker-width: 20rem;
467
+ }
468
+ .date-w-full {
469
+ --wui-datepicker-width: 100%;
470
+ }
438
471
  .datepicker.open .datepicker-trigger {
439
472
  background-color: var(--color-bg-muted);
440
473
  border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
@@ -486,10 +519,25 @@
486
519
  margin-bottom: 14px;
487
520
  }
488
521
  .datepicker-month {
522
+ display: inline-flex;
523
+ align-items: center;
524
+ gap: 6px;
525
+ border: none;
526
+ background: transparent;
527
+ padding: 6px 12px;
528
+ border-radius: 999px;
489
529
  font-size: 15px;
490
530
  font-weight: 600;
491
531
  color: var(--color-text);
492
532
  letter-spacing: -0.01em;
533
+ cursor: pointer;
534
+ transition:
535
+ background 0.14s,
536
+ color 0.14s;
537
+ }
538
+ button.datepicker-month:hover {
539
+ background: var(--color-primary-soft, color-mix(in srgb, var(--color-primary) 8%, transparent));
540
+ color: var(--color-primary);
493
541
  }
494
542
  .datepicker-nav {
495
543
  display: inline-flex;
@@ -613,6 +661,210 @@
613
661
  cursor: not-allowed;
614
662
  }
615
663
 
664
+ // Month / year drill-down grids — the header opens these, then drills back to days.
665
+ .datepicker-cells {
666
+ display: grid;
667
+ grid-template-columns: repeat(3, 1fr);
668
+ gap: 6px;
669
+ animation: datepicker-view-in 0.18s ease;
670
+ }
671
+ .datepicker-cell {
672
+ display: inline-flex;
673
+ align-items: center;
674
+ justify-content: center;
675
+ height: 48px;
676
+ border: none;
677
+ border-radius: var(--radius-lg, 12px);
678
+ background: transparent;
679
+ font-size: 13px;
680
+ font-weight: 500;
681
+ color: var(--color-text);
682
+ cursor: pointer;
683
+ transition:
684
+ background 0.12s,
685
+ color 0.12s,
686
+ box-shadow 0.12s;
687
+ }
688
+ .datepicker-cell:hover:not(:disabled):not(.is-selected) {
689
+ background: var(--color-primary-soft, color-mix(in srgb, var(--color-primary) 8%, transparent));
690
+ color: var(--color-primary);
691
+ }
692
+ .datepicker-cell.is-today {
693
+ box-shadow: inset 0 0 0 1.5px var(--color-primary);
694
+ color: var(--color-primary);
695
+ font-weight: 700;
696
+ }
697
+ .datepicker-cell.is-selected {
698
+ background: var(--color-primary);
699
+ color: #fff;
700
+ font-weight: 600;
701
+ }
702
+ .datepicker-cell:disabled {
703
+ color: var(--color-border-strong);
704
+ cursor: not-allowed;
705
+ }
706
+
707
+ // Keyboard focus ring — days and drill-down cells share it, drawn as a halo so it reads on
708
+ // both the transparent and the filled (selected) states.
709
+ .datepicker-day:focus-visible,
710
+ .datepicker-cell:focus-visible {
711
+ outline: none;
712
+ box-shadow:
713
+ 0 0 0 2px var(--color-surface),
714
+ 0 0 0 4px var(--color-primary);
715
+ }
716
+
717
+ // The panel view fades+scales in on every drill; each month nav slides in from its direction.
718
+ @keyframes datepicker-view-in {
719
+ from {
720
+ opacity: 0;
721
+ transform: scale(0.96);
722
+ }
723
+ to {
724
+ opacity: 1;
725
+ transform: scale(1);
726
+ }
727
+ }
728
+ .datepicker-grid.slide-next {
729
+ animation: datepicker-slide-next 0.2s ease;
730
+ }
731
+ .datepicker-grid.slide-prev {
732
+ animation: datepicker-slide-prev 0.2s ease;
733
+ }
734
+ @keyframes datepicker-slide-next {
735
+ from {
736
+ opacity: 0.35;
737
+ transform: translateX(10px);
738
+ }
739
+ to {
740
+ opacity: 1;
741
+ transform: translateX(0);
742
+ }
743
+ }
744
+ @keyframes datepicker-slide-prev {
745
+ from {
746
+ opacity: 0.35;
747
+ transform: translateX(-10px);
748
+ }
749
+ to {
750
+ opacity: 1;
751
+ transform: translateX(0);
752
+ }
753
+ }
754
+ @media (prefers-reduced-motion: reduce) {
755
+ .datepicker-pop,
756
+ .datepicker-cells,
757
+ .datepicker-grid.slide-next,
758
+ .datepicker-grid.slide-prev {
759
+ animation: none;
760
+ }
761
+ }
762
+
763
+ // Date RANGE — start/end field with a two-month popover. Reuses the .datepicker-day cell
764
+ // language; adds the start / in-range / end painting on top.
765
+ .daterange {
766
+ position: relative;
767
+ // Wider default than a single date, but honours the same --wui-datepicker-width var and
768
+ // .date-w-* presets so both range and single fields size the same way.
769
+ width: var(--wui-datepicker-width, 20rem);
770
+ max-width: 100%;
771
+ }
772
+ .daterange-trigger {
773
+ display: flex;
774
+ align-items: center;
775
+ justify-content: space-between;
776
+ gap: 8px;
777
+ text-align: left;
778
+ cursor: pointer;
779
+ }
780
+ .daterange-trigger [data-range-label] {
781
+ overflow: hidden;
782
+ white-space: nowrap;
783
+ text-overflow: ellipsis;
784
+ }
785
+ .daterange-trigger > .bi {
786
+ flex-shrink: 0;
787
+ color: var(--color-text-subtle);
788
+ }
789
+ .daterange-trigger:not(.has-value) {
790
+ color: var(--color-text-subtle);
791
+ }
792
+ .daterange.open .daterange-trigger {
793
+ background-color: var(--color-bg-muted);
794
+ border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
795
+ box-shadow: none;
796
+ }
797
+ .daterange-pop {
798
+ position: fixed;
799
+ z-index: 1600;
800
+ width: 620px;
801
+ max-width: calc(100vw - 16px);
802
+ padding: 18px;
803
+ background: var(--color-surface);
804
+ border: 1px solid var(--color-border);
805
+ border-radius: var(--radius-xl);
806
+ corner-shape: squircle;
807
+ box-shadow: var(--shadow-overlay);
808
+ animation: datepicker-in 0.14s ease;
809
+ }
810
+ .daterange-pop.drop-up {
811
+ animation: datepicker-in-up 0.14s ease;
812
+ }
813
+ // The two nav buttons bracket the calendars — one at each edge of the header row.
814
+ .daterange-head {
815
+ display: flex;
816
+ align-items: center;
817
+ justify-content: space-between;
818
+ margin-bottom: 6px;
819
+ }
820
+ .daterange-cals {
821
+ display: flex;
822
+ gap: 24px;
823
+ }
824
+ .daterange-cal {
825
+ flex: 1;
826
+ min-width: 0;
827
+ }
828
+ .datepicker-month-label {
829
+ text-align: center;
830
+ font-size: 15px;
831
+ font-weight: 600;
832
+ color: var(--color-text);
833
+ letter-spacing: -0.01em;
834
+ margin-bottom: 12px;
835
+ }
836
+ // Days inside the chosen span get a soft rail; the two endpoints get the solid fill.
837
+ .datepicker-day.in-range {
838
+ background: var(--color-primary-soft, color-mix(in srgb, var(--color-primary) 12%, transparent));
839
+ color: var(--color-primary);
840
+ border-radius: 6px;
841
+ }
842
+ .datepicker-day.is-range-start,
843
+ .datepicker-day.is-range-end {
844
+ background: var(--color-primary);
845
+ color: #fff;
846
+ font-weight: 600;
847
+ }
848
+ .daterange-hint {
849
+ font-size: 12px;
850
+ color: var(--color-text-subtle);
851
+ }
852
+ @media (max-width: 660px) {
853
+ // Below the two-month width, stack the calendars so the popover still fits the viewport.
854
+ .daterange-pop {
855
+ width: 320px;
856
+ }
857
+ .daterange-cals {
858
+ flex-direction: column;
859
+ gap: 16px;
860
+ }
861
+ }
862
+ @media (prefers-reduced-motion: reduce) {
863
+ .daterange-pop {
864
+ animation: none;
865
+ }
866
+ }
867
+
616
868
  // ── Phone input ──
617
869
  .app-phone-number-input {
618
870
  width: 100%;
@@ -630,6 +882,10 @@
630
882
  border-radius: var(--radius);
631
883
  corner-shape: squircle;
632
884
  background: var(--color-surface);
885
+ // Clip the inner control to the shell's rounded shape — a full-height child was painting
886
+ // over the squircle border at the bottom, which read as a cut-off bottom edge. The dial
887
+ // dropdown is position:fixed, so it is unaffected by this.
888
+ overflow: hidden;
633
889
  transition:
634
890
  border-color 0.12s,
635
891
  background-color 0.12s;
@@ -638,9 +894,10 @@
638
894
  background: var(--color-bg-muted);
639
895
  border-color: var(--color-border-strong);
640
896
  }
897
+ // Match AppInput's focus: a soft primary border, not a full-strength one.
641
898
  .app-phone-number-input .phone-input-wrap:focus-within {
642
899
  background: var(--color-bg-muted);
643
- border-color: var(--color-primary);
900
+ border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
644
901
  }
645
902
  .app-phone-number-input .phone-input-wrap:has(> .input:disabled) {
646
903
  background: var(--color-bg-muted);
@@ -781,9 +1038,10 @@
781
1038
  border-color: var(--color-border-strong);
782
1039
  }
783
1040
  // Border is the focus cue everywhere else in the system — no glow ring here.
1041
+ // Soft primary, matching AppInput / the phone field shell rather than a full-strength border.
784
1042
  .dropdown-search-input:focus {
785
1043
  background: var(--color-bg-muted);
786
- border-color: var(--color-primary);
1044
+ border-color: color-mix(in srgb, var(--color-primary) 30%, transparent);
787
1045
  }
788
1046
  .country-list-scroll {
789
1047
  max-height: 250px;
@@ -821,6 +1079,12 @@
821
1079
  background 0.12s ease,
822
1080
  color 0.12s ease;
823
1081
  }
1082
+ // The search hides non-matches with the `hidden` attribute. `display: flex !important` above
1083
+ // beats the UA `[hidden] { display: none }`, so filtered-out countries stayed visible — the
1084
+ // search looked broken. Restore the hide with matching precedence.
1085
+ .country-dropdown-item[hidden] {
1086
+ display: none !important;
1087
+ }
824
1088
  .country-dropdown-item:hover,
825
1089
  .country-dropdown-item:focus {
826
1090
  background: var(--color-primary-soft) !important;
@@ -866,7 +866,8 @@ wui-select,
866
866
  wui-autocomplete,
867
867
  wui-multiselect,
868
868
  wui-phone,
869
- wui-datepicker {
869
+ wui-datepicker,
870
+ wui-date-range {
870
871
  display: block;
871
872
  width: 100%;
872
873
  }
@@ -882,6 +883,7 @@ wui-select[compact] {
882
883
  .field > wui-multiselect,
883
884
  .field > wui-phone,
884
885
  .field > wui-datepicker,
886
+ .field > wui-date-range,
885
887
  .field > .search-select,
886
888
  .field > .input-with-unit,
887
889
  .field > .input,
@@ -163,6 +163,7 @@ a.info-row:hover .info-value {
163
163
  // The theme toggle (light/dark/system) is the canonical use.
164
164
  // ============================================================================
165
165
  .segmented {
166
+ position: relative;
166
167
  display: inline-flex;
167
168
  gap: 2px;
168
169
  padding: 3px;
@@ -171,6 +172,8 @@ a.info-row:hover .info-value {
171
172
  background: var(--color-bg-muted);
172
173
  }
173
174
  .segmented-option {
175
+ position: relative;
176
+ z-index: 1;
174
177
  display: inline-flex;
175
178
  align-items: center;
176
179
  gap: 5px;
@@ -187,7 +190,12 @@ a.info-row:hover .info-value {
187
190
  cursor: pointer;
188
191
  transition:
189
192
  background 0.14s ease,
190
- color 0.14s ease;
193
+ color 0.14s ease,
194
+ transform 0.12s ease;
195
+ }
196
+ // A quick press-in on click — a small, tactile "give".
197
+ .segmented-option:active {
198
+ transform: scale(0.95);
191
199
  }
192
200
  // The real radio stays in the accessibility tree and keeps focus — only its box is
193
201
  // hidden. `display: none` would drop it from the tab order and break arrow-key
@@ -216,12 +224,75 @@ a.info-row:hover .info-value {
216
224
  outline: 2px solid var(--color-primary);
217
225
  outline-offset: 2px;
218
226
  }
227
+ // Sliding thumb — one pill that glides to the selected segment. Positioned by JS (React in a
228
+ // layout effect, Razor in webority-ui.js), which adds .is-animated once it has measured the
229
+ // first segment. Without JS the per-option fill above stands in, so nothing looks broken.
230
+ .segmented-thumb {
231
+ position: absolute;
232
+ top: 3px;
233
+ bottom: 3px;
234
+ left: 0;
235
+ width: 0;
236
+ z-index: 0;
237
+ border-radius: 999px;
238
+ background: var(--color-primary);
239
+ box-shadow: 0 2px 8px color-mix(in srgb, var(--color-primary) 32%, transparent);
240
+ opacity: 0;
241
+ pointer-events: none;
242
+ }
243
+ .segmented.is-animated .segmented-thumb {
244
+ opacity: 1;
245
+ // A spring easing that overshoots a touch and settles — livelier than a flat slide.
246
+ transition:
247
+ transform 0.36s cubic-bezier(0.34, 1.42, 0.5, 1),
248
+ width 0.36s cubic-bezier(0.34, 1.42, 0.5, 1);
249
+ }
250
+ // While the thumb draws the fill, the checked option drops its own background (keeps white text).
251
+ .segmented.is-animated .segmented-option:has(.segmented-input:checked) {
252
+ background: transparent;
253
+ }
254
+ // Icon pop — the selected segment's glyph springs up a touch as it becomes active.
255
+ .segmented-option:has(.segmented-input:checked) .bi {
256
+ transform: scale(1.14);
257
+ transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
258
+ }
259
+ .segmented-sm .segmented-thumb,
260
+ .segmented-lg .segmented-thumb {
261
+ top: 3px;
262
+ bottom: 3px;
263
+ }
219
264
  .segmented-sm .segmented-option {
220
265
  padding: 4px 9px;
221
266
  font-size: 11px;
222
267
  }
268
+ .segmented-lg .segmented-option {
269
+ gap: 6px;
270
+ padding: 8px 16px;
271
+ font-size: 13px;
272
+ }
273
+ // Full width — segments share the row equally (a toggle spanning a card or toolbar).
274
+ .segmented-full {
275
+ display: flex;
276
+ width: 100%;
277
+ }
278
+ .segmented-full .segmented-option {
279
+ flex: 1;
280
+ justify-content: center;
281
+ }
282
+ // Disabled — the whole control, or a single option that stays visible but unpickable.
283
+ .segmented.is-disabled {
284
+ opacity: 0.55;
285
+ pointer-events: none;
286
+ }
287
+ .segmented-option:has(.segmented-input:disabled) {
288
+ opacity: 0.45;
289
+ cursor: not-allowed;
290
+ }
223
291
  @media (prefers-reduced-motion: reduce) {
224
- .segmented-option {
292
+ // The thumb still jumps to the selected segment — it just does not slide, pop or press.
293
+ .segmented-option,
294
+ .segmented.is-animated .segmented-thumb,
295
+ .segmented-option:has(.segmented-input:checked) .bi {
225
296
  transition: none;
226
297
  }
227
298
  }
@@ -267,6 +338,17 @@ a.info-row:hover .info-value {
267
338
  font-family: var(--bs-font-monospace);
268
339
  text-transform: uppercase;
269
340
  }
341
+ // Size steps mirror AppInput's .input-sm / .input-lg (which the hex box also carries).
342
+ // The swatch tracks the control height per size, less the 4px inset the md swatch already
343
+ // keeps against the 44px field, so the chip stays visually inset at every size.
344
+ .color-field-sm .color-field-swatch {
345
+ width: calc(var(--wui-control-height-sm) - 4px);
346
+ height: calc(var(--wui-control-height-sm) - 4px);
347
+ }
348
+ .color-field-lg .color-field-swatch {
349
+ width: calc(var(--wui-control-height-lg) - 4px);
350
+ height: calc(var(--wui-control-height-lg) - 4px);
351
+ }
270
352
 
271
353
  // ============================================================================
272
354
  // Timeline / activity feed. The rail is drawn on the LIST, not per item, so it