Slide HTML Authoring

Rules and conventions for writing slides as self-contained HTML.

Each slide in an AuraDeck deck is a self-contained HTML document rendered in an iframe. There is no shared JS runtime between slides, no template engine, no Markdown layer — just HTML, CSS, and JavaScript.

Requirements

A valid slide must:

  • Be a complete HTML document with <!DOCTYPE html>, <html>, <head>, and <body>.
  • Inline all CSS in <style> tags inside <head> — no external stylesheets.
  • Inline all JavaScript in <script> tags — no external scripts.
  • Reference images using the ./images/filename.ext convention so AuraDeck can inline them at present time.

That is the entire spec. Everything else is convention.

Conventions

These are not enforced by AuraDeck but make slides behave consistently:

Reset and box model

Start every slide with a tight reset:

*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 100%; height: 100%; overflow: hidden; }

overflow: hidden matters — without it, slides that overflow the viewport will scroll, which looks broken in a presentation.

Use viewport units

Sizing in vw, vh, vmin, and vmax lets slides scale naturally to any display:

h1 { font-size: 8vmin; }
.padding { padding: 4vw 6vw; }

Avoid pixel values for typography and layout — they will look tiny on a 4K projector and oversized on a 1024×768 conference-room display.

Pick a font you can rely on

The system stack works on every platform without bundling:

font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

If you want a custom font, embed it as a base64 @font-face declaration in the same <style> block. Network @font-face works in the editor and viewer but does not survive PDF/PPTX export because html2canvas runs before async font loads complete.

Minimal slide

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My Slide</title>
<style>
  *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
  html, body { width: 100%; height: 100%; overflow: hidden; }
  .slide {
    width: 100vw; height: 100vh; overflow: hidden;
    font-family: system-ui, sans-serif;
    display: flex; align-items: center; justify-content: center;
    background: #0a1628; color: #fff;
  }
  h1 { font-size: 8vmin; }
</style>
</head>
<body>
  <div class="slide">
    <h1>Hello World</h1>
  </div>
</body>
</html>

Animations

CSS animations and JavaScript run when the slide is displayed — use this for entrance effects:

<style>
  @keyframes fadein { from { opacity: 0; transform: translateY(2vh); } to { opacity: 1; } }
  .slide h1 { animation: fadein 0.6s ease-out 0.1s both; }
</style>

Each slide has its own DOM, so animations re-run every time you navigate to that slide — there is no need to reset state manually.

Interactive content

The full power of HTML/CSS/JS is available: Canvas, SVG, WebGL, embedded videos, chart libraries, anything. A few patterns work especially well:

  • Live-coded examples — embed CodeMirror or a <textarea> and a result <iframe> to demo something interactively.
  • Animated diagrams — drive an SVG with a JavaScript requestAnimationFrame loop.
  • Self-running demos — start a setInterval on slide load to advance through scripted states.

JS errors are silent in the viewer

A slide that throws a JavaScript error during load will simply render whatever it managed to render before the error. Test in tauri dev (right-click → Inspect) to see the actual stack trace.

What does not work

  • Cross-slide state — every slide is a fresh iframe; you cannot share variables across slides.
  • Top-level navigationwindow.location changes inside a slide are blocked.
  • Persistent storagelocalStorage writes inside a slide are scoped to that iframe and reset on reload.

If you need any of these, do it from the AuraDeck app shell, not from inside slide HTML.

CLI usage →