WebDev Bites: TwoPanelViewer Component Interface

index-based two-panel navigator with external toolbar controls

Basic usage

Three left-right pairs. Prev/Next navigate by index.

Panel width control

Narrow and Widen step the left panel in 4 rem increments.

Toggle and Reset

Toggle Left hides or restores the left panel. Reset returns to initial width.
Item 1 content - shown when index is 0.
Item 2 content - shown when index is 1.
Item 3 content - shown when index is 2.
x-two-panel is a W3C web component that renders a two-column layout where left-panel descriptions and right-panel content are paired by array index. Toolbar buttons (or JavaScript calls) navigate between pairs, adjust panel width, and toggle panel visibility. The component emits a two:nav event on every index change.

1.0 - Use in Application

Minimal usage with toolbar controls:
  <!-- toolbar -->
  <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="20rem" height="70vh">
    <div slot="left" class="left-slot">
      <div class="left-item"><h3>Section 1</h3><p>...</p></div>
      <div class="left-item"><h3>Section 2</h3><p>...</p></div>
    </div>
    <div slot="right" class="right-slot">
      <section class="right-item">...code 1...</section>
      <section class="right-item">...code 2...</section>
    </div>
  </x-two-panel>
The required script and stylesheet are:

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

2.0 - x-two-panel Interface

Interface surface What it does Example
left (attribute) Initial left panel width. Accepts any CSS length (px, rem, %). Sets data-left-fixed and switches the grid to fixed-plus-fill layout.
<x-two-panel left="20rem"></x-two-panel>
gap (attribute) Column gap between panels. Accepts any CSS length. Overrides the --two-gap CSS variable.
<x-two-panel gap="1rem"></x-two-panel>
height (attribute) Component height. Accepts any CSS length or expression (calc(), dvh). Default: max-content.
<x-two-panel height="70vh"></x-two-panel>
step (attribute) Default narrow/widen step. Overridden per-button by data-step. Default: 6 rem.
<x-two-panel step="4rem"></x-two-panel>
min-left (attribute) Minimum left panel width when stepping. Default: 8 rem. Also settable via --two-min-left CSS variable.
<x-two-panel min-left="12rem"></x-two-panel>
max-left (attribute) Maximum left panel width when stepping. Set to "auto" to derive from min-right. Also settable via --two-max-left.
<x-two-panel max-left="auto"></x-two-panel>
min-right (attribute) Minimum right panel width; used when max-left="auto" or --two-min-right is set. Default: 8 rem.
<x-two-panel min-right="10rem"></x-two-panel>
click-controls (attribute) Enables built-in click/dblclick panel controls: single click steps the panel; double-click on left resets, double-click on right toggles left.
<x-two-panel click-controls></x-two-panel>
data-desc-selector (attribute) CSS selector used to collect left-panel items into the navigation array. Default: .marked-box, .left-item.
<x-two-panel data-desc-selector=".desc"></x-two-panel>
data-right-selector (attribute) CSS selector used to collect right-panel items into the navigation array. Default: .right-item.
<x-two-panel data-right-selector=".code-card"></x-two-panel>
collapsed-left (attribute) Presence hides the left column and collapses the grid to a single right-only column. Toggled by toggleLeft() and the toggle-left control action.
<x-two-panel collapsed-left></x-two-panel>
--two-gap (CSS var) Column gap. Overrides the gap attribute when set on the host or a parent.
x-two-panel { --two-gap: 1.5rem; }
--two-height (CSS var) Component height. Set automatically from the height attribute.
x-two-panel { --two-height: calc(100dvh - 8rem); }
--two-left (CSS var) Left panel width used by the grid. Set automatically from the left attribute.
x-two-panel { --two-left: 24rem; }
--two-panel-bg, --two-panel-border, --two-panel-pad (CSS vars) Panel background color, border style, and scroller padding. Set from the host or a parent rule to restyle panels without ::part().
:root {
  --two-panel-border: 1px solid #d0d0d7;
  --two-panel-pad: .5rem;
}
setLeft(value) (method) Sets the left attribute and clears collapsed-left. Accepts any CSS length string.
panel.setLeft('24rem');
setGap(value) (method) Sets the gap attribute.
panel.setGap('1.5rem');
toggleLeft() (method) Adds or removes the collapsed-left attribute.
panel.toggleLeft();
reset() (method) Restores the left and gap attributes to the values they had when the component first connected, and clears collapsed-left.
panel.reset();
step(sign[, stepOverride]) (method) Adjusts left width by sign * step clamped between min-left and max-left. sign is +1 or -1; optional stepOverride is a CSS length string.
panel.step(+1, '4rem'); // widen by 4rem
panel.step(-1);          // narrow by default step
currentIndex() (method) Returns the zero-based index of the currently visible pair.
const i = panel.currentIndex();
setIndex(i) (method) Jumps directly to the pair at index i, clamped to valid range.
panel.setIndex(2);
next({wrap}) (method) Advances to the next pair. If wrap: true, wraps from the last to the first.
panel.next();
panel.next({ wrap: true });
prev({wrap}) (method) Moves to the previous pair. If wrap: true, wraps from the first to the last.
panel.prev();
panel.prev({ wrap: true });
scrollRightToTop() (method) Scrolls the right panel scroller to the top-left origin.
panel.scrollRightToTop();
Slot: left Content for the left panel. Items matching data-desc-selector are collected into the navigation array. All non-matching children remain visible at all times.
<div slot="left" class="left-slot">
  <div class="left-item">...</div>
  <div class="left-item">...</div>
</div>
Slot: right Content for the right panel. Items matching data-right-selector are collected as the paired counterparts of left items.
<div slot="right" class="right-slot">
  <section class="right-item">...</section>
  <section class="right-item">...</section>
</div>
External control: data-two="narrow" Calls step(-1, data-step). Use data-step to override the step size.
<button data-two="narrow" data-two-for="#p"
        data-step="6rem">Narrow</button>
External control: data-two="widen" Calls step(+1, data-step).
<button data-two="widen" data-two-for="#p"
        data-step="6rem">Widen</button>
External controls: next, prev Call next() / prev(). Buttons are auto-disabled at array boundaries by the bundled nav-sync helper.
<button data-two="prev" data-two-for="#p">Prev</button>
<button data-two="next" data-two-for="#p">Next</button>
External controls: toggle-left, reset, collapse-left, expand-left, set-left, set-gap Map directly to the corresponding public methods. set-left reads the value from data-left or data-value; set-gap reads from data-gap.
<button data-two="set-left"
        data-two-for="#p"
        data-left="30rem">Wide</button>
Event: two:nav Fired on every index change with detail: { index, count }. The bundled nav-sync helper listens to this event to enable/disable Prev/Next buttons.
panel.addEventListener('two:nav', e => {
  console.log(e.detail.index, e.detail.count);
});
CSS parts: wrap, left-panel, right-panel, left-scroller, right-scroller All five shadow DOM structural elements are exposed via part for external styling with ::part().
x-two-panel::part(left-panel) {
  border: 2px solid navy;
}