data-splitter
Add a resizable drag divider between panels with keyboard support. The lightweight attribute alternative to the full split-surface web component.
Overview
The data-splitter attribute adds a resizable drag divider between two panels. It supports horizontal and vertical orientations, keyboard navigation, and min/max constraints. This is the lightweight attribute alternative to the full <split-surface> web component.
For persistence, collapse, and triple-pane layouts, see the split-surface component page.
<div data-layout="sidebar" data-splitter> <nav> <strong>Sidebar</strong> <p>Drag the divider or use arrow keys.</p> </nav> <main> <strong>Main Content</strong> </main></div>
How It Works
Add data-splitter to a container with exactly two child elements. The init script:
- Injects a
div.split-dividerbetween the two children - Sets
role="separator"on the divider with full ARIA attributes - Listens for mouse drag, touch drag, and keyboard events on the divider
- Calculates the split position as a percentage and applies it to the first panel's flex-basis
- Reads
data-minanddata-maxfrom the first child for constraints - Sets
data-splitter-initto prevent double-binding
The container should use data-layout="sidebar" for horizontal splits or data-layout="stack" for vertical splits.
Attributes
| Attribute | Element | Description |
|---|---|---|
data-splitter |
Container | Enables the splitter. Empty string for horizontal, "vertical" for vertical. |
data-min |
First child | Minimum size of the first panel as a percentage (default: 10). |
data-max |
First child | Maximum size of the first panel as a percentage (default: 90). |
data-splitter-init |
Container | Set automatically to prevent double-binding. Do not set manually. |
Vertical Orientation
Set data-splitter="vertical" for a top/bottom split. The container needs a fixed height since vertical splits rely on the container's height to calculate proportions.
<div data-layout="stack" data-splitter="vertical" style="height: 300px;"> <div>Top Panel</div> <div>Bottom Panel</div></div>
Min/Max Constraints
Add data-min and data-max to the first child element to constrain the drag range. Values are percentages. The divider stops at these boundaries during drag and keyboard navigation.
<div data-layout="sidebar" data-splitter> <nav data-min="20" data-max="60">Sidebar</nav> <main>Content</main></div>
Injected Divider
The divider is a div.split-divider injected between the two child panels with the following accessibility attributes:
role="separator"andtabindex="0"for keyboard focusaria-orientationmatching the split directionaria-valuenow,aria-valuemin,aria-valuemaxtracking positionaria-label="Resize panels"for screen readers
Keyboard Navigation
When the divider has focus, the following keyboard controls are available:
| Key | Action |
|---|---|
| ArrowLeft / ArrowRight | Move divider by 1% (horizontal) |
| ArrowUp / ArrowDown | Move divider by 1% (vertical) |
| Shift + Arrow | Move divider by 10% |
| Home / End | Move to minimum / maximum position |
Events
| Event | Detail | Description |
|---|---|---|
split-surface:resize |
{ position } |
Fired during drag and keyboard resize. position is the first panel's size as a percentage. |
const panel = document.querySelector('[data-splitter]'); panel.addEventListener('split-surface:resize', (e) => { console.log('Panel resized to:', e.detail.position + '%');});
Nested Splitters
Splitters can be nested for IDE-style layouts. Combine a horizontal sidebar split with a vertical editor/terminal split.
<div data-layout="sidebar" data-splitter> <nav> <strong>File Tree</strong> </nav> <div data-layout="stack" data-splitter="vertical" style="height: 500px;"> <main> <strong>Editor</strong> </main> <div> <strong>Terminal</strong> </div> </div></div>
Styling
The divider is a plain div.split-divider styled via CSS. A ::after pseudo-element provides the grip indicator. All styles are gated on [data-splitter-init] so panels display in normal flow without JavaScript.
/* The divider element */.split-divider { background: var(--color-border); transition: background 0.15s ease;} /* Hover and focus states */.split-divider:hover,.split-divider:focus-visible { background: var(--color-primary);} /* Horizontal divider dimensions */[data-splitter]:not([data-splitter="vertical"]) > .split-divider { width: 4px; cursor: col-resize;} /* Vertical divider dimensions */[data-splitter="vertical"] > .split-divider { height: 4px; cursor: row-resize;} /* Grip indicator (three dots/lines) */.split-divider::after { content: ''; display: block; width: 24px; height: 2px; background: var(--color-text-muted); border-radius: var(--radius-pill);}
Dynamic Elements
Splitter containers added to the DOM after page load are automatically enhanced via a MutationObserver. No manual initialization is needed.
Side</nav><main>Content</main>" document.body.appendChild(container);="" divider="" is="" injected="" and="" ready="" —="" no="" manual="" init="" needed="" code-block="" <="" section=""> <section> <h2>Accessibility</h2> <ul> <li><code>role="separator"</code> identifies the divider as a resizable boundary to screen readers</li> <li><code>aria-valuenow</code>, <code>aria-valuemin</code>, and <code>aria-valuemax</code> convey position and range</li> <li><code>aria-orientation</code> indicates horizontal or vertical resize direction</li> <li><code>aria-label="Resize panels"</code> provides a clear accessible name</li> <li><code>tabindex="0"</code> makes the divider keyboard-focusable with a visible focus ring</li> <li>Full keyboard navigation: Arrow keys, Shift+Arrow, Home, and End</li> <li>The divider traps neither focus nor mouse — panel content remains fully interactive</li> <li>Without JavaScript, panels render in normal flow — progressive enhancement</li> </ul> </section> <section> <h2>See Also</h2> <ul> <li><a href="/docs/elements/web-components/splitter/">split-surface</a> — full web component with persistence, collapse/expand, and triple-pane layouts</li> </ul> </section>