Components Advanced
SplitPanel
StableA group of resizable panels separated by draggable, keyboard-accessible handles.
Use SplitPanel for workspaces where people balance two or three views, such as a template list beside a live signature preview. Panels are sized in percent with minimum and maximum limits; sizes can persist between visits. It is a primitive for content areas and does not impose an application layout.
Overview
import * as React from 'react';
import { ResizablePanel, ResizeHandle, SplitPanel } from '@bindshq/stance-react';
const pane: React.CSSProperties = { padding: 16, height: '100%', boxSizing: 'border-box' };
export function Basic() {
return (
<div
style={{
height: 260,
border: '1px solid var(--st-color-border-default)',
borderRadius: 12,
overflow: 'hidden',
}}
>
<SplitPanel>
<ResizablePanel id="sp-templates" defaultSize={35} minSize={20} maxSize={60}>
<div style={pane}>
<strong>Templates</strong>
<p>Standard · Sales team · Legal · Recruiting</p>
</div>
</ResizablePanel>
<ResizeHandle aria-label="Resize templates panel" />
<ResizablePanel id="sp-preview" minSize={30}>
<div style={{ ...pane, background: 'var(--st-color-bg-subtle)' }}>
<strong>Preview</strong>
<p>Priya Nair · Account Executive · Binds</p>
</div>
</ResizablePanel>
</SplitPanel>
</div>
);
}Light, dark and high contrast
Every Stance component is designed and tested in all three appearance modes.
Import
import { SplitPanel, ResizablePanel, ResizeHandle } from '@bindshq/stance-react';Examples
import * as React from 'react';
import { ResizablePanel, ResizeHandle, SplitPanel } from '@bindshq/stance-react';
const pane: React.CSSProperties = { padding: 16, height: '100%', boxSizing: 'border-box' };
export function Vertical() {
return (
<div
style={{
height: 300,
border: '1px solid var(--st-color-border-default)',
borderRadius: 12,
overflow: 'hidden',
}}
>
<SplitPanel orientation="vertical">
<ResizablePanel id="sp-html" defaultSize={60} minSize={25}>
<div style={pane}>
<strong>Signature HTML</strong>
</div>
</ResizablePanel>
<ResizeHandle aria-label="Resize editor" />
<ResizablePanel id="sp-console" minSize={15}>
<div style={{ ...pane, background: 'var(--st-color-bg-subtle)' }}>
<strong>Validation</strong>
<p>No issues found.</p>
</div>
</ResizablePanel>
</SplitPanel>
</div>
);
}Three panels with collapse and persistence
Press Enter on a handle to collapse the panel before it. Sizes persist with autoSaveId.
Layout: drag or focus a handle
import * as React from 'react';
import { ResizablePanel, ResizeHandle, SplitPanel } from '@bindshq/stance-react';
const pane: React.CSSProperties = { padding: 16, height: '100%', boxSizing: 'border-box' };
export function ThreePanes() {
const [sizes, setSizes] = React.useState<number[]>([]);
return (
<div style={{ display: 'grid', gap: 8 }}>
<div
style={{
height: 240,
border: '1px solid var(--st-color-border-default)',
borderRadius: 12,
overflow: 'hidden',
}}
>
<SplitPanel autoSaveId="docs-split-three" onLayout={setSizes}>
<ResizablePanel id="sp-nav" defaultSize={22} minSize={15} collapsible collapsedSize={0}>
<div style={pane}>Folders</div>
</ResizablePanel>
<ResizeHandle aria-label="Resize folders" />
<ResizablePanel id="sp-list" defaultSize={38} minSize={20}>
<div style={pane}>Templates</div>
</ResizablePanel>
<ResizeHandle aria-label="Resize templates" />
<ResizablePanel id="sp-detail" minSize={25}>
<div style={{ ...pane, background: 'var(--st-color-bg-subtle)' }}>Details</div>
</ResizablePanel>
</SplitPanel>
</div>
<p style={{ margin: 0 }}>
Layout:{' '}
{sizes.length
? sizes.map((s) => `${Math.round(s)}%`).join(' · ')
: 'drag or focus a handle'}
</p>
</div>
);
}API reference
SplitPanel
| Prop | Type | Default | Description |
|---|---|---|---|
orientation | 'horizontal' | 'vertical' | 'horizontal' | Horizontal places panels side by side. |
onLayout | (sizes: number[]) => void | None | Called with panel sizes in percent after user resizing. |
autoSaveId | string | None | Persist sizes in localStorage under this id. |
ResizablePanel
| Prop | Type | Default | Description |
|---|---|---|---|
defaultSize | number | None | Initial size in percent. Panels without one share the remainder. |
minSize | number | 10 | Minimum size in percent. |
maxSize | number | 100 | Maximum size in percent. |
collapsible | boolean | false | Allow collapsing with Enter on the following handle. |
collapsedSize | number | 0 | Size in percent when collapsed. |
id | string | None | Id referenced by the handle’s aria-controls. |
ResizeHandle
| Prop | Type | Default | Description |
|---|---|---|---|
aria-label | string | 'Resize panels' | Name the handle after the panel it resizes. |
keyboardStep | number | 5 | Percent per arrow key press (Shift doubles it). |
withGrip | boolean | true | Show the grip affordance. |
disabled | boolean | false | Lock the boundary. |
Accessibility
- Handles are focusable role="separator" elements with aria-valuenow, aria-valuemin and aria-valuemax for the panel before them, and aria-controls pointing to it (window splitter pattern).
- Arrow keys resize, Home and End jump to the limits, and Enter collapses and restores collapsible panels.
- The pointer hit area is larger than the visible line, meeting the 24px target size.
- Dragging always has a keyboard alternative (WCAG 2.5.7).
Keyboard
| Keys | Action |
|---|---|
| ← → | Horizontal groups: shrink or grow the panel before the handle. |
| ↑ ↓ | Vertical groups: shrink or grow the panel above the handle. |
| Shift | With an arrow key: move twice as far. |
| Home End | Minimum or maximum size. |
| Enter | Collapse or restore a collapsible panel. |
Usage guidelines
Do
- Set sensible minimum sizes so content never becomes unusable.
- Label each handle after the panel it controls.
Don’t
- Use SplitPanel for the overall application shell.
- Use it on phones; stack the views instead.