This document covers authoring behavior modules for @pantoken/interactions, the shared behavior extraction layer that powers both CSS-based components (via IIFE bundles) and web components.
Behaviors (src/behaviors/*.ts) — Pure JS functions that wire up DOM listeners and return cleanup objects. No Node.js imports, no framework dependencies. Shared by CSS IIFEs and web components.
IIFE entry points (src/components/*.ts) — Auto-generated or hand-authored side-effect modules that import a behavior, then register document-level listeners. Called via CDN <script> tags or ESM imports.
Capability manifest (dist/component-capabilities.json) — Auto-generated mapping of component name → capability type (css-only, js-only, both) + CDN URLs. Used by consumers to determine which bundles to load.
Node-free: No import("node:*") or Node-specific APIs. These bundles run in the browser.
Type-only imports OK: You can import types from any package for type checking; only value imports must be browser-safe.
Return cleanup object: If your behavior sets up listeners or state that needs teardown, return an object with a cleanup() method. Web components call this during re-renders; IIFE bundles usually call it once on app shutdown.
JSDoc escaping: Wrap HTML tags in backticks (`<dialog>`) in JSDoc block comments. VitePress's Vue compiler fails on bare tags in generated TypeDoc API pages.
initCloseButton uses data-close-target when present, or the nearest dialog, popover, [data-dismissible], [open], .instui-alert, or .instui-banner ancestor. Native popovertarget and commandfor controls keep their browser-provided behavior. Alert and banner close buttons use initRemove to fire a cancelable, bubbling dismiss event, fade out, and remove the message. Prevent the event to keep the message mounted, or add -transition-none to remove it without a fade. Other non-native targets retain their open-attribute and close-event behavior.