The stack — bottom-right, look there
Small round faces fixed to a viewport corner. The stack rests faded and comes up as your pointer nears it (from 300px out, fully up at 50px; phones see it always); hover (or keyboard-focus) one and it grows, its name sliding in beside it. The first registered icon sits nearest the corner. Three doors feed ONE list:
- Site config —
'site' => ['iconStacker' => ['icons' => […]]]: every page of the site (this showcase registers Feedback). - Route —
'iconStacker' => ['icons' => […]]inroute.php(or$h->set('page', ['iconStacker' => …])intask.php) REPLACES the list on that page;falsehides the stack there. - JS —
g.iconStacker.add({ name, icon, action })at any time (this page adds Assistant with an image face).
An icon needs a name + a face (icon = a Remix class, or image = a png/webp/svg url). The click fires icon-stacker:click on document (cancelable), then runs action(icon, event) or follows url — a url-only icon is a real link.
Clicks — the page decides what happens
Click the icons in the corner: Send feedback has no action — the page listens for the event and opens a dialog; Assistant runs its function; Read the docs is a plain link.
Layout & sizes — framework defaults, overridable
Every knob is a setting: in the site block, in the page block or at runtime with g.iconStacker.configure(). Try them live:
The toast lands ABOVE the stack — the two share the corner and the lib publishes its clearance.
The rules
- Register the site-wide icons in the site config, never from a page script — a page's
add()lives on that page only. - Names are interface strings: write them in English, they translate through
g.translate. - Keep it to a few icons — the corner is shared with toasts and an open panel's footer.
- The stack is chrome: no page markup, no per-page opt-in, nothing to clean up.