HTML export (Beta)

HTML export is a beta feature in Deckset for macOS. Use it to share a presentation with attendees or publish it on the web. Browsers can render slides differently from Deckset, so use Deckset itself when presenting live.

Accessibility is built in. When the presentation contains enough information, the export keeps semantic headings, lists, tables, links, media descriptions, and keyboard navigation. There is no separate accessibility option.

Choose Export Presentation, select HTML (Beta), then choose whether to include each stage of builds, presenter notes, or both.

By default, Deckset exports the final state of each slide. Select Include each stage of builds to export every build state as a separate slide.

Deckset writes the HTML document and an assets directory beside it. Each presentation gets its own directory within assets, so several exports can safely share a destination. Move or publish the HTML document and the entire assets directory together, without renaming generated files inside it.

Use one export as a document or player

Every HTML export includes two ways to view the same presentation. You do not need to export it again or choose a different export type:

  • Open presentation.html to show the regular, scrollable document.
  • Open presentation.html?view=player to show one slide at a time, fitted to the available width and height.

The player includes previous and next buttons, a slide counter, horizontal swipe navigation on touch devices, and a fullscreen button when the browser and website allow fullscreen. Keyboard navigation works in both views. Printing from either view prints the complete document rather than only the current slide. In fullscreen, Deckset removes the player gutters, permits the slide to scale above its exported size, and overlays the controls so the slide uses the largest available area. The slide keeps its aspect ratio, so screens with a different shape can still show letterboxing rather than cropping the presentation. In narrow embeds, the controls wrap rather than shrinking their buttons. When a navigation button reaches the first or last slide, keyboard focus moves to the available navigation button.

Changing slides in player view pauses native audio and video on the outgoing slide and keeps their playback position. Returning to a slide does not automatically resume playback. This does not control third-party iframe players; scrolling document view keeps its existing media behavior.

When the player is opened directly in the browser, pinch to zoom in for a closer look. While zoomed in, touch drags pan the slide instead of changing slides. Zoom back out to restore horizontal swipe navigation.

The URL decides which view opens. Placing the regular document in an iframe does not turn on the player, so links to the normal document keep their existing behavior.

Embed the player in a website

First upload the exported HTML document and its complete assets directory to a static web host. Preserve their relative locations so an HTML document at /presentations/quarterly-results.html can load its generated files from /presentations/assets/. Then add an iframe to the page where the presentation should appear:

<iframe
  src="/presentations/quarterly-results.html?view=player"
  title="Quarterly results presentation"
  loading="lazy"
  allow="fullscreen"
  allowfullscreen
  style="width: 100%; min-height: 200px; aspect-ratio: 16 / 9; border: 0;"
></iframe>

Change src to the hosted location of your exported document and give title a useful description of the presentation. The responsive aspect ratio lets the iframe grow and shrink with its container, while the 200px minimum height keeps useful room for both the slide and player controls on narrow pages. Change the ratio when the presentation uses a different slide size.

A plain iframe keeps touch gestures available for native browser panning and pinch zoom. Use the player’s buttons or keyboard to change slides. To also enable horizontal swipes at normal zoom, use the host script below: it reports the website’s zoom state so magnified touch drags pan rather than changing slides. Keep native touch gestures allowed on the host page and iframe; an ancestor’s touch-action: none can block pan/zoom.

Allow the presentation to be framed

Same-origin embedding normally needs no special response headers. It still fails if the presentation host sends X-Frame-Options: DENY or a Content Security Policy whose frame-ancestors directive excludes the parent page.

For a presentation hosted on a different origin, serve the exported HTML with a response header that names the website allowed to embed it:

Content-Security-Policy: frame-ancestors 'self' https://www.example.com

Replace https://www.example.com with the exact origin of the parent website. This header belongs on the exported presentation’s response. If the parent website has its own Content Security Policy, its frame-src directive must also allow the presentation’s origin.

X-Frame-Options: SAMEORIGIN permits same-origin embedding but blocks a parent on another origin. Remove that header for cross-origin embeds and use frame-ancestors instead. X-Frame-Options: ALLOW-FROM is obsolete and is not a replacement for frame-ancestors.

If a website builder applies a sandbox attribute to the iframe, it must include allow-scripts for player navigation. The origin-checked postMessage integration below also requires a non-opaque iframe origin, normally by including allow-same-origin. Grant both permissions only to HTML exports you trust. Fullscreen additionally requires the allow="fullscreen" and allowfullscreen attributes shown above.

You can link directly to a slide by adding its fragment after the player parameter, for example:

https://example.com/presentations/quarterly-results.html?view=player#slide-4

Internal links in the presentation update the current slide without leaving player view. Use browser Back and Forward to return to those linked slides. Returning to a player URL without a fragment shows the first slide again.

Control an embedded player from the host page

A website can connect its own controls to an immediately embedded player using postMessage. Messages use a versioned format so integrations can reject unrelated messages. The commands are next, previous, and goTo; slideNumber is one-based, and slideId can be used instead for goTo. The optional setViewport command reports a boolean zoomed: false enables touch swipes, while true restores native panning. Without a zoom report, the player retains native touch gestures. Buttons and keyboard controls work in either case.

The message version is independent of the data-deckset-export-version="1" marker on the exported document’s root element. Use the document marker when a same-origin integration reads the presentation DOM; use the message version when validating postMessage commands and events.

Give the iframe an ID and keep its URL in data-src until the host has installed its message listener:

<iframe
  id="deckset-player"
  data-src="/presentations/quarterly-results.html?view=player"
  title="Quarterly results presentation"
  allow="fullscreen"
  allowfullscreen
  style="width: 100%; min-height: 200px; aspect-ratio: 16 / 9; border: 0;"
></iframe>

Install the listener before assigning src, and send commands only after receiving the validated ready event. Commands sent while the iframe is loading are not queued by the player. Hosted presentations use their exact origin; the example makes a limited exception for trusted local-file previews:

const player = document.querySelector("#deckset-player");
const presentationURL = new URL(player.dataset.src, window.location.href);
const presentationOrigin = presentationURL.origin;
const isLocalFile = presentationURL.protocol === "file:";
const targetOrigin = isLocalFile ? "*" : presentationOrigin;
let playerReady = false;

function sendPlayerCommand(command, destination = {}) {
  if (!playerReady) return;
  player.contentWindow.postMessage({
    source: "deckset-host",
    version: 1,
    type: "deckset:command",
    command,
    ...destination
  }, targetOrigin);
}

function sendPlayerViewport() {
  const viewport = window.visualViewport;
  // A nested host's own viewport cannot report top-level zoom; retain native touch gestures.
  if (window.parent !== window || !viewport) return;
  const zoomed = viewport.scale > 1.01 || viewport.width <= 0
    || viewport.width < document.documentElement.clientWidth / 1.01;
  sendPlayerCommand("setViewport", { zoomed });
}

window.addEventListener("message", event => {
  const originMatches = event.origin === presentationOrigin
    || (isLocalFile && event.origin === "null");
  if (
    event.source !== player.contentWindow ||
    !originMatches ||
    event.data?.source !== "deckset" ||
    event.data?.version !== 1 ||
    event.data?.type !== "deckset:event"
  ) return;

  if (event.data.event === "ready") {
    playerReady = true;
    sendPlayerViewport();
    // Optional: choose a starting slide after initialization.
    sendPlayerCommand("goTo", { slideNumber: 4 });
  } else if (event.data.event === "slidechange") {
    // event.data.slide is { number: 4, id: "slide-4" }
    // event.data.slideCount is the number of exported slides.
  }
});

window.visualViewport?.addEventListener("resize", sendPlayerViewport, { passive: true });
window.addEventListener("resize", sendPlayerViewport, { passive: true });
window.addEventListener("pageshow", sendPlayerViewport);
player.src = presentationURL.href;

After ready, custom buttons can call sendPlayerCommand("next") or sendPlayerCommand("previous"). If the host replaces or reloads the presentation, reset playerReady before assigning its URL and wait for the new ready event.

Run this zoom reporting on the top-level website. An iframe’s own viewport does not report host pinch zoom, even on the same origin. If the integration itself is nested and cannot obtain top-level zoom information, omit setViewport and keep native touch gestures. Do not send a fixed zoomed: false without updating it when the viewport changes. The width comparison also detects zoom on websites that start downscaled; the 1.01 tolerance ignores tiny rounding differences.

To preview a trusted embed from disk, place the host HTML file beside the export and change data-src to a relative path such as quarterly-results.html?view=player. Browsers can give local files opaque origins, whose message origin is "null" even when the URL reports file://. The example uses "*" only when sending commands to a file: URL and accepts local replies with either the URL origin or "null". Keep the iframe-source check: "null" is not a unique identity. Use this exception only with a trusted local export, do not send confidential information through it, and do not extend it to sandboxed hosted presentations. Use HTTP/HTTPS hosting to test production behavior.

Set the document title and language

Add explicit metadata at the top of the Markdown file:

title: Quarterly results
language: en-GB

title sets the browser document title. language sets the HTML lang attribute and the base left-to-right or right-to-left direction. It also keeps the presentation’s language-aware text and font choices in the browser and selects the language for spoken formula descriptions when supported. Use a valid BCP 47 language tag, such as en-GB, zh-Hans, zh-Hant, ja, ko, or ar-SA.

If you leave these commands out, Deckset uses the presentation filename for the title and detects the main language from the exported text. If it cannot determine the language, the document uses lang="und" and the export report suggests adding a language command.

Decide whether to include presenter notes

Presenter notes are omitted from the HTML and its assets unless Include presenter notes is selected. Leave the option off before sharing an export when the notes contain private or internal information. Media used only in omitted notes is not copied into the package.

When you include presenter notes, Deckset adds them to their slides for assistive technologies and keeps them visible in the HTML source. They do not appear on the slide. If you also export every build stage, the notes appear only once for the original slide instead of repeating with every build.

Before sharing an export, add descriptions and transcripts to informative media as described in Accessible Media. Deckset carries supplied accessibility information into HTML, but it does not create missing captions or transcripts.

In a browser, use these keys when focus is not inside a link or media control:

  • Right Arrow, Down Arrow, or Page Down: next slide.
  • Left Arrow, Up Arrow, or Page Up: previous slide.
  • Home: first slide.
  • End: last slide.

Deckset also preserves internal slide links. Browser navigation respects the operating system’s Reduce Motion preference.

Review diagnostics

Deckset checks the document and its local assets before completing the export:

  • Errors block the export and leave the previous destination unchanged.
  • Warnings allow the export to finish and appear in the HTML export report.
  • When possible, unsupported slide content gets a visible placeholder.

The report can identify missing media descriptions, unavailable fonts, unsupported links, missing files, image filters that browsers cannot reproduce, and content rendered using a fallback. Review the affected slides before publishing.

Select an individual diagnostic and press Command-C to copy its privacy-safe details and recovery guidance.

Review the exported presentation with keyboard navigation and a screen reader before sharing. Check the reading order, links, media descriptions, and any included notes. A successful export cannot determine whether a chart description conveys its meaning.

Browser and hosting considerations

  • Local images, audio, video, generated visuals, and some supported fonts are copied into the assets directory.
  • Links to local files are not portable and block the export, including relative paths, absolute paths, and file: URLs. Use an HTTP or HTTPS URL, or link to an anchor within the exported presentation.
  • Presentations that use Google Fonts need an internet connection to retain those typefaces. A missing or unresolved font produces a warning, while a font that cannot legally be embedded blocks export.
  • YouTube embeds cannot play inline when the HTML is opened directly from disk. The local file shows a link to YouTube instead. Serve or upload the export over HTTP or HTTPS for inline playback.
  • Browsers cannot reproduce some Deckset image filters exactly. Deckset pre-renders supported still images when possible and reports any remaining differences.
  • Informative audio and video retain browser controls and accessible names. Decorative media does not expose controls and is hidden from assistive technologies.
  • Animated images remain animated and do not gain playback controls during export.
  • Copied media files may retain their original metadata. Review the files themselves before sharing sensitive material; omitting presenter notes does not remove metadata from other assets.
  • Google Fonts, YouTube, and other hosted content can still make network requests when the presentation opens. External links make requests when someone follows them.

Open the exported document locally for a quick review. For publishing—and for inline YouTube playback—upload the HTML document and its assets directory to the same static web host.