---
title: Window Splitter
description: Window splitters divide a container into two resizable panes for code editor layouts, preview panels, or master-detail views.
---

import { meta as WindowSplitterMeta, Default } from '@/content/stories/window-splitter';
import Canvas from '@/components/component-docs/canvas';
import Demo from '@/components/component-docs/demo';

export const config = {
	dependencies: {
		components: [Canvas, Demo],
		scripts: [
			'../../../components/component-docs/demo.script.tsx',
			'../../../components/component-docs/canvas.script.tsx',
			'../../../components/component-docs/controls.script.tsx',
		],
	},
};


# Window Splitter

<p class="docs-lede">Window splitters divide a container into two resizable panes for code editor layouts, preview panels, or master-detail views.</p>

## Try it

<Demo of={Default} meta={WindowSplitterMeta} />

## Usage

Pass `primary` and `secondary` pane content. Set `value` to the primary pane percentage and `orientation` for horizontal or vertical splits.

```tsx
import { RuiWindowSplitter } from '@ecopages/radiant-ui/window-splitter';

<RuiWindowSplitter
  value={50}
  orientation="horizontal"
  label="Split view"
  primary={<div>Editor</div>}
  secondary={<div>Preview</div>}
/>
```

## Custom markup

`<rui-window-splitter>` coordinates any light-DOM tree that matches its query contract. The
`RuiWindowSplitter` helper stamps these targets; it is not required.

```tsx
import '@ecopages/radiant-ui/window-splitter';

<rui-window-splitter value={50} orientation="horizontal" label="Split view">
  <div data-ref="root" class="rui-window-splitter rui-window-splitter--horizontal">
    <div data-ref="primary" class="rui-window-splitter__pane">Editor</div>
    <div data-ref="separator" class="rui-window-splitter__separator" role="separator" tabindex={0}></div>
    <div data-ref="secondary" class="rui-window-splitter__pane">Preview</div>
  </div>
</rui-window-splitter>;
```

Do not set primary pane `width` / `height` or separator `aria-valuenow` — the host owns those.

## Initial pane ratio

<p>`value` sets the primary pane percentage, clamped between 20 and 80. Users can drag the splitter to adjust.</p>

## Orientation

<p>Use `horizontal` for side-by-side panes and `vertical` for stacked editor/console layouts.</p>

## Theming

Window Splitter maps to **semantic border, surface, and focus roles**, never Tailwind palette steps:

| Part | CSS roles |
| --- | --- |
| Frame (`rui-window-splitter`) | `border`, `radius-container` |
| Panes | `background`, `on-background`, `--space-inset` |
| Separator | `border` |
| Separator focus | `focus-ring` |

Override at the theme layer, not in component CSS.

## API

`RuiWindowSplitter` is a custom element (`<rui-window-splitter>`) that owns the separator and pane sizing; the `RuiWindowSplitter` JSX view projects the two panes.

### Attributes

| Attribute | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `number` | `50` | Primary pane size as a percentage (clamped 20–80). A value that is not a number falls back to `50`. |
| `orientation` | `horizontal` · `vertical` | `horizontal` | Split axis. |
| `label` | `string` | `Split view` | Accessible name for the separator. |

### Light-DOM contract

| Target | Required | Host writes | Author owns |
| --- | --- | --- | --- |
| `[data-ref="root"]` | yes | orientation modifier classes | layout wrapper |
| `[data-ref="primary"]` | yes | inline `width` or `height` from `value` | pane content |
| `[data-ref="separator"]` | yes | `aria-orientation`, `aria-valuenow`, `aria-valuemin`, `aria-valuemax`, `aria-label` | view seeds `role="separator"`, `tabIndex` |
| `[data-ref="secondary"]` | yes | — | second pane content |

Nested hosts: none.

### Events

| Event | Detail | Description |
| --- | --- | --- |
| `rui-splitter-change` | `{ value: number }` | Emitted when the separator moves. |

### View helpers

| Component | Target stamped | Notes |
| --- | --- | --- |
| `RuiWindowSplitter` | `[data-ref="root"]`, `[data-ref="primary"]`, `[data-ref="separator"]`, `[data-ref="secondary"]` | `primary` and `secondary` props supply pane content. |

### Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `primary` | `JsxRenderable` | | First pane content for the `RuiWindowSplitter` view. |
| `secondary` | `JsxRenderable` | | Second pane content. |

### CSS classes

Public BEM classes on the composed surface (documented via `@cssclass` on `RuiWindowSplitterElement`):

| Class | Description |
| --- | --- |
| `.rui-window-splitter` | Root surface. |
| `.rui-window-splitter--horizontal` | Side-by-side panes. |
| `.rui-window-splitter--vertical` | Stacked panes. |
| `.rui-window-splitter__pane` | A pane region. |
| `.rui-window-splitter__separator` | Focusable separator (`role="separator"`). |

### Theme roles

| Part | CSS variables consumed |
| --- | --- |
| Frame | `--border`, `--radius-container` |
| Panes | `--background`, `--on-background`, `--space-inset` |
| Separator | `--border` |
| Separator focus | `--focus-ring` |

## Accessibility

- The splitter handle is keyboard focusable and adjustable with arrow keys.
- Provide `label` so screen readers identify the split region.
- Ensure both panes remain usable at minimum sizes; avoid content that breaks at narrow widths.
