Integrate

Plain HTML

The comic-strip custom element without Astro, in any page or framework.

@toonstrip/element is a custom element with no framework dependency. The Astro component is a thin wrapper around it; everything it does, the element does on its own.

Install

npm install @toonstrip/element @toonstrip/pack-comic-chat

Serve the pack’s files somewhere the browser can fetch them. The pack package contains pack.json, characters/, and backdrops/; copy that directory to your static assets, for example public/packs/comic-chat/.

Use

<comic-strip id="strip"></comic-strip>

<script type="module">
  import "@toonstrip/element";

  const strip = document.getElementById("strip");
  strip.packs = ["/packs/comic-chat/"];
  strip.document = {
    version: 1,
    panels: [
      {
        backdrop: "pastoral",
        camera: "establishing",
        bodies: [{ character: "dan", emotion: { angle: 0, intensity: 0.6 } }],
        speakers: [{ speaker: "caption", balloon: "caption", text: "A field outside town." }],
      },
    ],
  };
</script>

API

Member Type Meaning
packs string[] property Base URLs of asset packs, each serving pack.json and its files. Searched in order; duplicate ids across packs throw.
document ComicStripDocument property The strip. Validated on set. Setting it again re-renders.
columns number getter Column count from the last layout pass.

Setting document before packs is fine; the element rebuilds on either change and the last set wins.

Note

Validation errors and unknown ids are reported by replacing the strip with a short red notice and logging to the console. Nothing throws out of a property setter.

From a framework

React, Vue, Svelte, and the rest can all render a custom element and set properties on it. The one thing to remember is that packs and document are properties, not attributes: set them from a ref after mount, or use your framework’s property-binding syntax (.document={doc} in Lit, prop:document={doc} in Solid, :document.prop in Vue). For React and Vue specifically, @toonstrip/react and @toonstrip/vue package exactly this pattern as a <ComicStrip> component. See React reference and Vue reference.

Bundling

The element imports @toonstrip/core and @toonstrip/schema; any ESM bundler handles it. The schema is embedded at build time as plain data, and validation runs as ordinary JS with no eval, so a strict Content Security Policy does not need unsafe-eval.

No build step at all

The install/npm/bundler steps above assume something resolves the bare import "@toonstrip/element" specifier. With no bundler and no import map, use esm.sh instead, which resolves a package’s own dependencies for you:

<script type="module">
  import "https://esm.sh/@toonstrip/element@0.1.11";
</script>

Point packs at a CDN too: unpkg serves a pack’s static files (pack.json, sprite sheets, backdrops) as-is, with CORS enabled:

strip.packs = ["https://unpkg.com/@toonstrip/pack-comic-chat@0.1.1/"];

The full worked example is examples/vanilla-html/index.html in the repo: one file, no package.json, opens from file:// or any static file server. Pin exact versions in both URLs, same as any other dependency. See the repo’s docs/vanilla-html.md for the esm.sh-vs-unpkg reasoning in full.