Mechanically re-derived: bin/debrand runs over a fresh import of posthog/posthog@fa1a9a60d4 (2026-08-06). The pipeline (.hanzo/workflows), the Dockerfiles and bin/docker* entrypoints, compose files, LLM.md and the squashed initial migration are restored from the shipped tree, same as the previous import. The prune's import-closure guard kept 21 products the new core now imports; dropping them is a refactor now, recorded rather than forced.
3.3 KiB
name, description
| name | description |
|---|---|
| building-html-canvases | Author a Insights canvas with semantic HTML, CSS, and direct browser APIs — documents, articles, generative graphics, 2D canvas and WebGL experiences, and focused experiments where React components add no useful structure. Use after building-canvases has routed a canvas request to a plain-HTML/browser-API implementation. Covers the thin component wrapper the current runtime requires, styling and theming without Quill, drawing surfaces, and animation/cleanup patterns. |
Building HTML canvases
Some canvases are documents or graphics programs, not applications: a written report, a diagram, a generative-art piece, a WebGL scene. For these, semantic HTML, CSS, and direct browser APIs are the right tools — don't force Quill components or React state onto a static page.
The wrapper the current runtime requires
Every canvas keeps src/canvas.tsx as its mounted React entry component (default export, no
props). Keep the React layer as a thin shell and write the
experience in HTML/CSS/browser APIs inside it:
- A document is JSX that is effectively semantic HTML —
<article>, headings, lists, tables, figures — with a<style>block for typography and layout. Write real, specific copy. - A drawing/WebGL program renders a
<canvas>element and drives it imperatively from auseEffectvia a ref: get the 2D/WebGL context, run the setup and render loop there. - Clean up in the effect's return: cancel
requestAnimationFrameloops, remove listeners, and release contexts, so theme switches and remounts don't leak or double-run. - Mixing tiers is fine: a mostly static page can mount one interactive island, and a data board
can hand a chart's
<canvas>to imperative code while React owns the chrome.
The import allowlist still applies (react, react-dom, @hanzo/quill, recharts, lucide-react,
dayjs) — browser globals (document, CanvasRenderingContext2D, WebGLRenderingContext,
requestAnimationFrame, IntersectionObserver, Web Audio, etc.) need no import. Three.js and
other npm graphics libraries are not yet loadable; write against raw WebGL or 2D canvas until the
build pipeline's dependency admission ships.
Styling and theme without Quill
- Use Tailwind utilities and/or a
<style>block (keyframes and complex selectors are fine). - The host toggles a
.darkclass on the document root when the user's Insights theme changes. Define your colors as CSS variables under:root { … }with overrides underhtml.dark { … }, or use theme token utilities (bg-background,text-foreground,border-border) — never a light-only hardcoded color. - For canvas/WebGL drawing colors, read the resolved token at runtime
(
getComputedStyle(document.documentElement).getPropertyValue("--primary")) or your own CSS variables, and re-read on theme change if the scene is long-lived.
Rules that still apply
- No
fetch()/XMLHttpRequest, no<script>tags, no dynamicimport(), no remote assets — the sandbox blocks them. Insights data comes only through thephbridge (see thequerying-canvas-dataskill), includingph.capturefor interaction analytics. - External links go through
ph.openExternal(url)(hanzo.ai origins only), from a user interaction. - Validate and publish through the canvas tools as described in
validating-and-publishing-canvases.