The bag as a sheet on Sheet (Base UI Drawer): rolling line and subtotal figures, undoable removals and a free-shipping bar.
When to use
- The bag on any storefront: open it from the header and after Add to bag.
- When shoppers adjust quantities before checkout and need totals to stay in view.
When not to use
- The checkout itself: use a page; a drawer is for review, not payment.
- A one-item purchase flow (tickets, donations): go straight to checkout.
Installation
npx shadcn add @eased-pro/cart-drawerNeeds the @eased-pro registry and your Pro token in components.json. Create a token and copy the setup from your account .
Usage
import { CartDrawer } from "@/components/eased/cart-drawer/cart-drawer";<CartDrawer open={open} onOpenChange={setOpen} lines={lines} onLinesChange={setLines} onRemoveCommit={(removed) => api.remove(removed.map((l) => l.id))} freeShippingAt={500} discount={{ code: "FIELD15", off: 0.15 }} onCheckout={() => router.push("/checkout")}/>API reference
CartDrawer
| Prop | Type | Default | Description |
|---|---|---|---|
open / defaultOpen / onOpenChange | boolean | ||
lines / defaultLines / onLinesChange | CartDrawerLine[] | { id, name, detail?, price, compareAt?, qty, maxQty?, image? }. Removals leave lines at once. | |
onRemoveCommit | (lines) => void | After the 5s undo window. | |
freeShippingAt | number | Shows how far off the threshold the bag is. | |
discount | { code, off } | off is a share of the subtotal. | |
onCheckout | (lines) => void | ||
container | HTMLElement | null | document.body | Portal target; a contain: layout box keeps the drawer inside it. |
modal | boolean | "trap-focus" | true | "trap-focus" skips the scroll lock. |
size | "sm" | "md" | "lg" | "md" | Side width from 640px; below that it is a bottom sheet. |
footnote | ReactNode | A line under Checkout, e.g. "Shipping and taxes calculated at checkout". | |
defaultSummaryOpen | boolean | false | With a discount, start with Subtotal and Discount shown above Total. Closed, Total shows the pre-discount figure struck. |
Keyboard
| Key | Action |
|---|---|
Tab | Steppers (at 1 the minus becomes Remove), Total, Checkout; focus stays in the drawer. |
Enter / Space on Total | Shows or hides Subtotal and Discount. |
Escape | Closes it; focus returns to the trigger. |
⌘ Z / Ctrl Z | Undoes a removal while the pill shows. |
Accessibility
- Titled "Bag" with the item count; the shipping line describes it.
- Quantities are live outputs; every money figure has a plain-text copy for screen readers.
Motion
Opens on Sheet's 500ms enter curve and follows the finger back to its edge. Steps roll the line total, the discount and the subtotal on the snappy spring. Total unfolds Subtotal and Discount on Auto Height. A removed line fades and shrinks 4% in 150ms while the lines below glide up (FLIP), and an Undo Pill drains for 5s. The shipping bar fills on the smooth spring and turns success at the threshold. Reduced motion: no slide, opacity only.
Notes for AI
Pick it for the storefront bag. Hold lines in your state and commit removals in onRemoveCommit, not onLinesChange, so Undo needs no server round trip.
Don't add your own confirm dialog for Remove: the undo window replaces it (rule 73). Don't compute totals for display; pass prices and quantities.
Related
- Sheet: the drawer underneath.
- Undo Pill: the removal window.
- Product Card: fills the bag.