@ionic/core 8.3.4-nightly.20241024 → 8.3.4-nightly.20241028

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 (84) hide show
  1. package/components/ion-input.js +20 -5
  2. package/components/ion-textarea.js +19 -4
  3. package/components/overlays.js +62 -7
  4. package/dist/cjs/index.cjs.js +1 -1
  5. package/dist/cjs/ion-action-sheet.cjs.entry.js +1 -1
  6. package/dist/cjs/ion-alert.cjs.entry.js +1 -1
  7. package/dist/cjs/ion-datetime_3.cjs.entry.js +1 -1
  8. package/dist/cjs/ion-input.cjs.entry.js +20 -5
  9. package/dist/cjs/ion-loading.cjs.entry.js +1 -1
  10. package/dist/cjs/ion-menu_3.cjs.entry.js +1 -1
  11. package/dist/cjs/ion-modal.cjs.entry.js +1 -1
  12. package/dist/cjs/ion-popover.cjs.entry.js +1 -1
  13. package/dist/cjs/ion-select_3.cjs.entry.js +1 -1
  14. package/dist/cjs/ion-textarea.cjs.entry.js +19 -4
  15. package/dist/cjs/ion-toast.cjs.entry.js +1 -1
  16. package/dist/cjs/{overlays-0123d7d4.js → overlays-ba0f6986.js} +62 -7
  17. package/dist/collection/components/input/input.js +20 -5
  18. package/dist/collection/components/textarea/textarea.js +19 -4
  19. package/dist/collection/utils/overlays.js +62 -7
  20. package/dist/docs.json +1 -1
  21. package/dist/esm/index.js +1 -1
  22. package/dist/esm/ion-action-sheet.entry.js +1 -1
  23. package/dist/esm/ion-alert.entry.js +1 -1
  24. package/dist/esm/ion-datetime_3.entry.js +1 -1
  25. package/dist/esm/ion-input.entry.js +20 -5
  26. package/dist/esm/ion-loading.entry.js +1 -1
  27. package/dist/esm/ion-menu_3.entry.js +1 -1
  28. package/dist/esm/ion-modal.entry.js +1 -1
  29. package/dist/esm/ion-popover.entry.js +1 -1
  30. package/dist/esm/ion-select_3.entry.js +1 -1
  31. package/dist/esm/ion-textarea.entry.js +19 -4
  32. package/dist/esm/ion-toast.entry.js +1 -1
  33. package/dist/esm/{overlays-9c75ec54.js → overlays-ae10d43d.js} +62 -7
  34. package/dist/esm-es5/index.js +1 -1
  35. package/dist/esm-es5/ion-action-sheet.entry.js +1 -1
  36. package/dist/esm-es5/ion-alert.entry.js +1 -1
  37. package/dist/esm-es5/ion-datetime_3.entry.js +1 -1
  38. package/dist/esm-es5/ion-input.entry.js +1 -1
  39. package/dist/esm-es5/ion-loading.entry.js +1 -1
  40. package/dist/esm-es5/ion-menu_3.entry.js +1 -1
  41. package/dist/esm-es5/ion-modal.entry.js +1 -1
  42. package/dist/esm-es5/ion-popover.entry.js +1 -1
  43. package/dist/esm-es5/ion-select_3.entry.js +1 -1
  44. package/dist/esm-es5/ion-textarea.entry.js +1 -1
  45. package/dist/esm-es5/ion-toast.entry.js +1 -1
  46. package/dist/esm-es5/overlays-ae10d43d.js +4 -0
  47. package/dist/ionic/index.esm.js +1 -1
  48. package/dist/ionic/ionic.esm.js +1 -1
  49. package/dist/ionic/p-04fc24ee.system.js +4 -0
  50. package/dist/ionic/{p-b4c950f8.entry.js → p-0c7e32d6.entry.js} +1 -1
  51. package/dist/ionic/{p-100b83fd.system.entry.js → p-110e03be.system.entry.js} +1 -1
  52. package/dist/ionic/p-148b8abb.entry.js +4 -0
  53. package/dist/ionic/{p-4b0fedb7.system.entry.js → p-18105026.system.entry.js} +1 -1
  54. package/dist/ionic/{p-f50ae0d5.system.entry.js → p-2031811e.system.entry.js} +1 -1
  55. package/dist/ionic/{p-cff5585e.system.entry.js → p-2dc74eb3.system.entry.js} +1 -1
  56. package/dist/ionic/p-322c5fb4.system.js +1 -1
  57. package/dist/ionic/p-5c831f49.js +4 -0
  58. package/dist/ionic/{p-a72fb8a1.system.entry.js → p-63d65dbc.system.entry.js} +1 -1
  59. package/dist/ionic/{p-9fef1364.entry.js → p-6d50faff.entry.js} +1 -1
  60. package/dist/ionic/{p-a4d51b8d.system.js → p-79b12fda.system.js} +1 -1
  61. package/dist/ionic/{p-8ed31163.system.entry.js → p-79e7be3a.system.entry.js} +1 -1
  62. package/dist/ionic/{p-7c50965e.entry.js → p-8640e9cd.entry.js} +1 -1
  63. package/dist/ionic/{p-9c23044d.entry.js → p-87a4407b.entry.js} +1 -1
  64. package/dist/ionic/{p-a41699db.entry.js → p-8ff21f38.entry.js} +1 -1
  65. package/dist/ionic/{p-b0568945.system.entry.js → p-921e159f.system.entry.js} +1 -1
  66. package/dist/ionic/{p-2b7c93b4.entry.js → p-9895e7f3.entry.js} +1 -1
  67. package/dist/ionic/{p-5e66bcf2.entry.js → p-9e208825.entry.js} +1 -1
  68. package/dist/ionic/{p-755b27f0.system.entry.js → p-be715dd3.system.entry.js} +1 -1
  69. package/dist/ionic/{p-22c020db.system.entry.js → p-c3cd6947.system.entry.js} +1 -1
  70. package/dist/ionic/{p-0fa0c92b.entry.js → p-c444705a.entry.js} +1 -1
  71. package/dist/ionic/{p-d77e12ca.entry.js → p-d5abc4b3.entry.js} +1 -1
  72. package/dist/ionic/p-dda5c73d.entry.js +4 -0
  73. package/dist/ionic/{p-9cc3bcc5.system.entry.js → p-f0ab13a8.system.entry.js} +1 -1
  74. package/dist/ionic/{p-ecb57d24.system.entry.js → p-fd2f3a15.system.entry.js} +1 -1
  75. package/dist/types/components/input/input.d.ts +3 -0
  76. package/dist/types/components/textarea/textarea.d.ts +3 -0
  77. package/hydrate/index.js +101 -16
  78. package/hydrate/index.mjs +101 -16
  79. package/package.json +1 -1
  80. package/dist/esm-es5/overlays-9c75ec54.js +0 -4
  81. package/dist/ionic/p-2c4bdd9d.entry.js +0 -4
  82. package/dist/ionic/p-98871496.system.js +0 -4
  83. package/dist/ionic/p-a440397c.js +0 -4
  84. package/dist/ionic/p-bfa2e81c.entry.js +0 -4
@@ -25,6 +25,8 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
25
25
  this.ionBlur = createEvent(this, "ionBlur", 7);
26
26
  this.ionFocus = createEvent(this, "ionFocus", 7);
27
27
  this.inputId = `ion-input-${inputIds++}`;
28
+ this.helperTextId = `${this.inputId}-helper-text`;
29
+ this.errorTextId = `${this.inputId}-error-text`;
28
30
  this.inheritedAttributes = {};
29
31
  this.isComposing = false;
30
32
  /**
@@ -297,8 +299,21 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
297
299
  * Renders the helper text or error text values
298
300
  */
299
301
  renderHintText() {
300
- const { helperText, errorText } = this;
301
- return [h("div", { class: "helper-text" }, helperText), h("div", { class: "error-text" }, errorText)];
302
+ const { helperText, errorText, helperTextId, errorTextId } = this;
303
+ return [
304
+ h("div", { id: helperTextId, class: "helper-text" }, helperText),
305
+ h("div", { id: errorTextId, class: "error-text" }, errorText),
306
+ ];
307
+ }
308
+ getHintTextID() {
309
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
310
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
311
+ return errorTextId;
312
+ }
313
+ if (helperText) {
314
+ return helperTextId;
315
+ }
316
+ return undefined;
302
317
  }
303
318
  renderCounter() {
304
319
  const { counter, maxlength, counterFormatter, value } = this;
@@ -405,7 +420,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
405
420
  * TODO(FW-5592): Remove hasStartEndSlots condition
406
421
  */
407
422
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
408
- return (h(Host, { key: '907ce98a82b5cfae5a08504cd79e00a2330b7444', class: createColorClasses(this.color, {
423
+ return (h(Host, { key: 'cdde9963d71685e6a4f3f3b3bc52e75d022435cb', class: createColorClasses(this.color, {
409
424
  [mode]: true,
410
425
  'has-value': hasValue,
411
426
  'has-focus': hasFocus,
@@ -416,7 +431,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
416
431
  'in-item': inItem,
417
432
  'in-item-color': hostContext('ion-item.ion-color', this.el),
418
433
  'input-disabled': disabled,
419
- }) }, h("label", { key: '59d5bb45d2a5b828bba0ed8687a632a551e2f4d8', class: "input-wrapper", htmlFor: inputId }, this.renderLabelContainer(), h("div", { key: 'f93f129d08246d0e9a601c100d718534d6403853', class: "native-wrapper" }, h("slot", { key: '54eeb1a6bace662b7eb0d7e27180ea3d7e3a3729', name: "start" }), h("input", Object.assign({ key: 'b3e0be55bc1a4a539ae3b0fdcf7fc078723cca16', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: '5f6373504a6d0d074bfbf875c794d45ea2748175', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
434
+ }) }, h("label", { key: '4412791c93405f20e50e50363879265180b6078f', class: "input-wrapper", htmlFor: inputId }, this.renderLabelContainer(), h("div", { key: '5c71c43ef3eeb44dde76ed64ce10763834c8fbc7', class: "native-wrapper" }, h("slot", { key: '1d86b58dc299a4208dd02ccdc9ceaea7576698c3', name: "start" }), h("input", Object.assign({ key: '7fc932174485f7d09e6a797dfd49ea11fbad71b6', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (h("button", { key: '46de455e4e308b8d7640b04778f7c7559b4124c6', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
420
435
  /**
421
436
  * This prevents mobile browsers from
422
437
  * blurring the input when the clear
@@ -431,7 +446,7 @@ const Input = /*@__PURE__*/ proxyCustomElement(class Input extends HTMLElement {
431
446
  * for screen readers as it means users would be unable to swipe past the clear button.
432
447
  */
433
448
  ev.stopPropagation();
434
- }, onClick: this.clearTextInput }, h("ion-icon", { key: '230d77973aa83458ceb32bf52e3abe9bc322cfe6', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '9d69ac6e8a3c4b2b303dba2478f82695d5755ed2', name: "end" })), shouldRenderHighlight && h("div", { key: 'ac61f16237ce731e0745ab72d0fc3f066252464a', class: "input-highlight" })), this.renderBottomContent()));
449
+ }, onClick: this.clearTextInput }, h("ion-icon", { key: '375e860a20e1807ce0db0e1934a0650fe9929966', "aria-hidden": "true", icon: clearIconData }))), h("slot", { key: '859d47a1b88aa8377479e5d11e52a6a1c1c30e7e', name: "end" })), shouldRenderHighlight && h("div", { key: 'd2d4286b348107e33247b5c0afda0f8bd697ecee', class: "input-highlight" })), this.renderBottomContent()));
435
450
  }
436
451
  get el() { return this; }
437
452
  static get watchers() { return {
@@ -23,6 +23,8 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
23
23
  this.ionBlur = createEvent(this, "ionBlur", 7);
24
24
  this.ionFocus = createEvent(this, "ionFocus", 7);
25
25
  this.inputId = `ion-textarea-${textareaIds++}`;
26
+ this.helperTextId = `${this.inputId}-helper-text`;
27
+ this.errorTextId = `${this.inputId}-error-text`;
26
28
  /**
27
29
  * `true` if the textarea was cleared as a result of the user typing
28
30
  * with `clearOnEdit` enabled.
@@ -310,8 +312,21 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
310
312
  * Renders the helper text or error text values
311
313
  */
312
314
  renderHintText() {
313
- const { helperText, errorText } = this;
314
- return [h("div", { class: "helper-text" }, helperText), h("div", { class: "error-text" }, errorText)];
315
+ const { helperText, errorText, helperTextId, errorTextId } = this;
316
+ return [
317
+ h("div", { id: helperTextId, class: "helper-text" }, helperText),
318
+ h("div", { id: errorTextId, class: "error-text" }, errorText),
319
+ ];
320
+ }
321
+ getHintTextID() {
322
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
323
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
324
+ return errorTextId;
325
+ }
326
+ if (helperText) {
327
+ return helperTextId;
328
+ }
329
+ return undefined;
315
330
  }
316
331
  renderCounter() {
317
332
  const { counter, maxlength, counterFormatter, value } = this;
@@ -364,7 +379,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
364
379
  * TODO(FW-5592): Remove hasStartEndSlots condition
365
380
  */
366
381
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
367
- return (h(Host, { key: '37595a18d77dea1a337ac1c672e5f08a4128111d', class: createColorClasses(this.color, {
382
+ return (h(Host, { key: 'e8a5b5727c6a018bbd6f5507b690bc5f0959e352', class: createColorClasses(this.color, {
368
383
  [mode]: true,
369
384
  'has-value': hasValue,
370
385
  'has-focus': hasFocus,
@@ -373,7 +388,7 @@ const Textarea = /*@__PURE__*/ proxyCustomElement(class Textarea extends HTMLEle
373
388
  [`textarea-shape-${shape}`]: shape !== undefined,
374
389
  [`textarea-label-placement-${labelPlacement}`]: true,
375
390
  'textarea-disabled': disabled,
376
- }) }, h("label", { key: '67342758743e5a40448a32ff695876d39778621f', class: "textarea-wrapper", htmlFor: inputId }, this.renderLabelContainer(), h("div", { key: 'a994be8264bf5652811cf816d79a04178954e83f', class: "textarea-wrapper-inner" }, h("div", { key: 'e09c93ebcd5b3d227d51e682ca23dfc7fd7027ad', class: "start-slot-wrapper" }, h("slot", { key: 'd39758f21f19ae70aea21e9a9a7b7c20991fe593', name: "start" })), h("div", { key: '6a4e10e53de4bb235ee30def4c9ae5bdee888816', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '9e254e551f124d972e9bc6b09ef0f2bb55890be5', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown }, this.inheritedAttributes), value)), h("div", { key: 'a66aa2d2bc4778a0bec56a8b9ec9052a832eb3b2', class: "end-slot-wrapper" }, h("slot", { key: '8e6a90b4475de32e23afc593da4108610dcca663', name: "end" }))), shouldRenderHighlight && h("div", { key: '6da03205a8daff45581b20f0af3938634a9d5f8c', class: "textarea-highlight" })), this.renderBottomContent()));
391
+ }) }, h("label", { key: '48e889571e2d3e7e150b038e4b4b15862b418288', class: "textarea-wrapper", htmlFor: inputId }, this.renderLabelContainer(), h("div", { key: '6c6c7872ae4d351d8b8d772ad18ce3ba7a0e9a87', class: "textarea-wrapper-inner" }, h("div", { key: '03aef5dfa59af0fa78a3e5c92e0ed72396717d72', class: "start-slot-wrapper" }, h("slot", { key: '39aee9faebb0f1d10de5a5817fd9d9771b074b96', name: "start" })), h("div", { key: '738e8ff603f4c9b9083b3139db861f4b7ec20f79', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, h("textarea", Object.assign({ key: '7763fb4f8ffe94635167331c70d21d661544c4f8', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId }, this.inheritedAttributes), value)), h("div", { key: 'b3af9ec0982c6dbd381afaa02df153d7e90471be', class: "end-slot-wrapper" }, h("slot", { key: '60f99bcd49a5ec2e1fa6e3e77809aefeb9d2d0d6', name: "end" }))), shouldRenderHighlight && h("div", { key: '6a7f45639ba027f4da573adae53f1197a6dec383', class: "textarea-highlight" })), this.renderBottomContent()));
377
392
  }
378
393
  get el() { return this; }
379
394
  static get watchers() { return {
@@ -500,9 +500,19 @@ const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts)
500
500
  if (overlay.presented) {
501
501
  return;
502
502
  }
503
- setRootAriaHidden(true);
503
+ /**
504
+ * Due to accessibility guidelines, toasts do not have
505
+ * focus traps.
506
+ *
507
+ * All other overlays should have focus traps to prevent
508
+ * the keyboard focus from leaving the overlay.
509
+ */
510
+ if (overlay.el.tagName !== 'ION-TOAST') {
511
+ setRootAriaHidden(true);
512
+ }
504
513
  document.body.classList.add(BACKDROP_NO_SCROLL);
505
- hideOverlaysFromScreenReaders(overlay.el);
514
+ hideUnderlyingOverlaysFromScreenReaders(overlay.el);
515
+ hideAnimatingOverlayFromScreenReaders(overlay.el);
506
516
  overlay.presented = true;
507
517
  overlay.willPresent.emit();
508
518
  (_a = overlay.willPresentShorthand) === null || _a === void 0 ? void 0 : _a.emit();
@@ -542,6 +552,11 @@ const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts)
542
552
  * it would still have aria-hidden on being presented again.
543
553
  * Removing it here ensures the overlay is visible to screen
544
554
  * readers.
555
+ *
556
+ * If this overlay was being presented, then it was hidden
557
+ * from screen readers during the animation. Now that the
558
+ * animation is complete, we can reveal the overlay to
559
+ * screen readers.
545
560
  */
546
561
  overlay.el.removeAttribute('aria-hidden');
547
562
  };
@@ -599,17 +614,35 @@ const dismiss = async (overlay, data, role, name, iosLeaveAnimation, mdLeaveAnim
599
614
  if (!overlay.presented) {
600
615
  return false;
601
616
  }
602
- const lastOverlay = doc !== undefined && getPresentedOverlays(doc).length === 1;
603
617
  /**
604
- * If this is the last visible overlay then
605
- * we want to re-add the root to the accessibility tree.
618
+ * For accessibility, toasts lack focus traps and don’t receive
619
+ * `aria-hidden` on the root element when presented.
620
+ *
621
+ * All other overlays use focus traps to keep keyboard focus
622
+ * within the overlay, setting `aria-hidden` on the root element
623
+ * to enhance accessibility.
624
+ *
625
+ * Therefore, we must remove `aria-hidden` from the root element
626
+ * when the last non-toast overlay is dismissed.
606
627
  */
607
- if (lastOverlay) {
628
+ const overlaysNotToast = doc !== undefined ? getPresentedOverlays(doc).filter((o) => o.tagName !== 'ION-TOAST') : [];
629
+ const lastOverlayNotToast = overlaysNotToast.length === 1 && overlaysNotToast[0].id === overlay.el.id;
630
+ /**
631
+ * If this is the last visible overlay that is not a toast
632
+ * then we want to re-add the root to the accessibility tree.
633
+ */
634
+ if (lastOverlayNotToast) {
608
635
  setRootAriaHidden(false);
609
636
  document.body.classList.remove(BACKDROP_NO_SCROLL);
610
637
  }
611
638
  overlay.presented = false;
612
639
  try {
640
+ /**
641
+ * There is no need to show the overlay to screen readers during
642
+ * the dismiss animation. This is because the overlay will be removed
643
+ * from the DOM after the animation is complete.
644
+ */
645
+ hideAnimatingOverlayFromScreenReaders(overlay.el);
613
646
  // Overlay contents should not be clickable during dismiss
614
647
  overlay.el.style.setProperty('pointer-events', 'none');
615
648
  overlay.willDismiss.emit({ data, role });
@@ -844,6 +877,28 @@ const createTriggerController = () => {
844
877
  removeClickListener,
845
878
  };
846
879
  };
880
+ /**
881
+ * The overlay that is being animated also needs to hide from screen
882
+ * readers during its animation. This ensures that assistive technologies
883
+ * like TalkBack do not announce or interact with the content until the
884
+ * animation is complete, avoiding confusion for users.
885
+ *
886
+ * If the overlay is being presented, it prevents focus rings from appearing
887
+ * in incorrect positions due to the transition (specifically `transform`
888
+ * styles), ensuring that when aria-hidden is removed, the focus rings are
889
+ * correctly displayed in the final location of the elements.
890
+ *
891
+ * @param overlay - The overlay that is being animated.
892
+ */
893
+ const hideAnimatingOverlayFromScreenReaders = (overlay) => {
894
+ if (doc === undefined)
895
+ return;
896
+ /**
897
+ * Once the animation is complete, this attribute will be removed.
898
+ * This is done at the end of the `present` method.
899
+ */
900
+ overlay.setAttribute('aria-hidden', 'true');
901
+ };
847
902
  /**
848
903
  * Ensure that underlying overlays have aria-hidden if necessary so that screen readers
849
904
  * cannot move focus to these elements. Note that we cannot rely on focus/focusin/focusout
@@ -854,7 +909,7 @@ const createTriggerController = () => {
854
909
  * @param newTopMostOverlay - The overlay that is being presented. Since the overlay has not been
855
910
  * fully presented yet at the time this function is called it will not be included in the getPresentedOverlays result.
856
911
  */
857
- const hideOverlaysFromScreenReaders = (newTopMostOverlay) => {
912
+ const hideUnderlyingOverlaysFromScreenReaders = (newTopMostOverlay) => {
858
913
  var _a;
859
914
  if (doc === undefined)
860
915
  return;
@@ -16,7 +16,7 @@ const helpers = require('./helpers-afaa9001.js');
16
16
  const config = require('./config-4f60b98a.js');
17
17
  const theme = require('./theme-d1c573d2.js');
18
18
  const index$2 = require('./index-9cd00dc3.js');
19
- const overlays = require('./overlays-0123d7d4.js');
19
+ const overlays = require('./overlays-ba0f6986.js');
20
20
  require('./index-c8d52405.js');
21
21
  require('./index-73f75efb.js');
22
22
  require('./index-5915f9b3.js');
@@ -9,7 +9,7 @@ const index = require('./index-73f75efb.js');
9
9
  const buttonActive = require('./button-active-3f2f60b4.js');
10
10
  const helpers = require('./helpers-afaa9001.js');
11
11
  const lockController = require('./lock-controller-6585a42a.js');
12
- const overlays = require('./overlays-0123d7d4.js');
12
+ const overlays = require('./overlays-ba0f6986.js');
13
13
  const theme = require('./theme-d1c573d2.js');
14
14
  const ionicGlobal = require('./ionic-global-d9a8bb5b.js');
15
15
  const animation = require('./animation-b4fdf128.js');
@@ -10,7 +10,7 @@ const config = require('./config-4f60b98a.js');
10
10
  const buttonActive = require('./button-active-3f2f60b4.js');
11
11
  const helpers = require('./helpers-afaa9001.js');
12
12
  const lockController = require('./lock-controller-6585a42a.js');
13
- const overlays = require('./overlays-0123d7d4.js');
13
+ const overlays = require('./overlays-ba0f6986.js');
14
14
  const theme = require('./theme-d1c573d2.js');
15
15
  const ionicGlobal = require('./ionic-global-d9a8bb5b.js');
16
16
  const animation = require('./animation-b4fdf128.js');
@@ -15,7 +15,7 @@ const index$2 = require('./index-073c7cdc.js');
15
15
  const ionicGlobal = require('./ionic-global-d9a8bb5b.js');
16
16
  const data = require('./data-21dc0f81.js');
17
17
  const lockController = require('./lock-controller-6585a42a.js');
18
- const overlays = require('./overlays-0123d7d4.js');
18
+ const overlays = require('./overlays-ba0f6986.js');
19
19
  const animation = require('./animation-b4fdf128.js');
20
20
  const haptic = require('./haptic-f6b37aa3.js');
21
21
  require('./index-c8d52405.js');
@@ -29,6 +29,8 @@ const Input = class {
29
29
  this.ionBlur = index.createEvent(this, "ionBlur", 7);
30
30
  this.ionFocus = index.createEvent(this, "ionFocus", 7);
31
31
  this.inputId = `ion-input-${inputIds++}`;
32
+ this.helperTextId = `${this.inputId}-helper-text`;
33
+ this.errorTextId = `${this.inputId}-error-text`;
32
34
  this.inheritedAttributes = {};
33
35
  this.isComposing = false;
34
36
  /**
@@ -301,8 +303,21 @@ const Input = class {
301
303
  * Renders the helper text or error text values
302
304
  */
303
305
  renderHintText() {
304
- const { helperText, errorText } = this;
305
- return [index.h("div", { class: "helper-text" }, helperText), index.h("div", { class: "error-text" }, errorText)];
306
+ const { helperText, errorText, helperTextId, errorTextId } = this;
307
+ return [
308
+ index.h("div", { id: helperTextId, class: "helper-text" }, helperText),
309
+ index.h("div", { id: errorTextId, class: "error-text" }, errorText),
310
+ ];
311
+ }
312
+ getHintTextID() {
313
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
314
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
315
+ return errorTextId;
316
+ }
317
+ if (helperText) {
318
+ return helperTextId;
319
+ }
320
+ return undefined;
306
321
  }
307
322
  renderCounter() {
308
323
  const { counter, maxlength, counterFormatter, value } = this;
@@ -409,7 +424,7 @@ const Input = class {
409
424
  * TODO(FW-5592): Remove hasStartEndSlots condition
410
425
  */
411
426
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
412
- return (index.h(index.Host, { key: '907ce98a82b5cfae5a08504cd79e00a2330b7444', class: theme.createColorClasses(this.color, {
427
+ return (index.h(index.Host, { key: 'cdde9963d71685e6a4f3f3b3bc52e75d022435cb', class: theme.createColorClasses(this.color, {
413
428
  [mode]: true,
414
429
  'has-value': hasValue,
415
430
  'has-focus': hasFocus,
@@ -420,7 +435,7 @@ const Input = class {
420
435
  'in-item': inItem,
421
436
  'in-item-color': theme.hostContext('ion-item.ion-color', this.el),
422
437
  'input-disabled': disabled,
423
- }) }, index.h("label", { key: '59d5bb45d2a5b828bba0ed8687a632a551e2f4d8', class: "input-wrapper", htmlFor: inputId }, this.renderLabelContainer(), index.h("div", { key: 'f93f129d08246d0e9a601c100d718534d6403853', class: "native-wrapper" }, index.h("slot", { key: '54eeb1a6bace662b7eb0d7e27180ea3d7e3a3729', name: "start" }), index.h("input", Object.assign({ key: 'b3e0be55bc1a4a539ae3b0fdcf7fc078723cca16', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (index.h("button", { key: '5f6373504a6d0d074bfbf875c794d45ea2748175', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
438
+ }) }, index.h("label", { key: '4412791c93405f20e50e50363879265180b6078f', class: "input-wrapper", htmlFor: inputId }, this.renderLabelContainer(), index.h("div", { key: '5c71c43ef3eeb44dde76ed64ce10763834c8fbc7', class: "native-wrapper" }, index.h("slot", { key: '1d86b58dc299a4208dd02ccdc9ceaea7576698c3', name: "start" }), index.h("input", Object.assign({ key: '7fc932174485f7d09e6a797dfd49ea11fbad71b6', class: "native-input", ref: (input) => (this.nativeInput = input), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoComplete: this.autocomplete, autoCorrect: this.autocorrect, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, min: this.min, max: this.max, minLength: this.minlength, maxLength: this.maxlength, multiple: this.multiple, name: this.name, pattern: this.pattern, placeholder: this.placeholder || '', readOnly: readonly, required: this.required, spellcheck: this.spellcheck, step: this.step, type: this.type, value: value, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeydown, onCompositionstart: this.onCompositionStart, onCompositionend: this.onCompositionEnd, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId }, this.inheritedAttributes)), this.clearInput && !readonly && !disabled && (index.h("button", { key: '46de455e4e308b8d7640b04778f7c7559b4124c6', "aria-label": "reset", type: "button", class: "input-clear-icon", onPointerDown: (ev) => {
424
439
  /**
425
440
  * This prevents mobile browsers from
426
441
  * blurring the input when the clear
@@ -435,7 +450,7 @@ const Input = class {
435
450
  * for screen readers as it means users would be unable to swipe past the clear button.
436
451
  */
437
452
  ev.stopPropagation();
438
- }, onClick: this.clearTextInput }, index.h("ion-icon", { key: '230d77973aa83458ceb32bf52e3abe9bc322cfe6', "aria-hidden": "true", icon: clearIconData }))), index.h("slot", { key: '9d69ac6e8a3c4b2b303dba2478f82695d5755ed2', name: "end" })), shouldRenderHighlight && index.h("div", { key: 'ac61f16237ce731e0745ab72d0fc3f066252464a', class: "input-highlight" })), this.renderBottomContent()));
453
+ }, onClick: this.clearTextInput }, index.h("ion-icon", { key: '375e860a20e1807ce0db0e1934a0650fe9929966', "aria-hidden": "true", icon: clearIconData }))), index.h("slot", { key: '859d47a1b88aa8377479e5d11e52a6a1c1c30e7e', name: "end" })), shouldRenderHighlight && index.h("div", { key: 'd2d4286b348107e33247b5c0afda0f8bd697ecee', class: "input-highlight" })), this.renderBottomContent()));
439
454
  }
440
455
  get el() { return index.getElement(this); }
441
456
  static get watchers() { return {
@@ -9,7 +9,7 @@ const index = require('./index-73f75efb.js');
9
9
  const config = require('./config-4f60b98a.js');
10
10
  const helpers = require('./helpers-afaa9001.js');
11
11
  const lockController = require('./lock-controller-6585a42a.js');
12
- const overlays = require('./overlays-0123d7d4.js');
12
+ const overlays = require('./overlays-ba0f6986.js');
13
13
  const theme = require('./theme-d1c573d2.js');
14
14
  const ionicGlobal = require('./ionic-global-d9a8bb5b.js');
15
15
  const animation = require('./animation-b4fdf128.js');
@@ -7,7 +7,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
7
7
 
8
8
  const index = require('./index-73f75efb.js');
9
9
  const cubicBezier = require('./cubic-bezier-f2dccc53.js');
10
- const overlays = require('./overlays-0123d7d4.js');
10
+ const overlays = require('./overlays-ba0f6986.js');
11
11
  const gestureController = require('./gesture-controller-9436f482.js');
12
12
  const hardwareBackButton = require('./hardware-back-button-9e8a2c4f.js');
13
13
  const helpers = require('./helpers-afaa9001.js');
@@ -12,7 +12,7 @@ const helpers = require('./helpers-afaa9001.js');
12
12
  const lockController = require('./lock-controller-6585a42a.js');
13
13
  const index$4 = require('./index-5915f9b3.js');
14
14
  const capacitor = require('./capacitor-c04564bf.js');
15
- const overlays = require('./overlays-0123d7d4.js');
15
+ const overlays = require('./overlays-ba0f6986.js');
16
16
  const theme = require('./theme-d1c573d2.js');
17
17
  const index$5 = require('./index-f05acd21.js');
18
18
  const ionicGlobal = require('./ionic-global-d9a8bb5b.js');
@@ -6,7 +6,7 @@
6
6
  Object.defineProperty(exports, '__esModule', { value: true });
7
7
 
8
8
  const index = require('./index-73f75efb.js');
9
- const overlays = require('./overlays-0123d7d4.js');
9
+ const overlays = require('./overlays-ba0f6986.js');
10
10
  const frameworkDelegate = require('./framework-delegate-55f5683a.js');
11
11
  const helpers = require('./helpers-afaa9001.js');
12
12
  const lockController = require('./lock-controller-6585a42a.js');
@@ -9,7 +9,7 @@ const index = require('./index-73f75efb.js');
9
9
  const notchController = require('./notch-controller-d69150f5.js');
10
10
  const compareWithUtils = require('./compare-with-utils-df1001d7.js');
11
11
  const helpers = require('./helpers-afaa9001.js');
12
- const overlays = require('./overlays-0123d7d4.js');
12
+ const overlays = require('./overlays-ba0f6986.js');
13
13
  const dir = require('./dir-94c21456.js');
14
14
  const theme = require('./theme-d1c573d2.js');
15
15
  const watchOptions = require('./watch-options-f5f3e158.js');
@@ -28,6 +28,8 @@ const Textarea = class {
28
28
  this.ionBlur = index.createEvent(this, "ionBlur", 7);
29
29
  this.ionFocus = index.createEvent(this, "ionFocus", 7);
30
30
  this.inputId = `ion-textarea-${textareaIds++}`;
31
+ this.helperTextId = `${this.inputId}-helper-text`;
32
+ this.errorTextId = `${this.inputId}-error-text`;
31
33
  /**
32
34
  * `true` if the textarea was cleared as a result of the user typing
33
35
  * with `clearOnEdit` enabled.
@@ -315,8 +317,21 @@ const Textarea = class {
315
317
  * Renders the helper text or error text values
316
318
  */
317
319
  renderHintText() {
318
- const { helperText, errorText } = this;
319
- return [index.h("div", { class: "helper-text" }, helperText), index.h("div", { class: "error-text" }, errorText)];
320
+ const { helperText, errorText, helperTextId, errorTextId } = this;
321
+ return [
322
+ index.h("div", { id: helperTextId, class: "helper-text" }, helperText),
323
+ index.h("div", { id: errorTextId, class: "error-text" }, errorText),
324
+ ];
325
+ }
326
+ getHintTextID() {
327
+ const { el, helperText, errorText, helperTextId, errorTextId } = this;
328
+ if (el.classList.contains('ion-touched') && el.classList.contains('ion-invalid') && errorText) {
329
+ return errorTextId;
330
+ }
331
+ if (helperText) {
332
+ return helperTextId;
333
+ }
334
+ return undefined;
320
335
  }
321
336
  renderCounter() {
322
337
  const { counter, maxlength, counterFormatter, value } = this;
@@ -369,7 +384,7 @@ const Textarea = class {
369
384
  * TODO(FW-5592): Remove hasStartEndSlots condition
370
385
  */
371
386
  const labelShouldFloat = labelPlacement === 'stacked' || (labelPlacement === 'floating' && (hasValue || hasFocus || hasStartEndSlots));
372
- return (index.h(index.Host, { key: '37595a18d77dea1a337ac1c672e5f08a4128111d', class: theme.createColorClasses(this.color, {
387
+ return (index.h(index.Host, { key: 'e8a5b5727c6a018bbd6f5507b690bc5f0959e352', class: theme.createColorClasses(this.color, {
373
388
  [mode]: true,
374
389
  'has-value': hasValue,
375
390
  'has-focus': hasFocus,
@@ -378,7 +393,7 @@ const Textarea = class {
378
393
  [`textarea-shape-${shape}`]: shape !== undefined,
379
394
  [`textarea-label-placement-${labelPlacement}`]: true,
380
395
  'textarea-disabled': disabled,
381
- }) }, index.h("label", { key: '67342758743e5a40448a32ff695876d39778621f', class: "textarea-wrapper", htmlFor: inputId }, this.renderLabelContainer(), index.h("div", { key: 'a994be8264bf5652811cf816d79a04178954e83f', class: "textarea-wrapper-inner" }, index.h("div", { key: 'e09c93ebcd5b3d227d51e682ca23dfc7fd7027ad', class: "start-slot-wrapper" }, index.h("slot", { key: 'd39758f21f19ae70aea21e9a9a7b7c20991fe593', name: "start" })), index.h("div", { key: '6a4e10e53de4bb235ee30def4c9ae5bdee888816', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, index.h("textarea", Object.assign({ key: '9e254e551f124d972e9bc6b09ef0f2bb55890be5', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown }, this.inheritedAttributes), value)), index.h("div", { key: 'a66aa2d2bc4778a0bec56a8b9ec9052a832eb3b2', class: "end-slot-wrapper" }, index.h("slot", { key: '8e6a90b4475de32e23afc593da4108610dcca663', name: "end" }))), shouldRenderHighlight && index.h("div", { key: '6da03205a8daff45581b20f0af3938634a9d5f8c', class: "textarea-highlight" })), this.renderBottomContent()));
396
+ }) }, index.h("label", { key: '48e889571e2d3e7e150b038e4b4b15862b418288', class: "textarea-wrapper", htmlFor: inputId }, this.renderLabelContainer(), index.h("div", { key: '6c6c7872ae4d351d8b8d772ad18ce3ba7a0e9a87', class: "textarea-wrapper-inner" }, index.h("div", { key: '03aef5dfa59af0fa78a3e5c92e0ed72396717d72', class: "start-slot-wrapper" }, index.h("slot", { key: '39aee9faebb0f1d10de5a5817fd9d9771b074b96', name: "start" })), index.h("div", { key: '738e8ff603f4c9b9083b3139db861f4b7ec20f79', class: "native-wrapper", ref: (el) => (this.textareaWrapper = el) }, index.h("textarea", Object.assign({ key: '7763fb4f8ffe94635167331c70d21d661544c4f8', class: "native-textarea", ref: (el) => (this.nativeInput = el), id: inputId, disabled: disabled, autoCapitalize: this.autocapitalize, autoFocus: this.autofocus, enterKeyHint: this.enterkeyhint, inputMode: this.inputmode, minLength: this.minlength, maxLength: this.maxlength, name: this.name, placeholder: this.placeholder || '', readOnly: this.readonly, required: this.required, spellcheck: this.spellcheck, cols: this.cols, rows: this.rows, wrap: this.wrap, onInput: this.onInput, onChange: this.onChange, onBlur: this.onBlur, onFocus: this.onFocus, onKeyDown: this.onKeyDown, "aria-describedby": this.getHintTextID(), "aria-invalid": this.getHintTextID() === this.errorTextId }, this.inheritedAttributes), value)), index.h("div", { key: 'b3af9ec0982c6dbd381afaa02df153d7e90471be', class: "end-slot-wrapper" }, index.h("slot", { key: '60f99bcd49a5ec2e1fa6e3e77809aefeb9d2d0d6', name: "end" }))), shouldRenderHighlight && index.h("div", { key: '6a7f45639ba027f4da573adae53f1197a6dec383', class: "textarea-highlight" })), this.renderBottomContent()));
382
397
  }
383
398
  get el() { return index.getElement(this); }
384
399
  static get watchers() { return {
@@ -10,7 +10,7 @@ const config = require('./config-4f60b98a.js');
10
10
  const helpers = require('./helpers-afaa9001.js');
11
11
  const lockController = require('./lock-controller-6585a42a.js');
12
12
  const index$1 = require('./index-5915f9b3.js');
13
- const overlays = require('./overlays-0123d7d4.js');
13
+ const overlays = require('./overlays-ba0f6986.js');
14
14
  const theme = require('./theme-d1c573d2.js');
15
15
  const ionicGlobal = require('./ionic-global-d9a8bb5b.js');
16
16
  const animation = require('./animation-b4fdf128.js');
@@ -502,9 +502,19 @@ const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts)
502
502
  if (overlay.presented) {
503
503
  return;
504
504
  }
505
- setRootAriaHidden(true);
505
+ /**
506
+ * Due to accessibility guidelines, toasts do not have
507
+ * focus traps.
508
+ *
509
+ * All other overlays should have focus traps to prevent
510
+ * the keyboard focus from leaving the overlay.
511
+ */
512
+ if (overlay.el.tagName !== 'ION-TOAST') {
513
+ setRootAriaHidden(true);
514
+ }
506
515
  document.body.classList.add(gestureController.BACKDROP_NO_SCROLL);
507
- hideOverlaysFromScreenReaders(overlay.el);
516
+ hideUnderlyingOverlaysFromScreenReaders(overlay.el);
517
+ hideAnimatingOverlayFromScreenReaders(overlay.el);
508
518
  overlay.presented = true;
509
519
  overlay.willPresent.emit();
510
520
  (_a = overlay.willPresentShorthand) === null || _a === void 0 ? void 0 : _a.emit();
@@ -544,6 +554,11 @@ const present = async (overlay, name, iosEnterAnimation, mdEnterAnimation, opts)
544
554
  * it would still have aria-hidden on being presented again.
545
555
  * Removing it here ensures the overlay is visible to screen
546
556
  * readers.
557
+ *
558
+ * If this overlay was being presented, then it was hidden
559
+ * from screen readers during the animation. Now that the
560
+ * animation is complete, we can reveal the overlay to
561
+ * screen readers.
547
562
  */
548
563
  overlay.el.removeAttribute('aria-hidden');
549
564
  };
@@ -601,17 +616,35 @@ const dismiss = async (overlay, data, role, name, iosLeaveAnimation, mdLeaveAnim
601
616
  if (!overlay.presented) {
602
617
  return false;
603
618
  }
604
- const lastOverlay = index.doc !== undefined && getPresentedOverlays(index.doc).length === 1;
605
619
  /**
606
- * If this is the last visible overlay then
607
- * we want to re-add the root to the accessibility tree.
620
+ * For accessibility, toasts lack focus traps and don’t receive
621
+ * `aria-hidden` on the root element when presented.
622
+ *
623
+ * All other overlays use focus traps to keep keyboard focus
624
+ * within the overlay, setting `aria-hidden` on the root element
625
+ * to enhance accessibility.
626
+ *
627
+ * Therefore, we must remove `aria-hidden` from the root element
628
+ * when the last non-toast overlay is dismissed.
608
629
  */
609
- if (lastOverlay) {
630
+ const overlaysNotToast = index.doc !== undefined ? getPresentedOverlays(index.doc).filter((o) => o.tagName !== 'ION-TOAST') : [];
631
+ const lastOverlayNotToast = overlaysNotToast.length === 1 && overlaysNotToast[0].id === overlay.el.id;
632
+ /**
633
+ * If this is the last visible overlay that is not a toast
634
+ * then we want to re-add the root to the accessibility tree.
635
+ */
636
+ if (lastOverlayNotToast) {
610
637
  setRootAriaHidden(false);
611
638
  document.body.classList.remove(gestureController.BACKDROP_NO_SCROLL);
612
639
  }
613
640
  overlay.presented = false;
614
641
  try {
642
+ /**
643
+ * There is no need to show the overlay to screen readers during
644
+ * the dismiss animation. This is because the overlay will be removed
645
+ * from the DOM after the animation is complete.
646
+ */
647
+ hideAnimatingOverlayFromScreenReaders(overlay.el);
615
648
  // Overlay contents should not be clickable during dismiss
616
649
  overlay.el.style.setProperty('pointer-events', 'none');
617
650
  overlay.willDismiss.emit({ data, role });
@@ -846,6 +879,28 @@ const createTriggerController = () => {
846
879
  removeClickListener,
847
880
  };
848
881
  };
882
+ /**
883
+ * The overlay that is being animated also needs to hide from screen
884
+ * readers during its animation. This ensures that assistive technologies
885
+ * like TalkBack do not announce or interact with the content until the
886
+ * animation is complete, avoiding confusion for users.
887
+ *
888
+ * If the overlay is being presented, it prevents focus rings from appearing
889
+ * in incorrect positions due to the transition (specifically `transform`
890
+ * styles), ensuring that when aria-hidden is removed, the focus rings are
891
+ * correctly displayed in the final location of the elements.
892
+ *
893
+ * @param overlay - The overlay that is being animated.
894
+ */
895
+ const hideAnimatingOverlayFromScreenReaders = (overlay) => {
896
+ if (index.doc === undefined)
897
+ return;
898
+ /**
899
+ * Once the animation is complete, this attribute will be removed.
900
+ * This is done at the end of the `present` method.
901
+ */
902
+ overlay.setAttribute('aria-hidden', 'true');
903
+ };
849
904
  /**
850
905
  * Ensure that underlying overlays have aria-hidden if necessary so that screen readers
851
906
  * cannot move focus to these elements. Note that we cannot rely on focus/focusin/focusout
@@ -856,7 +911,7 @@ const createTriggerController = () => {
856
911
  * @param newTopMostOverlay - The overlay that is being presented. Since the overlay has not been
857
912
  * fully presented yet at the time this function is called it will not be included in the getPresentedOverlays result.
858
913
  */
859
- const hideOverlaysFromScreenReaders = (newTopMostOverlay) => {
914
+ const hideUnderlyingOverlaysFromScreenReaders = (newTopMostOverlay) => {
860
915
  var _a;
861
916
  if (index.doc === undefined)
862
917
  return;