# Offcanvas

> Build hidden sidebars into your project for navigation, shopping carts, and more with the offcanvas component.

Source: https://bootstrap-svelte.vercel.app/components/offcanvas — part of the Bootstrap Svelte documentation (index: https://bootstrap-svelte.vercel.app/llms.txt).

---

## Basic Example

The Offcanvas component is a sidebar that can be toggled to appear from any edge of the viewport. By default, it appears from the left side (start position).

```html
<Button onclick={toggleOffcanvas}>
  Launch Basic Offcanvas
</Button>

<Offcanvas.Root isShown={showOffcanvas} onHidden={() => showOffcanvas = false}>
    <Offcanvas.Header>
        <Offcanvas.Title>Offcanvas</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>
        <p>Some text as placeholder. Content can be virtually anything.</p>
        <Button colorVariant="secondary" onclick={() => showOffcanvas = false}>Close</Button>
    </Offcanvas.Body>
</Offcanvas.Root>
```

## Consumer-controlled triggers

Pass DOM references through `triggerElements` when a standalone button or link controls the panel. Use `bind:this` on a native element or `bind:elementRef` on `Button`. Your click handler owns visibility; an enabled primary press on a listed element (including its descendants) is not treated as an outside dismissal.

HTML and SVG element references are supported. The list may contain multiple references and null or undefined entries; a null list means no triggers. Remove a reference to revoke ownership; detached elements are ignored and conditional bindings follow replacement elements. No extra listeners or global registrations are retained. Keep references reactive when changing the list. Each panel recognizes only its own list, and only the top overlay handles dismissal.

Native disabled controls (including disabled fieldsets) and elements with `aria-disabled="true"` do not receive this exemption. Non-primary presses retain the existing outside-press behavior. A cancelled or abandoned primary press on an enabled owner leaves the panel unchanged; ownership never schedules a later toggle. Without a backdrop, outside presses remain ignored.

Ownership does not add click handlers, set ARIA, or raise the trigger above a backdrop. Classes and `aria-controls` alone do not establish ownership. This example uses `z-index: 1042`, between a single Offcanvas backdrop (1040) and panel (1045). The panel covers the trigger where they overlap on narrow screens. Stacked overlays receive higher layers, so this fixed value is only suitable for this single-overlay example; consumers control their trigger's stacking context. Keep consumer visibility in sync with dismissals using `onHide`.

```html
<script lang="ts">
    import { Button, Offcanvas } from '@winkintel/bootstrap-svelte';
    let open = $state(false);
    let trigger: HTMLElement | null = $state(null);
</script>

<Button bind:elementRef={trigger} aria-controls="my-panel" aria-expanded={open}
    style="position: relative; z-index: 1042;" onclick={() => open = !open}>
    Toggle Offcanvas
</Button>
<Offcanvas.Root id="my-panel" isShown={open} triggerElements={[trigger]}
    useBackdrop="static" placement="end" onHide={() => open = false}>
    <Offcanvas.Body>Panel content</Offcanvas.Body>
</Offcanvas.Root>
```

## Placement Options

Offcanvas supports four different placement options: `start` (default, left side), `end` (right side), `top`, and `bottom`.

```html
<Button onclick={() => showPlacementStart = true}>
    Offcanvas Start
</Button>
<Offcanvas.Root placement="start" isShown={showPlacementStart} onHidden={() => showPlacementStart = false}>
    <Offcanvas.Header>
        <Offcanvas.Title>Offcanvas Start</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>Positioned from the left of the screen (or right in RTL languages)</Offcanvas.Body>
</Offcanvas.Root>

<Button onclick={() => showPlacementEnd = true}>
    Offcanvas End
</Button>
<Offcanvas.Root placement="end" isShown={showPlacementEnd} onHidden={() => showPlacementEnd = false}>
    <Offcanvas.Header>
        <Offcanvas.Title>Offcanvas End</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>Positioned from the right of the screen (or left in RTL languages)</Offcanvas.Body>
</Offcanvas.Root>

<Button onclick={() => showPlacementTop = true}>
    Offcanvas Top
</Button>
<Offcanvas.Root placement="top" isShown={showPlacementTop} onHidden={() => showPlacementTop = false}>
    <Offcanvas.Header>
        <Offcanvas.Title>Offcanvas Top</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>Positioned from the top of the screen</Offcanvas.Body>
</Offcanvas.Root>

<Button onclick={() => showPlacementBottom = true}>
    Offcanvas Bottom
</Button>
<Offcanvas.Root placement="bottom" isShown={showPlacementBottom} onHidden={() => showPlacementBottom = false}>
    <Offcanvas.Header>
        <Offcanvas.Title>Offcanvas Bottom</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>Positioned from the bottom of the screen</Offcanvas.Body>
</Offcanvas.Root>
```

## Backdrop Options

You can control the behavior of the backdrop with the `useBackdrop` prop. It accepts three values: `true` (default, click outside to dismiss), `"static"` (backdrop present; outside clicks do not dismiss), or `false` (no backdrop). With `false`, outside clicks do not dismiss the panel. Use Escape (when keyboard dismissal is enabled), a dismiss button, the navbar toggler, or the `isShown` prop to close it. If you need click-away dismissal, enable the backdrop or provide your own outside-click handler.

When the panel is nested in a `Navbar.Root`, its `Navbar.Toggler` can open and close it with any backdrop setting, provided the toggler is enabled and remains reachable above the backdrop. A controlling toggler click changes visibility once and does not trigger `onHidePrevented`. Clicking outside a panel with a static backdrop still triggers that callback and keeps the panel open.

```html
<Offcanvas.Root useBackdrop="static" isShown={showStaticBackdrop} onHidden={() => showStaticBackdrop = false}>
    <Offcanvas.Header>
        <Offcanvas.Title>Static Backdrop</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>
        <p>Click the close button or press escape to close this offcanvas. Clicking outside won't close it.</p>
        <Button colorVariant="secondary" onclick={() => showStaticBackdrop = false}>Close</Button>
    </Offcanvas.Body>
</Offcanvas.Root>

<Offcanvas.Root useBackdrop={true} isShown={showTrueBackdrop} onHidden={() => showTrueBackdrop = false}>
    <Offcanvas.Header>
        <Offcanvas.Title>True Backdrop</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>
        <p>This offcanvas has a backdrop that you can click outside to close it.</p>
    </Offcanvas.Body>
</Offcanvas.Root>

<Offcanvas.Root useBackdrop={false} isShown={showFalseBackdrop} onHidden={() => showFalseBackdrop = false}>
    <Offcanvas.Header>
        <Offcanvas.Title>False Backdrop</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>
        <p>There is no backdrop for this offcanvas. You can still interact with the page content.</p>
        <Button colorVariant="secondary" onclick={() => showFalseBackdrop = false}>Close</Button>
    </Offcanvas.Body>
</Offcanvas.Root>
```

## Scrollable Body

By default, the Offcanvas component prevents scrolling of the main body when it's open. You can allow body scrolling by setting the `isBodyScrollable` prop to `true`.

```html
<Offcanvas.Root isBodyScrollable={true} isShown={showScrollable} onHidden={() => showScrollable = false}>
    <Offcanvas.Header>
        <Offcanvas.Title>Scrollable Offcanvas</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>
        <p>The page body remains scrollable when this offcanvas is open.</p>
        <p>Try scrolling the page while this offcanvas is open.</p>
        <Button colorVariant="secondary" onclick={() => showScrollable = false}>Close</Button>
    </Offcanvas.Body>
</Offcanvas.Root>
```

## Responsive Behaviors

The Offcanvas component can be configured to behave responsively using the `showOnBreakpoint` prop. When set, the component will be visible as a regular element at or above the specified breakpoint, and will behave as an offcanvas below it.

Supported breakpoints are `sm` through `xxl`. Omit `showOnBreakpoint` for a standalone dismissible overlay, or to inherit a parent Navbar's mode. For always-inline Navbar content, use `Navbar.Root expandOnBreakpoint="xs"` and omit the panel's breakpoint. `xs` is not an Offcanvas breakpoint: remove legacy `showOnBreakpoint="xs"` usages. Untyped runtime values of `xs` are treated as omitted, preserving Navbar inheritance without generating an invalid CSS class.

This example will show as a regular sidebar on screens lg and up, but as an offcanvas on smaller screens.

Resize your browser to show the responsive offcanvas toggle.

```html
<Button colorVariant="primary" class="d-lg-none" onclick={() => showResponsive = !showResponsive}>
    Toggle Responsive Offcanvas
</Button>

<Offcanvas.Root showOnBreakpoint="lg" placement="start">
    <Offcanvas.Header>
        <Offcanvas.Title>Responsive Offcanvas</Offcanvas.Title>
    </Offcanvas.Header>
    <Offcanvas.Body>
        <p>This is always visible on lg screens and above, but behaves like a regular offcanvas below lg breakpoint.</p>
        <Button colorVariant="secondary" class="d-lg-none" onclick={() => showResponsive = false}>Close</Button>
    </Offcanvas.Body>
</Offcanvas.Root>
```

---

## API Reference

### Offcanvas.Root Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `dir` | `'ltr' \| 'rtl'` | `'ltr'` | Text direction, affects the side from which the offcanvas appears |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `id` | `string` | Auto-generated | Unique identifier for the offcanvas |
| `isBodyScrollable` | `boolean` | `false` | When true, allows scrolling the main page body when offcanvas is open |
| `isKeyboardDismissible` | `boolean` | `true` | When true, allows closing the offcanvas with the Escape key |
| `isShown` | `boolean` | `false` | Controls the visibility of the offcanvas |
| `placement` | `'start' \| 'end' \| 'top' \| 'bottom'` | `'start'` | Determines the edge of the screen from which the offcanvas appears |
| `showOnBreakpoint` | `'sm' \| 'md' \| 'lg' \| 'xl' \| 'xxl'` | `undefined` | Inline at or above the selected breakpoint. Omitted inherits a parent Navbar, or keeps a standalone panel as an overlay. Legacy runtime `xs` is treated as omitted. |
| `triggerElements` | `readonly (Element \| null \| undefined)[] \| null` | `[]` | DOM references to consumer-controlled triggers. Enabled primary presses leave visibility to the consumer click handler. |
| `useBackdrop` | `'static' \| boolean` | `true` | Controls backdrop behavior: true (default, clickable), "static" (not clickable), false (no backdrop or outside-click dismissal) |
| `onHide` | `EventListener` | - | Callback when the offcanvas begins hiding |
| `onHidePrevented` | `EventListener` | - | Callback when the offcanvas is shown, its backdrop is static and a click outside of the offcanvas is performed. The event is also fired when the escape key is pressed and the keyboard option is set to false. |
| `onHidden` | `EventListener` | - | Callback when the offcanvas has finished hiding |
| `onShow` | `EventListener` | - | Callback when the offcanvas begins showing |
| `onShown` | `EventListener` | - | Callback when the offcanvas has finished showing |

### Offcanvas.Header Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `isDismissible` | `boolean` | `false` | When true, displays a close button in the header |

### Offcanvas.Title Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |
| `level` | `1 \| 2 \| 3 \| 4 \| 5 \| 6` | `5` | Heading level to use for the title (h1-h6) |

### Offcanvas.Body Props

| Name | Type | Default | Description |
| --- | --- | --- | --- |
| `class` | `string` | - | Additional CSS classes to apply to the component |
| `elementRef` | `HTMLElement \| null` | `null` | Reference to the DOM element |

### CSS Classes

The component applies Bootstrap's offcanvas classes based on the provided props:

- `offcanvas` - Base class for the Offcanvas component
- `offcanvas-[breakpoint]` - Applied for `sm` through `xxl`; there is no `offcanvas-xs` class
- `offcanvas-[placement]` - Position classes (start, end, top, bottom)
- `show` - Applied when the offcanvas is visible
- `offcanvas-header` - Applied to Offcanvas.Header components
- `offcanvas-title` - Applied to Offcanvas.Title components
- `offcanvas-body` - Applied to Offcanvas.Body components
- `offcanvas-backdrop` - Applied to the backdrop when enabled

### Responsive Behavior

The Offcanvas component can be configured for responsive behavior:

- When `showOnBreakpoint` is set, the component appears as a regular element on screens at or above the specified breakpoint
- On screens below the breakpoint, it behaves as a regular offcanvas that must be toggled to appear
- This enables creating responsive sidebar-like layouts that collapse into an offcanvas on smaller screens
