<!-- GENERATED by scripts/build-llms.mjs from llms/overlays.md — do not edit this file. -->

# `lr-drawer`

- **Import** `import '@aceshooting/lyra-ui/components/lr-drawer.js';` (stable tag alias; registers the tag)
- **Class** `LyraDrawer`, also available unregistered from `@aceshooting/lyra-ui/components/overlays/drawer/drawer.class.js`
- **Family** `components/overlays/` — see `llms/index.md` for its siblings
- **Status** `stable` since `4.0.0` — see the maturity and deprecation policy in `llms/shared.md`
- **Release history** [CHANGELOG.md](../../CHANGELOG.md); family-wide breaking-change summaries: [llms-full.txt](../../llms-full.txt)
- **Deprecations** none
- **Optional peers** none
- **Themeable via** 15 parts, 26 custom properties — see this component's own `@csspart`/`@cssprop` list below
- **Library-wide behavior** (events, form association, `locale`/`strings`, tokens, TS types): `llms/shared.md`

---

## `lr-drawer`

A modal panel anchored to one logical edge of the viewport. `LyraDrawer` extends `LyraDialog`, so it
inherits the entire dialog contract unchanged: focus trapping, Escape and opt-in backdrop dismissal,
document scroll locking, browser **top-layer** promotion, overlay stacking, accessible naming, the
`show()`/`hide()`/`close()` methods and the whole
`lr-show`/`lr-after-show`/`lr-hide`/`lr-after-hide`/`lr-close` lifecycle. `contained` switches
to an absolute, nonmodal panel inside the nearest containing block; only that mode, `placement`, and
the slide animation are its own.

**Properties:**

- `open: boolean = false` (attribute `open`, reflected) — assigning it runs the same lifecycle as
  `show()`/`hide()`, so the property, the reflected attribute and the two methods can never disagree
- `placement: 'start'|'end'|'top'|'bottom' = 'end'` (attribute `placement`, reflected). **Changed in
  8.0.0:** the default used to be `start`. `end` is what `wa-drawer` does, so a mechanical
  `wa-drawer` → `lr-drawer` rename no longer silently slides the panel in from the other edge.
- `contained: boolean = false` (attribute `contained`, reflected) — position within the nearest
  containing block without a backdrop, page inerting, focus trap, scroll lock, top-layer
  promotion, or global Escape ownership
- `heading?: string`, `label: string`, `accessibleLabel: string = ''` (attribute
  `accessible-label`), `closable: boolean = true`, `noHeader: boolean = false` (attribute
  `no-header`, Shoelace's spelling, reflected), `withoutHeader: boolean = false` (attribute
  `without-header`, Web Awesome's spelling, reflected; neither is deprecated),
  `withFooter: boolean = false` (attribute `with-footer`, reflected; SSR hint), and
  `lightDismiss: boolean = false` (attribute `light-dismiss`) — inherited dialog naming, chrome and
  dismissal options. A plain `aria-label` attribute on the host is honored too, inherited unchanged
  from `lr-dialog`: it is the strongest naming override, by attribute presence including an
  explicitly empty value, ahead of `accessible-label` and any slotted heading.
- `headingLevel: LyraHeadingLevel = '3'` (attribute `heading-level`, reflected) — semantic level of
  the generated title, from `1` through `6`, or `none` for visual-only title text. A direct slotted
  heading retains its own native level.
- `size: LyraSize = 'm'` (reflected) — inherited unchanged from `lr-dialog`; caps the panel's
  `max-inline-size` on the same six-step ladder for `start`/`end` placements (`top`/`bottom` are
  unaffected, since those axes are already unconditionally `100%`). At the `m` default the 32rem
  cap exceeds the panel's own 24rem default inline size, so it stays a no-op unless set. Distinct
  from the drawer-specific `--size` CSS custom property below, which maps to this same panel's own
  `inline-size`/`block-size` for the active axis.

**Methods:** `show(): Promise<void>`, `hide(): Promise<void>`,
`close(reason?: DialogCloseReason): Promise<void>` — inherited unchanged from `lr-dialog`; each
promise settles after the matching `lr-after-*` event.

**Events:** `lr-show` (cancelable), `lr-after-show`, `lr-hide` (cancelable), `lr-after-hide`, and
`lr-initial-focus` (cancelable), `lr-request-close` (cancelable, detail source), and `lr-close`
(`detail: DialogCloseReason`, cancelable) — all inherited unchanged from
`lr-dialog`; see that section for details and veto rules. `lr-after-show` /
`lr-after-hide` fire once the slide animation has finished, so they are deferred by roughly one
animation compared with the state flip. **`lr-close` is not drawer-scoped, same as on `lr-dialog`:**
several components nested inside a drawer (`lr-callout`, `lr-tab`/`lr-tab-group`, the tool dialogs,
and so on) emit the same bubbling, composed `lr-close` name, so a listener bound on `<lr-drawer>`
also receives a descendant's close. Guard with
`if (event.target !== event.currentTarget) return;` before reading `event.detail`, which those
descendants either omit or shape differently from `DialogCloseReason`.

**Animation registry:** the panel uses placement-specific names:
`drawer.showStart`/`drawer.hideStart`, `drawer.showEnd`/`drawer.hideEnd`,
`drawer.showTop`/`drawer.hideTop`, and `drawer.showBottom`/`drawer.hideBottom`. The backdrop uses
`drawer.overlay.show`/`drawer.overlay.hide`. Per-element overrides are RTL-aware through
`rtlKeyframes`; passing `null` disables interpolation while retaining the inherited event/promise
lifecycle.

**Slots:** default (drawer body), `label` (rich header content), `header-actions` (extra header
controls, rendered before the built-in close button), `footer` — all inherited from `lr-dialog`.

**CSS parts:** `base`; `backdrop overlay`; `panel dialog`; `header`; `heading title label`;
`header-actions`; `close-button close-button__base`; `close-button__control`; `body`; `footer`.
Names grouped together are aliases on the same functional node; `close-button__control` is the
composed `<lr-icon-button>`'s own native `<button>`, inherited from `lr-dialog` as of 16.0.0.

**Migrating a pre-16.0.0 `::part()` rule.** This component's icon-only action is a composed
`<lr-icon-button>`, so the part naming that action now names the composed child's HOST, which
paints nothing. A `border`, `background` or `border-radius` set on it is silently dead — only
`color` still appears to work, because it inherits, which makes such a rule look half-alive rather
than broken. Set `--lr-icon-button-background`/`-color`/`-border`/`-radius` (and their
`-hover`/`-active` variants) on this element or an ancestor instead: the composed control reads
those public tokens ahead of any default this component supplies. For SIZE use
`--lr-theme-icon-button-size`, not `--lr-icon-button-size` — every `LyraElement` re-declares the
latter on its own `:host`, so it never reaches a composed child (see `llms/tokens.md`).

**Themeable custom properties:** mapped `--size` controls the active axis. For start/end drawers,
the inherited `--width` and `--lr-dialog-width` remain compatibility fallbacks when neither
`--size` nor `--lr-drawer-width` is set, and `--lr-dialog-max-width` remains an effective cap,
falling back to the `size` property's own tier value (see Properties above) when unset.
The other mapped/inherited aliases are `--backdrop-filter`, `--spacing`, `--header-spacing`, `--body-spacing`,
`--footer-spacing`, `--show-duration`, and `--hide-duration`. Lyra compatibility tokens remain:
`--lr-drawer-width` (default `--lr-size-24rem`; used by
`placement="start"|"end"`, capped at `100%`), `--lr-drawer-height` (default `--lr-size-24rem`;
used by `placement="top"|"bottom"`), `--lr-drawer-enter-x` / `--lr-drawer-enter-y` (the panel's
slide translate offset, used for both the enter and the exit keyframes — `-x` for start/end, `-y`
for top/bottom; both default to `±var(--lr-size-1rem)` and are set per `placement`, with `-x`
explicitly flipped under `:dir(rtl)` since `translateX` is physical. Override to lengthen/shorten
the slide). It also inherits every `<lr-dialog>` token — `--lr-dialog-overlay-color`,
`--lr-dialog-backdrop-filter`, `--lr-dialog-width`, `--lr-dialog-max-width`, `--lr-dialog-spacing`,
`--lr-dialog-spacing-block`, `--lr-dialog-panel-duration` and `--lr-dialog-backdrop-duration` —
since `LyraDrawer` extends `LyraDialog`, including the shared overlay-surface family the panel
paints from: `--lr-overlay-surface`, `--lr-overlay-border`, `--lr-overlay-radius` and
`--lr-overlay-shadow-modal`. The drawer's own `[part~="panel"]` rule squares the corners and steps
the elevation down to `var(--lr-shadow-l)` — three of its edges are flush with the viewport — so on
this tag `--lr-overlay-radius` and `--lr-overlay-shadow-modal` are overridden and only the fill and
edge colour take effect. `--lr-dialog-height` is deliberately **not** among the
inherited tokens above: `<lr-drawer>`'s own `[part~="panel"]` rule unconditionally sets its own
`block-size` for every placement (`100%` for `start`/`end`, a `--lr-drawer-height`-driven `min()`
for `top`/`bottom`), which always wins the cascade over `<lr-dialog>`'s `--lr-dialog-height`-driven
rule regardless of value, so the property has no effect on `<lr-drawer>`. `<lr-drawer>` also opts
out of `<lr-dialog>`'s inherited `[part="body"]` growth: on `<lr-dialog>` that rule only fills the
panel once `--lr-dialog-height` is set (otherwise the panel is content-sized, so the rule is a
no-op); a drawer's panel is unconditionally a definite size for every placement, so without this
override `body` would always stretch and push `footer` to the panel's far edge. `<lr-drawer>`'s own
`[part="body"]` rule restores the natural content size instead, so `footer` follows immediately
after a short body exactly as it did before `--lr-dialog-height` existed. The drawer's own
size/width/height tokens take precedence
for its panel, and only the animation _name_ is overridden, so `--lr-dialog-panel-duration` retunes the
slide too and the reduced-motion flattening of the shared `--lr-duration-*` tokens still reaches it.

```html
<lr-drawer open placement="end" heading="Filters" closable>
  <button slot="header-actions" type="button">Reset</button>
  <lr-checkbox label="Only active"></lr-checkbox>
  <div slot="footer"><button type="button">Apply</button></div>
</lr-drawer>
```

---
