@tiba-spark/client-shared-lib 26.3.0-619 → 26.3.0-621

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 (80) hide show
  1. package/esm2022/libraries/components/cloud-confirm-dialog/cloud-confirm-dialog.component.mjs +3 -3
  2. package/esm2022/libraries/components/common-pages/error-page/error-page.component.mjs +32 -7
  3. package/esm2022/libraries/components/field-label/field-label.component.mjs +39 -6
  4. package/esm2022/libraries/components/field-validations/field-validations.component.mjs +6 -5
  5. package/esm2022/libraries/components/guest-open-ticket-dialog/guest-open-ticket-dialog.component.mjs +2 -2
  6. package/esm2022/libraries/components/intercom/intercom.component.mjs +2 -2
  7. package/esm2022/libraries/components/logo/logo.component.mjs +3 -3
  8. package/esm2022/libraries/components/message-bar/float-message-bar.service.mjs +30 -6
  9. package/esm2022/libraries/components/modal-buttons-wrapper/modal-buttons-wrapper.component.mjs +9 -4
  10. package/esm2022/libraries/components/notification/alert-notification/alert-notification.component.mjs +3 -3
  11. package/esm2022/libraries/components/notification/command-notification/command-notification.component.mjs +3 -3
  12. package/esm2022/libraries/components/notification/notification-type-label/notification-type-label.component.mjs +2 -2
  13. package/esm2022/libraries/components/notification/notification.service.mjs +6 -3
  14. package/esm2022/libraries/components/overlay-loading-container/empty-results/empty-results.component.mjs +3 -3
  15. package/esm2022/libraries/components/overlay-loading-container/error-results/error-results.component.mjs +2 -2
  16. package/esm2022/libraries/components/restart-link/restart-link.component.mjs +2 -2
  17. package/esm2022/libraries/directives/clear-all-button.directive.mjs +49 -0
  18. package/esm2022/libraries/directives/disabled.directive.mjs +121 -0
  19. package/esm2022/libraries/directives/icon-button.directive.mjs +36 -0
  20. package/esm2022/libraries/directives/index.mjs +4 -1
  21. package/esm2022/libraries/directives/shared-directives.module.mjs +16 -1
  22. package/esm2022/libraries/enums/localization.enum.mjs +40 -1
  23. package/esm2022/libraries/enums/message-bar-duration.enum.mjs +4 -2
  24. package/esm2022/libraries/interfaces/table-column.interface.mjs +1 -1
  25. package/esm2022/libraries/modules/layout/layout.state.mjs +2 -2
  26. package/esm2022/libraries/services/can-deactivate.service.mjs +4 -3
  27. package/esm2022/libraries/services/modal.service.mjs +14 -1
  28. package/fesm2022/tiba-spark-client-shared-lib.mjs +402 -48
  29. package/fesm2022/tiba-spark-client-shared-lib.mjs.map +1 -1
  30. package/libraries/components/common-pages/error-page/error-page.component.d.ts +10 -2
  31. package/libraries/components/common-pages/error-page/error-page.component.d.ts.map +1 -1
  32. package/libraries/components/field-label/field-label.component.d.ts +8 -3
  33. package/libraries/components/field-label/field-label.component.d.ts.map +1 -1
  34. package/libraries/components/field-validations/field-validations.component.d.ts +0 -4
  35. package/libraries/components/field-validations/field-validations.component.d.ts.map +1 -1
  36. package/libraries/components/message-bar/float-message-bar.service.d.ts +2 -0
  37. package/libraries/components/message-bar/float-message-bar.service.d.ts.map +1 -1
  38. package/libraries/components/modal-buttons-wrapper/modal-buttons-wrapper.component.d.ts.map +1 -1
  39. package/libraries/components/notification/alert-notification/alert-notification.component.d.ts.map +1 -1
  40. package/libraries/components/notification/command-notification/command-notification.component.d.ts.map +1 -1
  41. package/libraries/components/notification/notification.service.d.ts.map +1 -1
  42. package/libraries/directives/clear-all-button.directive.d.ts +19 -0
  43. package/libraries/directives/clear-all-button.directive.d.ts.map +1 -0
  44. package/libraries/directives/disabled.directive.d.ts +30 -0
  45. package/libraries/directives/disabled.directive.d.ts.map +1 -0
  46. package/libraries/directives/icon-button.directive.d.ts +14 -0
  47. package/libraries/directives/icon-button.directive.d.ts.map +1 -0
  48. package/libraries/directives/index.d.ts +3 -0
  49. package/libraries/directives/index.d.ts.map +1 -1
  50. package/libraries/directives/shared-directives.module.d.ts +7 -4
  51. package/libraries/directives/shared-directives.module.d.ts.map +1 -1
  52. package/libraries/enums/localization.enum.d.ts +40 -1
  53. package/libraries/enums/localization.enum.d.ts.map +1 -1
  54. package/libraries/enums/message-bar-duration.enum.d.ts +3 -1
  55. package/libraries/enums/message-bar-duration.enum.d.ts.map +1 -1
  56. package/libraries/interfaces/table-column.interface.d.ts +1 -0
  57. package/libraries/interfaces/table-column.interface.d.ts.map +1 -1
  58. package/libraries/services/can-deactivate.service.d.ts.map +1 -1
  59. package/libraries/services/modal.service.d.ts +1 -0
  60. package/libraries/services/modal.service.d.ts.map +1 -1
  61. package/package.json +1 -1
  62. package/src/libraries/theme/_calendar.scss +2 -1
  63. package/src/libraries/theme/_card.scss +6 -5
  64. package/src/libraries/theme/_forms.scss +1 -1
  65. package/src/libraries/theme/_mixins.scss +0 -1
  66. package/src/libraries/theme/_modals.scss +3 -2
  67. package/src/libraries/theme/_ngx.scss +0 -1
  68. package/src/libraries/theme/_tables.scss +3 -2
  69. package/src/libraries/theme/_tabs.scss +6 -4
  70. package/src/libraries/theme/_utils.scss +123 -4
  71. package/src/libraries/theme/_variables.scss +35 -8
  72. package/src/libraries/theme/components/_carousel.scss +2 -2
  73. package/src/libraries/theme/components/_tb-search.scss +10 -4
  74. package/src/libraries/theme/mat-components/_mat-date-picker.scss +4 -0
  75. package/src/libraries/theme/mat-components/_mat-input.scss +5 -0
  76. package/src/libraries/theme/mat-components/_mat-menu.scss +40 -14
  77. package/src/libraries/theme/mat-components/_mat-select.scss +23 -1
  78. package/src/libraries/theme/mat-components/_mat-selection-list.scss +2 -3
  79. package/src/libraries/theme/mat-components/_mat-step.scss +19 -0
  80. package/src/libraries/theme/theme.scss +119 -0
@@ -170,7 +170,7 @@
170
170
  }
171
171
 
172
172
  // Mobile width
173
- @media only screen and (max-width: 600px) {
173
+ @media only screen and (max-width: 640px) {
174
174
  .cdk-global-overlay-wrapper {
175
175
  justify-content: unset !important;
176
176
  margin: 0 10px;
@@ -187,7 +187,7 @@
187
187
  }
188
188
 
189
189
  mat-dialog-container.mat-mdc-dialog-container {
190
- min-width: 355px !important; // narrowest modal possible in mobile
190
+ min-width: min(355px, 100%) !important; // narrowest modal possible in mobile, never wider than the screen
191
191
  margin: auto;
192
192
  }
193
193
  }
@@ -203,7 +203,8 @@
203
203
  }
204
204
 
205
205
  .tb-primary-router-width {
206
- min-width: variables.$primary-router-min-width;
206
+ // Never wider than the viewport, so the page reflows at 320 px (WCAG 1.4.10).
207
+ min-width: min(variables.$primary-router-min-width, 100%);
207
208
  }
208
209
 
209
210
  .tb-sidebar {
@@ -218,6 +219,94 @@
218
219
  }
219
220
  }
220
221
 
222
+ // Narrow layout / high zoom: the side panel goes under the page instead of beside it (WCAG 1.4.10).
223
+ @media only screen and (max-width: variables.$reflow-max-width) {
224
+ .tb-flex:has(> .tb-sidebar),
225
+ .tb-flex-row:has(> .tb-sidebar) {
226
+ flex-direction: column;
227
+ flex-wrap: nowrap;
228
+ }
229
+
230
+ .tb-sidebar {
231
+ width: 100%;
232
+
233
+ tb-parker,
234
+ tb-parker-search,
235
+ tb-parker-search-results,
236
+ tb-global-counters {
237
+ width: 100%;
238
+ margin-left: 0;
239
+ }
240
+ }
241
+
242
+ // Filter bars: the groups inside a wrapping row wrap too, instead of one group widening the page,
243
+ // with 10px between wrapped lines unless the controls bring their own bottom spacing.
244
+ .tb-flex-wrap > .tb-flex {
245
+ flex-wrap: wrap;
246
+
247
+ &:not([class*='tb-mb-']):not(:has(> [class*='tb-pb-'], > [class*='tb-mb-'])),
248
+ &.tb-h-30 {
249
+ row-gap: 10px;
250
+ }
251
+
252
+ &.tb-h-30 {
253
+ height: auto;
254
+ }
255
+ }
256
+
257
+ // Section headers (title left, buttons right) wrap their buttons under the title.
258
+ .tb-flex-center.tb-space-between {
259
+ flex-wrap: wrap;
260
+ row-gap: 10px;
261
+
262
+ > .tb-flex {
263
+ flex-wrap: wrap;
264
+ row-gap: 10px;
265
+ }
266
+ }
267
+
268
+ // The min-width utilities are !important; the wide ones still give way to a narrow window.
269
+ @each $i in 280, 290, 315, 320, 460, 470 {
270
+ .tb-min-w-#{$i}.tb-min-w-#{$i} {
271
+ min-width: min(#{$i}px, 100%) !important;
272
+ }
273
+ }
274
+
275
+ // Fixed-width fields and fill areas give way to the window instead of widening the page.
276
+ main.tb-content [class*='tb-w-'] {
277
+ max-width: 100%;
278
+ }
279
+
280
+ main.tb-content .tb-flex > .tb-flex-grow {
281
+ min-width: 0;
282
+ }
283
+
284
+ // Radio options with long hints wrap instead of sizing the group to one line.
285
+ mat-radio-group.tb-elem-radio {
286
+ max-width: 100%;
287
+ flex-wrap: wrap;
288
+
289
+ > * {
290
+ min-width: 0;
291
+ max-width: 100%;
292
+ }
293
+ }
294
+
295
+ // Collapsible filter bars (tbCollapsibleFilters): only the search and the toggle show until it opens.
296
+ .toggle-filters-button.tb-filters-toggle {
297
+ display: block;
298
+ }
299
+
300
+ .tb-filters-collapsed .tb-collapsible-filter {
301
+ display: none !important;
302
+ }
303
+
304
+ // Short zoomed windows: pages scroll instead of squeezing tables; a basis keeps the fit-content minimum.
305
+ main.tb-content > router-outlet + * {
306
+ flex: 1 0 480px;
307
+ }
308
+ }
309
+
221
310
  .side-text .tb-field-label {
222
311
  padding-left: 0;
223
312
  }
@@ -848,6 +937,10 @@ a.clickable-link {
848
937
  overflow: auto;
849
938
  }
850
939
 
940
+ .tb-filters-toggle {
941
+ display: none;
942
+ }
943
+
851
944
  .toggle-filters-button tb-icon {
852
945
  outline: 1px solid variables.$border-color;
853
946
  outline-offset: -1px;
@@ -857,6 +950,11 @@ a.clickable-link {
857
950
  &.active {
858
951
  background-color: variables.$button-active-background;
859
952
  }
953
+
954
+ // The grey border is an outline, so focus recolours it instead of drawing a second ring.
955
+ &:focus-visible {
956
+ outline-color: variables.$focus-ring-color;
957
+ }
860
958
  }
861
959
 
862
960
  .active-button-text {
@@ -936,6 +1034,9 @@ a.clickable-link {
936
1034
 
937
1035
  .print-container {
938
1036
  position: absolute;
1037
+ // The hidden frame must not widen the page at 320 px (WCAG 1.4.10).
1038
+ left: 0;
1039
+ max-width: 100%;
939
1040
  z-index: -1;
940
1041
  overflow: auto;
941
1042
  height: 0px;
@@ -973,7 +1074,8 @@ a.clickable-link {
973
1074
  }
974
1075
 
975
1076
  .tb-rate-tables-container {
976
- min-width: 1268px;
1077
+ // The table scrolls inside its own box when narrower; headings and buttons stay in view (WCAG 1.4.10).
1078
+ min-width: min(1268px, 100% - 10px);
977
1079
  padding-right: 10px;
978
1080
  }
979
1081
 
@@ -1023,3 +1125,20 @@ a.clickable-link {
1023
1125
  height: calc(550px * (450 / 930)) !important; // ≈ 290px
1024
1126
  aspect-ratio: unset !important;
1025
1127
  }
1128
+
1129
+ // Narrow layout / high zoom: the rate image previews shrink to the window, keeping their shape (WCAG 1.4.10).
1130
+ @media only screen and (max-width: variables.$reflow-max-width) {
1131
+ .tb-upload-rate-on-display-image-vertical,
1132
+ .tb-upload-rate-on-display-image-horizontal {
1133
+ max-width: 100%;
1134
+ height: auto !important;
1135
+ }
1136
+
1137
+ .tb-upload-rate-on-display-image-vertical {
1138
+ aspect-ratio: 510 / 750 !important;
1139
+ }
1140
+
1141
+ .tb-upload-rate-on-display-image-horizontal {
1142
+ aspect-ratio: 930 / 450 !important;
1143
+ }
1144
+ }
@@ -14,8 +14,8 @@ $scrollbar-background-color: #0000004d;
14
14
  $big-shadow-color: #00000014;
15
15
  $title-color: #2c3e50;
16
16
  $text-color: #2c3e50;
17
- $secondary-text-color: #919eab;
18
- $error-color: #ff0e4d;
17
+ $secondary-text-color: #5f6b78;
18
+ $error-color: #e2003b;
19
19
  $error-light-color: #ffdbe4;
20
20
  $error-background-color: #ffdbe4;
21
21
  $success-background-color: #def9e1;
@@ -29,11 +29,11 @@ $inactive-color: #ecedf0;
29
29
  $white-color: #ffffff;
30
30
  $black-color: #000000;
31
31
  $grey-color: #9a9a9a;
32
- $form-field-border: #dee2e6;
33
- $form-field-border-hover: #919eab;
34
- $form-field-title: #919eab;
35
- $form-field-placeholder: #bdc3c7;
36
- $primary-color: #0985eb;
32
+ $form-field-border: #8a939c;
33
+ $form-field-border-hover: #5f6b78;
34
+ $form-field-title: #5f6b78;
35
+ $form-field-placeholder: #6b7785;
36
+ $primary-color: #0670c7;
37
37
  $blue-hover-backgroung-color: #0079dc;
38
38
  $secondary-color: #ffffff;
39
39
  $secondary-color-hover: #0985eb10;
@@ -70,10 +70,35 @@ $summary-background-color: #daedfc;
70
70
  $drop-down-menu-background-color: #f9f9f9;
71
71
  $modules-background-color: #f9f9f9;
72
72
  $sidenav-input-search-color: #3c4957;
73
+ $sidenav-input-text-color: #d8dbe0;
74
+ $sidenav-input-placeholder-color: #bdc3c7; // the light-theme placeholder is unreadable on the dark side menu
73
75
  $sidenav-child-background-color: #ffffff0d;
74
76
  $ngx-slider-background-color: #035aa1;
75
77
  $light-error-color: #ff0e4d40;
76
78
  $focus-border-color: #0985eb;
79
+ $focus-ring-color: #0670c7;
80
+ $focus-ring-color-on-dark: #ffffff;
81
+
82
+ // Focused dropdown option: blue bar plus a 1px ring.
83
+ $option-focus-shadow: inset 2px 0 0 $focus-ring-color, inset 0 0 0 1px $focus-ring-color;
84
+ // Selected option of a single-choice list: light tint plus a 3px blue bar.
85
+ $option-selected-shadow: inset 3px 0 0 $focus-ring-color;
86
+ // Focused selected option: the bar plus a 1px ring.
87
+ $option-selected-focus-shadow: inset 3px 0 0 $focus-ring-color, inset 0 0 0 1px $focus-ring-color;
88
+
89
+ // The bar, not the tint (about 1.2:1 on white), marks the selection (WCAG 1.4.1, 1.4.11).
90
+ @mixin single-choice-selected {
91
+ background: $active-background;
92
+ color: $text-color;
93
+ --mdc-list-list-item-label-text-color: #{$text-color};
94
+ --mdc-list-list-item-hover-label-text-color: #{$text-color};
95
+ --mdc-list-list-item-focus-label-text-color: #{$text-color};
96
+ box-shadow: $option-selected-shadow;
97
+
98
+ &:focus-visible {
99
+ box-shadow: $option-selected-focus-shadow;
100
+ }
101
+ }
77
102
  $ngx-slider-full-bar-background: #dee2e6;
78
103
  $ngx-slider-selection-background: #0985eb80;
79
104
  $ngx-slider-pointer-color: #0985eb;
@@ -119,6 +144,8 @@ $primary-router-min-width: 335px; // Changed to support mobile compatability
119
144
 
120
145
  $sidebar-container-width: 528px;
121
146
  $sidebar-width: 518px;
147
+ // Narrow layout (side panels stack, filters wrap): 400% zoom, and wide screens zoomed to 200-300% (WCAG 1.4.10).
148
+ $reflow-max-width: 999px;
122
149
  $sidebar-margin-left: 10px;
123
150
  $sidenav-width: 240px;
124
151
 
@@ -166,7 +193,7 @@ $progress-bar-yellow: #ffd362;
166
193
 
167
194
  // special-days
168
195
  $special-day-1: #e488ff;
169
- $special-day-2: #8da0fe;
196
+ $special-day-2: #94a6fe;
170
197
  $special-day-3: #ffd362;
171
198
  $special-day-4: #09c4eb;
172
199
  $special-day-5: #0eff77;
@@ -2,7 +2,7 @@
2
2
  @use 'variables';
3
3
 
4
4
  owl-carousel-o.full, owl-carousel-o.thumbnail, owl-carousel-o.section, owl-carousel-o.modal {
5
- min-width: 380px;
5
+ min-width: min(380px, 100%);
6
6
 
7
7
  .owl-carousel {
8
8
  padding: 10px 0;
@@ -164,7 +164,7 @@ tb-update-plate-id-dialog owl-carousel-o.full {
164
164
  }
165
165
  }
166
166
 
167
- @media (max-width: 600px) {
167
+ @media (max-width: 640px) {
168
168
  owl-carousel-o.section .owl-carousel {
169
169
  height: 70vw;
170
170
  }
@@ -18,6 +18,12 @@ tb-search {
18
18
  &:hover {
19
19
  opacity: .9;
20
20
  }
21
+
22
+ // A blue ring is invisible on the blue button; draw it white, inside.
23
+ &:focus-visible {
24
+ outline-color: variables.$focus-ring-color-on-dark;
25
+ outline-offset: -3px;
26
+ }
21
27
  }
22
28
  }
23
29
  }
@@ -50,18 +56,18 @@ tb-search.tb-sidenav-search {
50
56
  background-color: variables.$sidenav-input-search-color;
51
57
 
52
58
  &:focus-within {
53
- border-color: variables.$input-border-on-focus;
59
+ border-color: variables.$focus-ring-color-on-dark;
54
60
  }
55
61
 
56
62
  .mat-mdc-form-field-infix {
57
63
  input.mat-mdc-input-element {
58
64
  font-size: 13px;
59
- color: variables.$form-field-title;
60
- caret-color: variables.$form-field-title;
65
+ color: variables.$sidenav-input-text-color;
66
+ caret-color: variables.$sidenav-input-text-color;
61
67
  background-color: transparent;
62
68
 
63
69
  &::placeholder {
64
- color: variables.$form-field-placeholder;
70
+ color: variables.$sidenav-input-placeholder-color;
65
71
  opacity: 1;
66
72
  }
67
73
  }
@@ -170,6 +170,10 @@ mat-form-field.mat-mdc-form-field.tb-elem-date-picker {
170
170
  mat-form-field.mat-mdc-form-field.tb-elem-date-picker.mat-form-field-invalid {
171
171
  .mat-mdc-text-field-wrapper .mat-mdc-form-field-flex {
172
172
  border-color: variables.$error-color;
173
+
174
+ &:focus-within {
175
+ border-color: variables.$input-border-on-focus;
176
+ }
173
177
  }
174
178
  }
175
179
 
@@ -194,6 +194,11 @@ mat-form-field.mat-mdc-form-field.tb-elem-input.mat-form-field-invalid:not(.mat-
194
194
  .mat-mdc-text-field-wrapper .mat-mdc-form-field-flex {
195
195
  border-color: variables.$error-color;
196
196
 
197
+ // Focus stays visible on an invalid field, like tb-mat-select.
198
+ &:focus-within {
199
+ border-color: variables.$input-border-on-focus;
200
+ }
201
+
197
202
  .mat-mdc-form-field-label {
198
203
  color: variables.$form-field-placeholder;
199
204
  }
@@ -86,8 +86,10 @@
86
86
  background-color: variables.$hover-background;
87
87
  }
88
88
 
89
- &[aria-selected="true"] {
90
- background-color: variables.$active-background;
89
+ // Inside a menu the current option is a checked radio item (tb-menu-icon useMenuRoles).
90
+ &[aria-selected="true"],
91
+ &[aria-checked="true"] {
92
+ @include variables.single-choice-selected;
91
93
  }
92
94
 
93
95
  .mdc-list-item__content {
@@ -140,6 +142,13 @@
140
142
  background-color: variables.$drawer-item-hover-background;
141
143
  }
142
144
 
145
+ // The blue ring barely shows on the dark panel; white inside the row, as in the side menu.
146
+ &:focus-visible {
147
+ outline: 1px solid variables.$focus-ring-color-on-dark;
148
+ outline-offset: -3px;
149
+ background-color: variables.$drawer-item-hover-background;
150
+ }
151
+
143
152
  .mat-mdc-list-item-unscoped-content {
144
153
  span {
145
154
  color: variables.$white-color;
@@ -272,7 +281,6 @@
272
281
  overflow-y: auto;
273
282
  overflow-x: hidden;
274
283
  max-height: calc(100vh - 400px);
275
- outline: none;
276
284
 
277
285
  mat-list-option.mat-mdc-list-option {
278
286
  height: 40px;
@@ -551,11 +559,9 @@ mat-selection-list.mat-mdc-selection-list {
551
559
  }
552
560
 
553
561
  .mat-mdc-selection-list {
554
- outline: none;
555
562
  padding: 0;
556
563
 
557
564
  .mat-mdc-list-option {
558
- outline: none;
559
565
  font-size: 13px;
560
566
  height: 40px;
561
567
 
@@ -594,6 +600,11 @@ mat-selection-list.mat-mdc-selection-list {
594
600
  .mat-mdc-menu-item {
595
601
  background: transparent;
596
602
  opacity: 1 !important;
603
+
604
+ // Material drops the outline on focus; mark it like a focused option.
605
+ &:focus-visible {
606
+ box-shadow: variables.$option-focus-shadow;
607
+ }
597
608
  }
598
609
 
599
610
  .tb-menu.tb-header-menu.tb-module-menu {
@@ -615,6 +626,21 @@ mat-selection-list.mat-mdc-selection-list {
615
626
  border-bottom-left-radius: 4px;
616
627
  border-bottom-right-radius: 4px;
617
628
  }
629
+
630
+ // The current module is a place, not a chosen value: light tint plus a blue bar, not the strong fill.
631
+ mat-selection-list.mat-mdc-selection-list .mat-mdc-list-option[aria-selected='true'],
632
+ mat-selection-list.mat-mdc-selection-list .mat-mdc-list-option[aria-checked='true'] {
633
+ background: variables.$active-background;
634
+ color: variables.$text-color;
635
+ --mdc-list-list-item-label-text-color: #{variables.$text-color};
636
+ --mdc-list-list-item-hover-label-text-color: #{variables.$text-color};
637
+ --mdc-list-list-item-focus-label-text-color: #{variables.$text-color};
638
+ box-shadow: inset 3px 0 0 variables.$focus-ring-color;
639
+
640
+ &:focus-visible {
641
+ box-shadow: variables.$option-focus-shadow;
642
+ }
643
+ }
618
644
  }
619
645
  }
620
646
 
@@ -754,32 +780,32 @@ tb-sidenav {
754
780
  opacity: 0.5;
755
781
  }
756
782
 
757
- &.parent-node[aria-level='1'] .list-option-item,
758
- &.parent-node[aria-level='1'] .mat-mdc-checkbox {
783
+ &.parent-node:is([aria-level='1'], [data-level='1']) .list-option-item,
784
+ &.parent-node:is([aria-level='1'], [data-level='1']) .mat-mdc-checkbox {
759
785
  font-weight: 600;
760
786
  }
761
787
 
762
- &[aria-level='2'] .mdc-list-item__content {
788
+ &:is([aria-level='2'], [data-level='2']) .mdc-list-item__content {
763
789
  padding-left: 17px;
764
790
  }
765
791
 
766
- &.expendable-disabled[aria-level='3'] .mdc-list-item__content {
792
+ &.expendable-disabled:is([aria-level='3'], [data-level='3']) .mdc-list-item__content {
767
793
  padding-left: 64px;
768
794
  }
769
795
 
770
- &.expendable-disabled[aria-level='2'] .mdc-list-item__content {
796
+ &.expendable-disabled:is([aria-level='2'], [data-level='2']) .mdc-list-item__content {
771
797
  padding-left: 43px;
772
798
  }
773
799
 
774
- &.expendable-disabled[aria-level='1'] .mdc-list-item__content {
800
+ &.expendable-disabled:is([aria-level='1'], [data-level='1']) .mdc-list-item__content {
775
801
  padding-left: 26px;
776
802
  }
777
803
 
778
- &[aria-level='1'].tb-pointer-event-none~[aria-level='2'] .mdc-list-item__content {
804
+ &:is([aria-level='1'], [data-level='1']).tb-pointer-event-none~:is([aria-level='2'], [data-level='2']) .mdc-list-item__content {
779
805
  padding-left: 7px;
780
806
  }
781
807
 
782
- &[aria-level='2'] tb-icon .tb-icon {
808
+ &:is([aria-level='2'], [data-level='2']) tb-icon .tb-icon {
783
809
  margin-left: 18px;
784
810
  }
785
811
 
@@ -789,7 +815,7 @@ tb-sidenav {
789
815
  }
790
816
 
791
817
  &:not(.has-children) mat-tree-node.mat-tree-node {
792
- &[aria-level='1'] .mdc-list-item__content {
818
+ &:is([aria-level='1'], [data-level='1']) .mdc-list-item__content {
793
819
  padding-left: 10px;
794
820
 
795
821
  mat-checkbox {
@@ -172,6 +172,13 @@ mat-form-field.mat-mdc-form-field.tb-mat-select.tb-h-30 {
172
172
  &:focus,
173
173
  &.mat-mdc-option-active {
174
174
  --mat-option-selected-state-layer-color: #{variables.$hover-background};
175
+ // The grey tint alone is too faint to show the focused option.
176
+ box-shadow: inset 2px 0 0 variables.$focus-ring-color;
177
+ }
178
+
179
+ &:focus,
180
+ &.mat-mdc-option-active {
181
+ box-shadow: variables.$option-focus-shadow;
175
182
  }
176
183
 
177
184
  &.mdc-list-item--selected {
@@ -182,6 +189,21 @@ mat-form-field.mat-mdc-form-field.tb-mat-select.tb-h-30 {
182
189
  }
183
190
  }
184
191
 
192
+ // Single choice: light tint plus a blue bar (variables.single-choice-selected).
193
+ &.mdc-list-item--selected:not(.mat-mdc-option-multiple) {
194
+ --mat-option-selected-state-layer-color: #{variables.$active-background};
195
+ box-shadow: variables.$option-selected-shadow;
196
+
197
+ .mdc-list-item__primary-text {
198
+ --mat-option-selected-state-label-text-color: #{variables.$text-color};
199
+ }
200
+
201
+ &:focus,
202
+ &.mat-mdc-option-active {
203
+ box-shadow: variables.$option-selected-focus-shadow;
204
+ }
205
+ }
206
+
185
207
  &.list-option-item {
186
208
  .mdc-list-item__primary-text {
187
209
  --mat-option-selected-state-label-text-color: #{variables.$form-field-placeholder};
@@ -218,7 +240,7 @@ mat-form-field.mat-mdc-form-field.tb-mat-select.tb-h-30 {
218
240
  }
219
241
 
220
242
  &.tb-disable-selected-bg {
221
- mat-option.mat-mdc-option.mdc-list-item--selected {
243
+ mat-option.mat-mdc-option.mdc-list-item--selected:not(.mat-mdc-option-multiple) {
222
244
  --mat-option-selected-state-layer-color: #{variables.$white-color};
223
245
 
224
246
  &:hover {
@@ -17,10 +17,8 @@
17
17
 
18
18
  mat-selection-list.mat-mdc-selection-list.special-days-list {
19
19
  padding: 0;
20
- outline: none;
21
20
 
22
21
  .mat-mdc-list-option {
23
- outline: none;
24
22
  font-size: 13px;
25
23
  height: 40px;
26
24
 
@@ -77,7 +75,8 @@ mat-selection-list.mat-mdc-selection-list[aria-multiselectable="false"] {
77
75
  background: variables.$hover-background;
78
76
  }
79
77
 
78
+ // Single choice: a strong fill, so the selection is not shown by a light tint alone (WCAG 1.4.1).
80
79
  .mat-mdc-list-option[aria-selected="true"] {
81
- background: variables.$active-background;
80
+ @include variables.single-choice-selected;
82
81
  }
83
82
  }
@@ -138,6 +138,11 @@ mat-horizontal-stepper.mat-stepper-horizontal {
138
138
  display: flex;
139
139
  flex-direction: column;
140
140
  flex: 1;
141
+ // Material's container clips; a step taller than a short (zoomed) dialog scrolls instead of being cut off.
142
+ // The scroll box clips too, so it keeps the ring room on its left edge.
143
+ overflow-y: auto;
144
+ padding-left: 5px;
145
+ margin-left: -5px;
141
146
 
142
147
  &:not(.mat-horizontal-stepper-content-inactive) {
143
148
  height: 100%;
@@ -236,3 +241,17 @@ tb-tab-icon {
236
241
  .mat-horizontal-content-container {
237
242
  padding: unset;
238
243
  }
244
+
245
+ // These boxes clip; padding offset by a negative margin leaves room for the focus ring of a radio or checkbox
246
+ // at the step's left (and top) edge without moving anything. Never on the right or bottom: there the box
247
+ // would stick out of a scrolling parent and add a scrollbar.
248
+ .mat-stepper-horizontal,
249
+ .mat-horizontal-stepper-wrapper {
250
+ padding-left: 5px;
251
+ margin-left: -5px;
252
+ }
253
+
254
+ .mat-horizontal-content-container.mat-horizontal-content-container {
255
+ padding: 5px 0 0 5px;
256
+ margin: -5px 0 0 -5px;
257
+ }
@@ -11,6 +11,125 @@ body {
11
11
  --mat-sidenav-content-text-color: #{ variables.$text-color};
12
12
  }
13
13
 
14
+ // Text fields show focus with their own border.
15
+ :focus-visible:not(input, textarea) {
16
+ outline: 1px solid variables.$focus-ring-color;
17
+ outline-offset: 2px;
18
+ }
19
+
20
+ // The side menu and tab strip clip outside the element, so draw the ring inside.
21
+ .mat-drawer :focus-visible,
22
+ .mdc-tab:focus-visible {
23
+ outline-offset: -1px;
24
+ }
25
+
26
+ // Dropdowns look like text fields on focus: a 1px blue line over their own border.
27
+ .tb-dropdown-menu-trigger:focus-visible {
28
+ outline-width: 1px;
29
+ outline-offset: -1px;
30
+ }
31
+
32
+ // The select's field border already turns blue on focus.
33
+ .mat-mdc-select:focus-visible {
34
+ outline: none;
35
+ }
36
+
37
+ // Dialog X and minimize: rounded like their hover background, so the focus ring is rounded too.
38
+ .modal-close-icon,
39
+ .modal-minimize-icon {
40
+ border-radius: 4px;
41
+ }
42
+
43
+ // Round ring on icon buttons inside a field (search clear X), inset: the field clips anything outside it.
44
+ .mat-mdc-form-field-icon-suffix [role='button']:focus-visible,
45
+ .mat-mdc-form-field-icon-suffix .mat-mdc-icon-button:focus-visible {
46
+ border-radius: 50%;
47
+ margin: auto 0;
48
+ outline-offset: -2px;
49
+ }
50
+
51
+ // Focused icon buttons get the 1px ring only, not Material's gray focus fill.
52
+ .mat-mdc-icon-button.cdk-keyboard-focused,
53
+ .mat-mdc-icon-button.cdk-program-focused {
54
+ .mat-mdc-button-persistent-ripple::before {
55
+ opacity: 0;
56
+ }
57
+ }
58
+
59
+ // Radios and checkboxes hide their native input; ring the visible circle or box, not the input.
60
+ .mdc-radio__native-control:focus-visible,
61
+ .mdc-checkbox__native-control:focus-visible {
62
+ outline: none;
63
+ }
64
+
65
+ .mdc-radio__native-control:focus-visible ~ .mdc-radio__background,
66
+ .mdc-checkbox__native-control:focus-visible ~ .mdc-checkbox__background {
67
+ outline: 1px solid variables.$focus-ring-color;
68
+ outline-offset: 2px;
69
+ }
70
+
71
+ .mdc-radio__native-control:focus-visible ~ .mdc-radio__background {
72
+ border-radius: 50%;
73
+ // Clear of the selected radio's own blue circle.
74
+ outline-offset: 3px;
75
+ }
76
+
77
+ // The ring is drawn by ::after below.
78
+ .mdc-switch:focus-visible {
79
+ outline: none;
80
+ }
81
+
82
+ // The 20px knob overhangs the 15px track; ring it with an even gap around both.
83
+ .mdc-switch:focus-visible::after {
84
+ content: '';
85
+ position: absolute;
86
+ inset: -6px -3px;
87
+ border: 1px solid variables.$focus-ring-color;
88
+ border-radius: 14px;
89
+ pointer-events: none;
90
+ }
91
+
92
+ .mat-drawer .mdc-switch:focus-visible::after {
93
+ border-color: variables.$focus-ring-color-on-dark;
94
+ }
95
+
96
+ // Adjacent tabs (all z-index 1) and joined fields paint over the ring; lift the focused one.
97
+ .mdc-tab:focus-visible,
98
+ .mat-mdc-form-field:has(:focus-visible) {
99
+ z-index: 2;
100
+ }
101
+
102
+ .mat-drawer :focus-visible {
103
+ outline-color: variables.$focus-ring-color-on-dark;
104
+ }
105
+
106
+ // Material sets outline: none on expansion headers; the panel clips, so the ring goes inside.
107
+ .mat-expansion-panel .mat-expansion-panel-header:focus-visible {
108
+ outline: 1px solid variables.$focus-ring-color;
109
+ outline-offset: -1px;
110
+ }
111
+
112
+ // Same for list items (Material sets outline: 0); dropdown options show a bar instead.
113
+ .mat-mdc-list-item.mdc-list-item:focus-visible:not(.list-option-item) {
114
+ outline: 1px solid variables.$focus-ring-color;
115
+ outline-offset: -1px;
116
+ }
117
+
118
+ // Side menu: the row's own children paint over an outline on hover, so draw the ring as an overlay.
119
+ .mat-drawer .mat-expansion-panel .mat-expansion-panel-header:focus-visible,
120
+ .mat-drawer .mat-mdc-list-item.mdc-list-item:focus-visible:not(.list-option-item) {
121
+ outline: none;
122
+
123
+ &::after {
124
+ content: '';
125
+ position: absolute;
126
+ inset: 2px;
127
+ border: 1px solid variables.$focus-ring-color-on-dark;
128
+ pointer-events: none;
129
+ z-index: 1;
130
+ }
131
+ }
132
+
14
133
  /* Override all Material Typography */
15
134
  * {
16
135
  letter-spacing: 0 !important;