The <luna-split-panel> component creates resizable split panels with snapping, nesting, and custom divider styling.
Paths
/lunadom/components/split-panel/split-panel.js REQUIRED
Horizontal Split
Left Panel
Drag the divider to resize. The left panel maintains its size while the right fills remaining space.
Right Panel
This panel flexes to fill the available space. Try resizing the browser window.
Vertical Split
Top Panel
Drag horizontally to resize vertically stacked panels.
Bottom Panel
Bottom panel flexes to fill remaining height.
With Snap Points
Snap at 25%, 50%, 75%
Drag the divider near these points to feel the snap effect. Higher threshold (40px) makes it easier to snap.
The divider will magnetically snap to the defined positions when you get close.
Snap with repeat() Notation
Grid Snapping
repeat(5, 20%) creates snap points at 20%, 40%, 60%, 80%, 100%
Useful for creating evenly-spaced layouts like column grids.
Fixed Header with Label
Fixed Header (60px)
The header maintains a fixed 60px height while this content area fills the remaining space.
Scrollable content below...
Primary Panel (Fixed Sidebar)
Liquid Panel
This panel expands/contracts to fill available space.
Fixed Sidebar (200px)
This panel maintains 200px width while the left panel flexes.
Custom Divider Styling
Thick Green Divider
Custom 8px wide divider with green hover color and larger grip handle.
Hover over the divider to see the custom green highlight.
Scrollable Content
Scrollable Left
Each panel has automatic scrolling when content overflows. The scrollbars are hidden but you can still scroll with mouse wheel or touch.
Tall content area that triggers scrolling...
Scrollable Right
This panel also scrolls independently. Try scrolling each side separately.
Even taller content to demonstrate independent scrolling...
Nested Split Panels
Customization
Slots
start - Content for the first (left or top) panel.
end - Content for the second (right or bottom) panel.
divider - Optional custom content rendered inside the divider bar.
Attributes
position - Initial split position (% or px). Defaults to '50%'.
vertical - Stack panels top/bottom instead of left/right.
primary - Which panel keeps explicit size (start, end).
snap - Space-separated snap positions (e.g., '25% 50% 75%'). Supports repeat() notation.
snap-threshold - Pixel distance for snap activation. Defaults to 30.
min-size - Minimum size of either panel. Defaults to '50px'.
disabled - Prevents resizing.
CSS Variables
--luna-sp-divider-width - Width (or height if vertical) of divider bar. Defaults to 4px.
--luna-sp-divider-hit-area - Invisible pointer target around bar. Defaults to 16px.
--luna-sp-divider-color - Resting bar color. Defaults to var(--luna-border, #222).
--luna-sp-divider-color-hover - Bar color on hover/drag. Defaults to var(--luna-accent, #2563eb).
--luna-sp-handle-size - Length of grip handle. Defaults to 28px.
--luna-sp-handle-color - Grip dot color. Defaults to #555.
--luna-sp-handle-color-hover - Grip dot color on hover/drag. Defaults to #aaa.
--luna-sp-panel-bg - Background applied to both panels. Defaults to transparent.
Events
luna-reposition - Fired while dragging. detail: { position: string, value: number }
Example Code
<!-- Horizontal Split -->
<luna-split-panel position="50%">
<div slot="start">Left Panel</div>
<div slot="end">Right Panel</div>
</luna-split-panel>
<!-- Vertical Split -->
<luna-split-panel vertical position="40%">
<div slot="start">Top Panel</div>
<div slot="end">Bottom Panel</div>
</luna-split-panel>
<!-- With Snap Points -->
<luna-split-panel position="50%" snap="25% 50% 75%" snap-threshold="40">
<div slot="start">Snaps at 25%, 50%, 75%</div>
<div slot="end">Right Panel</div>
</luna-split-panel>
<!-- Scrollable Content (automatic) -->
<luna-split-panel position="50%">
<div slot="start">
<div style="height: 1000px;">Tall content scrolls automatically</div>
</div>
<div slot="end">
<div style="height: 1500px;">Independent scrolling</div>
</div>
</luna-split-panel>
<!-- Fixed Header with Label -->
<luna-split-panel vertical position="60px" primary="start">
<div slot="start">Fixed Header (60px)</div>
<div slot="end">Scrollable content area</div>
</luna-split-panel>
<!-- Snap with repeat() Notation -->
<luna-split-panel position="50%" snap="repeat(5, 20%)" snap-threshold="35">
<div slot="start">Snaps every 20%: 20%, 40%, 60%, 80%, 100%</div>
<div slot="end">Right Panel</div>
</luna-split-panel>
<!-- Primary Panel (Fixed Size) -->
<luna-split-panel position="200px" primary="end">
<div slot="start">Liquid Panel (expands/contracts)</div>
<div slot="end">Fixed Sidebar (200px)</div>
</luna-split-panel>
<!-- Custom Divider Styling -->
<luna-split-panel
position="60%"
style="
--luna-sp-divider-width: 8px;
--luna-sp-divider-color: #2a2a2a;
--luna-sp-divider-color-hover: #22c55e;
--luna-sp-handle-size: 40px;
--luna-sp-handle-color: #555;
--luna-sp-handle-color-hover: #22c55e;
">
<div slot="start">Thick Green Divider</div>
<div slot="end">Right Panel</div>
</luna-split-panel>
<!-- Nested Split Panels -->
<luna-split-panel position="200px" primary="start">
<div slot="start">Sidebar</div>
<luna-split-panel slot="end" vertical position="50%">
<div slot="start">Top Content Area</div>
<div slot="end">Bottom Content Area</div>
</luna-split-panel>
</luna-split-panel>
<!-- Listening to reposition events -->
<script>
const splitPanel = document.querySelector('luna-split-panel');
splitPanel.addEventListener('luna-reposition', (e) => {
console.log('New position:', e.detail.position);
console.log('Value:', e.detail.value);
});
</script>