Skip to content
Kappa
Resizable
@dicehub/kappav0.4.2

Resizable

A precise splitter for resizable panels, workspaces, and dense application layouts.

Navigation
Inspector
<script setup>
import { Resizable } from "@dicehub/kappa/components/resizable";
</script>

<template>
  <Resizable.Root
    class="layout"
    :panels="[
      { id: 'navigation', minSize: 22 },
      { id: 'inspector', minSize: 32 },
    ]"
    :default-size="[36, 64]"
    aria-label="Workspace layout"
  >
    <Resizable.Panel id="navigation">Navigation</Resizable.Panel>
    <Resizable.Handle id="navigation:inspector" aria-label="Resize navigation and inspector" />
    <Resizable.Panel id="inspector">Inspector</Resizable.Panel>
  </Resizable.Root>
</template>

<style>
.layout { inline-size: 100%; block-size: 16rem; }
.layout [data-slot="resizable-panel"] { display: grid; place-items: center; padding: 1rem; font-weight: 650; }
</style>

Installation

Resizable is part of the main Kappa package. Ark UI owns panel measurements, pointer capture, keyboard behavior, and splitter semantics; Kappa provides the visual divider, grip, and semantic token treatment.

Barrel

import { Resizable } from "@dicehub/kappa";

Granular

import {
  Resizable,
  ResizableHandle,
  ResizablePanel,
  ResizableResizeTrigger,
  ResizableResizeTriggerIndicator,
} from "@dicehub/kappa/components/resizable";

Usage

Declare every panel in panels, render a Panel for each id, and place a Handle between adjacent panels. Give Root a size through its parent or a block-size class so the divider has a measurable area. Kappa panels are scroll containers, so long content can scroll without changing the splitter’s flex sizing.

Files
Preview
<script setup>
import { Resizable } from "@dicehub/kappa/components/resizable";
</script>

<template>
  <Resizable.Root
    class="workspace"
    :panels="[
      { id: 'files', minSize: 25 },
      { id: 'preview', minSize: 30 },
    ]"
    :default-size="[32, 68]"
    aria-label="File preview layout"
  >
    <Resizable.Panel id="files">Files</Resizable.Panel>
    <Resizable.Handle id="files:preview" aria-label="Resize files and preview" />
    <Resizable.Panel id="preview">Preview</Resizable.Panel>
  </Resizable.Root>
</template>

<style>
.workspace { inline-size: 100%; block-size: 16rem; }
.workspace [data-slot="resizable-panel"] { display: grid; place-items: center; padding: 1rem; font-weight: 650; }
</style>

Use the full id form before:after for predictable composition. A Handle is a Kappa alias for ResizeTrigger and includes ResizeTriggerIndicator when it has no custom slot.

Composition

Typical part hierarchy
Resizable.Root
├── Resizable.Panel
├── Resizable.Handle / Resizable.ResizeTrigger
│   └── Resizable.ResizeTriggerIndicator
├── Resizable.Panel
└── Resizable.Context (optional)

Ark UI's splitter machine remains the behavior layer. Kappa wraps the current Ark Vue 5.39.1 API and adds named parts, styling, a default grip for Handle, and the same Root / RootProvider / Context composition model.

Use RootProvider with useSplitter when the machine belongs in setup code. Use Context to call setSizes, collapsePanel, expandPanel, or other API methods from a descendant.

<script setup>
import { useSplitter } from "@dicehub/kappa/components/resizable";
import { Resizable } from "@dicehub/kappa/components/resizable";

const api = useSplitter({
  panels: [
    { id: "left", minSize: 25 },
    { id: "right", minSize: 25 },
  ],
  defaultSize: [40, 60],
});
</script>

<template>
  <Resizable.RootProvider :value="api">
    <!-- Compose Panel, Handle, and ResizeTriggerIndicator parts. -->
  </Resizable.RootProvider>
</template>

Examples

Vertical

Set orientation to vertical when the divider should separate stacked panels.

Chart
Events
<script setup>
import { Resizable } from "@dicehub/kappa/components/resizable";
</script>

<template>
  <Resizable.Root
    class="layout"
    orientation="vertical"
    :panels="[
      { id: 'chart', minSize: 30 },
      { id: 'events', minSize: 22 },
    ]"
    :default-size="[62, 38]"
    aria-label="Chart and events layout"
  >
    <Resizable.Panel id="chart">Chart</Resizable.Panel>
    <Resizable.Handle id="chart:events" aria-label="Resize chart and events" />
    <Resizable.Panel id="events">Events</Resizable.Panel>
  </Resizable.Root>
</template>

<style>
.layout { inline-size: 100%; block-size: 16rem; }
.layout [data-slot="resizable-panel"] { display: grid; place-items: center; padding: 1rem; font-weight: 650; }
</style>

Handle and indicator

Handle includes a quiet grip by default. Use the Ark-aligned ResizeTrigger and indicator parts for custom content.

Editor
Output
<script setup>
import { Resizable } from "@dicehub/kappa/components/resizable";
</script>

<template>
  <Resizable.Root
    class="layout"
    :panels="[
      { id: 'editor', minSize: 28 },
      { id: 'output', minSize: 28 },
    ]"
    :default-size="[58, 42]"
    aria-label="Editor and output layout"
  >
    <Resizable.Panel id="editor">Editor</Resizable.Panel>
    <Resizable.ResizeTrigger id="editor:output" aria-label="Resize editor and output">
      <Resizable.ResizeTriggerIndicator />
    </Resizable.ResizeTrigger>
    <Resizable.Panel id="output">Output</Resizable.Panel>
  </Resizable.Root>
</template>

<style>
.layout { inline-size: 100%; block-size: 16rem; }
.layout [data-slot="resizable-panel"] { display: grid; place-items: center; padding: 1rem; font-weight: 650; }
</style>

Collapsible panels

Mark a panel collapsible, then use Enter on its handle or Context methods to collapse and expand it.

Details
Summary
<script setup>
import { Resizable } from "@dicehub/kappa/components/resizable";
</script>

<template>
  <Resizable.Root
    class="layout"
    :panels="[
      { id: 'details', minSize: 20, collapsible: true, collapsedSize: 8 },
      { id: 'summary', minSize: 28 },
    ]"
    :default-size="[34, 66]"
    aria-label="Summary and details layout"
  >
    <Resizable.Panel id="details">Details</Resizable.Panel>
    <Resizable.Handle id="details:summary" aria-label="Resize details and summary" />
    <Resizable.Panel id="summary">
      <Resizable.Context v-slot="api">
        <div class="summary">
          <span>Summary</span>
          <div class="actions">
            <button type="button" @click="api.expandPanel('details')">Expand details</button>
            <button type="button" @click="api.collapsePanel('details')">Collapse details</button>
          </div>
        </div>
      </Resizable.Context>
    </Resizable.Panel>
  </Resizable.Root>
</template>

<style>
.layout { inline-size: 100%; block-size: 16rem; }
.layout [data-slot="resizable-panel"] { display: grid; place-items: center; padding: 1rem; font-weight: 650; }
.summary { display: grid; justify-items: center; gap: 0.75rem; }
.actions { display: flex; gap: 0.5rem; }
</style>

Nested splitters

Compose a second Root inside a panel when a workbench needs independent horizontal and vertical boundaries.

Outline
Code
Terminal
<script setup>
import { Resizable } from "@dicehub/kappa/components/resizable";
</script>

<template>
  <Resizable.Root
    class="layout"
    :panels="[
      { id: 'outline', minSize: 22 },
      { id: 'workbench', minSize: 38 },
    ]"
    :default-size="[28, 72]"
    aria-label="Project outline and workbench"
  >
    <Resizable.Panel id="outline">Outline</Resizable.Panel>
    <Resizable.Handle id="outline:workbench" aria-label="Resize outline and workbench" />
    <Resizable.Panel id="workbench" class="nested-host">
      <Resizable.Root
        class="nested-layout"
        orientation="vertical"
        :panels="[
          { id: 'code', minSize: 30 },
          { id: 'terminal', minSize: 24 },
        ]"
        :default-size="[64, 36]"
        aria-label="Code and terminal layout"
      >
        <Resizable.Panel id="code">Code</Resizable.Panel>
        <Resizable.Handle id="code:terminal" aria-label="Resize code and terminal" />
        <Resizable.Panel id="terminal">Terminal</Resizable.Panel>
      </Resizable.Root>
    </Resizable.Panel>
  </Resizable.Root>
</template>

<style>
.layout { inline-size: 100%; block-size: 18rem; }
.layout [data-slot="resizable-panel"] { display: grid; place-items: center; padding: 1rem; font-weight: 650; }
.nested-host { place-items: stretch; }
.nested-layout { inline-size: 100%; block-size: auto; align-self: stretch; }
</style>

Controlled layout

Use v-model:size when application state or another control owns the current layout.

Canvas · 42%
Metrics · 58%
<script setup>
import { ref } from "vue";
import { Resizable } from "@dicehub/kappa/components/resizable";

const size = ref([42, 58]);
</script>

<template>
  <Resizable.Root
    v-model:size="size"
    class="layout"
    :panels="[
      { id: 'canvas', minSize: 25 },
      { id: 'metrics', minSize: 25 },
    ]"
    aria-label="Controlled canvas and metrics layout"
  >
    <Resizable.Panel id="canvas">Canvas · {{ size[0] }}%</Resizable.Panel>
    <Resizable.Handle id="canvas:metrics" aria-label="Resize canvas and metrics" />
    <Resizable.Panel id="metrics">Metrics · {{ size[1] }}%</Resizable.Panel>
  </Resizable.Root>
  <div class="actions">
    <button type="button" @click="size = [28, 72]">28 / 72</button>
    <button type="button" @click="size = [60, 40]">60 / 40</button>
  </div>
</template>

<style>
.layout { inline-size: 100%; block-size: 16rem; }
.layout [data-slot="resizable-panel"] { display: grid; place-items: center; padding: 1rem; font-weight: 650; }
.actions { display: flex; justify-content: end; gap: 0.5rem; margin-block-start: 0.75rem; }
</style>

Accessibility

  • Give every Handle or ResizeTrigger a concise accessible label. Ark UI exposes a separator role, orientation, current value, limits, and controlled panel ids.
  • Keep panel ids unique within each Root and keep the panels array synchronized with rendered Panel parts.
  • Use visible focus styles. Pointer dragging and keyboard resizing remain available in light, dark, forced-colors, and reduced-motion modes.
  • Use disabled on a divider when that boundary must not move. Use panel constraints for minimum and maximum sizes.
  • When a panel is collapsible, explain the behavior in nearby text if the affordance is not obvious from the layout.

Keyboard Support

KeyBehavior
ArrowLeft / ArrowRightResize a horizontal divider by one step.
ArrowUp / ArrowDownResize a vertical divider by one step.
Shift + ArrowResize by 10 px for faster keyboard adjustments.
Home / EndMove the adjacent panel to its minimum or maximum boundary.
EnterCollapse or expand the adjacent panel when it is collapsible.
F6 / Shift + F6Move focus to the next or previous divider.

API Reference

Resizable.Root

PropTypeDefaultDescription
panelsResizablePanelData[]requiredPanel ids and optional order, minimum/maximum, and collapse constraints.
defaultSize / sizenumber[]equal / —Initial or controlled panel percentages.
orientation"horizontal" | "vertical""horizontal"Sets the divider axis and keyboard direction.
keyboardResizeBynumber1 pxPixels moved by an arrow key. Shift + Arrow uses 10 px.
id / idsstring / Partial<ElementIds>generatedStable machine and part identifiers for composition.
registrySplitterRegistry—Enables coordinated multi-drag interactions between splitters.
noncestring—Nonce for the global cursor style injected during pointer resize.
asChildbooleanfalseMerges root behavior into its direct child.

Parts

PartElementDescription
Resizable.RootdivOwns panel sizes, constraints, orientation, and resize state.
Resizable.RootProviderdivProvides an externally created useSplitter machine.
Resizable.PaneldivMeasured, constrained, and scrollable panel. Its id must exist in Root panels.
Resizable.HandlebuttonKappa alias for ResizeTrigger with a visible grip indicator by default.
Resizable.ResizeTriggerbuttonArk-aligned keyboard and pointer divider control. Use id='before:after'.
Resizable.ResizeTriggerIndicatordivOptional Ark indicator for a custom ResizeTrigger grip.
Resizable.ContextrenderlessExposes the splitter API for collapse, expand, and programmatic resizing.

Events

EventPayloadDescription
update:sizenumber[]Updates v-model after a size change.
resizeResizableResizeDetailsArk invokes this callback for controlled size changes; use update:size for the current model and resizeEnd for interaction completion.
resizeEndResizableResizeEndDetailsReports the final sizes after a resize interaction.
resizeStartvoidReports the start of a pointer or keyboard resize.
collapse / expandResizableExpandCollapseDetailsReports a collapsible panel changing state.

Slots

SlotDescription
Root.default / RootProvider.defaultPanel and divider parts in their layout order.
Panel.defaultPanel content. Give the panel an id present in Root panels.
Handle.defaultReplaces the default grip indicator when custom handle content is needed.
ResizeTrigger.defaultCustom content for an Ark-aligned divider control.
ResizeTriggerIndicator.defaultCustom indicator content inside a ResizeTrigger.
Context.defaultReceives the renderless ResizableApi value.

Data Attributes

AttributeValueDescription
data-slot"resizable" and part namesIdentifies Kappa roots and parts.
data-orientation"horizontal" | "vertical"Exposes the active axis on roots, panels, handles, and indicators.
data-id / data-indexpanel or trigger identityExposes Ark panel and divider identity for styling and testing.
data-draggingpresent while activeExposes active pointer or keyboard resize state.
data-focus / data-disabledpresent when applicableExposes divider focus and disabled states.
role / aria-valuenow / aria-controlson ResizeTriggerArk UI separator semantics and current layout values.

Exports

ExportDescription
ResizableCompound Ark-backed splitter component.
ResizableRoot / ResizableRootProvider / ResizablePanelNamed root, provider, and panel components.
ResizableHandleFriendly divider alias with a default grip indicator.
ResizableResizeTrigger / ResizableResizeTriggerIndicatorArk-aligned divider and optional indicator components.
ResizableContextRenderless access to the active splitter API.
ResizableProps / Resizable*Props / Resizable*Slots / ResizableEmitsPublic root, part, slot, and event contracts.
ResizablePanelData / ResizableApi / ResizableOrientationPanel constraints, machine API, and orientation types.
useSplitter / useSplitterContext / splitterAnatomyArk UI composition functions and anatomy.