npm i @accessible/drawer
An accessible and versatile drawer component for React
-  Style-agnostic You can use this component with the styling library of your choice. It
works with CSS-in-JS, SASS, plain CSS, plain 
styleobjects, anything! - Portal-friendly The drawer target will render into React portals of your choice when configured to do so.
 - a11y/aria-compliant This component works with screen readers out of the box and manages focus for you.
 
Check out the example on CodeSandbox
import * as React from 'react'
import * as Drawer from '@accessible/drawer'
const Component = () => (
  <Drawer.Drawer>
    <Drawer.Trigger>
      <button>Open me</button>
    </Drawer.Trigger>
    <Drawer.Target>
      <div className='my-drawer'>
        <Drawer.CloseButton>
          <button>Close me</button>
        </Drawer.CloseButton>
        <div>I've been revealed!</div>
      </div>
    </Drawer.Target>
  </Drawer.Drawer>
)| Component | Description | 
|---|---|
<Drawer> | 
This component creates the context for your drawer target and trigger and contains some configuration options. | 
<Target> | 
This component wraps any React element and turns it into a drawer target. | 
<Trigger> | 
This component wraps any React element and turns it into a drawer trigger. | 
<CloseButton> | 
This is a convenience component that wraps any React element and adds an onClick handler to close the drawer. | 
| Hook | Description | 
|---|---|
useDrawer() | 
This hook provides the value of the drawer's DrawerContextValue object. | 
useA11yTarget() | 
A React hook for creating a headless drawer target to WAI-ARIA authoring practices. | 
useA11yTrigger() | 
A React hook for creating a headless drawer trigger to WAI-ARIA authoring practices. | 
useA11yCloseButton() | 
A React hook for creating a headless close button to WAI-ARIA authoring practices. | 
This component creates the context for your drawer target and trigger and contains some configuration options.
| Prop | Type | Default | Required? | Description | 
|---|---|---|---|---|
| defaultOpen | boolean | 
false | 
No | This sets the default open state of the drawer. By default the drawer is closed. | 
| open | boolean | 
undefined | 
No | This creates a controlled drawer component where the open state of the drawer is controlled by this property. | 
| onChange | (open: boolean) => void | 
undefined | 
No | This callback is invoked any time the open state of the drawer 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 drawer contents and any other children. | 
A React hook for creating a headless drawer target to WAI-ARIA authoring practices.
| Argument | Type | Required? | Description | 
|---|---|---|---|
| target | React.RefObject<T> | T | null | 
Yes | A React ref or HTML element | 
| options | UseA11yTargetOptions | 
No | Configuration options | 
export interface UseA11yTargetOptions {
  /**
   * Sets the placement of the drawer menu
   * @default "left"
   */
  placement?: 'top' | 'right' | 'bottom' | 'left'
  /**
   * Adds this class name to props when the drawer is open
   */
  openClass?: string
  /**
   * Adds this class name to props when the drawer is closed
   */
  closedClass?: string
  /**
   * Adds this style to props when the drawer is open
   */
  openStyle?: React.CSSProperties
  /**
   * Adds this style to props when the drawer 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
}type A11yProps = {
  readonly 'aria-hidden': boolean
  readonly id: string | undefined
  readonly className: string | undefined
  readonly style: {
    readonly visibility: 'hidden' | 'visible'
  } & React.CSSProperties
} & {
  readonly style:
    | ({
        readonly visibility: 'hidden' | 'visible'
      } & React.CSSProperties & {
          readonly position: 'fixed'
          readonly top: 0
          readonly right: 0
          readonly bottom: 'auto'
          readonly left: 0
          readonly transform: 'translate3d(0, -100%, 0)'
        })
    | ({
        readonly visibility: 'hidden' | 'visible'
      } & React.CSSProperties & {
          readonly position: 'fixed'
          readonly top: 0
          readonly right: 0
          readonly bottom: 0
          readonly left: 'auto'
          readonly transform: 'translate3d(100%, 0, 0)'
        })
    | ({
        readonly visibility: 'hidden' | 'visible'
      } & React.CSSProperties & {
          readonly position: 'fixed'
          readonly top: 'auto'
          readonly right: 0
          readonly bottom: 0
          readonly left: 0
          readonly transform: 'translate3d(0, 100%, 0)'
        })
    | ({
        readonly visibility: 'hidden' | 'visible'
      } & React.CSSProperties & {
          readonly position: 'fixed'
          readonly top: 0
          readonly right: 'auto'
          readonly bottom: 0
          readonly left: 0
          readonly transform: 'translate3d(-100%, 0, 0)'
        })
}import * as React from 'react'
import {useA11yTarget} from '@accessible/drawer'
const MyTarget = () => {
  const ref = React.useRef(null)
  const a11yProps = useA11yTarget(ref, {preventScroll: true})
  return (
    <div ref={ref} {...a11yProps}>
      I am the drawer content
    </div>
  )
}This component wraps any React element and turns it into a drawer target.
| Prop | Type | Default | Required? | Description | 
|---|---|---|---|---|
| placement | "top" | "right" | "bottom" | "left" | 
false | 
No | When true this will render the drawer 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. | 
| portal | boolean | string | PortalizeProps | 
false | 
No | When true this will render the drawer 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 drawer 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 drawer is closed. | 
| openClass | string | 
undefined | 
No | This class name will be applied to the child element when the drawer is open. | 
| closedStyle | React.CSSProperties | 
undefined | 
No | These styles will be applied to the child element when the drawer 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 drawer 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. | 
<Target>
  <div className='alert'>Alert</div>
</Target>
// <div
//   class="alert"
//   aria-hidden="true"
//   id="π
°12"
//   style="visibility: hidden;"
// >
//   Alert
// </div>A React hook for creating a headless drawer trigger to WAI-ARIA authoring practices. In addition to providing accessibility props to your component, this hook will add events for interoperability between actual elements and fake ones e.g. and
| Argument | Type | Required? | Description | 
|---|---|---|---|
| target | React.RefObject<T> | T | null | 
Yes | A React ref or HTML element | 
| options | UseA11yTriggerOptions | 
No | Configuration options | 
export interface UseA11yTriggerOptions {
  /**
   * Adds this class name to props when the drawer is open
   */
  openClass?: string
  /**
   * Adds this class name to props when the drawer is closed
   */
  closedClass?: string
  /**
   * Adds this style to props when the drawer is open
   */
  openStyle?: React.CSSProperties
  /**
   * Adds this style to props when the drawer is closed
   */
  closedStyle?: React.CSSProperties
  /**
   * Adds an onClick handler in addition to the default one that
   * toggles the drawer's open state.
   */
  onClick?: (e: MouseEvent) => any
}interface A11yProps<E extends React.MouseEvent<any, MouseEvent>> {
  readonly 'aria-controls': string | undefined
  readonly 'aria-expanded': boolean
  readonly role: 'button'
  readonly tabIndex: 0
  readonly className: string | undefined
  readonly style: React.CSSProperties | undefined
}import * as React from 'react'
import {useA11yTrigger} from '@accessible/drawer'
const MyTrigger = () => {
  const ref = React.useRef(null)
  const a11yProps = useA11yTrigger(ref, {
    openClass: 'open',
    closedClass: 'closed',
  })
  return (
    <button ref={ref} {...a11yProps}>
      Clicking me toggles the drawer content
    </button>
  )
}This component wraps any React element and adds an onClick handler which toggles the open state
of the drawer target.
| Prop | Type | Default | Required? | Description | 
|---|---|---|---|---|
| closedClass | string | 
undefined | 
No | This class name will be applied to the child element when the drawer is closed. | 
| openClass | string | 
undefined | 
No | This class name will be applied to the child element when the drawer is open. | 
| closedStyle | React.CSSProperties | 
undefined | 
No | These styles will be applied to the child element when the drawer is closed. | 
| openStyle | React.CSSProperties | 
undefined | 
No | These styles name will be applied to the child element when the drawer 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. | 
<Trigger on='click'>
  <button className='my-button'>Open me!</button>
</Trigger>
// <button
//   class="my-button"
//   aria-controls="π
°12"
//   aria-expanded="false"
// >
//   Open me!
// </button>A React hook for creating a headless close button to WAI-ARIA authoring practices. In addition to providing accessibility props to your component, this hook will add events for interoperability between actual
| Argument | Type | Required? | Description | 
|---|---|---|---|
| target | React.RefObject<T> | T | null | 
Yes | A React ref or HTML element | 
| options | UseA11yCloseButtonOptions | 
No | Configuration options | 
export interface UseA11yCloseButtonOptions {
  /**
   * Adds an onClick handler in addition to the default one that
   * closes the drawer.
   */
  onClick?: (e: MouseEvent) => any
}interface A11yProps<E extends React.MouseEvent<any, MouseEvent>> {
  readonly 'aria-controls': string | undefined
  readonly 'aria-expanded': boolean
  readonly 'aria-label': 'Close'
  readonly role: 'button'
  readonly tabIndex: 0
}import * as React from 'react'
import {useA11yCloseButton} from '@accessible/drawer'
const MyTrigger = () => {
  const ref = React.useRef(null)
  const a11yProps = useA11yCloseButton(ref, {
    onClick: () => console.log('Closing!'),
  })
  return (
    <button ref={ref} {...a11yProps}>
      Clicking me closes the drawer content
    </button>
  )
}This is a convenience component that wraps any React element and adds an onClick handler which closes the drawer.
| 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. | 
<CloseButton>
  <button className='my-button'>Close me</button>
</CloseButton>
// <button
//   class="my-button"
//   aria-controls="drawer--12"
//   aria-expanded="false"
// >
//   Close me
// </button>This hook provides the value of the drawer's DrawerContextValue object
export interface DrawerContextValue {
  /**
   * The open state of the drawer
   */
  isOpen: boolean
  /**
   * Opens the drawer
   */
  open: () => void
  /**
   * Closes the drawer
   */
  close: () => void
  /**
   * Toggles the open state of the drawer
   */
  toggle: () => void
  /**
   * A unique ID for the drawer target
   */
  id?: string
}const Component = () => {
  const {open, close, toggle, isOpen} = useDrawer()
  return <button onClick={toggle}>Toggle the drawer</button>
}MIT