hp-unfold-list
Ring-expanding unfold primitive. The source hex stays on the surface; detail children
explode outward into the 6-ring of axial neighbours, aligned with the surrounding grid's
lattice when nested inside <hp-grid> / <hp-cluster>.
Default is ordered — children land in clockwise spiral order with a
matching stagger, mirroring an <ol>'s sense of sequence. The
unordered boolean shuffles position + stagger together (stable per-mount,
so the random layout doesn't reshuffle when reopened), reading like a <ul>.
Two more primitives are coming as siblings, not modes:
<hp-unfold-overlay> (lightbox-style hex popover) and
<hp-unfold-page> (View Transitions to a new route).
The source is a grid cell at axial (0, 0). On open, children fill the six
axial neighbours clockwise from the top-left, with a matching stagger. All children share
the source's hex size so the spiral lattice stays clean — mixed sizes break the step
math and overlap.
Three children, randomly assigned to three of the six ring slots. The shuffle is stable — close + reopen lands them at the same positions.
Install
bun add @hexpunk/core lit import "@hexpunk/core"; Properties
| Property | Attribute | Type | Default | Description |
|---|---|---|---|---|
open | open | boolean | false | Reflected open / closed state. Drives the bloom CSS + the `aria-pressed` mirror on the source. |
unordered | unordered | boolean | false | When set, children land at a randomly-shuffled subset of the 6 ring positions and stagger in random order (both keyed off the same permutation). Without it, children land in clockwise spiral order and stagger in that same order. Mirrors the ordered / unordered semantics of `<ol>` vs `<ul>`. |
Events
| Event | Type | Description |
|---|---|---|
| CustomEvent | |
hp-unfold-open | Event | When the list opens |
hp-unfold-close | Event | When the list closes |
Slots
| Slot | Description |
|---|---|
source | The trigger hex (always visible) |
(default) | Children fanned into the ring when open |
When to use
unordered for tag clouds, related-item ribbons, faceted filters, or
any set where the children are peers and order shouldn't be implied. The random
stagger reads as "all at once" rather than "in this order".
<hp-grid> so the 6-ring lands on the grid's axial
lattice — the unfold inherits the grid's --hp-col-step /
--hp-row-step so children sit exactly where neighbouring grid cells would.
<hp-unfold-page>. Ring
expansion is for in-place detail, not page transitions.
unfold-depth-max: 3.
Grid alignment
Inside <hp-grid>, the unfold-list's children land at the axial
neighbours of the source's (q, r). The grid's occupancy map doesn't
currently know about them, so consumers should keep neighbour cells empty — or accept
visual overlap until a later phase wires the hp-unfold-open event to a
grid-level dim / occupancy hook.
Drag handle (inside the source)
When mounted in light DOM, <hp-unfold-list> auto-inserts an
<hp-module-handle slot="handle"> centred inside the source hex and
sets drag-handle="[slot='handle']" on itself. Inside an
<hp-grid>, only the handle initiates the grid's drag — the rest of
the source's hex polygon stays clickable for toggling open / closed. Bbox corners
outside the source's polygon fall through to the grid for pan-passthrough as usual.
To opt out, the consumer can remove the auto-inserted handle and clear the
drag-handle attribute after mount, or render the unfold-list outside any
grid (the handle is harmless decoration in that context).
Keyboard
The source slot auto-gets tabindex="0" on slotchange (consumers can override
explicitly). Enter / Space on the focused source toggles open.
Esc closes if open and returns focus to the source.
Reduced motion
Children appear / disappear instantly with no stagger when
prefers-reduced-motion: reduce is active. The blue → green hue swap on the
source (driven by aria-pressed) remains, since hue change is a state signal
rather than decorative motion.
No per-component entries in CHANGELOG.md yet.