Resizable
Panes and the rule between them. Take the rule and it inks; while you hold it, each pane's share is drawn as a dimension.
Report an issue with ResizableExample
The brief
A new identity for a ferry line that has run between the islands since 1911.
The notes
Keep the flag. Lose the anchor. The timetable is the real product.
Halden
The line, its flag and its timetable.
Quay
Where the timetable is read.
The ferry line has run between the islands since 1911, and for most of that time its timetable was the only thing anyone read. The flag stayed; the anchor came and went with each new owner. What the islanders asked for was simple: a timetable large enough to read from the quay, the boats' names where they could find them, and nothing on the hulls that the type did not already say.
The brief
A new identity for a ferry line that has run between the islands since 1911.
The notes
Keep the flag. Lose the anchor. The timetable is the real product.
The brief
A new identity for a ferry line that has run between the islands since 1911.
The notes
Keep the flag. Lose the anchor. The timetable is the real product.
Halden
The line, its flag and its timetable.
Quay
Where the timetable is read.
Halden
The line, its flag and its timetable.
Quay
Where the timetable is read.
The ferry line has run between the islands since 1911, and for most of that time its timetable was the only thing anyone read. The flag stayed; the anchor came and went with each new owner. What the islanders asked for was simple: a timetable large enough to read from the quay, the boats' names where they could find them, and nothing on the hulls that the type did not already say.
Install
Run it in a project that has the shadcn CLI set up. It brings the 0nlyType base along.
npx shadcn@latest add https:// thedirectors.agency/ ui/ r/resizable.jsonUsage
import { ResizableHandle, ResizablePanel, ResizablePanelGroup, ResizableTitle } from "@/components/ui/resizable"const story ="The ferry line has run between the islands since 1911, and for most of that time its timetable was the only thing anyone read. The flag stayed; the anchor came and went with each new owner. What the islanders asked for was simple: a timetable large enough to read from the quay, the boats' names where they could find them, and nothing on the hulls that the type did not already say."function Rule({ at = 50, force }: { at?: number; force?: boolean }) {return (<ResizablePanelGroup direction="horizontal"><ResizablePanel defaultSize={at} minSize={20}><p className="ot-mp mb-2 text-ink">The brief</p><p>A new identity for a ferry line that has run between the islands since 1911.</p></ResizablePanel><ResizableHandle aria-label="Width of the brief" data-force={force ? "hover" : undefined} /><ResizablePanel defaultSize={100 - at} minSize={20}><p className="ot-mp mb-2 text-ink">The notes</p><p>Keep the flag. Lose the anchor. The timetable is the real product.</p></ResizablePanel></ResizablePanelGroup>)}function Fit({ at = 62 }: { at?: number }) {return (<ResizablePanelGroup direction="horizontal" variant="fit"><ResizablePanel defaultSize={at} minSize={15}><ResizableTitle>Halden</ResizableTitle><p>The line, its flag and its timetable.</p></ResizablePanel><ResizableHandle aria-label="Width of Halden" /><ResizablePanel defaultSize={100 - at} minSize={15}><ResizableTitle>Quay</ResizableTitle><p>Where the timetable is read.</p></ResizablePanel></ResizablePanelGroup>)}function Flow({ at = 50 }: { at?: number }) {return (<ResizablePanelGroup direction="horizontal" variant="flow" text={story}><ResizablePanel defaultSize={at} minSize={25} /><ResizableHandle aria-label="Width of the first column" /><ResizablePanel defaultSize={100 - at} minSize={25} /></ResizablePanelGroup>)}export default function Example() {return (<div className="grid gap-10"><Rule /><Fit /><Flow /></div>)}Contract
- Underneath: hook (a separator you can drag or move with the arrow keys).
- Anatomy:
.ot-resizeholding two.ot-resize-pane,<div class="ot-resize-handle" role="separator" tabindex="0" aria-valuenow>between them, and.ot-resize-dim(aria-hidden). Script sets--split(a percentage) anddata-dragging. - Take the rule and it inks; while held, each pane's share is drawn as a dimension, after Paul Rand.
- Keyboard: Left and Right move it by 5, Home and End go to 20 and 80.
- The frame is a construction line, dotted in rule-strong, after Rand's working drawings: it marks where the panes may go rather than boxing them.
- Variants go on
data-variant(ResizablePanelGroup variant): rule (the default), fit, flow. - Fit, after "It has to be design." set heavy and condensed, and wood type locked up to the measure: each pane's
<h3 class="ot-resize-title" data-slot="resizable-title"><span>is set in 800 to fill its pane edge to edge. The script moves Archivo's width axis first (62% as the pane narrows, 125% as it widens), and changes the size only past those ends, so narrowing a pane condenses its word before it shrinks it. It setsdata-setonce measured, and re-sets on every resize and when<html>changes its face. - Flow, after the newspaper's continued column: the group's
textruns through the panes as.ot-resize-flowparagraphs of.ot-resize-linelines (aria-hidden; the whole text once, in the first, as.ot-sr). The first pane holds as many lines as its height does, and the rest carry on at the top of the next, at its measure, the first of them in ink; move the rule and the break moves. pretext lays the lines; until it has, the first pane holds the plain paragraph. The group takes a fixed height (17rem). - Forced colours: the rules and dimensions are drawn in CanvasText (Highlight when taken).
Props
| Prop | Type | Default | What it does |
|---|---|---|---|
ResizablePanelGroup direction | "horizontal" | "vertical" | Which way the panels sit. Panels and handles must be its direct children, a handle between each pair, in that order (panel, handle, panel). They are told apart by place, not type. | |
ResizablePanelGroup variant | "rule" | "fit" | "flow" | "rule" | rule: panes and the rules between them. fit: each pane's ResizableTitle is set heavy to the pane's measure, as wood type is locked up: it condenses as the pane narrows and extends as it widens, and changes size only past the ends of the width axis. flow: the group's text runs through the panes as continued columns; the first holds as many lines as its height allows, the rest carry on in the next, and moving the rule moves the break. Flow sets the group's height. |
ResizablePanelGroup text | string | In flow, the one paragraph that runs through the panes. Plain text: pretext measures it. A reader of the page gets it whole, once, in the first pane. | |
ResizableTitle | h3 | A pane's title. In fit it fills its pane edge to edge; keep it to a word or two. Elsewhere it is a plain heading. | |
ResizablePanel defaultSize | number | Its share of the group in percent. Panels without one share what's left. | |
ResizablePanel minSize | number | 10 | The least it can be, in percent. |
ResizableHandle | separator | role=separator with aria-valuenow, min and max. Arrow keys move it in fives (Shift, tens); Home and End go to the limits. Give it an aria-label naming the panel it sizes. |
Craft
Where its move comes from
| Component | Reference | Move |
|---|---|---|
| Resizable, aspect ratio | Paul Rand dimension lines | Shares measured; a printer's crop marks |
| Fit resizable | "It has to be design." heavy condensed; wood type locked to the measure | The title fills its pane, condensing before it shrinks |
| Flow resizable | The newspaper's continued column | One paragraph runs through the panes; the rule moves the break |
How it moves
| Component | Articulation | What moves |
|---|---|---|
| Resizable | Measure | The rule inks; the dimensions appear while held |
| Fit resizable | Measure | The titles condense and extend with the rule, frame by frame |
| Flow resizable | Measure | The lines re-break under the hand as the rule moves |