WebDev TwoPanelViewer Component

paired left/right panels with index-based navigation and resizable left column

1.0 Contents

A W3C custom element (<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.

2.0 Files

TwoPanelComponentRefactored/
  js/TwoPanelComponentRefactored.js   component definition
  css/TwoPanelComponentRefactored.css  host-page placement helpers
  TwoPanelComponentRefactored.html     demo / test page

3.0 Setup

<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;
}

4.0 Usage

1) Configuration

Define shared settings in a config module so every part of the app reads from one source.

2) Core Utilities

Pure functions for summing and clamping values - no side effects, easy to test.

3) Event Wiring

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!");

Place in host page header:

<link rel="stylesheet" href="css/TwoPanelComponentRefactored.css">
<script src="js/TwoPanelComponentRefactored.js" defer></script>

Place in body:

<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.

5.0 Attributes

AttributeDefaultDescription
left(none) Fixed width of the left panel; any CSS length (18rem, 40%, 300px)
gap0.5rem Column gap between the two panels
heightmax-content Overall component height; any CSS length or keyword
step6rem Default width delta per narrow/widen action
min-left8rem Minimum left-panel width
max-leftauto Maximum left-panel width; auto reserves min-right for the right panel
min-right8rem 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

6.0 External Controls

Any element with data-two="<action>" and data-two-for="#id" drives the panel. No JavaScript event wiring is required in the host page.
ActionExtra data attributeEffect
narrowdata-step (optional)Reduce left-panel width by step
widendata-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-leftdata-left or data-valueSet left-panel width to an explicit value
set-gapdata-gapSet column gap to an explicit value
prev / prev-mark-Navigate to previous pair
next / next-mark-Navigate to next pair
Add 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.

7.0 JavaScript API

MethodDescription
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

8.0 Events

The component fires a 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}`);
});