Blog

HTML slides | 9 min read | 2026-04-25 | Updated 2026-04-26 | By Variant Team

How to Build a Presentation as a Single HTML File

A practical guide to building a single file HTML presentation: what goes in the file, when HTML beats PPTX or PDF, and how to keep it portable.

Author: Variant Team. Variant is built by a small team working on HTML-native presentation tools, MCP workflows, and agent-editable decks.

A single file HTML presentation is one .html document that contains every slide, every style, every script, and every asset needed to render the deck. You build it by writing one HTML file with each slide as a section, inlining your CSS in a <style> tag, and either embedding images as base64 or referencing them by URL.

That's the short answer. The longer answer is more interesting, because once you actually try this you start to notice how much friction goes away compared to PPTX or Google Slides. Fonts don't vanish on someone else's machine. The deck doesn't act differently on three different screens. No exporter quietly rearranges your layout. The deck is a webpage, and webpages are old, boring technology in the best possible way.

Below is a working approach: the parts that matter, where HTML wins (and where it doesn't), and how to keep your file portable enough to email, host, or check into Git.

#Quick answer: what's in a single file HTML deck?

A self contained HTML presentation file usually has:

  1. A single <!DOCTYPE html> document.
  2. One <style> block (or several) with all the CSS for every slide.
  3. A series of <section> elements, one per slide.
  4. Inline assets: fonts via @font-face with base64 src, images via data: URLs or external URLs you trust to keep working.
  5. A small <script> for navigation (arrow keys, swipe, fullscreen).

That's it. You open the file in any browser and it works. No build step. No runtime. No server.

#Why bother with HTML instead of PPTX?

PPTX is fine. It really is. But if you've ever sent a .pptx to someone and watched them open it in Keynote, then in LibreOffice, then in PowerPoint Online, and watched all three render slightly different layouts, you know the pain. PowerPoint files are a zipped XML format with strong opinions about fonts, embedded media, and renderer quirks. Different apps interpret those opinions differently.

HTML doesn't have that problem nearly as badly. A browser is a browser. Chrome, Safari, Firefox, Edge, the one on your phone. They all render HTML and CSS to the same well specified standard. The deck looks the same in all of them, give or take a font fallback.

Here's a quick comparison.

ConcernPPTXPDFSingle file HTML
Opens without special softwareNeeds PowerPoint, Keynote, or SlidesYes (PDF reader)Yes (any browser)
Editable laterYes, but tied to the appPainfulYes, in any text editor
Animation and interactivityLimited and quirkyNoneFull CSS and JS
Hostable on the webNoSort ofYes, drop it on any static host
Inspectable for AI agentsXML inside a zipBinary streamJust text
Version control friendlyBad (binary zip)Bad (binary)Good (plain text)
Portable across machinesOften breaksYesYes

PDF is great for "I never want anyone to change this." HTML is great for "I want this to work everywhere, and I might need to crack it open and tweak a headline two minutes before the meeting."

#A minimal single file HTML presentation

Here's the smallest version that's actually useful. Save it as deck.html and double click it.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <title>Q2 Review</title>
  <style>
    :root { color-scheme: dark; }
    html, body { margin: 0; height: 100%; background: #0b0b0c; color: #f5f5f5;
      font-family: ui-sans-serif, system-ui, sans-serif; }
    .deck { height: 100vh; overflow: hidden; }
    .slide {
      position: absolute; inset: 0;
      display: grid; place-items: center;
      padding: 6vmin; box-sizing: border-box;
      opacity: 0; transition: opacity .25s ease;
    }
    .slide.active { opacity: 1; }
    h1 { font-size: clamp(40px, 8vw, 120px); margin: 0; letter-spacing: -0.02em; }
    h2 { font-size: clamp(28px, 4vw, 56px); margin: 0 0 .5em; }
    p  { font-size: clamp(18px, 2.2vw, 28px); max-width: 60ch; line-height: 1.5; }
    .num { position: fixed; bottom: 2vmin; right: 3vmin; opacity: .4; font-variant-numeric: tabular-nums; }
  </style>
</head>
<body>
  <div class="deck" id="deck">
    <section class="slide active"><h1>Q2 Review</h1></section>
    <section class="slide"><div><h2>What worked</h2><p>Shipped the new editor. Cut p95 latency in half. Closed three enterprise pilots.</p></div></section>
    <section class="slide"><div><h2>What didn't</h2><p>Onboarding still drops 40% of new users on step 3. Pricing page is confusing.</p></div></section>
    <section class="slide"><h1>Thanks.</h1></section>
  </div>
  <div class="num" id="num"></div>

  <script>
    const slides = [...document.querySelectorAll('.slide')];
    let i = 0;
    const num = document.getElementById('num');
    const go = (n) => {
      i = Math.max(0, Math.min(slides.length - 1, n));
      slides.forEach((s, idx) => s.classList.toggle('active', idx === i));
      num.textContent = `${i + 1} / ${slides.length}`;
    };
    addEventListener('keydown', (e) => {
      if (e.key === 'ArrowRight' || e.key === ' ') go(i + 1);
      if (e.key === 'ArrowLeft') go(i - 1);
      if (e.key === 'f') document.documentElement.requestFullscreen?.();
    });
    go(0);
  </script>
</body>
</html>

Forty lines of code. Real keyboard navigation. Fullscreen on f. It looks decent on a laptop, a projector, or a phone. You can email it to someone and they can present before the room has finished debating adapters.

#What goes inside the file

For a deck that holds up in real meetings, you want a few more things baked in.

#Fonts

Web fonts loaded from a CDN will fail the moment someone presents on hotel wifi. Inline them. Use @font-face with a base64 encoded woff2 file in the src. Or pick a system font stack. system-ui is fine for a lot of decks and weighs zero bytes.

If you do embed a custom font, subset it first. A full font file might be 200 KB. The subset of glyphs you actually use is often under 30 KB. Tools like glyphhanger or pyftsubset will trim a font to the characters you need.

#Images

You have two options.

  • Embed as data: URLs. The file gets bigger, but the deck is truly portable. A 4 MB deck with a few images is not a problem.
  • Reference external URLs. Smaller file, but it breaks without internet. Fine for hosted decks, risky for offline.

For a single self contained HTML presentation, lean toward embedding. Portability is the whole trick.

If you have many images, pre-compress them. WebP and AVIF will cut file size by half compared to PNG without visible quality loss for most slide art.

#Layout

CSS Grid and Flexbox handle most slide layouts cleanly. Two columns, a hero with a caption, a table of metrics, a quote with attribution: all easy. Use clamp() on font sizes so the deck scales between a 13" laptop and a 4K projector without you doing anything.

A useful trick: pick a base font size that scales with the viewport (font-size: clamp(14px, 1.2vw, 22px) on :root), then use rem everywhere else. Your spacing and type stay in proportion no matter what screen the deck lands on.

#Slide transitions

Keep them simple. Opacity fades, slight transforms, maybe a small stagger on text. Heavy animations on every slide get tiring fast. Save the big moves for one or two slides that deserve them.

#Speaker notes

You can hide an <aside> per slide with display: none and pop it open with a key binding. Or keep notes in a separate file. Notes in the deck are convenient but not strictly necessary.

A common pattern: a ?notes query string flag swaps the layout into a presenter view with the current slide on the left and notes plus next slide on the right. About thirty extra lines of JavaScript.

Arrow keys, space bar to advance, b to blank, f for fullscreen. If you want a progress bar or a slide grid overview, add it. The script in the example above is the minimum.

If you're presenting from a phone or tablet, add touch handlers. Swipe left advances, swipe right goes back. Five lines of touchstart and touchend math.

#Hosting and sharing

Once the file is self contained, distribution gets pleasantly dull.

  • Email the file. Most providers allow attachments up to 10–25 MB. Most decks fit.
  • Static hosting: GitHub Pages, Netlify, Cloudflare Pages, S3, your own nginx, a USB stick. They all work because there's nothing to build.
  • Git: a .html file is plain text, so diffs are readable and merges are sane. Compare that to a binary .pptx, which Git treats as one opaque blob.
  • Direct print to PDF: a clean HTML deck prints to PDF via the browser. Add a small @media print block that fixes each slide to one page (page-break-after: always) and you have an instant PDF export with no extra tooling.

#Why this format plays nice with AI

Generic AI slide generators usually emit slides into their own proprietary structure. You get something that looks fine until you want to change one word, and then you're either fighting their UI or asking the model to regenerate the whole slide and hoping the rest of the layout survives.

HTML flips that around. Every slide is text. Every style is text. An AI coding agent can read the slide, find the line you want to change, and edit it. No regenerating the whole thing. You can also do the edit yourself, by hand, in the same file. The model's output and your hand edits live in the same medium.

That makes HTML decks a reasonable target format if you're using a coding agent to draft slides. The agent writes plain HTML. You correct the parts that are wrong. The two of you converge on a deck without ever leaving the file.

#When HTML isn't the right answer

A single file HTML deck is great. It's not always the best choice.

  • The audience will edit it in PowerPoint. Export PPTX. Converting HTML to PPTX after the fact loses fidelity.
  • You need locked down redaction. PDF. HTML can be edited by anyone with a text editor.
  • You need exact print output. PDF.
  • You're in a regulated environment that only allows .pptx attachments. PPTX, sorry.
  • Your audience lives in Google Slides for collaborative editing. Google Slides. HTML is for handoff, not real time co-editing.

For everything else, HTML is hard to beat: portable, inspectable, easy to host, easy to tweak, friendly to AI coding agents, and friendly to version control.

#A small example: editing a single slide

Say slide 6 has the wrong revenue number. With a PPTX you'd open PowerPoint, click around, fix it, save, re-export, re-attach. With a single file HTML deck:

# open the file
code deck.html

# search for "$4.2M", change to "$4.7M", save

Done. Two minutes before the meeting, no exporter, no fidelity loss. If you're using a coding agent, you can also describe the change in plain English ("on slide 6, change the revenue number from $4.2M to $4.7M") and let the agent do the find and replace.

#FAQ

#Can a single HTML file really hold a whole presentation?

Yes. Slide decks are mostly text and a few images. A 30 slide deck with embedded images and a custom font usually lands between 1 and 10 MB as a single HTML file. Comfortable for email. Fine for hosting.

#How do I present an HTML deck full screen?

In any browser, press F11 (Windows/Linux) or Ctrl+Cmd+F (macOS) for browser fullscreen. Most decks add their own f keybinding using the Fullscreen API so the page itself goes edge to edge with no browser chrome.

#Can I convert a PPTX to a single file HTML presentation?

Sort of. There are converters but they tend to produce a tangled mess of absolutely positioned divs that look like the original and are painful to edit. Better to start in HTML, or rebuild the deck once and keep the HTML version as the source of truth from then on.

#Will fonts and images break when I send the file to someone else?

Only if you reference them as external URLs. If you embed fonts via @font-face with base64 src, and embed images as data: URLs, the file is fully self contained and renders the same on any machine.

#Can I use AI to build the slides for me?

Yes. AI coding agents work well with HTML decks because every slide is plain text. The agent can write the initial draft, you can hand edit, and the agent can come back later for revisions. The two of you stay in the same file the whole time.

#How is this different from frameworks like reveal.js or Slidev?

Those frameworks give you more out of the box: themes, transitions, plugins, presenter views, Markdown authoring. They also produce HTML decks. The difference is mostly about authoring style. A hand rolled single file deck is one document you control top to bottom. A framework is a project you maintain in a repo with a build step (or at least a runtime script). Both produce HTML; the workflow is different.

#How do I print an HTML deck to PDF?

Use the browser's "Print to PDF" with a small @media print rule that sizes each slide to one page and adds page-break-after: always. No extra tooling, no fidelity loss.

#Is it accessible?

It can be. Use real headings (<h1>, <h2>), real lists, alt text on images, and sufficient contrast. Make sure keyboard navigation reaches every slide. HTML decks are easier to make accessible than most binary formats because all the tooling that already works for the web works here too.