Last active
May 26, 2020 19:26
-
-
Save chrisjpatty/69804ee54e3fd262842bc9bb2049e421 to your computer and use it in GitHub Desktop.
A React component for announcing messages to a screen reader that are visually hidden from users. This component comes with development warnings for invalid usages. As these messages are unnecessary in production, I recommend using a Webpack or Rollup plugin that will exclude the warnings from production builds such as https://www.npmjs.com/pack…
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.visually-hidden { | |
position: absolute; | |
left: -10000px; | |
top: auto; | |
width: 1px; | |
height: 1px; | |
overflow: hidden; | |
color: transparent; | |
opacity: .01; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react' | |
import PropTypes from 'prop-types' | |
const Announcer = ({ message, method = 'polite' }) => { | |
const previousMethod = usePrevious(method) | |
const componentHasMounted = React.useRef(false) | |
React.useEffect(() => { | |
if (componentHasMounted.current && method !== previousMethod) { | |
console.error( | |
`The value of the "method" prop was changed from ${previousMethod} to ${method}. This prop should not change over the lifecycle of this component.` | |
) | |
} | |
}, [method]) | |
React.useEffect(() => { | |
if (message) { | |
console.warn( | |
`The <Announcer/> was mounted with a message, but this message may not be read to screen readers as the aria-live region has not yet mounted. To ensure that messages are announced, mount this component with no message, and then supply it with a message in a later render to trigger the announcement.` | |
) | |
} | |
componentHasMounted.current = true; | |
}, []) | |
return ( | |
<div aria-live={method} role="status" className="visually-hidden"> | |
{message} | |
</div> | |
) | |
} | |
Announcer.propTypes = { | |
message: PropTypes.string.isRequired, | |
method: PropTypes.oneOf(['polite', 'assertive']) | |
} | |
export default Announcer | |
export const usePrevious = value => { | |
const ref = React.useRef(); | |
React.useEffect(() => { | |
ref.current = value; | |
}, [value]); | |
return ref.current; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment