https://github.com/accessible-ui/modal
🅰 An accessible and versatile modal component for React
https://github.com/accessible-ui/modal
a11y a11y-dialog a11y-modal accessibility accessible-modal aria aria-modal modal react-a11y react-a11y-modal react-aria-modal react-modal
Last synced: 4 months ago
JSON representation
🅰 An accessible and versatile modal component for React
- Host: GitHub
- URL: https://github.com/accessible-ui/modal
- Owner: accessible-ui
- License: mit
- Created: 2019-12-13T00:37:11.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2023-01-06T02:20:55.000Z (over 2 years ago)
- Last Synced: 2025-01-08T13:53:48.999Z (5 months ago)
- Topics: a11y, a11y-dialog, a11y-modal, accessibility, accessible-modal, aria, aria-modal, modal, react-a11y, react-a11y-modal, react-aria-modal, react-modal
- Language: TypeScript
- Homepage: https://codesandbox.io/s/accessiblemodal-example-v4koo
- Size: 3.22 MB
- Stars: 16
- Watchers: 2
- Forks: 0
- Open Issues: 17
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
<Modal>
npm i @accessible/modal
An accessible and versatile modal component for React
## Features
- [x] **Style-agnostic** You can use this component with the styling library of your choice. It
works with CSS-in-JS, SASS, plain CSS, plain `style` objects, anything!
- [x] **Portal-friendly** The modal target will render into React portals of your choice when configured
to do so.
- [x] **a11y/aria-compliant** This component works with screen readers out of the box and manages
focus for you.## Quick Start
[Check out the example on **CodeSandbox**](https://codesandbox.io/s/accessiblemodal-example-v4koo)
```jsx harmony
import * as React from 'react'
import * as Modal from '@accessible/modal'const Component = () => (
Open me
Close me
You did a thing
)
```## API
### Components
| Component | Description |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| [``](#modal) | This component creates the context for your modal target and trigger and contains some configuration options. |
| [``](#target) | This component wraps any React element and turns it into a modal target. |
| [``](#trigger) | This component wraps any React element and turns it into a modal trigger. |
| [``](#closebutton) | This is a convenience component that wraps any React element and adds an onClick handler to close the modal. | |### Hooks
| Hook | Description |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [`useModal()`](#usemodal) | This hook provides the value of the modal's [ModalContextValue object](#modalcontextvalue). |
| [`useA11yTarget()`](#usea11ytargettarget-options) | A React hook for creating a headless modal target to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html). |
| [`useA11yTrigger()`](#usea11ytriggertarget-options) | A React hook for creating a headless modal trigger to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html). |
| [`useA11yCloseButton()`](#usea11yclosebuttontarget-options) | A React hook for creating a headless close button to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html). |### <Modal>
This component creates the context for your modal target and trigger and contains some
configuration options.#### Props
| Prop | Type | Default | Required? | Description |
| ----------- | ------------------------- | ----------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| defaultOpen | `boolean` | `false` | No | This sets the default open state of the modal. By default the modal is closed. |
| open | `boolean` | `undefined` | No | This creates a controlled modal component where the open state of the modal is controlled by this property. |
| onChange | `(open: boolean) => void` | `undefined` | No | This callback is invoked any time the `open` state of the modal changes. |
| id | `string` | `undefined` | No | By default this component creates a unique id for you, as it is required for certain aria attributes. Supplying an id here overrides the auto id feature. |
| children | `React.ReactNode` | `undefined` | No | Your modal contents and any other children. |### useA11yTarget(target, options?)
A React hook for creating a headless modal target to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html).
#### Arguments
| Argument | Type | Required? | Description |
| -------- | -------------------------------------------------- | --------- | --------------------------- |
| target |React.RefObject<T> \| T \| null
| Yes | A React ref or HTML element |
| options | [`UseA11yTargetOptions`](#usea11ytargetoptions) | No | Configuration options |#### UseA11yTargetOptions
```ts
export interface UseA11yTargetOptions {
/**
* Adds this class name to props when the modal is open
*/
openClass?: string
/**
* Adds this class name to props when the modal is closed
*/
closedClass?: string
/**
* Adds this style to props when the modal is open
*/
openStyle?: React.CSSProperties
/**
* Adds this style to props when the modal is closed
*/
closedStyle?: React.CSSProperties
/**
* Prevents the `window` from scrolling when the target is
* focused after opening.
*/
preventScroll?: boolean
/**
* When `true`, this closes the target element when the `Escape`
* key is pressed.
* @default true
*/
closeOnEscape?: boolean
}
```#### Returns
```ts
interface A11yProps {
readonly role: 'dialog'
readonly 'aria-hidden': boolean
readonly id: string | undefined
readonly className: string | undefined
readonly style: {
readonly visibility: 'hidden' | 'visible'
readonly position: 'fixed'
readonly margin: 'auto'
readonly left: '50%'
readonly top: '50%'
readonly transform: 'translate3d(-50%, -50%, 0)'
}
}
```#### Example
```jsx harmony
import * as React from 'react'
import {useA11yTarget} from '@accessible/modal'const MyTarget = () => {
const ref = React.useRef(null)
const a11yProps = useA11yTarget(ref, {closeOnEscape: false})return (
I am the modal dialog
)
}
```### <Target>
This component wraps any React element and turns it into a modal target.
#### Props
| Prop | Type | Default | Required? | Description |
| ------------- | ------------------------------------------------- | ----------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| portal |boolean \| string \| PortalizeProps
| `false` | No | When `true` this will render the modal into a React portal with the id `#portals`. You can render it into any portal by providing its query selector here, e.g. `#foobar`, `[data-portal=true]`, or `.foobar`. |
| closeOnEscape | `boolean` | `true` | No | By default the modal will close when the `Escape` key is pressed. You can turn this off by providing `false` here. |
| closedClass | `string` | `undefined` | No | This class name will be applied to the child element when the modal is `closed`. |
| openClass | `string` | `undefined` | No | This class name will be applied to the child element when the modal is `open`. |
| closedStyle | `React.CSSProperties` | `undefined` | No | These styles will be applied to the child element when the modal is `closed` in addition to the default styles that set the target's visibility. |
| openStyle | `React.CSSProperties` | `undefined` | No | These styles name will be applied to the child element when the modal is `open` in addition to the default styles that set the target's visibility. |
| preventScroll | `boolean` | `false` | No | When `true` this will prevent your browser from scrolling the document to bring the newly-focused tab into view. |
| children | `React.ReactElement` | `undefined` | Yes | The child is cloned by this component and has aria attributes injected into its props as well as the events defined above. |#### Example
```jsx harmony
Alert```
### useA11yTrigger(target, options?)
A React hook for creating a headless modal trigger to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html).
In addition to providing accessibility props to your component, this hook will add events
for interoperability between actual elements and fake ones e.g. andto the
target element#### Arguments
| Argument | Type | Required? | Description |
| -------- | -------------------------------------------------- | --------- | --------------------------- |
| target |React.RefObject<T> \| T \| null
| Yes | A React ref or HTML element |
| options | [`UseA11yTriggerOptions`](#usea11ytriggeroptions) | No | Configuration options |#### UseA11yTriggerOptions
```ts
export interface UseA11yTriggerOptions {
/**
* Adds this class name to props when the modal is open
*/
openClass?: string
/**
* Adds this class name to props when the modal is closed
*/
closedClass?: string
/**
* Adds this style to props when the modal is open
*/
openStyle?: React.CSSProperties
/**
* Adds this style to props when the modal is closed
*/
closedStyle?: React.CSSProperties
/**
* Adds an onClick handler in addition to the default one that
* toggles the modal's open state.
*/
onClick?: (e: MouseEvent) => any
}
```#### Returns
```ts
interface A11yProps> {
readonly 'aria-haspopup': 'dialog'
readonly 'aria-controls': string | undefined
readonly 'aria-expanded': boolean
readonly role: 'button'
readonly tabIndex: 0
readonly className: string | undefined
readonly style: React.CSSProperties | undefined
}
```#### Example
```jsx harmony
import * as React from 'react'
import {useA11yTrigger} from '@accessible/modal'const MyTrigger = () => {
const ref = React.useRef(null)
const a11yProps = useA11yTrigger(ref, {
openClass: 'open',
closedClass: 'closed',
})return (
Clicking me toggles the modal dialog
)
}
```### <Trigger>
This component wraps any React element and adds an `onClick` handler which toggles the open state
of the modal target.#### Props
| Prop | Type | Default | Required? | Description |
| ----------- | --------------------- | ----------- | --------- | -------------------------------------------------------------------------------------------------------------------------- |
| closedClass | `string` | `undefined` | No | This class name will be applied to the child element when the modal is `closed`. |
| openClass | `string` | `undefined` | No | This class name will be applied to the child element when the modal is `open`. |
| closedStyle | `React.CSSProperties` | `undefined` | No | These styles will be applied to the child element when the modal is `closed`. |
| openStyle | `React.CSSProperties` | `undefined` | No | These styles name will be applied to the child element when the modal is `open`. |
| children | `React.ReactElement` | `undefined` | Yes | The child is cloned by this component and has aria attributes injected into its props as well as the events defined above. |```jsx harmony
Open me!
```
### useA11yCloseButton(target, options?)
A React hook for creating a headless close button to [WAI-ARIA authoring practices](https://www.w3.org/TR/wai-aria-practices/examples/dialog-modal/dialog.html).
In addition to providing accessibility props to your component, this hook will add events
for interoperability between actual elements and fake ones e.g. andto the
target element#### Arguments
| Argument | Type | Required? | Description |
| -------- | --------------------------------------------------------- | --------- | --------------------------- |
| target |React.RefObject<T> \| T \| null
| Yes | A React ref or HTML element |
| options | [`UseA11yCloseButtonOptions`](#usea11yclosebuttonoptions) | No | Configuration options |#### UseA11yCloseButtonOptions
```ts
export interface UseA11yCloseButtonOptions {
/**
* Adds an onClick handler in addition to the default one that
* closes the modal.
*/
onClick?: (e: MouseEvent) => any
}
```#### Returns
```ts
interface A11yProps> {
readonly 'aria-haspopup': 'dialog'
readonly 'aria-controls': string | undefined
readonly 'aria-expanded': boolean
readonly 'aria-label': 'Close'
readonly role: 'button'
readonly tabIndex: 0
}
```#### Example
```jsx harmony
import * as React from 'react'
import {useA11yCloseButton} from '@accessible/modal'const MyTrigger = () => {
const ref = React.useRef(null)
const a11yProps = useA11yCloseButton(ref, {
onClick: () => console.log('Closing!'),
})return (
Clicking me closes the modal dialog
)
}
```### <CloseButton>
This is a convenience component that wraps any React element and adds an onClick handler which closes the modal.
#### Props
| Prop | Type | Default | Required? | Description |
| -------- | -------------------- | ----------- | --------- | -------------------------------------------------------------------------------------------------------------------------- |
| children | `React.ReactElement` | `undefined` | Yes | The child is cloned by this component and has aria attributes injected into its props as well as the events defined above. |```jsx harmony
Close me
```
### useModal()
This hook provides the value of the modal's [ModalContextValue object](#modalcontextvalue)
### ModalContextValue
```typescript
export interface ModalContextValue {
/**
* The open state of the modal
*/
isOpen: boolean
/**
* Opens the modal
*/
open: () => void
/**
* Closes the modal
*/
close: () => void
/**
* Toggles the open state of the modal
*/
toggle: () => void
/**
* A unique ID for the modal target
*/
id?: string
}
```#### Example
```jsx harmony
const Component = () => {
const {open, close, toggle, isOpen} = useModal()
return Toggle the modal
}
```## LICENSE
MIT