Resizable
Split panes with draggable dividers on plain flex and Pointer Events — no panel library. Arrow keys resize too.
INSTALL
1pnpm dlx @sayagodev/mo add resizableOr import the files from the package (no copy):
1import "@sayagodev/mo/css/resizable.css";2import "@sayagodev/mo/js/resizable.js";
EXAMPLES
Default
Preview
Header
Content
1<mo-resizable style="height: 200px; max-width: 24rem; border: 1px solid var(--border); border-radius: var(--radius-medium)">2 <div data-resizable-panel>3 <textarea aria-label="Markdown" placeholder="Markdown" style="width: 100%; height: 100%; border: none; resize: none"></textarea>4 </div>5 <!-- handles are inserted automatically between panels -->6 <div data-resizable-panel>7 <div style="display: flex; height: 100%; align-items: center; justify-content: center; padding: var(--space-6); font-weight: var(--font-medium)">Preview</div>8 </div>9</mo-resizable>10 11<mo-resizable data-orientation="vertical" data-sizes="25,75" style="height: 200px; margin-top: var(--space-4); max-width: 24rem; border: 1px solid var(--border); border-radius: var(--radius-medium)">12 <section data-resizable-panel>13 <div style="display: flex; height: 100%; align-items: center; justify-content: center">Header</div>14 </section>15 <section data-resizable-panel>16 <div style="display: flex; height: 100%; align-items: center; justify-content: center">Content</div>17 </section>18</mo-resizable>19
With handle
One
Two
Three
1<mo-resizable style="height: 200px; max-width: 24rem; border: 1px solid var(--border); border-radius: var(--radius-medium)">2 <div data-resizable-panel>3 <div style="display: flex; height: 100%; align-items: center; justify-content: center">One</div>4 </div>5 <div data-resizable-handle data-with-grip aria-label="Resize"></div>6 <div data-resizable-panel>7 <div style="display: flex; height: 100%; align-items: center; justify-content: center">Two</div>8 </div>9 <div data-resizable-handle data-with-grip aria-label="Resize"></div>10 <div data-resizable-panel data-min="20">11 <div style="display: flex; height: 100%; align-items: center; justify-content: center">Three</div>12 </div>13</mo-resizable>14
Vertical
Header
Content
1<mo-resizable data-orientation="vertical" style="height: 200px; max-width: 24rem; border: 1px solid var(--border); border-radius: var(--radius-medium)">2 <section data-resizable-panel>3 <div style="display: flex; height: 100%; align-items: center; justify-content: center">Header</div>4 </section>5 <section data-resizable-panel>6 <div style="display: flex; height: 100%; align-items: center; justify-content: center">Content</div>7 </section>8</mo-resizable>9
Collapsible
1<mo-resizable style="height: 16rem; border: 1px solid var(--border); border-radius: var(--radius-large); overflow: hidden;">2 <div data-resizable-panel data-collapsible data-min="0" style="padding: var(--space-4); background: var(--muted);">3 <strong>Collapsible</strong>4 <p class="text-light" style="font-size: var(--text-7);">Drag the handle to collapse. Double-click the divider to toggle.</p>5 </div>6 <div data-resizable-handle data-with-grip></div>7 <div data-resizable-panel style="padding: var(--space-4);">8 <strong>Main content</strong>9 <p class="text-light" style="font-size: var(--text-7);">This panel fills the space when the sidebar is collapsed.</p>10 </div>11</mo-resizable>12
Three panels
1<mo-resizable style="height: 14rem; border: 1px solid var(--border); border-radius: var(--radius-large); overflow: hidden;">2 <div data-resizable-panel style="padding: var(--space-4); background: var(--card);">3 <strong>Panel 1</strong>4 <p class="text-light" style="font-size: var(--text-7);">33%</p>5 </div>6 <div data-resizable-handle></div>7 <div data-resizable-panel style="padding: var(--space-4); background: var(--muted);">8 <strong>Panel 2</strong>9 <p class="text-light" style="font-size: var(--text-7);">33%</p>10 </div>11 <div data-resizable-handle></div>12 <div data-resizable-panel style="padding: var(--space-4); background: var(--card);">13 <strong>Panel 3</strong>14 <p class="text-light" style="font-size: var(--text-7);">33%</p>15 </div>16</mo-resizable>17
Nested (half + half split)
Half, and the other half split in two — like shadcn
1<mo-resizable style="height: 18rem; border: 1px solid var(--border); border-radius: var(--radius-large); overflow: hidden;">2 <div data-resizable-panel style="padding: var(--space-4); display:flex; align-items:center; justify-content:center; background: var(--card);">3 <strong>Left 50%</strong>4 </div>5 <div data-resizable-handle></div>6 <div data-resizable-panel style="padding:0; overflow:hidden;">7 <mo-resizable data-orientation="vertical" style="height:100%; border:none;">8 <div data-resizable-panel style="padding: var(--space-4); display:flex; align-items:center; justify-content:center; background: var(--muted);">9 <strong>Top 25%</strong>10 </div>11 <div data-resizable-handle data-with-grip></div>12 <div data-resizable-panel style="padding: var(--space-4); display:flex; align-items:center; justify-content:center; background: var(--card);">13 <strong>Bottom 25%</strong>14 </div>15 </mo-resizable>16 </div>17</mo-resizable>18<small class="text-light" style="display:block; text-align:center; margin-block-start: var(--space-2);">Half, and the other half split in two — like shadcn</small>19
USAGE
1<mo-resizable>2 <div data-resizable-panel>One</div>3 <div data-resizable-panel>Two</div>4</mo-resizable>5<!-- sleek 1px line by default; pill chip only with data-with-grip -->6<mo-resizable data-with-grip>7 <div data-resizable-panel>One</div>8 <div data-resizable-panel>Two</div>9</mo-resizable>10<mo-resizable data-orientation="vertical" data-sizes="25,75">11 <div data-resizable-panel data-min="20">Header</div>12 <div data-resizable-handle data-with-grip></div>13 <div data-resizable-panel>Content</div>14</mo-resizable>15<!-- or a custom manual grip (6 dots) -->16<mo-resizable>17 <div data-resizable-panel>One</div>18 <div data-resizable-handle><span data-resizable-grip><svg …>●●/●●/●●</svg></span></div>19 <div data-resizable-panel>Two</div>20</mo-resizable>
API
NAMEKINDDESCRIPTION
<mo-resizable>elementSplit-pane group; emits mo-resize { sizes } on pointerup/keyup
data-orientationattributehorizontal (default) · vertical
data-sizesattributePanel percentages "25,75" — read on init, persisted live
[data-resizable-panel] + data-minattributePanel; minimum percent (default 10)
[data-resizable-panel] + data-collapsibleattributeMark a panel collapsible — dragging near 0 snaps closed; double-click handle toggles
[data-resizable-handle]attributeAuto-inserted hr if absent; focusable separator, arrows ±2% (Shift 10%)
[data-resizable-handle] + data-with-gripattributeShows the oval ⋮ chip (shadcn withHandle). Without it, just the sleek 1px line. Goes on the handle, or on the <mo-resizable data-with-grip> group for all handles.
[data-resizable-handle] > [data-resizable-grip]elementManual chip: drop your own SVG inside the handle for a custom grip (e.g. 6 dots ●●/●●/●●). If absent and data-with-grip is set, ⋮ is auto-injected.
FILES
src/css/resizable.csssrc/js/resizable.jsInstall:
mo add resizableTHEMING · PLAYGROUND
This is the exact component from the examples above, wearing a completely different theme. Edit the tokens and --mo-* hooks in the editor — autocomplete knows them all — and the preview obeys in real time. Your CSS always wins.
Global tokens
--border--card--foreground--radius--ringComponent hooks
--mo-resizable-active--mo-resizable-hit--mo-resizable-linePreview
Your CSS
Try it: type --mo- for the component hooks, or edit any value — the preview obeys in real time.