@asd20/ui-next 2.21.6 → 2.21.8

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.21.8](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.21.7...ui-next-v2.21.8) (2026-08-26)
4
+
5
+ ## [2.21.7](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.21.6...ui-next-v2.21.7) (2026-08-26)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * mobile scroll fixes; ios input zoom issue ([56ba4a7](https://github.com/academydistrict20/asd20-ui-next/commit/56ba4a75391744db8a4f43ff74e30b1fa0bd5f75))
11
+
3
12
  ## [2.21.6](https://github.com/academydistrict20/asd20-ui-next/compare/ui-next-v2.21.5...ui-next-v2.21.6) (2026-08-25)
4
13
 
5
14
 
package/README.md CHANGED
@@ -87,6 +87,20 @@ npm test
87
87
  npm run build
88
88
  ```
89
89
 
90
+ Optional harnesses are available to facilitate repeatable physical-device
91
+ checks for scrolling, touch, focus, keyboard, and viewport changes:
92
+
93
+ ```sh
94
+ npm run test:mobile:storybook
95
+ npm run test:mobile:package
96
+ ```
97
+
98
+ These commands are manual testing aids. They are not testing, CI, release, or
99
+ package-publishing requirements.
100
+
101
+ See [Physical Mobile Device Testing](docs/mobile-device-testing.md) for the
102
+ device checklist, packed-consumer workflow, options, and troubleshooting.
103
+
90
104
  Regenerate icon metadata after adding, removing, or renaming SVG icon assets:
91
105
 
92
106
  ```sh
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asd20/ui-next",
3
- "version": "2.21.6",
3
+ "version": "2.21.8",
4
4
  "private": false,
5
5
  "description": "ASD20 UI component library for Vue 3.",
6
6
  "license": "MIT",
@@ -108,8 +108,8 @@ export default {
108
108
  border: none;
109
109
  border-radius: var(--website-shape__radius-s);
110
110
  // outline: 1px solid red;
111
- // font-size: space(0.75);
112
- font-size: space(0.625);
111
+ // Keep form text at least 16px so iOS Safari does not zoom the page on focus.
112
+ font-size: $base-font-size;
113
113
  line-height: 1;
114
114
  height: space(1.5);
115
115
  background: white;
@@ -141,7 +141,7 @@ export default {
141
141
  top: 0;
142
142
  bottom: space(0.5);
143
143
  left: 0;
144
- right: 3rem;
144
+ right: 2rem;
145
145
  z-index: -1;
146
146
  background: var(--website-header__background-style);
147
147
  background-color: var(--website-header__background-color);
@@ -208,7 +208,8 @@ export default {
208
208
 
209
209
  &__content {
210
210
  margin-top: space(3);
211
- background: var(--website-header__background-color);
211
+ width: 90%;
212
+ // background: var(--website-header__background-color);
212
213
  }
213
214
 
214
215
  &__lead {
@@ -310,7 +311,7 @@ $max: 4rem;
310
311
  @media (min-width: 667px) {
311
312
  .asd20-school-homepage-header {
312
313
  &::before {
313
- right: space(3);
314
+ right: 2rem;
314
315
  bottom: space(2);
315
316
  }
316
317
  &__top {
@@ -404,6 +405,7 @@ $max: 4rem;
404
405
  &__content {
405
406
  margin-top: 0;
406
407
  position: relative;
408
+ width: auto;
407
409
  grid-column: 2 / 6;
408
410
  grid-row: 8 / 9;
409
411
  }
@@ -54,43 +54,45 @@
54
54
  <!-- Pause icon (⏸) -->
55
55
  <span v-else>&#10073;&#10073;</span>
56
56
  </button>
57
- <div
58
- id="main"
59
- class="asd20-school-homepage-video-header__content"
60
- >
61
- <h1
62
- v-if="heading"
63
- v-html="heading"
64
- />
57
+ <div class="asd20-school-homepage-video-header__text">
65
58
  <div
66
- v-if="lead"
67
- class="asd20-school-homepage-video-header__lead"
59
+ id="main"
60
+ class="asd20-school-homepage-video-header__content"
68
61
  >
69
- <span v-html="lead"></span>
70
- <a
71
- v-if="detailLink && !isDetailPage"
72
- :href="detailLink"
73
- @click="onDetailLinkClick"
74
- >
75
- {{ detailLinkLabel }}
76
- </a>
62
+ <h1
63
+ v-if="heading"
64
+ v-html="heading"
65
+ />
77
66
  <div
78
- v-if="callsToAction.length > 0"
79
- class="asd20-school-homepage-video-header__call-to-action"
67
+ v-if="lead"
68
+ class="asd20-school-homepage-video-header__lead"
80
69
  >
81
- <asd20-button
82
- v-for="(cta, index) in callsToAction"
83
- :key="index"
84
- :size="['sm', 'md'].indexOf(mq) > -1 ? 'sm' : 'lg'"
85
- :label="cta.label"
86
- :icon="cta.icon"
87
- :link="cta.url"
88
- reversed
89
- horizontal
90
- centered
91
- />
70
+ <span v-html="lead"></span>
71
+ <a
72
+ v-if="detailLink && !isDetailPage"
73
+ :href="detailLink"
74
+ @click="onDetailLinkClick"
75
+ >
76
+ {{ detailLinkLabel }}
77
+ </a>
92
78
  </div>
93
79
  </div>
80
+ <div
81
+ v-if="callsToAction.length > 0"
82
+ class="asd20-school-homepage-video-header__call-to-action"
83
+ >
84
+ <asd20-button
85
+ v-for="(cta, index) in callsToAction"
86
+ :key="index"
87
+ :size="['sm', 'md'].indexOf(mq) > -1 ? 'sm' : 'lg'"
88
+ :label="cta.label"
89
+ :icon="cta.icon"
90
+ :link="cta.url"
91
+ reversed
92
+ horizontal
93
+ centered
94
+ />
95
+ </div>
94
96
  </div>
95
97
  </div>
96
98
 
@@ -220,16 +222,16 @@ export default {
220
222
  display: block;
221
223
  position: absolute;
222
224
  top: 0;
223
- bottom: space(0);
225
+ bottom: space(0.5);
224
226
  left: 0;
225
- right: space(1);
227
+ right: 2rem;
226
228
  z-index: -1;
227
- background: var(--website-header__background-color)
228
- var(--website-header__background-style);
229
- border-radius: var(--website-shape__radius-l);
229
+ background: var(--website-header__background-style);
230
+ background-color: var(--website-header__background-color);
230
231
  border-top-left-radius: 0;
231
- border-top-right-radius: 0;
232
+ border-top-right-radius: 3rem;
232
233
  border-bottom-left-radius: 0;
234
+ border-bottom-right-radius: 3rem;
233
235
  }
234
236
  &::after {
235
237
  content: '';
@@ -249,14 +251,13 @@ export default {
249
251
  --fill-one: rgba(255, 255, 255, 1);
250
252
  --fill-two: rgba(255, 255, 255, 0.625);
251
253
 
252
- position: relative;
254
+ width: 100%;
255
+ position: absolute;
253
256
  z-index: 101;
254
- background: white;
255
257
  display: flex;
256
- justify-content: space-between;
258
+ margin-top: space(0.5);
257
259
  align-items: center;
258
- flex-direction: column;
259
- align-items: flex-start;
260
+ flex-direction: row;
260
261
  & :deep(.asd20-picker) {
261
262
  display: none;
262
263
  }
@@ -265,38 +266,44 @@ export default {
265
266
  margin-left: 1rem;
266
267
  }
267
268
  }
269
+ &__text {
270
+ display: contents;
271
+ }
268
272
  &__content {
269
- position: absolute;
270
- top: 0;
271
- background: rgba(0, 0, 0, 0.4);
272
- height: 100%;
273
- padding-top: space(3);
273
+ order: 1;
274
+ width: 90%;
275
+ // background: var(--website-header__background-color);
274
276
  }
275
277
  &__image {
278
+ display: flex;
279
+ flex-direction: column;
280
+ padding-top: space(3);
281
+
276
282
  img {
283
+ order: 2;
277
284
  display: block;
278
285
  width: 100%;
279
- height: 600px;
280
- min-height: 600px;
281
- object-fit: cover;
282
286
  object-position: center;
283
287
  }
284
288
  }
285
289
 
286
290
  h1 {
287
291
  position: relative;
288
- font-size: 32px !important;
289
- color: #ffffff !important;
290
- margin: space(2) space(1) space(1) space(1);
292
+ color: var(--website-header__title-color);
293
+ @include asd20-font(
294
+ 2,
295
+ var(--website-typography__font-family-headlines),
296
+ 1.2,
297
+ 700
298
+ );
299
+ margin: space(1);
291
300
  z-index: 1;
292
- border-bottom: 5px solid var(--color__accent);
293
- padding-bottom: space(0.5);
294
301
  }
295
302
 
296
303
  &__lead {
297
304
  position: relative;
298
305
  z-index: 1;
299
- color: #f6f6f6 !important;
306
+ color: var(--website-header__foreground-color);
300
307
  @include asd20-font(1, var(--website-typography__font-family-body));
301
308
  margin: space(-0.5) space(2) space(1) space(1);
302
309
  a {
@@ -305,19 +312,15 @@ export default {
305
312
  }
306
313
 
307
314
  &__call-to-action {
315
+ order: 3;
316
+ margin: space(1);
317
+ z-index: 1;
308
318
  display: flex;
309
319
  flex-direction: column;
310
- left: space(1);
311
- z-index: 2;
312
- margin: 0;
313
- height: min-content;
314
- align-self: end;
315
320
  .asd20-button {
316
- min-width: 95%;
317
- border-radius: var(--website-shape__radius-m);
318
- margin: space(0.5) 0 0;
319
321
  flex-grow: 1;
320
- max-width: 100%;
322
+ width: calc(100% - space(3));
323
+ margin-bottom: space(0.5);
321
324
  font-size: 1rem !important;
322
325
  background-color: var(
323
326
  --website-header__button-background-color,
@@ -328,7 +331,7 @@ export default {
328
331
  var(--color__accent-t100)
329
332
  );
330
333
  position: relative;
331
- padding: space(0.5) space(0.75);
334
+ padding: space(0.5) space(1.5);
332
335
  transition: background-color 0.3s ease;
333
336
  z-index: 2;
334
337
  overflow: hidden;
@@ -360,6 +363,10 @@ export default {
360
363
  background-color: var(--color__accent-t60);
361
364
  }
362
365
  }
366
+
367
+ &:not(:last-child) {
368
+ margin-bottom: space(0.5);
369
+ }
363
370
  }
364
371
  }
365
372
  .scroll-down-indicator {
@@ -442,30 +449,27 @@ $max: 4rem;
442
449
  }
443
450
  @media (min-width: 667px) {
444
451
  .asd20-school-homepage-video-header {
452
+ &::before {
453
+ right: 2rem;
454
+ bottom: space(2);
455
+ }
445
456
  &__call-to-action {
457
+ flex-direction: row;
458
+ margin: space(1);
446
459
  .asd20-button {
447
- min-width: 50%;
448
- max-width: 50%;
460
+ flex-grow: 0;
461
+ width: max-content;
462
+ min-width: 4rem;
463
+ margin-bottom: 0 !important;
464
+ &:not(:last-child) {
465
+ margin-right: space(0.5);
466
+ }
449
467
  }
450
468
  }
451
469
  &__top {
452
470
  flex-direction: row;
453
- justify-content: flex-start;
454
- padding: 0 space(1);
455
471
  align-items: center !important;
456
472
  }
457
- &__image {
458
- img {
459
- display: block;
460
- width: 100%;
461
- height: 80vh;
462
- object-fit: cover;
463
- object-position: center;
464
- }
465
- }
466
- h1 {
467
- font-size: 40px !important;
468
- }
469
473
  }
470
474
  }
471
475
 
@@ -538,6 +542,8 @@ $max: 4rem;
538
542
  }
539
543
 
540
544
  &__top {
545
+ position: relative;
546
+ width: inherit;
541
547
  margin: 0;
542
548
  padding: space(0.5) space(3) space(1) space(3);
543
549
  grid-column: 1/-1;
@@ -549,7 +555,9 @@ $max: 4rem;
549
555
  }
550
556
  }
551
557
 
552
- &__content {
558
+ &__text {
559
+ display: block;
560
+ position: absolute;
553
561
  top: space(1);
554
562
  left: space(1);
555
563
  z-index: 100;
@@ -564,6 +572,11 @@ $max: 4rem;
564
572
  border-bottom-left-radius: 2rem;
565
573
  }
566
574
 
575
+ &__content {
576
+ width: auto;
577
+ background: transparent;
578
+ }
579
+
567
580
  h1 {
568
581
  margin: 0 0 space(1) 0;
569
582
  font-size: 40px !important;
@@ -581,9 +594,18 @@ $max: 4rem;
581
594
  margin-top: 0;
582
595
  }
583
596
  &__call-to-action {
597
+ flex-direction: column;
598
+ margin: 0;
599
+ height: min-content;
600
+ align-self: end;
584
601
  .asd20-button {
602
+ flex-grow: 1;
603
+ width: auto;
604
+ margin: space(0.5) 0 0;
585
605
  min-width: 60%;
586
606
  max-width: 60%;
607
+ border-radius: var(--website-shape__radius-m);
608
+ padding: space(0.5) space(0.75);
587
609
  }
588
610
  }
589
611
 
@@ -600,6 +622,7 @@ $max: 4rem;
600
622
  height: auto;
601
623
  margin: 0;
602
624
  padding: 0;
625
+ background: transparent;
603
626
  transform: translate3d(0, calc(-20% * var(--scroll-progress)), 0);
604
627
  border-radius: var(--website-shape__radius-l);
605
628
  overflow: hidden;
@@ -682,6 +705,8 @@ $max: 4rem;
682
705
  }
683
706
 
684
707
  &__top {
708
+ position: relative;
709
+ width: inherit;
685
710
  margin: 0;
686
711
  padding: space(0.5) space(3) space(1) space(3);
687
712
  grid-column: 1/-1;
@@ -693,7 +718,9 @@ $max: 4rem;
693
718
  }
694
719
  }
695
720
 
696
- &__content {
721
+ &__text {
722
+ display: block;
723
+ position: absolute;
697
724
  top: 2.6875rem;
698
725
  left: -1rem;
699
726
  z-index: 100;
@@ -708,6 +735,11 @@ $max: 4rem;
708
735
  border-bottom-left-radius: 2rem;
709
736
  }
710
737
 
738
+ &__content {
739
+ width: auto;
740
+ background: transparent;
741
+ }
742
+
711
743
  h1 {
712
744
  margin: 0 0 space(1) 0;
713
745
  font-size: 40px !important;
@@ -725,9 +757,18 @@ $max: 4rem;
725
757
  margin-top: 0;
726
758
  }
727
759
  &__call-to-action {
760
+ flex-direction: column;
761
+ margin: 0;
762
+ height: min-content;
763
+ align-self: end;
728
764
  .asd20-button {
765
+ flex-grow: 1;
766
+ width: auto;
767
+ margin: space(0.5) 0 0;
729
768
  min-width: 60%;
730
769
  max-width: 60%;
770
+ border-radius: var(--website-shape__radius-m);
771
+ padding: space(0.5) space(0.75);
731
772
  }
732
773
  }
733
774
 
@@ -744,6 +785,7 @@ $max: 4rem;
744
785
  height: auto;
745
786
  margin: 0;
746
787
  padding: 0;
788
+ background: transparent;
747
789
  transform: translate3d(0, calc(-20% * var(--scroll-progress)), 0);
748
790
  border-radius: var(--website-shape__radius-l);
749
791
  overflow: visible;
@@ -153,6 +153,10 @@ import {
153
153
  shouldOpenInNewWindow,
154
154
  shouldShowExternalIcon,
155
155
  } from '../../../helpers/linkPolicy'
156
+ import {
157
+ acquireDocumentScrollLock,
158
+ releaseDocumentScrollLock,
159
+ } from '../../../helpers/documentScrollLock'
156
160
 
157
161
  export default {
158
162
  name: 'Asd20SiteMenu',
@@ -168,6 +172,7 @@ export default {
168
172
  focusedItemIndex: -1,
169
173
  hasFocus: false,
170
174
  keyboardTriggeredLastAction: false,
175
+ bodyScrollLock: null,
171
176
  }),
172
177
  computed: {
173
178
  activeSection() {
@@ -179,12 +184,43 @@ export default {
179
184
  return resolveCurrentHostname(this)
180
185
  },
181
186
  },
187
+ watch: {
188
+ active(newVal) {
189
+ if (newVal && !this.emptyNav) {
190
+ this.lockBodyScroll()
191
+ } else {
192
+ this.unlockBodyScroll()
193
+ }
194
+ },
195
+ emptyNav(newVal) {
196
+ if (this.active && !newVal) {
197
+ this.lockBodyScroll()
198
+ } else {
199
+ this.unlockBodyScroll()
200
+ }
201
+ },
202
+ },
182
203
  mounted() {
204
+ if (this.active && !this.emptyNav) {
205
+ this.lockBodyScroll()
206
+ }
183
207
  if (!this.emptyNav) {
184
208
  this.setFocus()
185
209
  }
186
210
  },
211
+ beforeUnmount() {
212
+ this.unlockBodyScroll()
213
+ },
187
214
  methods: {
215
+ lockBodyScroll() {
216
+ if (this.bodyScrollLock) return
217
+ this.bodyScrollLock = acquireDocumentScrollLock()
218
+ },
219
+ unlockBodyScroll() {
220
+ if (!this.bodyScrollLock) return
221
+ releaseDocumentScrollLock(this.bodyScrollLock)
222
+ this.bodyScrollLock = null
223
+ },
188
224
  onFocus(event) {
189
225
  this.hasFocus = true
190
226
  this.focusedSectionIndex = 0
@@ -380,6 +416,7 @@ export default {
380
416
  display: flex;
381
417
  flex-direction: column;
382
418
  overflow: hidden;
419
+ overscroll-behavior: none;
383
420
  &:focus {
384
421
  // outline: none;
385
422
  }
@@ -393,6 +430,8 @@ export default {
393
430
  bottom: space(7);
394
431
  background: var(--website-menu__open-background-color, white);
395
432
  overflow: auto;
433
+ overscroll-behavior: contain;
434
+ -webkit-overflow-scrolling: touch;
396
435
  // overflow-y: scroll !important;
397
436
  }
398
437
  .asd20-table-of-contents {
@@ -493,8 +532,11 @@ export default {
493
532
  }
494
533
  }
495
534
  .asd20-site-menu__items {
535
+ flex: 1 1 auto;
536
+ min-height: 0;
496
537
  overflow-y: scroll; /* has to be scroll, not auto */
497
- // -webkit-overflow-scrolling: touch;
538
+ overscroll-behavior: contain;
539
+ -webkit-overflow-scrolling: touch;
498
540
  }
499
541
  .asd20-site-menu__item {
500
542
  display: block;
@@ -40,7 +40,7 @@
40
40
  /> -->
41
41
  <asd20-action-menu
42
42
  v-show="
43
- (menuOpen && (mq === 'sm' || mq === 'md') && isLoaded && !zoomed) ||
43
+ (menuOpen && (mq === 'sm' || mq === 'md') && isLoaded) ||
44
44
  ((mq === 'lg' || mq === 'xl') && isLoaded)
45
45
  "
46
46
  :items="actionItems"
@@ -54,7 +54,6 @@
54
54
  <asd20-site-menu
55
55
  v-show="menuOpen"
56
56
  ref="siteMenu"
57
- :class="zoomed ? 'zoomed' : ''"
58
57
  :active="menuOpen"
59
58
  :sections="navigation"
60
59
  :empty-nav="emptyNav"
@@ -290,11 +289,6 @@ export default {
290
289
  &.active::before {
291
290
  opacity: 1;
292
291
  }
293
- .zoomed {
294
- & :deep(.asd20-site-menu__viewport) {
295
- bottom: 0 !important;
296
- }
297
- }
298
292
  }
299
293
 
300
294
  @media (min-width: 1024px) {
@@ -563,6 +563,10 @@ import queryAiSite from '../../../helpers/queryAiSite'
563
563
  import queryFiles from '../../../helpers/queryFiles'
564
564
  import queryPeople from '../../../helpers/queryPeople'
565
565
  import queryPages, { NO_PAGE_RESULTS_FALLBACKS } from '../../../helpers/queryPages'
566
+ import {
567
+ acquireDocumentScrollLock,
568
+ releaseDocumentScrollLock,
569
+ } from '../../../helpers/documentScrollLock'
566
570
 
567
571
  // Mixins
568
572
  import globalPropMixinFactory from '../../../mixins/globalPropMixinFactory.js'
@@ -2162,18 +2166,13 @@ export default {
2162
2166
  },
2163
2167
 
2164
2168
  lockBodyScroll() {
2165
- if (typeof window === 'undefined' || typeof document === 'undefined') {
2166
- return
2167
- }
2168
2169
  if (this.bodyScrollLock) return
2169
- this.bodyScrollLock = true
2170
+ this.bodyScrollLock = acquireDocumentScrollLock()
2170
2171
  },
2171
2172
 
2172
2173
  unlockBodyScroll() {
2173
- if (typeof window === 'undefined' || typeof document === 'undefined') {
2174
- return
2175
- }
2176
2174
  if (!this.bodyScrollLock) return
2175
+ releaseDocumentScrollLock(this.bodyScrollLock)
2177
2176
  this.bodyScrollLock = null
2178
2177
  },
2179
2178
 
@@ -3601,6 +3600,7 @@ export default {
3601
3600
  display: flex;
3602
3601
  flex-direction: column;
3603
3602
  overflow: hidden;
3603
+ overscroll-behavior: none;
3604
3604
 
3605
3605
  /* allow descendants to use flex+overflow scrolling properly (Safari/iOS) */
3606
3606
  min-height: 0;
@@ -3852,6 +3852,8 @@ export default {
3852
3852
  border-radius: 0.5rem;
3853
3853
  padding: space(0.5);
3854
3854
  font: inherit;
3855
+ // Match the iOS-safe minimum used by Asd20SearchField.
3856
+ font-size: $base-font-size;
3855
3857
  color: var(--color__primary);
3856
3858
  background: white;
3857
3859
  box-sizing: border-box;
@@ -4188,11 +4190,6 @@ export default {
4188
4190
  transform: translate3d(0, -0.75rem, 0);
4189
4191
  }
4190
4192
 
4191
- :global(html.asd20-site-search--scroll-locked),
4192
- :global(body.asd20-site-search--scroll-locked) {
4193
- overscroll-behavior: none;
4194
- }
4195
-
4196
4193
  @media (min-width: 768px) {
4197
4194
  .asd20-site-search {
4198
4195
  .asd20-notificaiton {
@@ -117,6 +117,7 @@ export default {
117
117
  padding: 0 space(0.75);
118
118
  width: 100%;
119
119
  overflow-x: auto;
120
+ overflow-y: hidden;
120
121
  scrollbar-width: thin;
121
122
  }
122
123
 
@@ -325,11 +325,9 @@ export default {
325
325
  flex-direction: row;
326
326
  align-items: center;
327
327
  flex-wrap: wrap;
328
- margin: space(0.25);
329
328
  justify-content: flex-end;
330
- width: calc(100% - 1rem);
331
- position: absolute;
332
- top: space(0.25);
329
+ width: 100%;
330
+ margin: 0 space(0.5);
333
331
  .language-wrapper {
334
332
  display: flex;
335
333
  flex-direction: row;
@@ -338,7 +336,6 @@ export default {
338
336
  flex-wrap: wrap;
339
337
  gap: space(0.25);
340
338
  width: 100%;
341
- margin-bottom: space(0.25);
342
339
 
343
340
  .ai-search {
344
341
  order: 1;
@@ -352,6 +349,11 @@ export default {
352
349
 
353
350
  .asd20-language-translation {
354
351
  order: 3;
352
+ border-radius: 3rem;
353
+ & .asd20-select-input {
354
+ color: currentColor;
355
+ font-family: Arial, Helvetica, sans-serif;
356
+ }
355
357
  }
356
358
  .asd20-notification-group--floating {
357
359
  /* position: absolute;*/
@@ -365,20 +367,10 @@ export default {
365
367
  background: white;
366
368
  border-radius: 3rem;
367
369
  }
368
- .asd20-language-translation {
369
- z-index: 2;
370
- border-radius: 3rem;
371
- margin: 0 0.05rem;
372
- height: 2.75rem;
373
- & .asd20-select-input {
374
- padding: 0.375rem 0.375rem;
375
- color: currentColor;
376
- font-family: Arial, Helvetica, sans-serif;
377
- }
378
- }
379
370
  }
380
371
  .no-block-widget {
381
- top: 0;
372
+ position: relative;
373
+ top: unset;
382
374
  }
383
375
  .asd20-language-loader {
384
376
  order: 1;
@@ -417,25 +409,15 @@ export default {
417
409
  @media (min-width: 667px) {
418
410
  .asd20-school-home-video-template {
419
411
  .notification-translation-container {
420
- margin: 0;
412
+ margin: 0 space(1);
421
413
  justify-content: flex-end;
422
- position: absolute;
423
- right: space(0.5);
424
- top: space(0.5);
425
- width: auto;
426
414
  flex-wrap: nowrap;
427
415
  .language-wrapper {
428
416
  justify-content: flex-end;
429
- margin-bottom: 0;
430
- flex-wrap: nowrap;
431
417
  }
432
- .language-wrapper .ai-search {
433
- width: 100%;
434
- flex: 1 1 100%;
435
- order: 0;
436
- }
437
- .asd20-notification-group--floating {
438
- position: unset;
418
+ .no-block-widget {
419
+ position: relative;
420
+ top: unset;
439
421
  }
440
422
  }
441
423
  .asd20-quicklinks-menu {
@@ -466,6 +448,7 @@ export default {
466
448
  }
467
449
  }
468
450
  .notification-translation-container {
451
+ position: absolute;
469
452
  top: space(5.25);
470
453
  right: space(3.5);
471
454
  width: min(34rem, calc(100vw - 8rem));
@@ -0,0 +1,60 @@
1
+ const SCROLL_LOCK_CLASS = 'asd20-document-scroll-locked'
2
+
3
+ const activeLocks = new Set()
4
+ let originalDocumentStyles = null
5
+
6
+ export function acquireDocumentScrollLock() {
7
+ if (typeof document === 'undefined') return null
8
+
9
+ const docEl = document.documentElement
10
+ const body = document.body
11
+ if (!docEl || !body) return null
12
+
13
+ // A primitive token is preserved when stored in Vue reactive component data.
14
+ const lock = Symbol(SCROLL_LOCK_CLASS)
15
+
16
+ if (activeLocks.size === 0) {
17
+ originalDocumentStyles = {
18
+ bodyOverflow: body.style.overflow,
19
+ bodyOverscrollBehavior: body.style.overscrollBehavior,
20
+ docOverflow: docEl.style.overflow,
21
+ docOverscrollBehavior: docEl.style.overscrollBehavior,
22
+ bodyHadLockClass: body.classList.contains(SCROLL_LOCK_CLASS),
23
+ docHadLockClass: docEl.classList.contains(SCROLL_LOCK_CLASS),
24
+ }
25
+
26
+ body.classList.add(SCROLL_LOCK_CLASS)
27
+ docEl.classList.add(SCROLL_LOCK_CLASS)
28
+ body.style.overflow = 'hidden'
29
+ body.style.overscrollBehavior = 'none'
30
+ docEl.style.overflow = 'hidden'
31
+ docEl.style.overscrollBehavior = 'none'
32
+ }
33
+
34
+ activeLocks.add(lock)
35
+ return lock
36
+ }
37
+
38
+ export function releaseDocumentScrollLock(lock) {
39
+ if (!lock || !activeLocks.delete(lock) || activeLocks.size > 0) return
40
+ if (typeof document === 'undefined' || !originalDocumentStyles) return
41
+
42
+ const docEl = document.documentElement
43
+ const body = document.body
44
+ if (!docEl || !body) return
45
+
46
+ body.style.overflow = originalDocumentStyles.bodyOverflow
47
+ body.style.overscrollBehavior =
48
+ originalDocumentStyles.bodyOverscrollBehavior
49
+ docEl.style.overflow = originalDocumentStyles.docOverflow
50
+ docEl.style.overscrollBehavior = originalDocumentStyles.docOverscrollBehavior
51
+
52
+ if (!originalDocumentStyles.bodyHadLockClass) {
53
+ body.classList.remove(SCROLL_LOCK_CLASS)
54
+ }
55
+ if (!originalDocumentStyles.docHadLockClass) {
56
+ docEl.classList.remove(SCROLL_LOCK_CLASS)
57
+ }
58
+
59
+ originalDocumentStyles = null
60
+ }