hp-tether
Arc-tether primitive — curved SVG bezier connecting two distant molecules. Drag any anchor;
the arc tracks the move, reroutes around obstacles, and re-settles on snap.
directed adds an arrowhead.
The classic shape: three hexes placed by axial coords, three
<hp-tether> elements authored next to the grid pointing at the endpoints
by selector. Drag any node — the arcs reroute around obstacles and re-settle on snap.
Same arc primitive, different authoring path. The grid carries
tetherable, so dropping any hex onto another toggles an
<hp-tether> arc between the pair — created with the draw-in animation,
removed if it already exists. No manual tether authoring; the grid handles
from / to selectors and dedup. Drop on an empty cell to move
a hex normally. The target hex highlights green while the source is hovering over it.
Install
bun add @hexpunk/core lit import "@hexpunk/core"; Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
from | from | string | undefined | — | CSS selector for the source molecule. Resolved against document. |
to | to | string | undefined | — | CSS selector for the target molecule. Resolved against document. |
state | state | "idle" | "active" | "active" | `active` (default) shows full-opacity stroke + travelling pulse; `idle` drops to the container shade and silences the pulse. |
directed | directed | boolean | false | Render an arrowhead at the target end for directed graphs. |
Events
| Event | Type | Description |
|---|---|---|
hp-tether-settle | CustomEvent | When the tether settles on a vertex pair. detail: { fromEl, toEl, fromVertexIdx, toVertexIdx } |
CSS Custom Properties
| Property | Default | Description |
|---|---|---|
--hp-tether-arc-width | — | Stroke width of the arc |
--hp-tether-arc-glow | — | Glow filter blur radius |
--hp-tether-arc-pulse-dot | — | Diameter of the pulse dot |
When to use
directed for flow / causality (data flow, prerequisite trees, signal
paths). Skip it for peer relationships where both ends are equivalent.
state="idle|active". Idle dims to
secondary-container; active uses the full secondary with the
pulse dot travelling along the arc.
<hp-bond>
for shared-edge relationships and <hp-tether> for at-distance ones.
<hp-tether> is flexible. It tracks endpoints by
selector across the entire document, not by DOM ancestry — sibling-of-grid (manual
authoring) and child-of-grid (tetherable mode auto-appends here) both work. The
element's own position: absolute; inset: 0 covers its positioned ancestor
so geometry resolves correctly either way.
Geometry
Each endpoint contributes one of six hex vertices. Control points are pulled along the outward radial direction from each vertex so the curve leaves and enters face-on (Rete editor style). When endpoints move, the tether re-scores all 36 pairs and morphs over ~60ms to the new pick — hysteresis (24px) prevents flapping near the decision boundary.
Performance
Tethers use a single MutationObserver per endpoint, watching the parent grid
for q/r and data-hp-dragging changes. Capture-mode
transition event listeners catch sibling movements without re-binding. Several hundred
tethers on a single surface is fine; thousands are not — for that scale, switch to a
canvas / WebGL renderer and apply Hexpunk tokens / classes on the canvas chrome.
No per-component entries in CHANGELOG.md yet.