Skip to content

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.

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>

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>

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.

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.

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.

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.

PDF generation is a build-time concern, not a runtime embed concern. Use the CLI:

Terminal window
djed-deck pdf ./decks/atlas --out atlas-pitch.pdf

If 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).