Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Split pane

Two children and a divider you can drag, with a position that survives a resize and minimums that do not.

By the end of this chapter you can put a list beside a detail view, keep either side from being squeezed to nothing, let the user collapse one, and move the divider from the keyboard.

split-pane

A split-pane lays out exactly two children along one axis with a draggable divider between them.

split-pane id="demo-split" first-min=120 second-min=140 collapsible=#true {
    panel class="split-demo" {
        text class="caption" "The map"
        text "Tab to the divider and use the arrows, or Enter to collapse."
    }
    panel class="split-demo" {
        text class="caption" "The road"
        text "This one takes whatever is left."
    }
}
var split = new SplitPane(map, road);

new SplitPane(first, second) is horizontal, starts in the middle, and keeps its own position. The controlled form takes the position from the application and reports a drag back to it.

new SplitPane(SplitAxis.HORIZONTAL, position, this::setSplit, map, road)

The full constructor is SplitPane(axis, position, onResize, firstMin, secondMin, collapsible, children, attributes). A list of any size other than two is an IllegalArgumentException. A three-way split is two split panes, one inside the other.

The position is a fraction and the minimums are pixels, deliberately. A divider a third of the way across stays a third of the way across when the window widens. A list that needs 160 points or its labels wrap needs them whatever the window does. The fraction is clamped against the pixels on every layout, which needs the pane’s measured length, and that arrives once a frame through Measured.

The first pane is given its size in logical pixels and the second grows into the rest. Two flex-grows would not do it, because flex-grow shares out what is left after content and would ignore the fraction.

Attributes

AttributeTypeDefaultWhat it does
axisstringhorizontalhorizontal is side by side with a vertical bar. vertical is stacked with a horizontal bar
positionnumber0.5Where the divider starts, as a fraction of the pane’s length from 0 to 1
resizeaction namenoneAn action that takes a number. Naming one makes the pane controlled, and position is then where the divider is rather than where it starts
first-minnumber48The least the first child may be, in logical pixels
second-minnumber48The least the second child may be
collapsibleboolean#falseWhether dragging past a minimum collapses that child to nothing rather than stopping at it
idstringnoneThe node’s id, which lands on the split-pane node
classstringnoneClasses separated by spaces

The default minimum is 48 points. The design system’s hit-target floor is 32, and a pane squeezed to exactly that has no room for anything in it.

Styling

NodeWhat it isIn controls.css
split-paneThe whole panealign-items: stretch; flex-grow: 1; flex-shrink: 1
split-pane-sideEach child’s boxflex-direction: column; overflow: hidden
split-dividerThe bar6 px thick, flex-shrink: 0, --gb-border

The divider carries the axis as a class, horizontal or vertical, and collapsed when a side has gone to nothing. split-divider:hover and split-divider:focus-visible are both --gb-accent, on purpose: a divider has no state worth two appearances, and both mean “this is the thing you are about to move”. The focus ring is turned off because a ring at the usual offset would be drawn across both panes. split-divider.collapsed takes --gb-surface-2.

Each side clips what is in it, so a pane dragged narrower than its content does not spill across the divider.

A split pane grows to fill what it is given. In a column that sizes to its content it needs a height from the stylesheet, which is why the showcase gives its card a definite one. The pane measures its own length and asks for a rebuild when it changes, and that value cannot feed itself because what the rebuild changes is the children.

#demo-split { height: 220px; }

Keyboard

The divider is the Tab stop. The pane itself is not focusable and neither side is a composite.

KeyMoves
The arrows along the axis16 px
Page Up, Page Down64 px
Home, EndAs far as the minimums allow
Enter, SpaceCollapses and restores, when collapsible

The arrows across the axis are left alone, so a horizontal split has nothing to say about Up and the key reaches whatever does.

Read more