js-dojo

10 embedded mods

What this module drills

Two ways to hang somebody else's panel inside your game, and the bill each one sends. A custom element is a class the browser builds wherever its dashed tag lands — <mod-panel> written from JSX is a mount, no import, no shared build. A frame is a second document with a window of its own. Same panel on screen; nothing underneath is the same.

What the fiction stands for

The host is React — the JSX in these koans is your own app, the one you ship. The mod is an app somebody else built in another framework: Angular, Vue and Svelte can each compile a component down to a custom element, and that element is the whole handoff. Your bundler never learns their framework exists; it sees a dashed tag.

The koans register the element by hand with customElements.define, because the seam behaves identically whichever framework produced it — Angular's createCustomElement emits the same kind of class. The frame half stands for the other option: shipping that same app as a standalone page and pointing an <iframe> at it.

Why it bites

The seams are quiet. A JSX prop lands as a property when the element already has one by that name, and the object arrives whole; with no such property it lands as an attribute, where setAttribute runs String() over it and a loadout becomes the literal text [object Object]. Name a callback prop onNotify and React keeps it: no property, no attribute, no listener, nothing thrown.

Past the props, one line decides the rest. A custom element owns no document, no window and no realm of its own — its backdrop is a host node, its pushState moves the host's address bar, its <style> letter-spaces the game's own pause button, and nothing it wired up comes undone until it unwires it. A frame owns all three, which settles those and opens the opposite set: teardown is free, and list instanceof Array is false for a perfectly good array the mod just built.

Two findings never reach a test — open a real browser and look instead. A position: fixed backdrop inside a frame dims the frame and nothing else, because fixed resolves against the frame's viewport, not the page's. And a frame keeps its own history, so the view the player is on has no link to paste, and Back steps the frame instead of the page.

Order

Straight through. 01 and 02 are the handoff — a tag name, then which door a prop comes in by. 03 is both ways back: the callback React hands over, and the one it swallows. 04 and 05 are those two browser findings in the only form a test can hold, which is why they are written the way they are — 04 proves by containment which document a backdrop landed in, 05 keeps every navigation on the host's side. 06 and 07 are what the custom element charges: a tree of its own for the stylesheet, a disconnectedCallback for the rest. 08 is what the frame charges — a second realm, and a door guard that stops asking whose Array stamped the list. 08 runs under Node, the rest under pnpm koan.