GitHub

Components / Layout & Navigation / Split Panel

Split Panel beta

Two panes and a handle — drag it, or tab to it and use the arrow keys.

Preview
Notebooks
Product notes
Meeting log
Reading list
Archive
Product notes

The left pane is a list, the right pane is the thing itself. Drag the handle — or focus it and use the arrow keys — and the split holds anywhere between 20% and 80%.

<div class="k-split" data-split style="max-width:600px">
  <div class="k-split-a">
    <h5>Notebooks</h5>
    <div class="row on">Product notes</div>
    <div class="row">Meeting log</div>
    <div class="row">Reading list</div>
    <div class="row">Archive</div>
  </div>
  <div class="k-split-h" data-split-h tabindex="0" role="separator" aria-orientation="vertical" aria-label="Resize panes" aria-valuenow="38" aria-valuemin="20" aria-valuemax="80"><span class="dots"></span></div>
  <div class="k-split-b">
    <h5>Product notes</h5>
    <p>The left pane is a list, the right pane is the thing itself. Drag the handle — or focus it and use the arrow keys — and the split holds anywhere between 20% and 80%.</p>
  </div>
</div>
.k-split{display:flex;border:1px solid var(--k-line);border-radius:14px;overflow:hidden;background:var(--k-bg);font-family:var(--k-font);text-align:left;color:var(--k-ink);min-height:210px}
.k-split-a{flex:none;overflow:auto;padding:15px}
.k-split-b{flex:1;overflow:auto;padding:15px;min-width:0}
.k-split-h{flex:none;width:9px;margin:0 -1px;cursor:col-resize;display:grid;place-items:center;color:var(--k-ink-3);background:transparent;border-left:1px solid var(--k-line);border-right:1px solid var(--k-line);touch-action:none;z-index:2}
.k-split-h:hover,.k-split-h:focus-visible{background:var(--k-accent-bg);color:var(--k-accent);outline:none}
.k-split h5{font-size:11px;letter-spacing:.07em;text-transform:uppercase;color:var(--k-ink-3);font-weight:600;margin-bottom:9px}
.k-split p{font-size:13px;line-height:1.75;color:var(--k-ink-2)}
.k-split .row{font-size:13px;padding:7px 9px;border-radius:8px;white-space:nowrap}
.k-split .row.on{background:var(--k-accent-bg);color:var(--k-accent-ink);font-weight:550}
.k-split-h .dots{width:4px;height:24px;background-image:radial-gradient(circle,currentColor 1.3px,transparent 1.5px);background-size:4px 7px;background-position:center}
const split = document.querySelector('[data-split]');
const splitA = split.querySelector('.k-split-a');
const splitH = split.querySelector('[data-split-h]');
let splitPct = 38;
function splitSet(p) {
  splitPct = Math.max(20, Math.min(80, p));
  splitA.style.width = splitPct + '%';
  splitH.setAttribute('aria-valuenow', String(Math.round(splitPct)));
}
splitSet(splitPct);
splitH.addEventListener('pointerdown', e => {
  e.preventDefault();
  splitH.setPointerCapture(e.pointerId);
  const move = ev => {
    const r = split.getBoundingClientRect();
    splitSet((ev.clientX - r.left) / r.width * 100);
  };
  const up = () => {
    splitH.removeEventListener('pointermove', move);
    splitH.removeEventListener('pointerup', up);
  };
  splitH.addEventListener('pointermove', move);
  splitH.addEventListener('pointerup', up);
});
splitH.addEventListener('keydown', e => {
  const step = e.shiftKey ? 10 : 2;
  if (e.key === 'ArrowLeft') { e.preventDefault(); splitSet(splitPct - step); }
  if (e.key === 'ArrowRight') { e.preventDefault(); splitSet(splitPct + step); }
});

Usage

For editors, mail clients and inspectors: a list on one side, the thing itself on the other. Below two panes, stop — stack instead.

Accessibility

The divider is a real separator: role="separator" with aria-valuenow for the split percentage, arrow keys to move it, so the layout is adjustable without a pointer.

Related parts