Skip to content
This repository has been archived by the owner on Jun 5, 2023. It is now read-only.

Commit

Permalink
feat(Modal): Add PreventModalAriaHidden component
Browse files Browse the repository at this point in the history
  • Loading branch information
diondiondion committed Jun 11, 2020
1 parent 76b486e commit 0f1be28
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/Modal/ModalManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import React, {
} from 'react';

import useEventListener from '../useEventListener';
import {hideOthers} from 'aria-hidden';
import {hideForModal} from './accessiblyHideModalBackground';

const noop = () => {};

Expand Down Expand Up @@ -100,7 +100,7 @@ function ModalManager({children}) {
useEffect(() => {
if (modalStack.length) {
const topMostModal = modalStack[modalStack.length - 1];
const undoAriaHideOthers = hideOthers(topMostModal?.ref.current);
const undoAriaHideOthers = hideForModal(topMostModal?.ref.current);

return function cleanUp() {
undoAriaHideOthers();
Expand Down
16 changes: 15 additions & 1 deletion src/Modal/README.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ React components and hooks for building accessible modals (dialogs).
- Handles focus management:
- Contain keyboard focus inside of the topmost modal
- Return keyboard focus to the element that triggered the modal after it is closed
- Contain screen readers from navigating outside of the topmost modal
- Contain screen reader navigation within the topmost modal
- Close the topmost modal when the <kbd>Esc</kbd> key is pressed
- Prevent scrolling "behind" the modal

Expand Down Expand Up @@ -153,3 +153,17 @@ function Modal({children, name, onRequestClose, ...otherProps}) {
);
}
```

### Preventing content outside of modals from being hidden to screen readers

When a modal is open, the `aria-hidden` attribute will be applied to any elements outside of it. This is done to prevent users of screen reader software from accidentally navigating outside of the modal boundaries.

If you want to prevent some elements (such as a live region used for accessible status updates) from being hidden in this way, you can wrap them in the `PreventModalAriaHidden` component:

```jsx
import {PreventModalAriaHidden} from 'base5-ui/Modal';

<PreventModalAriaHidden>
<Status>...</Status>
<PreventModalAriaHidden>
```
27 changes: 27 additions & 0 deletions src/Modal/accessiblyHideModalBackground.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import {hideOthers} from 'aria-hidden';
import PropTypes from 'prop-types';

const PREVENT_ARIA_HIDDEN_ATTRIBUTE = 'data-prevent-aria-hidden';

// hide everything except modalElement and elements with
// the PREVENT_ARIA_HIDDEN_ATTRIBUTE
function hideForModal(modalElement) {
return hideOthers([
modalElement,
...document.querySelectorAll(`[${PREVENT_ARIA_HIDDEN_ATTRIBUTE}]`),
]);
}

// A helper component that applies the PREVENT_ARIA_HIDDEN_ATTRIBUTE
// to its (only) child component
function PreventModalAriaHidden({children}) {
return React.cloneElement(React.Children.only(children), {
[PREVENT_ARIA_HIDDEN_ATTRIBUTE]: true,
});
}
PreventModalAriaHidden.propTypes = {
children: PropTypes.element.isRequired,
};

export {hideForModal, PreventModalAriaHidden};
1 change: 1 addition & 0 deletions src/Modal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ export {
} from './ModalManager';

export {BodyScrollLock, useScrollLockStyles} from './BodyScrollLock';
export {PreventModalAriaHidden} from './accessiblyHideModalBackground';

export default Modal;

0 comments on commit 0f1be28

Please sign in to comment.