# SplitPanel

> A group of resizable panels separated by draggable, keyboard-accessible handles.

Category: Advanced · Status: stable · Package: @bindshq/stance-react@0.1.1 · Docs: https://stanceui.com/components/split-panel

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.

## Import

```tsx
import { SplitPanel, ResizablePanel, ResizeHandle } from '@bindshq/stance-react';
```

Styles are imported once per app: `@bindshq/stance-tokens/css` and `@bindshq/stance-react/styles.css`.

## Examples

### Two panels

```tsx
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>
  );
}
```

### Vertical

```tsx
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.

```tsx
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

| Component | Prop | Type | Default | Description |
| --- | --- | --- | --- | --- |
| SplitPanel | `orientation` | `'horizontal' \| 'vertical'` | `'horizontal'` | Horizontal places panels side by side. |
| SplitPanel | `onLayout` | `(sizes: number[]) => void` | None | Called with panel sizes in percent after user resizing. |
| SplitPanel | `autoSaveId` | `string` | None | Persist sizes in localStorage under this id. |
| ResizablePanel | `defaultSize` | `number` | None | Initial size in percent. Panels without one share the remainder. |
| ResizablePanel | `minSize` | `number` | `10` | Minimum size in percent. |
| ResizablePanel | `maxSize` | `number` | `100` | Maximum size in percent. |
| ResizablePanel | `collapsible` | `boolean` | `false` | Allow collapsing with Enter on the following handle. |
| ResizablePanel | `collapsedSize` | `number` | `0` | Size in percent when collapsed. |
| ResizablePanel | `id` | `string` | None | Id referenced by the handle’s aria-controls. |
| ResizeHandle | `aria-label` | `string` | `'Resize panels'` | Name the handle after the panel it resizes. |
| ResizeHandle | `keyboardStep` | `number` | `5` | Percent per arrow key press (Shift doubles it). |
| ResizeHandle | `withGrip` | `boolean` | `true` | Show the grip affordance. |
| ResizeHandle | `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. |

## 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.

## Related

- https://stanceui.com/components/resizable-panel
- https://stanceui.com/components/scroll-area
