Overlays

AppSidebarMenu

A slide-in drawer for filters, details, or mobile navigation — behavior borrowed from Offcanvas.

<app-sidebar-menu id="filters" title="Filters" side="end">…</app-sidebar-menu>

A sidebar menu is a panel that slides in from an edge — a filter tray, a details pane, a mobile navigation drawer. <app-sidebar-menu> renders Bootstrap's offcanvas, so the backdrop, slide animation, background scroll-lock, Escape-to-close and focus trap all come for free — none of it is hand-rolled.

Its look comes entirely from the shared @webority/theme, so the Razor <app-sidebar-menu> and the React <AppSidebarMenu> render identically. It's an overlay: open it from a trigger. In Razor that's any element carrying data-bs-toggle="offcanvas" data-bs-target="#id".

Playground

Every attribute, live. Each change re-renders the real tag helper on the server, so the markup underneath is the exact call that produced the drawer above it.

Filters

Tweak the controls to see every combination.

<app-sidebar-menu id="scPgDrawer" title="Filters" title-icon="settings">…</app-sidebar-menu>
<app-sidebar-footer>…</app-sidebar-footer>
backdrop is a plain on/off switch in RazorReact's backdrop also accepts "static" (a dimmed backdrop that ignores clicks — Escape or the × still close it). The Razor tag helper's Backdrop is bool only, so that middle state has no Razor equivalent today.

A basic drawer

A header (icon + title + close), a body, and an optional footer for actions. The trigger points at the panel's id.

Open a filter drawer
Escape and the backdrop both dismiss it — the focus trap and scroll-lock are handled for you.
Filters

Drawer body content goes here — forms, filters, details.

Show code
<app-button data-bs-toggle="offcanvas" data-bs-target="#scFilterDrawer">Open filters</app-button>
<app-sidebar-menu id="scFilterDrawer" title="Filters" title-icon="settings" side="end">
    <p class="wui-text-sm">Drawer body — forms, filters, details.</p>
    <div class="d-flex gap-2 justify-content-end mt-3">
        <app-button variant="ghost" data-bs-dismiss="offcanvas">Reset</app-button>
        <app-button variant="primary" data-bs-dismiss="offcanvas">Apply</app-button>
    </div>
</app-sidebar-menu>
The behavior is borrowed, not rebuiltBackdrop, animation, scroll-lock, Escape, and the focus trap come from Offcanvas underneath. Reach for <app-sidebar-menu> instead of a hand-rolled drawer so you never re-implement — or get wrong — that accessibility wiring.

Sides

side picks the edge it slides from. end suits filters and details beside the content; start suits a navigation drawer.

start and end
Same primitive — only the edge changes.
Menu

A start-edge navigation drawer.

Show code
<app-sidebar-menu id="scNavDrawer" title="Menu" title-icon="menu" side="start">…</app-sidebar-menu>
<app-sidebar-menu id="scFilterDrawer" title="Filters" title-icon="settings" side="end">…</app-sidebar-menu>

Width & max width

width sets the panel's target size; max-width caps it so a wide fixed width still fits a small viewport (default 92vw). On a phone (below 576px) a side panel opens at the full screen width.

A wider panel, capped on small screens
width="640px" with max-width="90vw" — the panel targets 640px but never exceeds 90% of the viewport.
Details

A wider panel, still capped so it fits a small viewport.

Show code
<app-sidebar-menu id="scWideDrawer" title="Details" title-icon="info" width="640px" max-width="90vw">…</app-sidebar-menu>

When to use

Use it when

  • A filter or details panel that slides in beside the current content (side="end").
  • A navigation drawer, especially the collapsed mobile menu (side="start").
  • You want a panel that behaves accessibly below a breakpoint and inline above it.

Reach for something else when

  • It's a self-contained task or form in a centred card. → AppModal
  • It's a yes/no confirmation. → AppConfirmDialog
  • It's a small menu of actions behind one trigger. → AppDropdown
  • It's a transient message. → a toast

Best practices

Do
A clear trigger opens a drawer with a title and a footer of actions — driven by Bootstrap's offcanvas.
Don't
A hand-rolled slide panel with no focus trap or Escape handling is inaccessible and reinvents Offcanvas.
Do
A start-edge drawer for navigation, an end-edge one for filters — the side matches the panel's job.
Don't
A drawer is the wrong home for a yes/no confirm — that's AppConfirmDialog.

Every option

The whole surface of the component, one cell per value — every side, the header/footer slots, and every behavior flag.

side — all six values

side="left"
Side: left

Opens from left.

side="right"
Side: right

Opens from right.

side="start"
Side: start

Opens from start.

side="end"
Side: end

Opens from end.

side="top"
Side: top

Opens from top.

side="bottom"
Side: bottom

Opens from bottom.

titleIcon / title-icon

title-icon="settings"
Filters

A chip icon sits beside the title.

(no title-icon)
Filters

Title only, no chip.

header / app-sidebar-header — a custom node replaces the default title bar

<app-sidebar-header>…</app-sidebar-header>
Custom header node

The title attribute above is ignored once app-sidebar-header is set.

close-button="false" (no title, no default header)

No header row, no close button — body only.

footer / app-sidebar-footer

<app-sidebar-footer>…</app-sidebar-footer>
With footer

A sticky action bar pins to the bottom.

(no app-sidebar-footer)
No footer

Body only, no sticky bar.

backdrop — true (default) · false · "static" (React) / true (default) · false (Razor is bool-only)

backdrop (default true)
Backdrop

Clicking outside the panel closes it.

backdrop="false"
No backdrop

No dimming layer; the page stays fully interactive.

scroll

scroll (default false — page locked)
Locked

Background scroll is locked while open.

scroll="true"
Scrollable

The page behind can still scroll.

keyboard & closeButton / close-button

keyboard="false"
No Escape

Pressing Escape does nothing.

close-button="false"
No close button

No header close button — use the footer, backdrop, or Escape.

width & maxWidth / max-width

width="640px"
Wide

Targets 640px.

width="640px" max-width="90vw"
Wide, capped

Targets 640px but never exceeds 90% of the viewport.

responsive — inline at/above the breakpoint

responsive="lg"
Inline above lg

At/above the lg breakpoint this renders inline as regular content, not an overlay — shrink the window below lg and reload to see it become a drawer.

Attributes

AttributeTypeDefaultDescription
idstring—The panel id a trigger points at with data-bs-target.
titlestring—Default header title.
sideleft | right | start | end | top | bottomrightEdge the panel slides from — right/end for filters/details, left/start for navigation.
(content)markup—The panel body — forms, filters, details, and any footer actions.
title-iconstring—Bootstrap Icons glyph beside the title (React titleIcon).
<app-sidebar-header>markup—Custom header child that replaces the default title bar entirely (React header).
<app-sidebar-footer>markup—Sticky action bar child at the bottom of the panel (React footer).
aria-labelstring—Accessible name for the dialog (React ariaLabel), defaulting to the title.
widthstring—Panel width, e.g. "420px" (React width).
max-widthstring92vwCaps the panel width, e.g. "600px" (React maxWidth).
backdropbooltrueShow the dimming backdrop (React backdrop, default true). Razor is bool-only — React's backdrop="static" (dimmed, click-through-proof) has no Razor equivalent.
scrollboolfalseAllow the page behind to scroll (React scroll).
keyboardbooltrueClose on Escape (React keyboard, default true).
close-buttonbooltrueRender the header close button (React closeButton, default true).
responsivestring—Breakpoint below which it behaves as an offcanvas, e.g. "lg" (React responsive).
body-class-namestring—Classes on the scrollable body region (React bodyClassName).