Splitter
A draggable divider that resizes an adjacent pane, with configurable bounds and steps.
Live demo
Splitter is the handle between two panes: drag it (or arrow it with the keyboard) and the neighboring pane grows or shrinks. It is a role="separator" control, so it announces its current, minimum, and maximum size and takes the full keyboard.
The size it manages is _data_ the consumer owns: the splitter clamps a value (in px) to min/max, snaps it to an integral step, writes it into a CSS custom property (var) on a target so a grid or flex track resizes declaratively, and fires resize (live, during the drag) and resize-end (on release) so the consumer can react or persist. orientation picks the axis (vertical resizes width, horizontal resizes height), and reversed flips the direction for a trailing-edge pane like a right rail. Its own chrome is derived: the grip and its focus ring read from the same tokens the rest of the UI does.
When to use
How this component composes with the rest of the set.
Props
12 props, straight from the manifest.
| Prop | Type | Default | Bindings | Description |
|---|---|---|---|---|
Appearance
Sizes
sm
A slim divider for dense layouts.
md
The default divider footprint.
lg
A wide divider that's easy to grab.
States
focus-visible
The handle focused by keyboard: a ring picks it out, the grip tints to accent.
disabled
A locked splitter: the grip dims and the resize cursor drops.
Anatomy
The named parts that make up the component, with their selectors.
splitter
The separator handle: a fixed-size cell in the layout track that captures the drag.
grip
The visible grab affordance, accent-tinted on hover and focus.
Tokens & coverage
What the component consumes, checked live against what the algorithm produces.
Live coverage check against the xoji-default register
(derive(xojiDefault, { anchors }) →
coverComponent(manifest, register)). Every token this component
consumes must be a key the algorithm produces.
--accent
--border-normal
--border-thick
--border-thin
--duration-fast
--ease-standard
--line
--radius-sm
--ring
--space-2
--space-3
--space-5
--space-6
--space-8
Accessibility
Code
A resizable rail
A splitter between a side rail and the content, writing the rail width into a CSS variable the grid reads.
<div class="layout" style="display:grid;grid-template-columns:var(--rail,16rem) auto minmax(0,1fr);height:20rem">
<aside class="panel">Rail</aside>
<xoji-splitter var="--rail" value="256" min="160" max="480" step="8" label="Resize rail"></xoji-splitter>
<main class="panel">Content</main>
</div>
<script>
document.querySelector("xoji-splitter")
.addEventListener("resize", (e) => console.log(e.detail.value));
</script>
<script lang="ts">
import { Splitter } from "@xoji/svelte";
let rail = $state(256);
</script>
<div style={`display:grid;grid-template-columns:${rail}px auto minmax(0,1fr);height:20rem`}>
<aside>Rail</aside>
<Splitter value={rail} min={160} max={480} step={8}
onresize={(e) => (rail = e.detail.value)} label="Resize rail" />
<main>Content</main>
</div>
---
import Splitter from "@xoji/astro/Splitter.astro";
---
<div style="display:grid;grid-template-columns:var(--rail,16rem) auto minmax(0,1fr);height:20rem">
<aside>Rail</aside>
<Splitter var="--rail" value={256} min={160} max={480} step={8} label="Resize rail" />
<main>Content</main>
</div>