Navigation

Drawer

<oblyx-drawer> is an edge-anchored panel: the same native <dialog> plus showModal() foundation as dialog, with different CSS: it slides in from a viewport edge instead of appearing centered. Every dialog behaviour on this page (dismissal, focus, scroll lock) applies to a drawer identically, because both sit on the same shared base class. Compose it with dialog's own <oblyx-dialog-header>, <oblyx-dialog-body> and <oblyx-dialog-footer>; a drawer has no header-equivalent element of its own.

Basic drawer

Filter controls go here. Apply
HTMLKotlin
<button type="button" onclick="document.getElementById('ox-doc-drawer-basic').open = true">
  Open filters
</button>
<oblyx-drawer id="ox-doc-drawer-basic" label="Filters">
  <oblyx-dialog-header heading="Filters"></oblyx-dialog-header>
  <oblyx-dialog-body>Filter controls go here.</oblyx-dialog-body>
  <oblyx-dialog-footer>
    <oblyx-button variant="primary">Apply</oblyx-button>
  </oblyx-dialog-footer>
</oblyx-drawer>
button {
  onClick = "document.getElementById('ox-doc-drawer-basic').open = true"
  +"Open filters"
}
oblyxDrawer(label = "Filters") {
  id = "ox-doc-drawer-basic"
  oblyxDialogHeader(heading = "Filters")
  oblyxDialogBody { +"Filter controls go here." }
  oblyxDialogFooter {
    oblyxButton(variant = OblyxButtonVariant.PRIMARY) { +"Apply" }
  }
}

Edges

start/end are logical and mirror under :dir(rtl);top/bottom are the block-axis equivalents. end is the default: the common "panel slides in from the right" case in a left-to-right page.

Drawer edges

Slides in from the start edge (the left, in LTR; mirrors under :dir(rtl)). Slides up from the bottom edge.
HTMLKotlin
<button type="button" onclick="document.getElementById('ox-doc-drawer-edge-start').open = true">
  From start
</button>
<button type="button" onclick="document.getElementById('ox-doc-drawer-edge-bottom').open = true">
  From bottom
</button>
<oblyx-drawer id="ox-doc-drawer-edge-start" edge="start" label="Navigation">
  <oblyx-dialog-header heading="Navigation"></oblyx-dialog-header>
  <oblyx-dialog-body>Slides in from the start edge (the left, in LTR; mirrors under :dir(rtl)).</oblyx-dialog-body>
</oblyx-drawer>
<oblyx-drawer id="ox-doc-drawer-edge-bottom" edge="bottom" label="Details">
  <oblyx-dialog-header heading="Details"></oblyx-dialog-header>
  <oblyx-dialog-body>Slides up from the bottom edge.</oblyx-dialog-body>
</oblyx-drawer>
button {
  onClick = "document.getElementById('ox-doc-drawer-edge-start').open = true"
  +"From start"
}
button {
  onClick = "document.getElementById('ox-doc-drawer-edge-bottom').open = true"
  +"From bottom"
}
oblyxDrawer(edge = OblyxDrawerEdge.START, label = "Navigation") {
  id = "ox-doc-drawer-edge-start"
  oblyxDialogHeader(heading = "Navigation")
  oblyxDialogBody { +"Slides in from the start edge (the left, in LTR; mirrors under :dir(rtl))." }
}
oblyxDrawer(edge = OblyxDrawerEdge.BOTTOM, label = "Details") {
  id = "ox-doc-drawer-edge-bottom"
  oblyxDialogHeader(heading = "Details")
  oblyxDialogBody { +"Slides up from the bottom edge." }
}

Size

size sets the panel's width on a start/end drawer, or its height on a top/bottom one. Default md.

Drawer size

size="lg" widens the panel (edge start/end) or heightens it (edge top/bottom).
HTMLKotlin
<button type="button" onclick="document.getElementById('ox-doc-drawer-size-lg').open = true">
  Open large drawer
</button>
<oblyx-drawer id="ox-doc-drawer-size-lg" size="lg" label="Order details">
  <oblyx-dialog-header heading="Order details"></oblyx-dialog-header>
  <oblyx-dialog-body>size="lg" widens the panel (edge start/end) or heightens it (edge top/bottom).</oblyx-dialog-body>
</oblyx-drawer>
button {
  onClick = "document.getElementById('ox-doc-drawer-size-lg').open = true"
  +"Open large drawer"
}
oblyxDrawer(size = OblyxDrawerSize.LG, label = "Order details") {
  id = "ox-doc-drawer-size-lg"
  oblyxDialogHeader(heading = "Order details")
  oblyxDialogBody { +"size=\"lg\" widens the panel (edge start/end) or heightens it (edge top/bottom)." }
}

Opening and dismissing

Escape and a backdrop click both close the drawer, gated by dismissible (default true) exactly like dialog: one flag for both, not two, since a drawer that shouldn't lose input to a stray Escape shouldn't lose it to a stray backdrop click either. Escape is intercepted at keydown rather than relying on the native <dialog> cancel event, because that event reports cancelable: false in Chromium and closes the dialog regardless of preventDefault(). A backdrop click is detected by comparing event.target to event.currentTarget: a genuine ::backdrop click always targets the <dialog> element itself, never a child. An <oblyx-dialog-header closable> gets the same treatment as a plain close button, and setting dismissible to false turns off all three at once, the same as dialog. dismissible defaults to true, so bare presence (or omitting the attribute) means true; writing the literal string dismissible="false" in server-rendered markup turns it off. See dialog's own note on this for a working example.

Focus

Focus trapping and Tab-cycling come from showModal() making the rest of the page inert while the drawer is open: the platform's own work, not a hand-built trap. On open, focus moves to [data-autofocus] if present, otherwise to the header's title, otherwise to the browser's own default first-focusable-descendant. On close, focus returns to whatever element opened the drawer, sequenced strictly after dialog.close() completes.

A dialog and a drawer opened at the same time (a confirm dialog launched from inside a drawer, for instance) share one reference-counted page-scroll lock rather than fighting over it independently: the second opener doesn't stomp the scroll position the first one already saved.

Reference

<oblyx-drawer>

AttributeTypeDefaultDescription
edgestartendtopbottomendWhich viewport edge the panel slides from.
sizesmmdlgmdPanel width (start/end) or height (top/bottom).
openbooleanfalseWhether the dialog is open. Setting this directly always takes effect: it is the "force open/closed" path, matching a native <dialog>'s own close(). For a dismissal a user can cancel, see dismiss.
dismissiblebooleantrueWhether Escape and a backdrop click attempt to close the dialog at all.
labelstring | undefinedAccessible name, used when no <oblyx-dialog-header heading> (or <oblyx-drawer>'s own light content) provides one. Required in that case; its absence is flagged loudly, not silently ignored.

Events

EventTypeDescription
closeEvent