Stable

Split Pane

A generic two-column layout: a left pane and a right pane, optionally user-resizable by dragging a handle between them. It's a positioning primitive rather than a single canonical M3 pattern — three anchor modes cover the different places a split layout can live on a page.

Overview

M3's closest canonical layout is list-detail, but that pattern carries specific list/detail semantics (a selectable list driving a detail view). SplitPane here is deliberately more general — a resizable two-column primitive with no opinion about what goes in either side. This library's own root Rail layout and the docs secondary nav (this very sidebar's parent structure) are both built on it, alongside more list-detail-shaped uses.

Anchor Modes

anchor picks how the left pane is positioned. Getting this wrong under nested layouts is the most common mistake — pick based on what already surrounds the split, not just what "looks right" in isolation.

viewport (default)

The left pane fixes to the true browser edge (position: fixed). Use this when SplitPane is the outermost / only positioned element on the page — e.g. this library's root Rail layout.

parent

The left pane is absolutely positioned within a relative ancestor. Good for embedded/contained demos (a bounded sandbox box) — it does not stay pinned while the page itself scrolls.

sticky

Left and right sit in normal flex flow and the left pane sticks (position: sticky) as the page scrolls. Use this for a sidebar nested inside content that is already offset by something else — e.g. the docs secondary component-list nav, which lives inside the root layout's Rail-offset content column. viewport would fix it to the true left edge (underneath/behind the Rail); parent would lose the "stays visible while scrolling" behavior. Pair with resizable={false} for a static (non-draggable) sidebar.

Import

import { SplitPane } from '@noxlovette/material';

Live Demo

See it live, with Controls, in Storybook rather than a hand-rolled preview here. Separate stories demo the viewport and sticky anchor modes inside a bounded canvas.

widgets

Playground

Open the SplitPane story — includes Anchor Viewport and Anchor Sticky variants.

Basic Usage

A resizable split, anchored to the viewport:

<script lang="ts">
  import { SplitPane } from '@noxlovette/material';
</script>

<SplitPane anchor="viewport" leftWidth={320} minLeft={240} maxLeft={480}>
  {#snippet left()}
    <nav>...</nav>
  {/snippet}
  {#snippet right()}
    <main>...</main>
  {/snippet}
</SplitPane>

A static, non-draggable sidebar nested inside already-offset content:

<SplitPane anchor="sticky" resizable={false} leftWidth={240}>
  {#snippet left()}
    <nav>...</nav>
  {/snippet}
  {#snippet right()}
    <main>...</main>
  {/snippet}
</SplitPane>

SplitPane Props

PropTypeDefaultDescription
left required
SnippetContent for the left pane.
right required
SnippetContent for the right pane.
anchor
"viewport" | "parent" | "sticky""viewport"Positioning strategy for the left pane — see Anchor Modes below.
resizable
booleantrueWhether the split is user-draggable. Set false for a static sidebar — hides the drag handle and skips width persistence.
leftWidth
number396Initial left pane width in px. Bindable.
minLeft
number280Minimum left pane width in px, enforced while dragging.
maxLeft
number720Maximum left pane width in px, enforced while dragging.
storageKey
string"splitpane:leftWidth"localStorage key used to persist the dragged width.
persist
booleantrueWhether to persist and restore the width from localStorage. Ignored when resizable is false.
onPaddingChange
(padding: number) => voidCalled on mount/resize/destroy with leftWidth + 80px, letting a parent adjust footer padding to avoid the fixed left pane overlapping page-end content.
mobilePane
"left" | "right""right"Which pane is shown below the md breakpoint; both are always visible on desktop.
full
booleantrueWhether the panes stretch to fill available height.
rounded
booleantrueRounds the top/bottom corners of the panes on md+ breakpoints.
leftClass
stringExtra class applied to the left pane element.
rightClass
stringExtra class applied to the right pane element.

Accessibility

drag_indicator

Resize handle semantics

The drag handle renders role="separator" aria-orientation="vertical" with aria-valuenow/min/max reflecting the current and clamped width range.

visibility_off

Desktop-only split

The left pane and handle are hidden below the md breakpoint (hidden md:block); on mobile only one pane is shown at a time, chosen by mobilePane.