Renderer¶
packages/renderer draws every on-page element: tooltips, hotspots, banners, modals, slide-outs, toasts, the help bubble and the resource centre. It uses Preact and has no extension API dependency.
Host element¶
createHost() creates one <guidepost-root> element appended to <html>.
| Property | Behaviour |
|---|---|
| Shadow root | Closed by default so the page cannot reach into the UI. The dev flavour opens it for end-to-end tests |
| Styles | One stylesheet adopted through adoptedStyleSheets, with a <style> fallback |
| Theme | Tokens are applied as CSS custom properties on the host; data-scheme carries light or dark |
| Direction | dir is copied from <html dir> and is ltr or rtl |
| Re-attach | A MutationObserver on <html> re-appends the host if the page removes it |
Top layer¶
Floating surfaces are popovers: showLayer calls showPopover() when the browser supports it, so they sit above page z-index and stacking contexts. registerLayer records each one in order.
Host modal dialogs complicate this. dialog.showModal() makes everything outside the dialog inert, so even a top-layer popover would not receive pointer or key events. The host therefore:
- watches for an open
dialogthat matches:modal; - moves the
<guidepost-root>element into that dialog while it is open; - returns it to
<html>when the dialog closes; - calls
reshowLayers(), which hides and re-shows every registered layer in order, lifting them above the dialog and keeping their relative stacking.
flowchart LR
a["Host opens<br/>modal dialog"] --> b["Observer sees<br/>dialog open"]
b --> c["Move host into<br/>the dialog"]
c --> d["reshowLayers()"]
e["Dialog closes"] --> f["Move host back<br/>to html"]
f --> d
| Node | Meaning |
|---|---|
| Observer | A MutationObserver on <html> for open attribute changes and added or removed dialogs |
reshowLayers() |
Hide then show each registered layer, in registration order |
Rich text¶
Markdown is converted and sanitised with DOMPurify before render.
Gotchas¶
- Without the popover API, surfaces fall back to ordinary positioned elements with no top-layer guarantee.
- The renderer unit tests run in jsdom with mocked rectangles. Layout is checked only by the Playwright suite.
- A surface that must appear above a host dialog has to be registered with
registerLayer, or it will not be lifted.