DEV3 Technologies home

Slide-over cart drawer

A cart panel that slides in from the right with a backdrop, line items, quantity steppers and a live subtotal.

Tailwind CSSModalscartdrawerslide-overecommerce

Live preview

Code

<button id="cart-open" type="button" class="rounded-lg border border-slate-300 px-4 py-2 text-sm">
  Cart (<span id="cart-count">0</span>)
</button>

<div id="cart-backdrop" class="fixed inset-0 z-40 hidden bg-slate-900/50"></div>
<aside id="cart-drawer" role="dialog" aria-modal="true" aria-labelledby="cart-title" inert
  class="fixed inset-y-0 right-0 z-50 flex w-full max-w-sm translate-x-full flex-col bg-white shadow-xl transition-transform duration-300">
  <header class="flex items-center justify-between border-b border-slate-200 p-4">
    <h2 id="cart-title" class="font-semibold">Your cart</h2>
    <button id="cart-close" type="button" class="text-sm text-slate-500">Close</button>
  </header>
  <ul id="cart-list" class="flex-1 divide-y divide-slate-100 overflow-y-auto px-4"></ul>
  <footer class="border-t border-slate-200 p-4">
    <p class="flex justify-between font-medium"><span>Subtotal</span><span id="cart-total" aria-live="polite"></span></p>
    <a href="/checkout" class="mt-3 block rounded-lg bg-slate-900 py-2.5 text-center font-semibold text-white">Checkout</a>
  </footer>
</aside>

How it works

What it solves

Opening a cart on a separate page breaks the shopping flow. A slide-over lets shoppers review items, change quantities and keep browsing, and it must behave like any dialog: contained focus, an Escape exit and a readable total.

How it works

The drawer is an aside with role="dialog" and aria-modal, positioned fixed on the right and moved with translate-x-full until opened. A sibling backdrop fades in with the same toggle. Line items are rendered from an array of objects with a price in cents; each row has minus and plus buttons wired through event delegation on the list, so new rows need no extra listeners. After every change the script recalculates the subtotal in integer cents and formats it with Intl.NumberFormat, avoiding floating point drift. Quantity zero removes the row. The count in the header button is updated, and the total sits in an aria-live="polite" region so changes are announced. Escape and the backdrop close the panel and focus returns to the cart button.

Adapting it

Replace the seeded array with data from your cart API, and post changes back inside the click handler. Add a free shipping bar by comparing the subtotal to a threshold. Currency and locale are two constants at the top.

Pitfalls

  • Store money as integers; floats add cents errors.
  • Lock page scroll while the drawer is open.
  • Do not render product names with innerHTML.

How to use it

  1. Paste cart-drawer.html into your layout and keep the ids.
  2. Load cart-drawer.js with defer; replace the items array with your cart data.
  3. Set LOCALE and CURRENCY for your shop.
  4. After each quantity change, send the update to your server in change().

Licence: DEV3 Snippet Licence. Free to use, change and ship in personal and commercial projects. Attribution is appreciated, not required. Please do not redistribute snippets as a collection.

Related snippets