diff --git a/documentation/specs/Drawer.md b/documentation/specs/Drawer.md
new file mode 100644
index 000000000..2d28b9643
--- /dev/null
+++ b/documentation/specs/Drawer.md
@@ -0,0 +1,210 @@
+# `Drawer` Component Specification
+
+## Overview
+
+A `Drawer` is a modal that stays on the side of the screen.
+
+### Use Cases
+
+- Use a `` when you want to capture information from the user as a part of a concentrated workflow without having them leave the parent page.
+- Use a `` when you want to show additional complex information to the user without losing context of the parent page.
+
+### Features
+
+- Supports composability with a container, header, and body components
+- Supports self-contained state management by default
+- Supports being controlled
+- Supports default state
+- Supports being nondismissable
+
+### Prior Art
+
+- [Paste ``](https://paste.twilio.design/components/side-modal)
+- [Aria ``](https://react-spectrum.adobe.com/react-aria/Dialog.html)
+
+---
+
+## Design
+
+`Drawer` will use `useDialog` and `useModalOverlay` from `react-aria` to provide the foundation for an accessible dialog.
+
+The component design was inspired by Aria's Dialog component and Twilio's SideModal component.
+
+`Drawer` will manage its own state by default but can be controlled if the consumer opts in.
+
+`Drawer` will be a compound component consistenting of `Drawer`, `Drawer.Header`, and `Drawer.Body`.
+
+While the above components allow for any design, `Drawer` includes `Drawer.Banner`, `Drawer.BanneredContentArea`, `Drawer.StandaloneContentArea`, and `Drawer.CloseButton` for using preset Drawer patterns.
+
+`Drawer` should be attached to a focusable trigger element such as a `Button` through the `Drawer.Trigger` component. This ensures the trigger and modal are accessible. In the event that a focusable element can't be used, `DrawerContainer` can be used for custom triggering.
+
+`Drawer.Trigger` must contain exactly two direct children. The first child must be a focusable trigger such as `Button`. The second child must be either a `Drawer` or a render function that returns a `Drawer`. If using a render function, a `close` argument will be passed to allow for programmatically closing the `Drawer`. This pattern is adopted from the suggested patterns of React Aria and React Spectrum.
+
+`Drawer` will use React CSS Transition Group to slide the dialog in and out of the screen.
+
+### API
+
+```ts
+type DrawerTriggerProps = {
+ /**
+ * Content of drawer trigger. Must be exactly two elements.
+ */
+ children: [ReactElement, CloseableModalElement | ReactElement];
+
+ /**
+ * Whether the drawer is open by default (uncontrolled).
+ */
+ defaultOpen?: boolean;
+
+ /**
+ * Whether or not the drawer can be dismissed.
+ */
+ isDismissable?: boolean;
+
+ /**
+ * Whether the drawer is open by default (controlled).
+ */
+ isOpen?: boolean;
+
+ /**
+ * Handler that is called when the overlay's open state changes.
+ */
+ onOpenChange?: (isOpen: boolean) => void;
+};
+
+type DrawerProps = {
+ /**
+ * Content of the drawer.
+ */
+ children: ReactNode;
+};
+
+type DrawerHeaderProps = {
+ /**
+ * Drawer header content.
+ */
+ children: string;
+};
+
+type DrawerBannerProps = {
+ /**
+ * Drawer header banner content.
+ */
+ children: ReactNode;
+};
+
+type DrawerBodyProps = {
+ /**
+ * Drawer body content.
+ * This provides a scrollable region without any predefined padding.
+ */
+ children: ReactNode;
+};
+
+type DrawerBanneredContentAreaProps = {
+ /**
+ * Content area for a drawer with a banner.
+ */
+ children: ReactNode;
+};
+
+type DrawerStandaloneContentAreaProps = {
+ /**
+ * Content area for a drawer without a banner (standalone).
+ */
+ children: ReactNode;
+};
+
+/**
+ * Close button for the drawer. Connected to Drawer state.
+ */
+type DrawerCloseButtonProps = {};
+
+/**
+ * Title for the drawer. Uses aria-labelledby to attach to the dialog.
+ */
+type DrawerTitleProps = TextProps;
+```
+
+### Example Usage
+
+_Standalone_:
+
+```tsx
+import { Drawer } from "@easypost/easy-ui/Drawer";
+
+function PageWithDrawer() {
+ return (
+
+
+
+
+
+
+ Title
+
Content
+
+
+
+
+
+ );
+}
+```
+
+_Bannered with Tabs_:
+
+```tsx
+import { Drawer } from "@easypost/easy-ui/Drawer";
+
+function PageWithDrawer() {
+ return (
+
+
+ {() => (
+
+
+
+
+
+ Title
+
+
+
+
+ Tab 1
+ Tab 2
+
+
+
+
+
+ Tab 1
+ Tab 2
+
+
+
+
+
+ )}
+
+ );
+}
+```
+
+---
+
+## Behavior
+
+### Accessibility
+
+- All elements required to interact with the drawer, including closing or acknowledging it, are contained in the drawer since they trap focus, and users can't interact with the underlying page.
+- Tabbing through a Drawer will cycle through its content in the same way it does on a page. A Drawer also supports pressing the Escape key to close the Drawer.
+- The element that serves as the drawer container has a role of dialog.
+- The Drawer must be labelled by Drawer.Title.
+
+### Dependencies
+
+- `useDialog`, `useModalOverlay` from `react-aria`
+- `IntersectionObserver` for styling when header and footer are stuck, as denoted here: https://ryanmulligan.dev/blog/sticky-header-scroll-shadow/
+- `ReactTransitionGroup` for slide in/out animations: https://reactcommunity.org/react-transition-group/