import { Drawer } from '@uhg-abyss/web/ui/Drawer';() => { const [isOpen, setIsOpen] = useState(false);
return ( <React.Fragment> <Drawer header="Drawer header" isOpen={isOpen} onClose={() => setIsOpen(false)} > Press escape to close the drawer </Drawer> <Button onClick={() => setIsOpen(true)} aria-haspopup="dialog"> Toggle Drawer </Button> </React.Fragment> );}Opening the drawer
There are two methods of controlling the open state of a Drawer: by using the useOverlay hook or by using the isOpen prop.
useOverlay
The useOverlay hook returns an object containing various methods used to control the state of the modal. Each modal must be assigned a unique model prop and wrapped in an OverlayProvider.
Note: If you're encountering issues ensure the modal is properly wrapped in an OverlayProvider / refer to the docs perspective pages for troubleshooting.
useState
Using the useState hook to set the open state of the drawer.
Header
Use the header prop to set the header of the drawer. This prop is optional but strongly recommended. It accepts a React node, and is typically a string representing the title of the drawer.
If not using header, please use ariaLabel to provide a title and keep the component accessible.
Footer
Use the footer prop to add a footer to the drawer. It accepts a React node, typically one or two buttons.
Note: If closing the drawer with a button, please refer to our Drawer.Close documentation.
Closing the drawer
Drawer shows a close (X) button in the top right corner by default (to hide it, set hideClose={true}).
Important: For proper animation, always use the Drawer.Close sub-component when adding a button for closing the drawer. Direct state manipulation (like setIsOpen(false)) will skip the closing animation.
Drawer.Close
The Drawer.Close sub-component is used to close Drawer with animation, and accepts all the same props as the Button component.
The only difference is in how the onClick handler works: If your onClick callback returns { preventClose: true }, the drawer will remain open. Otherwise, the drawer will close and play its animation.
<Drawer.Close onClick={() => { form.handleSubmit(onSubmit)(); // If the form is not valid, prevent the drawer from closing if (!form.formState.isValid) { return { preventClose: true }; } // Otherwise, the drawer will close }}> Submit / Close</Drawer.Close>closeOnClickOutside
Use the closeOnClickOutside prop to control whether a user can dismiss the drawer by clicking on the overlay. The default value is true.
Passing data
External data can be passed into the drawer when using the useOverlay hook. The open and toggle methods returned by the hook accept an object of the following type:
{ isOpen?: boolean; data?: object;}The getState method retrieves the state of the drawer as an object of the same type.
Size
Use the size prop to set the width of the drawer. The options are 'sm' or custom.
Position
Use the position prop to set the position of the drawer. The default is 'right'.
Overflow
Overflow is handled within the content of the Drawer. The header and footer, if present, will remain fixed.
Note: If no header is provided and hideClose={false} the close button will be fixed to the top right corner of the drawer.
Drawer Props
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
ariaLabel | string | - | - | The aria label for the modal base |
children | React.ReactNode | - | - | The contents of the modal base |
className | string | - | - | CSS class name to apply to each element within the component |
closeOnClickOutside | boolean | true | - | If true, the modal base will close when the overlay is clicked |
css | Abyss.CSSProperties | Partial<Record<`abyss-${T}`, Abyss.CSSProperties>> | - | - | Object containing CSS styling to apply; uses the classes listed in the "Integration" tab of the component's documentation |
data-testid | string | - | - | Suffix used to create a unique ID used for automated testing |
disableAriaDescribedBy | boolean | false | - | If true, the dialog will not set an aria-describedby value. Use this only when the dialog content is long or already fully described elsewhere. |
disableDefaultProviderProps | boolean | false | - | If set to true, the component will not use the DefaultPropsProvider values. If you aren’t using the DefaultPropsProvider, this prop can be ignored. |
footer | React.ReactNode | - | - | If present, the modal base will have a footer |
header | React.ReactNode | - | - | The header of the modal base. |
hideClose | boolean | false | - | If true, the modal base will not have a close button in the top right corner |
isOpen | boolean | false | - | If true, the modal base is open |
model | string | - | - | A unique identifier for the modal base; only used when using OverlayProvider |
onClose | () => void | - | - | Callback function executed when the modal base is closed |
position | "right" | "left" | undefined | - | - | The position of the Drawer |
size | string | number | 'sm' | - | The size of the Drawer |
Drawer Classes
| Class Name | Description |
|---|---|
| .abyss-modal-base-root | ModalBase root element |
| .abyss-modal-base-overlay | ModalBase overlay element |
| .abyss-modal-base-content-container | ModalBase content container |
| .abyss-modal-base-header-container | ModalBase header container |
| .abyss-modal-base-close-button | ModalBase close button element |
| .abyss-modal-base-close-icon | ModalBase close button icon element |
| .abyss-modal-base-header | ModalBase header element |
| .abyss-modal-base-body | ModalBase body container |
| .abyss-modal-base-footer | ModalBase footer container |
| .abyss-modal-base-footer-primary-button | ModalBase footer primary button element |
| .abyss-modal-base-footer-secondary-button | ModalBase footer secondary button element |
Keyboard Interactions
| Key | Description |
|---|---|
| Esc | Closes the Drawer. |
| Tab | Moves focus to the next interactive element within the Drawer. Once the last interactive element in the Drawer is reached, pressing Tab again moves focus to the first interactive element within the Drawer. |
| Shift + Tab | Moves focus to the previous interactive element within the Drawer. Once the first interactive element in the Drawer is reached, pressing Shift + Tab again moves focus to the last interactive element within the Drawer. |
Drawer Accessible Example
Drawer Content
The content included on the Drawer must be accessible.
Triggering Elements
Use the aria-haspopup attribute on buttons or other triggering elements that open content like dialogs, listboxes, trees, menus, grids, etc. Use a corresponding value that indicates what kind of popup will be displayed when the trigger element is activated. In turn, the element that pops up must be of the role indicated. In the case of Drawer, use aria-haspopup="dialog" on the element that opens the drawer. Drawer sets role="dialog" on the dialog container internally.
See the docs on aria-haspopup for more details.
Dialog descriptions
Drawer sets aria-describedby to the drawer body by default, which works best for short drawers that need a concise description. For long or content-heavy drawers (where the full body would be repeated), set disableAriaDescribedBy to remove the attribute and avoid redundant announcements.
Reduced Motion
Animations and transitions that have been changed when a user has prefers-reduced-motion set to reduced:
- Transition upon expand/collapse is removed
Component Tokens
Note: Click on the token row to copy the token to your clipboard.
Drawer Tokens
| Token Name | Value | |
|---|---|---|
| drawer.color.border.separator | #E5E5E6 | |
| drawer.color.icon.active | #000000 | |
| drawer.color.icon.hover | #323334 | |
| drawer.color.icon.rest | #4B4D4F | |
| drawer.color.surface.container | #FFFFFF | |
| drawer.color.surface.overlay | rgba(0,0,0,0.4) | |
| drawer.color.text.heading | #002677 | |
| drawer.color.text.paragraph | #4B4D4F | |
| drawer.border-width.bottom.separator | 1px | |
| drawer.border-width.top.separator | 1px | |
| drawer.sizing.all.icon | 24px | |
| drawer.spacing.gap.horizontal.header | 8px | |
| drawer.spacing.gap.vertical.content | 16px | |
| drawer.spacing.padding.bottom.content | 16px | |
| drawer.spacing.padding.bottom.footer | 16px | |
| drawer.spacing.padding.bottom.header | 12px | |
| drawer.spacing.padding.horizontal.content | 16px | |
| drawer.spacing.padding.horizontal.footer | 16px | |
| drawer.spacing.padding.horizontal.header | 12px | |
| drawer.spacing.padding.top.content | 16px | |
| drawer.spacing.padding.top.header | 12px | |
| drawer.spacing.padding.vertical.footer | 16px | |
| drawer.spacing.padding.left.overlay.web | 40px | |
| drawer.spacing.padding.left.overlay.mobile | 16px | |
| drawer.spacing.padding.right.overlay.web | 40px | |
| drawer.spacing.padding.right.overlay.mobile | 16px |