Sidebar#
Component <neo-sidebar> is a responsive navigation rail. Below the overlay-breakpoint width (50rem default) it switches to a fixed overlay with a backdrop; dismiss via the header X, backdrop click, or
The selected state's markup and its scoped CSS. The CSS is wrapped in <style>@scope { ... }</style> and applies to the preview only. Edits change the preview above, applied live while Sync is on, or when you click Patch or Replace. Patch morphs the preview; Replace swaps it outright.
Signals
Datastar signals declared in the selected state's markup. Change a value to drive the preview, applied live while Sync is on, or when you click Patch.
Reference#
Attributes #
| Name | Type | Default | Description |
|---|---|---|---|
open | boolean | false | Whether the sidebar is expanded. |
side | "left" | "right" | left | Edge of the viewport the sidebar attaches to. |
manual | boolean | false | Disable auto-open at wide layouts. |
overlay | boolean | false | Always render as a fixed overlay. |
overlay-breakpoint | string | "50rem" | CSS length below which the sidebar acts as an overlay. |
touch-dismiss | "off" | CSS length | on | Drag-to-close gesture in overlay mode. On by default; set touch-dismiss="off" (or "false" / "no" / "none") to disable. The default close threshold is half the host's width; pass a CSS length (e.g. 120, 120px, or 40%) to pin it explicitly. Touches that start on a horizontally-scrollable child or on an element that owns a horizontal gesture (range input, slider, resizable handle, color field, or any [data-neo-sidebar-touch-ignore] descendant) are ignored. |
States #
| Name | Description |
|---|---|
:state(overlay) | Set while the sidebar renders as a fixed overlay: overlay is pinned, or the parent's width sits at or below overlay-breakpoint. Page CSS branches its shell layout on this. |
:state(dragging) | Set while a touch-dismiss drag is in flight. |
Slots #
| Name | Description |
|---|---|
[data-neo-sidebar-header] | Top section. |
[data-neo-sidebar-content] | Scrollable middle section. |
[data-neo-async-placeholder] | Resting content inside [data-neo-sidebar-content]; snapshotted on connect and restored on close so each open loads fresh. |
[data-neo-sidebar-footer] | Bottom section. |
[data-neo-sidebar-minimized] | Compact wide-mode closed state. |
[data-neo-sidebar-close] | Any descendant carrying this attribute closes the sidebar on click. |
Events #
| Name | Detail | Bubbles | Description |
|---|---|---|---|
neo-sidebar-open | none | yes | Fires when the sidebar opens. |
neo-sidebar-close | none | yes | Fires when the sidebar closes. |
CSS variables #
| Name | Default | Description |
|---|---|---|
--neo-sidebar-width | 16rem | Open column width. |
--neo-sidebar-bg | var(--btn-bg) | Background color. |
--neo-sidebar-fg | var(--btn-fg) | Text color. |
--neo-sidebar-border-color | var(--btn-border) | Edge border color. |
--neo-sidebar-padding | round(calc(var(--page-spacing, 0.25rem) * 3), 1px) | Padding of header, content, and footer sections. |
--neo-sidebar-gap | round(calc(var(--page-spacing, 0.25rem) * 2), 1px) | Gap within header and footer rows. |
--neo-sidebar-transition-duration | calc(200ms * var(--neo-duration-scale, 1)) | Open / close animation duration. |
--neo-sidebar-overlay-breakpoint | 50rem | Width below which the sidebar acts as an overlay. The overlay-breakpoint attribute overrides this per instance. |
--neo-sidebar-minimized-width | 3.5rem | Width of the minimized wide-mode rail. |
--neo-sidebar-z-index | 60 | Stacking index in overlay mode; the backdrop sits one below. |
--neo-sidebar-backdrop-bg | rgba(0, 0, 0, 0.45) | Overlay backdrop color. |
Examples#
Both sidebars use the global 50rem threshold by default; the overlay-breakpoint attribute overrides it per instance. With a left nav and a right drawer both in-flow, push the drawer into overlay mode sooner (larger breakpoint) while keeping the nav in-flow longer (smaller breakpoint).
Mixed breakpoints#
The preview has a left nav at overlay-breakpoint="30rem" and a right settings drawer at overlay-breakpoint="50rem", the same strategy the doc-app frame uses. Drag the frame's right handle to try it.
- Above 50rem both stay in-flow.
- Between 30rem and 50rem only the right one becomes a drawer (toggle it with the gear button).
- Below 30rem both are drawers.
Drag the right edge of this frame to shrink it. The nav stays in-flow down to 30rem; the settings drawer flips to overlay below 50rem.
A right-side settings drawer.
Wrap the sidebar in a <neo-resizable> and drive --neo-sidebar-width from --neo-resizable-width so the sidebar follows the wrapper's width. The wrapper's min-width / max-width set the drag bounds (here 12rem to 22rem). Drag the inner edge of either sidebar to try it.
Resizable widths#
When the preview crosses the overlay breakpoint, or when either sidebar is closed, the resizable wrappers release their flex space. The sidebars keep the last resized width when they reopen or become drawers. Use the menu and info buttons below to bring them back.
Drag the right edge of the left sidebar or the left edge of the right sidebar to resize them, bounded to a readable range.
A right-side info column. Same resize pattern, handle on the left edge.
Touch-dismiss#
Mobile-style drag-to-close, on by default in overlay mode. A single-touch horizontal swipe in the closing direction follows the finger; release past the threshold or a fast flick closes, otherwise it snaps back.
- The threshold defaults to half the host's width; pass a CSS length (here
touch-dismiss="120px") to pin it, ortouch-dismiss="off"to opt out. - Try the preview on a touchscreen, or with browser device-toolbar touch emulation.
The gesture stands down for descendants that own a horizontal gesture of their own: horizontally scrollable containers, range inputs, sliders, resizable handles, color fields, or any element marked [data-neo-sidebar-touch-ignore], and only engages in overlay mode (it has no meaning for the in-flow wide-mode column). The <neo-slider> in the panel below demonstrates this: dragging the slider thumb horizontally never closes the sidebar.
Touch devices only.
Open the menu, then swipe it left (toward its anchored side) to drag it out. Release past the 120px threshold to close.
Examples: Datastar#
Async loading#
Load content on every open. Mark the resting content with [data-neo-async-placeholder]: the sidebar snapshots it on connect and restores it on close, so every open loads fresh (same slot pattern as <neo-dialog> and <neo-drawer>).
- Open emits
neo-sidebar-open; wiredata-on:neo-sidebar-opento post and morph the returned panel into the[data-neo-sidebar-content]slot (matched by id). - The
neo-sidebar-closehandler only aborts the in-flight request; the host resets the placeholder.
Loading states only show with simulated delay. Raise network latency or handler delay in the Settings panel.
Open the sidebar from the menu button. Each open posts to the server and morphs the response into the content slot; closing it resets the slot to its skeleton so the next open loads fresh.
Async load with failure swap#
Async load that shows a failure state and a retry button when the request keeps failing.
- The loader lives in the sidebar's
[data-neo-async-placeholder]slot, so close/reopen restores it; on success the server morphs<div id="<host-id>-body">in place. - Once Datastar exhausts its retry budget, a failure view with a
[data-neo-popover-async-retry]button replaces the content without closing the sidebar. - Same failure-swap contract as PopoverAsync.
Loading states only show with simulated delay. Raise network latency or handler delay in the Settings panel.
Open the sidebar from the menu button. Each open posts to the server; on repeated failure the content slot shows a retry callout. Retry re-posts without closing the sidebar.