Skip to content

Commit 4710e9c

Browse files
committed
Restore focus trap in iframe modals
1 parent 4b872db commit 4710e9c

File tree

5 files changed

+39
-4
lines changed

5 files changed

+39
-4
lines changed

tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/call_to_action.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ <h2 class="heading heading--two-b call-to-action__heading">{{ value.text }}</h2>
2828
{% if value.get_button_link_block.block_type == "modal_iframe" %}
2929
<!-- Modal content -->
3030
<div class="modal" id="iframe-embed-modal" aria-hidden="true">
31-
<div class="modal__overlay" tabindex="-1" data-micromodal-close></div>
31+
<div class="modal__overlay" data-micromodal-close></div>
3232
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-title" >
3333
<header class="modal__header">
3434
<h2 class="modal__heading heading heading--two" id="modal-title">Service Enquiry</h2>
@@ -47,4 +47,4 @@ <h2 class="modal__heading heading heading--two" id="modal-title">Service Enquiry
4747
</main>
4848
</div>
4949
</div>
50-
{% endif %}
50+
{% endif %}

tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/contact_call_to_action.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ <h2 class="heading heading--two-b contact-cta__heading">{{ cta.value.text }}</h2
4545
{% if cta.value.get_button_link_block.block_type == "modal_iframe" %}
4646
<!-- Modal content -->
4747
<div class="modal" id="iframe-embed-modal" aria-hidden="true">
48-
<div class="modal__overlay" tabindex="-1" data-micromodal-close></div>
48+
<div class="modal__overlay" data-micromodal-close></div>
4949
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-title" >
5050
<header class="modal__header">
5151
<h2 class="modal__heading heading heading--two" id="modal-title">Service Enquiry</h2>

tbx/project_styleguide/templates/patterns/molecules/streamfield/blocks/sticky_call_to_action.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
{% if value.get_button_link_block.block_type == "modal_iframe" %}
2929
<!-- Modal content -->
3030
<div class="modal" id="iframe-embed-modal" aria-hidden="true">
31-
<div class="modal__overlay" tabindex="-1" data-micromodal-close></div>
31+
<div class="modal__overlay" data-micromodal-close></div>
3232
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="modal-title" >
3333
<header class="modal__header">
3434
<h2 class="modal__heading heading heading--two" id="modal-title">Service Enquiry</h2>

tbx/static_src/javascript/components/modal.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ class Modal {
5252
const modalId = trigger.getAttribute('data-micromodal-trigger');
5353
if (modalId && typeof MicroModal !== 'undefined') {
5454
MicroModal.show(modalId);
55+
// Ensure tabbing forward from iframes stays within the modal
56+
Modal.ensurePostIframeFocusTrap(modalId);
5557
}
5658
}
5759

@@ -81,6 +83,35 @@ class Modal {
8183
}
8284
}
8385
}
86+
87+
// When a modal contains an iframe, browser-level tabbing inside the iframe
88+
// does not bubble key events to the parent, so focus-trap libraries
89+
// cannot reliably intercept the Tab press. Add a focus sentinel immediately
90+
// after the iframe that redirects focus to the Close button.
91+
static ensurePostIframeFocusTrap(modalId) {
92+
const modal = document.getElementById(modalId);
93+
if (!modal) return;
94+
const container = modal.querySelector('.modal__container');
95+
if (!container) return;
96+
97+
const iframe = container.querySelector('iframe');
98+
if (!iframe) return;
99+
100+
// Only add once per modal instance
101+
if (container.querySelector('.modal__focus-sentinel')) return;
102+
103+
const sentinel = document.createElement('span');
104+
sentinel.tabIndex = 0;
105+
106+
sentinel.addEventListener('focus', () => {
107+
const closeButton = modal.querySelector('[data-micromodal-close]');
108+
if (closeButton) {
109+
closeButton.focus();
110+
}
111+
});
112+
113+
iframe.parentNode.insertBefore(sentinel, iframe.nextSibling);
114+
}
84115
}
85116

86117
export default Modal;

tbx/static_src/sass/components/_modal.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@
7878
position: relative;
7979
background: transparent;
8080
border: 0;
81+
82+
button:focus {
83+
@include focus-style();
84+
}
8185
}
8286

8387
&__container,

0 commit comments

Comments
 (0)