Skip to content
On this page Keyboard

Layout

ResizablePanels

Panels split by draggable window splitters with keyboard resize, min and max sizes and an optional saved layout.

<script>
  import { ResizableHandle, ResizablePanel, ResizablePanels } from "@codeatlasdev/ui";
</script>

<ResizablePanels label="Workspace" persist="workspace-layout">
  <ResizablePanel id="files" defaultSize={25} minSize={15}>…</ResizablePanel>
  <ResizableHandle label="Resize files" />
  <ResizablePanel id="editor" defaultSize={75}>…</ResizablePanel>
</ResizablePanels>

The replacement for react-resizable-panels / shadcn Resizable. Sizes are percentages of the group and always add up to 100; the group fills its container, so give the parent a height. Each ResizableHandle moves the boundary between its two neighbours only, and never past either panel’s minSize / maxSize.

#962

Two panels

Drag the line or focus it and press the arrow keys.

Files

Editor

Files 25% · Editor 75%. Focus the handle and use the arrow keys.

Reference #962 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { ResizableHandle, ResizablePanel, ResizablePanels } from '@codeatlasdev/ui';

  let sizes = $state<number[]>([25, 75]);
</script>

<div class="frame">
  <ResizablePanels label="Workspace" onlayout={(s) => (sizes = s)}>
    <ResizablePanel id="files" defaultSize={25} minSize={15} maxSize={50}>
      <p class="pane">Files</p>
    </ResizablePanel>
    <ResizableHandle label="Resize files" />
    <ResizablePanel id="editor" defaultSize={75}>
      <p class="pane">Editor</p>
    </ResizablePanel>
  </ResizablePanels>
</div>
<p class="note">Files {Math.round(sizes[0])}% · Editor {Math.round(sizes[1])}%. Focus the handle and use the arrow keys.</p>

<style>
  .frame { height: 12rem; max-width: 36rem; border: var(--ca-border-width) solid var(--ca-outline-default); border-radius: var(--ca-radius-md); overflow: hidden; }
  .pane { margin: 0; padding: var(--ca-space-3); color: var(--ca-text-secondary); font-size: var(--ca-font-size-caption); }
  .note { margin: var(--ca-space-2) 0 0; color: var(--ca-text-secondary); font-size: var(--ca-font-size-caption); }
</style>
#963

Nested and saved

A vertical group inside a horizontal one; the layout is kept in localStorage.

Folders

Messages

Reader

Reference #963 · Pick in the gallery

Code
svelte
<script lang="ts">
  import { ResizableHandle, ResizablePanel, ResizablePanels } from '@codeatlasdev/ui';
</script>

<div class="frame">
  <ResizablePanels label="Mail" persist="ca-demo-mail-layout">
    <ResizablePanel id="folders" defaultSize={20} minSize={12}>
      <p class="pane">Folders</p>
    </ResizablePanel>
    <ResizableHandle label="Resize folders" />
    <ResizablePanel id="main" defaultSize={80}>
      <ResizablePanels direction="vertical" label="Message list and reader">
        <ResizablePanel id="list" defaultSize={40} minSize={20}>
          <p class="pane">Messages</p>
        </ResizablePanel>
        <ResizableHandle label="Resize message list" />
        <ResizablePanel id="reader" defaultSize={60} minSize={20}>
          <p class="pane">Reader</p>
        </ResizablePanel>
      </ResizablePanels>
    </ResizablePanel>
  </ResizablePanels>
</div>

<style>
  .frame { height: 14rem; max-width: 36rem; border: var(--ca-border-width) solid var(--ca-outline-default); border-radius: var(--ca-radius-md); overflow: hidden; }
  .pane { margin: 0; padding: var(--ca-space-3); color: var(--ca-text-secondary); font-size: var(--ca-font-size-caption); }
</style>

Keyboard

A handle is a WAI-ARIA window splitter: role="separator", focusable, with aria-valuenow the size of the panel before it, and aria-valuemin / aria-valuemax the reachable range. Arrows move 1% (Left and Right in a horizontal group, Up and Down in a vertical one, mirrored in RTL), Shift plus an arrow 10%, Home and End go to the limits. Name each handle after what it resizes.

Saving the layout

persist="key" saves { panelId: percent } in localStorage when a drag or key press settles and restores it on mount. A stored layout that does not name exactly the panels on screen is ignored, so adding a panel never breaks a returning user. Panel ids are therefore part of the key: keep them stable. onlayout receives the same sizes without storage, for your own persistence.

Props

PropTypeDefaultDescription
direction'horizontal' | 'vertical''horizontal'Side by side, or stacked.
labelstringundefinedAccessible name of the group.
persiststringundefinedlocalStorage key for the layout.
onlayout(sizes: number[]) => voidundefinedSizes in percent, in panel order, when a drag or key press settles.
classstring''Extra class names.

Props

PropTypeDefaultDescription
idstring–Stable key of the panel, unique in the group. Required.
defaultSizenumberundefinedStarting size in percent. Unset panels share what is left.
minSizenumber10Smallest size in percent.
maxSizenumber100Largest size in percent.

Props

PropTypeDefaultDescription
labelstring'Resize panels' (localized)Accessible name; say what it resizes.
classstring''Extra class names.