{% load i18n brickwork_icons %} {% comment %} Slide-over / side panel (04-interfaces section 4b, 0.14.0, brickwork#55). Consumed by EXTENDING, exactly like _modal.html: a consumer's slide-over partial opens with {% extends "brickwork/components/_slide_over.html" %} and fills the named blocks. Reuses the modal's a11y machinery (dialog semantics, focus trap, dismissal contract) but is edge-anchored instead of centred, so it can sit BESIDE page content rather than over it. DUAL CONSUMPTION, one partial, two documented render paths (BR-BW-HTMX-001, mirroring the modal): 1. htmx path (needs the #bw-slide-over-root container shell/base.html ships from 0.14.0): the trigger is a real anchor carrying hx-get hx-target="#bw-slide-over-root" hx-swap="innerHTML"; the view returns the consumer's slide-over partial and on insertion bwSlideOver opens it as an edge overlay. 2. no-JS floor: the SAME anchor's href is a full-page route; the view (branching on request.htmx) renders the SAME consumer partial inside a full page. With no JavaScript this markup is simply an in-flow, visible panel on that page: the slide-over is then a page, which is the floor. Nothing here is display:none at rest; the scrim and fixed overlay positioning apply only under the JS-set bw-slide-over--open state, so the floor never hides content behind JavaScript. DEDICATED root, not #bw-modal-root (0.14.0 build decision): a modal and a slide-over must be able to coexist open (e.g. a split-shell detail screen where a slide-over sits beside content while a confirm modal opens over it), and #bw-modal-root's own CSS and bwModal's htmx-path auto-open detection are both keyed to that one container. Reusing it would either block that coexistence or force bwModal and bwSlideOver to disambiguate inside a shared root; a second stable root is simpler and matches the one-container-per-overlay-family shape shell/base.html already uses for the modal and toast regions. Named blocks (semver-public, BR-BW-TPL-001): slide_over_title: defaults to rendering {{ title }} inside the chrome-owned heading (heading-sm type role). slide_over_body: the panel content. A form inside it follows BR-BW-HTMX-003 unchanged (hx-target="this" hx-swap="outerHTML", 422 re-render with inline errors); a failed request NEVER closes the panel (CBH-026 applied here). On success the view closes it server-side: respond 204 (or with OOB toast content) plus HX-Trigger: {"bw:slide-over:close": {"id": ""}}; bwSlideOver listens for bw:slide-over:close at the window. slide_over_footer: empty renders nothing; the filler owns the wrapper element (write ), matching _modal.html's footer convention. Sticky header/footer with a scrolling body. Required context: title: the accessible name, rendered as the dialog heading and wired via aria-labelledby. A slide-over with neither title nor an overriding slide_over_title block is a template-authoring defect in the ICO-008 class (extend-consumed, so this cannot raise at render time). Optional context: size ("sm"|"md"|"lg", default "md"): resolves the same --bw-size-max-width-modal-{sm,md,lg} tokens the modal uses, so a slide-over's panel width follows the same sizing vocabulary a consumer already knows (no new width token: the modal's ramp already spans 28rem-48rem, which covers a side panel's usual range too). placement ("start"|"end", default "end"): the logical edge the panel anchors to (inline-end by default, RTL-aware via dir="rtl" alone, no mirrored markup). backdrop_dismiss (bool, default True): pass False for destructive confirms; bwSlideOver ignores the backdrop route when False. Escape ALWAYS closes: a must-confirm flow routes Escape through the same confirm branch, never disables it. slide_over_id (str, default "bw-slide-over"): the instance identity carried in bw:slide-over:open/close event details and matched by the HX-Trigger server-close header. close_url (str): the no-JS floor's exit for the always-rendered close control; when set the control is a real anchor (typically back to the page that linked here), so the floor never shows a dead trigger. With JS running bwSlideOver intercepts it and closes in place. Focus and keyboard (WAI-ARIA APG Dialog (Modal) pattern, wired by bwSlideOver + the wrapped @alpinejs/focus trap; a consumer never authors x-trap, BR-BW-JS-003): on open, focus moves into the panel (the first focusable element, or [data-bw-autofocus] when present); Tab/Shift+Tab cycle inside the trap (guaranteed, no opt-out, CBH-002/A11Y-004); Escape closes unconditionally (CBH-001); on close, focus returns to the trigger on every dismissal route (CBH-004). The close control below is always rendered, visible and keyboard-reachable (BR-BW-JS-007). Body scroll locks while open (the trap's noscroll modifier); the trap's inert modifier removes the page behind from the accessibility tree. Wiring shape (bwSlideOver's DOM contract; the data-bw-slide-over-* attributes are its internal hooks, not consumer surface): bwSlideOver attaches its own scrim/close/Escape listeners at init (the scrim route honours backdropDismiss inside close()'s guard), exposes its boolean state as isOpen (mirroring bwModal's own shape note), and reflects open state as [data-bw-open] on the root for the CSS overlay/scrim presentation. There is deliberately NO x-show here: the closed state must stay visible markup, because on the floor page this partial IS the page content. Events (optional conventions, BR-BW-HTMX-004): bw:slide-over:open, bw:slide-over:close (detail { id, reason: "escape"|"backdrop"|"close-button"|"server"|"programmatic" }); listens for bw:slide-over:close at the window. Tokens, elevation, motion (CSS lane): panel at --bw-z-modal over the scrim at --bw-z-overlay (the same overlay pairing as the modal; the two are mutually exclusive in the z-scale's intent but never both at that layer by construction); scrim --bw-color-surface-overlay; panel --bw-color-surface-raised, --bw-elevation-4, no radius on the anchored edge (a slide-over meets the viewport edge flush) but --bw-radius-xl on the remaining corners. Enter: scrim fades, panel slides in from the anchored edge at --bw-duration-normal/--bw-ease-out. Reduced motion: enter and exit render instantly (BR-BW-TOK-009). {% endcomment %}{% firstof slide_over_id "bw-slide-over" as bw_slide_over_instance_id %}