Reference

Optional modules

The parts a deck imports on demand · activated by an attribute on deck-root or by an explicit import. None of them are in the core bundle; each fetches on first use.

What loads on demand, and what triggers it

Rikiki ships optional runtime modules and optional component modules. The initial load · the engine (dist/index.js, 26 KB gzip) plus Lit and marked, 43 KB gzip together · doesn't include any of them. Each one fetches separately the first time it is needed, so a deck that never uses a module pays nothing for it.

ModuleSize (gz)Activated byUse-case
deck-transition1.7 KBtransition="..." attr on deck-rootSlide-in / fade / zoom / flip animations between slides
deck-presenter6 KBPress PSpeaker window with notes + next-slide preview + timer
shiki0.4 KB loader + 113 KB runtimeExplicit import + installShiki()Production-grade syntax highlighting for deck-code
click-stages2.6 KBExplicit import + installClickStages()Reveal elements one click at a time with data-click
deck-overview5 KBPress OEvery slide as a thumbnail, with search · path layout up to 60 slides, two columns above
deck-help1.4 KBPress ? or HKeyboard shortcut overlay

deck-notes is not on this list: it ships inside the core bundle, so a slide can carry speaker notes with nothing imported.Speaker notes below covers it.

Optional component modules

The elements below are not imported by the default bundle and ship as separate files. Import the file only when the deck uses that element; the complete catalogue, attributes, and examples live in thecomponent reference.

deck-agenda ·deck-annotate ·deck-bar ·deck-checklist ·deck-figure ·deck-flow ·deck-graph ·deck-icon ·deck-kpi-grid ·deck-persona ·deck-pull ·deck-quote ·deck-table ·deck-timeline ·deck-versus

deck-transition · animations between slides

Setting a transition attribute on deck-roottriggers a lazy import of the module on first navigation. The module then animates both the outgoing and incoming slides with transforms only, so no background shows through whatever colors the two slides use.

basic usage
<!-- Set on deck-root · the entire deck animates -->
<deck-root transition="slide">
  <deck-cover>...</deck-cover>
  <deck-section>...</deck-section>
  <deck-feature>...</deck-feature>
</deck-root>

Available transitions

ValueEffectDefault duration
slide Auto-directional horizontal. Forward enters from the right, backward from the left.560 ms
slide-right Same as slide but starts in the left-to-right direction (still auto-flips on backward nav).560 ms
slide-up Auto-directional vertical. Forward enters from below, backward from above.520 ms
slide-down Mirror of slide-up.520 ms
fade Soft scale-up · the default if no value matches.480 ms
zoom Aggressive scale-up with spring ease (good for hooks/punchlines).520 ms
flip 3D rotateY · subtle card-flip feel.560 ms

Per-slide override

Set data-transition on any slide host to override the deck-wide default for that slide only.

per-slide
<!-- Per-slide override · data-transition wins over the parent attribute -->
<deck-root transition="fade">
  <deck-cover>...</deck-cover>
  <deck-section data-transition="zoom">...</deck-section>
  <deck-takeaway data-transition="flip">...</deck-takeaway>
</deck-root>

All transitions are transform-only · no opacity, no background animation, no flashing. Honours prefers-reduced-motion: reduce (animations disable cleanly).

deck-presenter · speaker window

Press P during the talk · a popup window opens (you'll need to allow popups for the deck's origin). The window shows:

  • Current slide · live mirror of what the audience sees
  • Next slide · preview of what's coming
  • Speaker notes · the text content of deck-notes inside the current slide
  • Timer · auto-starts, pause / resume / reset buttons
  • Slide counter · current / total

The popup syncs with the main deck via BroadcastChannel · no localStorage races, no postMessage ceremony. Forward keyboard input from the popup also drives the main deck, so you can use one laptop with the second screen mirroring the audience view.

Second screen (Chromium)

With an external screen attached, pressing P sends the deck fullscreen to that screen (the projector) and opens the speaker window on the screen you are working on. The audience sees the slides, you keep the presenter view on your laptop; closing the presenter releases the deck's fullscreen.

The first press asks for Window Management

Placing the two windows needs the Window Management permission, so the firstP press prompts for it. The speaker window opens either way, and the slides move to the projector once the screen layout is known · the very first time, that can take a second P press right after you grant the permission.

Without the API or a second screen

On Firefox and Safari, or with the permission refused, or on a single screen, the deck stays where it is and the speaker window opens at the browser's default placement. Everything else · notes, previews, timer, sync · works the same.

While the speaker window is open

The Current and Next previews are constrained to a 16:9 box, so a thumbnail keeps the projected slide's geometry whatever shape the window takes. The projected deck also auto-hides its key-hint chips and nav arrows (the same effect as no-hint and no-arrows) and restores them when the presenter closes.

Speaker notes · the deck-notes element

Add deck-notes inside any slide host. The element is hidden in the audience view (:host { display: none }) and read by the presenter window via textContent. Free-form text · use markdown-style bullets if you want, but it's rendered aswhite-space: pre-wrap.

notes example
<deck-root>
  <deck-cover>
    <h1>Hello</h1>
  </deck-cover>
  <deck-feature>
    <h1 slot="title">My slide</h1>
    <p slot="lead">Audience sees this.</p>

    <deck-notes>
      Speaker-only text. Only visible in the presenter window.
      - Bullet point one
      - Pause for laughter
      - Mention the rollout date
    </deck-notes>
  </deck-feature>
</deck-root>
Live preview · slide canvas

shiki · production code highlighting

The default deck-code highlighter is a hand-rolled regex pass over a fixed keyword list, with no template-literal awareness and no JSX. Good enough for blog snippets, brittle for serious prod code.

Opt in to Shiki when you need TextMate-quality highlighting for TypeScript, JavaScript, HTML, CSS or JSON. Rikiki vendors only those five grammars and theone-dark-pro theme, which keeps the optional runtime at 113 KB gzip. The module loads dist/vendor/shiki.js on first use, from the deck's own folder rather than a CDN.

install shiki
<!doctype html>
<html>
<head>
  <link rel="stylesheet" href="rikiki/themes/rikiki.css">
  <script type="module" src="rikiki/dist/index.js"></script>

  <!-- Opt-in Shiki highlighter · loads the vendored Shiki bundle on first highlight (offline) -->
  <script type="module">
    import { installShiki } from './rikiki/dist/shiki.js';
    await installShiki({
      theme: 'one-dark-pro',
      langs: ['ts', 'js', 'html', 'css', 'json'],
    });
  </script>
</head>
<body>
  <deck-root>
    <deck-feature>
      <h1 slot="title">Real syntax</h1>
      <deck-code lang="ts" hero>
        const fn = <T,>(x: T): T => x;
      </deck-code>
    </deck-feature>
  </deck-root>
</body>
</html>
Live preview · slide canvas

After install, every existing and future deck-code instance re-renders through Shiki. Request one or more of ts,typescript, js, javascript,html, css and json; unsupported names are rejected during installation instead of silently growing the bundle. A code block whose language was not preloaded falls back to the regex highlighter. Under the hood it registers a highlighter through deck-code's publicsetDeckCodeHighlighter hook (see Writing a plugin) rather than patching the component.

click-stages · reveal elements on click

Click-through mode. Tag any element with data-click and it stays hidden until you click, then fades in. The step dots at the bottom count the reveals, and stepping back hides them again. It builds on the same step engine deck-code already uses, so one slide can mix click reveals and code step-groups.

install click-stages
<!doctype html>
<html>
<head>
  <link rel="stylesheet" href="rikiki/themes/rikiki.css">
  <script type="module" src="rikiki/dist/index.js"></script>

  <!-- Opt-in click-through reveals -->
  <script type="module">
    import { installClickStages } from './rikiki/dist/click-stages.js';
    installClickStages();
  </script>
</head>
<body>
  <deck-root>
    <deck-feature>
      <h1 slot="title">Build it up</h1>
      <p data-click>Shows on the first click.</p>
      <p data-click data-anim="slide-up">Then this one, sliding up.</p>
      <p data-click="3" data-anim="scale">Jumps straight to click 3.</p>
      <p data-click-hide>Here now, gone on the next click.</p>
    </deck-feature>
  </deck-root>
</body>
</html>
Live preview · slide canvas

Attributes

AttributeEffect
data-clickHidden until the next click, then revealed.
data-click="N"Revealed on click N specifically.
data-click-hideVisible at first, hidden on the next click.
data-anim="..."How it enters · fade (default), slide-up, slide-down, slide-left, slide-right, scale, blur, flip-up, draw (strokes an SVG on).
data-anim-duration="600"Entry duration in ms · default 320.
data-anim-delay="120"Delay before the entry starts, in ms · default 0.
data-anim-ease="spring"out (default), spring, in-out, or a raw cubic-bezier(…).

Honours prefers-reduced-motion: reduce · reveals snap in without the transform, and the delay drops to zero, when the reader asks for less motion.

Writing a plugin

Both click-stages and shiki extend the deck through a small public contract rather than reaching into engine internals. The contract is DeckPlugin, an object you register on a deck with deckRoot.use(plugin), which returns an unregister function. The hooks are all optional:

HookWhen
setup(ctx)On register · may return a teardown run on unregister.
steps(slide, ctx)Contributes to the slide's step count · combined with the engine's own as a maximum.
applyStep(step, slide, ctx)After the engine applied a step · toggle your elements here.
navigate(to, ctx, proceed)Wraps the navigation · the engine hands you the move and you decide when it happens, by calling proceed() (optionally inside something of your own, a View Transition for instance). Return truthy to take ownership; the first plugin with this hook wins.
a deck-root plugin
import type { DeckPlugin } from './rikiki/dist/index.js';

const stopwatch: DeckPlugin = {
  name: 'stopwatch',
  setup(ctx) {
    const t = setInterval(() => console.log('on slide', ctx.current), 1000);
    return () => clearInterval(t);          // teardown on unregister
  },
  steps: (slide) => slide.querySelectorAll('[data-reveal]').length,
  applyStep(step, slide) { /* toggle your elements for this step */ },
  navigate(to, ctx, proceed) { return false; }, // false = let the engine navigate
};

document.querySelector('deck-root').use(stopwatch);

The ctx (DeckContext) is the only surface a plugin touches: ctx.host, ctx.current, ctx.step,ctx.slides, ctx.requestUpdate(). Fordeck-code highlighting there's a separate hook,setDeckCodeHighlighter(fn), where fn(code, lang)returns the block's inner HTML or null to fall back to the built-in regex highlighter. DeckPlugin, DeckContextand setDeckCodeHighlighter are re-exported fromdist/index.js.

installClickStages() and installShiki() are back-compat shims that attach the plugin to every deck-rootalready on the page · a deck created dynamically after the call must register the plugin itself with use().

Slide zoom

In the default fixed canvas, Ctrl/ + wheel and trackpad pinch magnify the active slide around the cursor; +/- zoom by steps and 0 resets. While magnified, a plain wheel and pointer drag pan the slide, and changing slide snaps back to fit. It scales the whole slide uniformly (fonts and layout together, no reflow), so it doubles as a "bigger fonts" control. It is a no-op in fluid mode and in overview; set no-zoom on deck-root to leave zoom to the browser.

What opting in costs

The modules in the table at the top of this page are small. Two of them pull a vendored third-party payload behind them, which is where the real weight is · measured from the shipped files:

Opting intoModule (gz)Vendored payload (gz)
deck-mermaid · a diagram on a slide1.7 KB975 KB · dist/vendor/mermaid.min.js
installShiki() · TextMate highlighting0.4 KB113 KB · dist/vendor/shiki.js

Neither payload is fetched by a deck that does not use the feature, and the engine (dist/index.js) stays at 26 KB gzip whichever modules the deck ends up importing.

Next

  • The component catalogue · every optional element with its attributes, slots and tokens.
  • API reference · the DeckPlugin contract and the deck-root surface it uses.
  • Navigation · the overview and help modules, and the keys that open them.