<x-two-panel>) that displays two side-by-side panels.
The left panel shows descriptions; the right panel shows corresponding code or content.
Navigation buttons step through paired left/right items by index. The left panel width is
adjustable via toolbar buttons or single-click on either panel.
TwoPanelComponentRefactored/
js/TwoPanelComponentRefactored.js component definition
css/TwoPanelComponentRefactored.css host-page placement helpers
TwoPanelComponentRefactored.html demo / test page
<link rel="stylesheet" href="css/TwoPanelComponentRefactored.css">
<script src="js/TwoPanelComponentRefactored.js" defer></script>
The containing page should define --light and --dark CSS custom properties
for the default color scheme:
:root {
--light: #f0f0f0;
--dark: #333;
}
Define shared settings in a config module so every part of the app reads from one source.
Pure functions for summing and clamping values - no side effects, easy to test.
Wire DOM events after the page loads; keep handler logic out of HTML markup.
// config.js
export const config = {
appName: "TwoPanel Demo",
version: "1.0.0"
};
// utils.js
export const sum = (...xs) => xs.reduce((a,x) => a + Number(x||0), 0);
export const clamp = (x,lo,hi) => Math.max(lo, Math.min(hi, x));
// wire.js
const btn = document.getElementById("run-btn");
const out = document.getElementById("run-out");
btn?.addEventListener("click", () => out.textContent = "clicked!");
<link rel="stylesheet" href="css/TwoPanelComponentRefactored.css">
<script src="js/TwoPanelComponentRefactored.js" defer></script>
<div class="toolbar">
<button data-two="narrow" data-two-for="#panel" data-step="6rem">Narrow</button>
<button data-two="reset" data-two-for="#panel">Reset</button>
<button data-two="widen" data-two-for="#panel" data-step="6rem">Widen</button>
<button data-two="toggle-left" data-two-for="#panel">Toggle Left</button>
<button data-two="prev" data-two-for="#panel">Prev</button>
<button data-two="next" data-two-for="#panel">Next</button>
</div>
<x-two-panel id="panel" class="with-buttons" left="18rem" height="70vh"
data-desc-selector=".left-item"
data-right-selector=".right-item">
<div slot="left">
<div class="left-item"><h3>1) Topic</h3><p>Description text.</p></div>
<div class="left-item"><h3>2) Topic</h3><p>Description text.</p></div>
</div>
<div slot="right">
<section class="right-item"><pre><code>// code block 1</code></pre></section>
<section class="right-item"><pre><code>// code block 2</code></pre></section>
</div>
</x-two-panel>
The component pairs items by index: the nth left-slot child matching
data-desc-selector pairs with the nth right-slot child matching
data-right-selector.
| Attribute | Default | Description |
|---|---|---|
left | (none) | Fixed width of the left panel; any CSS length (18rem, 40%, 300px) |
gap | 0.5rem |
Column gap between the two panels |
height | max-content |
Overall component height; any CSS length or keyword |
step | 6rem |
Default width delta per narrow/widen action |
min-left | 8rem |
Minimum left-panel width |
max-left | auto | Maximum left-panel width; auto reserves min-right for the right panel |
min-right | 8rem |
Minimum right-panel width; used when max-left="auto" |
click-controls | (absent) | Enables panel-click resizing: single-click widens left or narrows left; double-click resets or toggles |
data-desc-selector | .marked-box, .left-item |
CSS selector for left-panel navigation items |
data-right-selector | .right-item |
CSS selector for right-panel navigation items |
data-two="<action>" and data-two-for="#id"
drives the panel. No JavaScript event wiring is required in the host page.
| Action | Extra data attribute | Effect |
|---|---|---|
narrow | data-step (optional) | Reduce left-panel width by step |
widen | data-step (optional) | Increase left-panel width by step |
toggle-left | - | Show or hide the left panel |
collapse-left | - | Hide the left panel |
expand-left | - | Show the left panel |
reset | - | Restore initial left and gap values |
set-left | data-left or data-value | Set left-panel width to an explicit value |
set-gap | data-gap | Set column gap to an explicit value |
prev / prev-mark | - | Navigate to previous pair |
next / next-mark | - | Navigate to next pair |
class="with-buttons" to <x-two-panel> when toolbar buttons are
present. This enables automatic Prev/Next button disable at the ends of the list.
| Method | Description |
|---|---|
setLeft(value) | Set left-panel width; shows panel if collapsed |
setGap(value) | Set column gap |
toggleLeft() | Toggle left panel visibility |
reset() | Restore initial left and gap attribute values |
step(sign[, stepOverride]) | Change left-panel width; sign is +1 or -1 |
currentIndex() | Return the current pair index (0-based) |
setIndex(i) | Navigate to pair at index i |
next({wrap}) | Advance one pair; wraps if wrap:true |
prev({wrap}) | Go back one pair; wraps if wrap:true |
scrollRightToTop() | Scroll the right panel to its top |
CustomEvent named 'two:nav' on each index change.
panel.addEventListener('two:nav', e => {
const { index, count } = e.detail;
console.log(`pair ${index + 1} of ${count}`);
});