DJED Deck Framework — Embedding
Every deck rendered by services/decks is a fully self-contained
multi-page route under /d/<slug>/index.html. That makes embedding
trivial: drop an <iframe> in any host, or — for hosts that already
ship @alexbayerl/djed-deck-stage — mount the engine directly.
The hub at /index.html ships a built-in Embed dialog per card
that copies these snippets with the deck’s host pre-filled.
iframe (recommended)
Section titled “iframe (recommended)”The simplest, host-agnostic embed. The 16/9 wrapper preserves aspect ratio at any container width:
<div style="aspect-ratio:16/9;width:100%"> <iframe src="https://decks.example.com/d/atlas/index.html" title="Atlas Pitch" style="width:100%;height:100%;border:0" allow="fullscreen" loading="lazy" ></iframe></div>Web component (advanced)
Section titled “Web component (advanced)”If your host already imports @alexbayerl/djed-deck-stage (or you want
to manage slide content yourself), mount the engine directly:
<script type="module" src="https://cdn.jsdelivr.net/npm/@alexbayerl/djed-deck-stage/dist/deck-stage.js"></script>
<deck-stage width="1920" height="1080" persist-key="atlas-embed-acme" mounted-slides="adjacent"> <!-- supply your own deck-slot children, or render with @alexbayerl/djed-deck-kit --></deck-stage>persist-key namespacing
Section titled “persist-key namespacing”The engine writes the active slide index to
localStorage["deck-stage:v2:<persist-key>:slide"]. Always namespace
your persist-key per host when embedding the same deck multiple
times on a page:
<deck-stage persist-key="atlas-embed-acme-hero" ...><deck-stage persist-key="atlas-embed-acme-mid" ...>If two embeds share a persist-key, they will compete for the same storage slot.
Listening for slide changes
Section titled “Listening for slide changes”The engine emits a slidechange CustomEvent and posts a structured
message via window.postMessage for cross-frame consumption:
window.addEventListener("message", (e) => { if (e.data?.slideIndexChanged != null) { console.log("active slide:", e.data.slideIndexChanged); }});Inside the same page, the typed listener is more ergonomic:
import type { DeckStageElement } from "@alexbayerl/djed-deck-stage";const stage = document.querySelector<DeckStageElement>("deck-stage")!;stage.addEventListener("slidechange", (e) => { console.log("active slide:", e.detail.index);});The event payload carries eventVersion: 1; future engine major
revisions will bump this number to allow consumers to gate their code.
Catalog API
Section titled “Catalog API”Hosts that want to render multiple decks dynamically can fetch the
build-time catalog at /decks.json:
{ "catalogVersion": 1, "engineCompat": 1, "generatedAt": "2026-04-22T12:34:56.000Z", "entries": [ { "slug": "atlas", "title": "Atlas Pitch", "theme": "atlas", "slideCount": 11, "fingerprint": "2c02a238", "routes": { "screen": "/d/atlas/index.html", "print": "/d/atlas/print.html", "yaml": "/d/atlas/yaml.html" }, "pdfFilename": "atlas-pitch.pdf", "hub": true } ]}Every entry whose YAML sets meta.hub: true is included; fixture or
private decks (e.g. decks/mini) are filtered out by omission. The
shape is locked behind catalogVersion: 1.
Theming the embed
Section titled “Theming the embed”Set the host page’s theme on <html> or on a wrapper above the
<deck-stage> element:
<html data-deck-theme="atlas">Available themes: djed, atlas, osiris, adventurelink,
editorial. Each theme is a self-contained CSS variable contract
shipped from @alexbayerl/djed-deck-kit/themes/<slug>.css.
What about PDFs?
Section titled “What about PDFs?”PDF generation is a build-time concern, not a runtime embed concern. Use the CLI:
djed-deck pdf ./decks/atlas --out atlas-pitch.pdfIf you need on-demand PDF in production, point Playwright at
/d/<slug>/print.html with the locked print defaults
(printBackground: true, preferCSSPageSize: true, outline: true).