npm package: @peterpalmer/react-offcanvas
Description: Reusable off‑canvas panel component for React and TypeScript, inspired by Bootstrap’s Offcanvas but fully framework‑agnostic.
Author: Peter Palmer
Latest version: 1.0.1

# react-offcanvas

Reusable off‑canvas panel component for React and TypeScript, inspired by Bootstrap’s Offcanvas but fully framework‑agnostic.

## ✨ Features

- **Positions**: `start`, `end`, `top`, or `bottom`.
- Built‑in backdrop & body‑scroll locking.
- Close via **outside click** or **ESC**.
- **Customisable** size, animation duration & easing.
- SSR‑friendly (Next.js, Remix …).
- Zero dependencies apart from `react` and `react‑dom`.

## 📦 Installation

```bash
npm i react-offcanvas
# or
yarn add react-offcanvas
# or
pnpm add react-offcanvas
# or
bun add react-offcanvas
```

> **Peer‑dependencies**: `react >= 18`, `react-dom >= 18`.

## 🚀 Quick start

```tsx
import React, { useState } from "react";
import OffCanvas from "react-offcanvas";

function App() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onClick={() => setOpen(true)}>Open menu</button>

      <OffCanvas
        isOpen={open}
        onClose={() => setOpen(false)}
        placement="end" // end (default)
        width="300px" // optional
        animationDuration={400} // optional
      >
        <nav style={{ padding: 24 }}>{/* ...your content... */}</nav>
      </OffCanvas>
    </>
  );
}
```

## 🔧 Props

| Prop                | Type                             | Default                         | Description                  |            |         |                             |
| ------------------- | -------------------------------- | ------------------------------- | ---------------------------- | ---------- | ------- | --------------------------- |
| `isOpen`            | **boolean**                      | —                               | Controls visibility.         |            |         |                             |
| `onClose`           | **() => void**                   | —                               | Callback for closing.        |            |         |                             |
| `placement`         | \`'start'                        | 'end'                           | 'top'                        | 'bottom'\` | `'end'` | Lado o borde donde aparece. |
| `backdrop`          | **boolean**                      | `true`                          | Shows darkened backdrop.     |            |         |                             |
| `width`             | **string**                       | `'320px'`                       | Width for `start`/`end`.     |            |         |                             |
| `height`            | **string**                       | `'320px'`                       | Height for `top`/`bottom`.   |            |         |                             |
| `animationDuration` | **number** (ms)                  | `450`                           | Animation duration.          |            |         |                             |
| `easing`            | **string** (CSS timing‑function) | `cubic-bezier(0.25,0.8,0.25,1)` | Easing function.             |            |         |                             |
| `className`         | **string**                       | `""`                            | Extra classes for the panel. |            |         |                             |
| `children`          | **React.ReactNode**              | —                               | Contenido dentro del panel.  |            |         |                             |

## 🎨 Personalización rápida

All styles are injected in a `<style>` tag to avoid dependencies. If you need a more elaborate theme, add a more specific selector or use the `className` prop to apply your own Tailwind/CSS-in-JS classes.

## 🛠️ Build from source

```bash
# clone the repo
git clone https://github.com/peter-palmer/react-offcanvas.git && cd react-offcanvas

# install deps
pnpm install # or npm / yarn

# build     -> dist/
pnpm build

# ejecuta ejemplos locales
pnpm dev
```

## 📝 License

MIT

---

Made with ❤️ by **Peter Palmer**.