ResizableScreen
Two panels and a handle between them, so the reader decides how the width is divided.
A resizable screen is two panels with a handle that moves the boundary between them. The first panel takes the size you give it, the second takes whatever is left, and the reader adjusts the division by dragging the handle or by focusing it and pressing an arrow key. It is the layout of an editor with a file tree, a mail client with a list beside a message, and a data tool with a query beside its results.
It is the interactive counterpart of Split: a split states a ratio and keeps it, while a
resizable screen hands the ratio to the reader. The component is web-only, because a draggable
divider is a desktop idiom.
The handle is a real separator rather than a decorated border: dragging it captures the pointer,
the arrow keys move the boundary by step, and Home and End jump to the
clamps. hideHandle renders the same split without it, for the case where the division is fixed
today but the layout is not.
Usage
import { ResizableScreen } from "@asheeui/web";
<ResizableScreen defaultSize={35} minSize={20} maxSize={60}>
<nav aria-label="Documents">
<ul>
<li>INV-1041</li>
</ul>
</nav>
<article>
<p>The invoice.</p>
</article>
</ResizableScreen>
children is a pair of exactly two nodes, and the component reads them as a tuple: the first is
the panel the percentage applies to, the second takes the remainder, and the handle is rendered
between them. One child is a type error rather than a silent layout, because a split with one
panel has nothing to divide, that case is Split or a plain Stack.
A size that is controlled or not
defaultSize seeds an uncontrolled split. The component owns the number, dragging changes it, and
onSizeChange is told what it became, so a page that only wants to react can listen without
taking ownership.
When size is given the component is controlled: it draws that percentage and reports every
request through onSizeChange, and the boundary only moves when the page writes the number back.
left is an alias for defaultSize, kept for callers that named the first panel's position
rather than its size.
Sizes are percentages, so a split keeps its proportions when the window changes. A controlled split therefore leaves the page the owner of a number it has to keep in state beside whatever else that panel's width decides, which is the reason to control it in the first place.
Clamps, steps and orientation
minSize and maxSize clamp the first panel as a percentage, 20 and 80 unless the instance or
components.resizableScreen say otherwise. Every path into the number runs through the clamp: the
drag, the arrow keys, Home and End, and the value onSizeChange reports.
step is how far one arrow press moves the boundary, 2 by default.
orientation states which way the split divides. horizontal puts the panels side by side and
resizes with ← and →; vertical stacks them and resizes with ↑
and ↓.
minContainerWidth and minContainerHeight are pixels rather than percentages, and they exist
because a split inside a parent with no definite size would otherwise collapse: the container
claims at least 200 pixels of each unless the instance or components.resizableScreen say
otherwise, and a definite height from the layout around it, h-64, a flex row, a grid row, is
what the percentage is measured against. The two are configuration as well as instance props, so a
product whose panels are never smaller than a particular size states the floor once.
Props
defaultSize, minSize, maxSize, step, orientation, minContainerWidth and
minContainerHeight are registered by the component as 50, 20, 80, 2, horizontal, 200
and 200, so each resolves from the instance, then components.resizableScreen, then that value.
hideHandle is not registered: its fallback is false, so the handle is drawn unless the instance
or the application hides it. left, size, onSizeChange and handleClassName are statements
about one split rather than about the product, so they are instance props.
Accessibility
- The handle is a focusable
role="separator"withtabindex="0", the pattern ARIA defines for a resizable split. It carries a name (Resize panel split),aria-valuenowwith the first panel's percentage,aria-valueminandaria-valuemaxfrom the clamps, andaria-orientationdescribing the separator's own axis: a side-by-side split is divided by a vertical separator, so it reportsvertical, and a stacked split reportshorizontal. That is the line the separator draws, the opposite of the axis the panels are laid out on, and the arrows move the separator across it, which is why a vertical one answers to ← and →. - The keyboard contract is complete: the arrows move the boundary by
stepin the directionorientationimplies, and Home and End jump to the minimum and the maximum. The handle can therefore be used without a pointer, which is what a resizable split usually fails at. - The drag captures the pointer, so a fast drag that leaves the handle keeps resizing, and the
handle is
touch-none, so a scroll gesture is not stolen while the drag is in flight. - Each panel scrolls on its own, so a long list in the first panel does not push the second out of view, and the panels keep the order they are written in for a reader moving through the document.
A resizable screen is the interactive end of the layout family; the static arrangements it sits beside are described once in Layout and spacing.
Native
The compatibility matrix classifies this component as web-only, and the reason is the
interaction rather than the arrangement: a draggable divider is a desktop idiom, and a native
split view, where a platform offers one, is the platform's to draw, with its own gestures and
its own presentation. @asheeui/native exports nothing for it.
What to reach for on a device: Split for the static two-pane layout, which stacks the panes on a
narrow screen, and the platform's own split view when the user really must move the boundary. See
React Native.