Skip to content

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:

  1. watches for an open dialog that matches :modal;
  2. moves the <guidepost-root> element into that dialog while it is open;
  3. returns it to <html> when the dialog closes;
  4. 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.