data-splitter

Add a resizable drag divider between panels with keyboard support. The lightweight attribute alternative to the full splitter-wc 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 <splitter-wc> web component.

For persistence, collapse, and triple-pane layouts, see the splitter-wc 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:

  1. Injects a div.splitter-divider between the two children
  2. Sets role="separator" on the divider with full ARIA attributes
  3. Listens for mouse drag, touch drag, and keyboard events on the divider
  4. Calculates the split position as a percentage and applies it to the first panel's flex-basis
  5. Reads data-min and data-max from the first child for constraints
  6. Sets data-splitter-init to 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.

Main Content
<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.splitter-divider injected between the two child panels with the following accessibility attributes:

  • role="separator" and tabindex="0" for keyboard focus
  • aria-orientation matching the split direction
  • aria-valuenow, aria-valuemin, aria-valuemax tracking position
  • aria-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
splitter-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('splitter-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.splitter-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 */ .splitter-divider { background: var(--color-border); transition: background 0.15s ease; } /* Hover and focus states */ .splitter-divider:hover, .splitter-divider:focus-visible { background: var(--color-primary); } /* Horizontal divider dimensions */ [data-splitter]:not([data-splitter="vertical"]) > .splitter-divider { width: 4px; cursor: col-resize; } /* Vertical divider dimensions */ [data-splitter="vertical"] > .splitter-divider { height: 4px; cursor: row-resize; } /* Grip indicator (three dots/lines) */ .splitter-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.

// Dynamically added splitters are auto-enhanced via MutationObserver const container = document.createElement('div'); container.dataset.layout = 'sidebar'; container.dataset.splitter = ''; container.innerHTML = '<nav>Side</nav><main>Content</main>'; document.body.appendChild(container); // divider is injected and ready — no manual init needed

Accessibility

  • role="separator" identifies the divider as a resizable boundary to screen readers
  • aria-valuenow, aria-valuemin, and aria-valuemax convey position and range
  • aria-orientation indicates horizontal or vertical resize direction
  • aria-label="Resize panels" provides a clear accessible name
  • tabindex="0" makes the divider keyboard-focusable with a visible focus ring
  • Full keyboard navigation: Arrow keys, Shift+Arrow, Home, and End
  • The divider traps neither focus nor mouse — panel content remains fully interactive
  • Without JavaScript, panels render in normal flow — progressive enhancement

See Also

  • splitter-wc — full web component with persistence, collapse/expand, and triple-pane layouts