Honeydeck Docs
Core

Steps and reveals

Steps and reveals documentation for Honeydeck.

Steps & Reveals

Honeydeck has a first-class step concept. Each slide has a timeline built from Reveal/RevealGroup/Fade/FadeGroup components, RevealWith/FadeWith synchronized content, custom component step blocks, code highlight ranges, and Magic Code states, counted in document order.

Timeline Model

  • Slides start at step 0 — no reveals or custom component steps active.
  • Stepped code blocks show their first highlight group immediately whenever the block is visible.
  • Magic Code blocks show their first inner code fence and first highlight group immediately whenever the block is visible.
  • Each Reveal, Fade, RevealGroup/FadeGroup child, TimelineSteps block, code highlight group after the first, or Magic Code state after the first adds a step.
  • RevealWith and FadeWith add no steps. They watch existing steps by target or numeric at.
  • All step-producing elements share one slide-local timeline.
  • Nested step-producing elements are flattened into that same timeline for reveal targets. A parent reveal target appears first; nested reveal/fade groups and code highlight ranges appear after the parent and before the next sibling target.
  • Fade targets must not contain nested timeline producers because a faded parent would hide later nested steps. Put fades inside reveals instead.

Reveal

import { Reveal } from '@honeydeck/honeydeck'

# Why Honeydeck?

<Reveal>Start with Markdown</Reveal>

<Reveal>
  Add <strong>interactive React</strong> when needed
</Reveal>

<Reveal>Export to PDF</Reveal>

Behavior

  • Cumulative — once visible, content stays visible for the rest of the slide.
  • Layout-stable by default — hidden content reserves space (opacity: 0 / visibility: hidden, not display: none).
  • Ephemeral when requested — add ephemeral to render hidden content as null, so it reserves no layout space. Presenter previews still show a muted ghost.
  • Default effect — fade-in. Customize with className and Tailwind/CSS.
  • Optional name="..." gives a reveal a slide-local target for RevealWith.

RevealWith

Use RevealWith when content should appear at the same step as another reveal, code highlight, group item, Magic Code state, or custom TimelineSteps state. It never adds a new step.

import { Reveal, RevealWith } from '@honeydeck/honeydeck'

<Reveal name="headline">Headline appears first</Reveal>
<RevealWith target="headline">Supporting detail appears with it</RevealWith>

```ts {1|2|3}
const answer = 42
console.log(answer)
```

<RevealWith at={2}>This appears with the second slide step</RevealWith>

Rules:

  • Use exactly one of target="name" or at={n}.
  • target points to a same-slide <Reveal name="name">; forward references work.
  • at is a 1-based slide-local step and can target any existing timeline step.
  • name, target, and at must be literal values so Honeydeck can validate them at build time.

Fade

Fade is the inverse of Reveal: content starts visible and fades out at its timeline step.

import { Fade } from '@honeydeck/honeydeck'

<Fade>Visible at step 0, gone at step 1</Fade>
<Fade ephemeral>Gone at step 2 and no longer reserves space</Fade>

Behavior:

  • Visible while stepIndex < at.
  • Hidden while stepIndex >= at.
  • Hidden content reserves layout space by default.
  • With ephemeral, hidden content renders null and reserves no layout space.
  • Fade content must not contain nested timeline producers. Put Fade inside Reveal, not Reveal inside Fade.

FadeWith

Use FadeWith for a fade-out controlled by an explicit target step or named reveal target without adding its own timeline step:

import { FadeWith } from '@honeydeck/honeydeck'

<FadeWith target={3}>Disappears when step 3 is active</FadeWith>
<FadeWith at={2}>Disappears with slide step 2</FadeWith>

FadeWith never increments the slide step count. It uses the same target/at validation rules as RevealWith.

RevealGroup

Reveals each direct child as its own step. If a direct child is a list, each top-level list item is revealed as its own step:

import { RevealGroup } from '@honeydeck/honeydeck'

<RevealGroup>
  - Markdown-first
  - React-powered
  - PDF-ready
</RevealGroup>

Set listRevealMode="nested" to reveal nested list items depth-first:

<RevealGroup listRevealMode="nested">
  - Parent
    - Child A
    - Child B
  - Sibling
</RevealGroup>

Timeline: parent → child A → child B → sibling. The default "direct" mode keeps nested items grouped with their parent.

To reveal multiple elements together, wrap them in a single Reveal instead. Add ephemeral to the group when hidden generated children should not reserve layout space.

Nested reveals and code walkthroughs work inside group items:

<RevealGroup>
  <div>
    Parent item
    <Reveal>Nested detail</Reveal>
  </div>
  <div>
    Code item
    ```ts {1|2}
    const a = 1
    const b = 2
    ```
  </div>
</RevealGroup>

Timeline:

  1. Parent item appears
  2. Nested detail appears
  3. Code item appears with line 1 highlighted
  4. Code highlights line 2

FadeGroup

FadeGroup is the inverse of RevealGroup: each meaningful direct child starts visible and fades out one by one. Direct lists are preserved, and each list item gets its own fade-out step. Fade group targets must not contain nested timeline producers.

import { FadeGroup } from '@honeydeck/honeydeck'

<FadeGroup ephemeral>
  - Remove this at step 1
  - Remove this at step 2
  - Remove this at step 3
</FadeGroup>

Custom Component Steps

Use TimelineSteps when an imported React component should control part of the slide timeline, such as an accordion, tab set, or diagram walkthrough:

import { Reveal, TimelineSteps } from '@honeydeck/honeydeck'
import { AccordionDemo } from './AccordionDemo'

<Reveal>Before the custom component</Reveal>

<TimelineSteps steps={3}>
  <AccordionDemo />
</TimelineSteps>

<Reveal>After the custom component</Reveal>

Inside the custom component:

import { useTimelineSteps } from '@honeydeck/honeydeck'

export function AccordionDemo() {
  const { phase, stepIndex, stepCount, isPdfFinalRender } = useTimelineSteps()
  // Open section stepIndex - 1 when phase is "active".
  // Open all sections when isPdfFinalRender is true.
}

TimelineSteps must be written in slide MDX with a literal positive integer steps value. Rendering it only inside an imported TSX component does not register steps, because Honeydeck counts the timeline at build time.

When PDF export renders one final-state page per slide (pdfSteps: final), useTimelineSteps() exposes isPdfFinalRender: true. Step-driven custom components can use that to render a PDF-specific final composition, such as an accordion with every section open. In pdfSteps: all, the flag stays false because each step is captured as its own page.

Code Step-Through

Code blocks support highlight ranges that participate in the same timeline:

```ts {2-3|5|all}
const a = 1
const b = 2
const c = 3
console.log(a + b)
console.log(c)
```
  • {2-3|5|all} — starts with lines 2-3 active, then steps to line 5, then all lines.
  • Index starts at 1.
  • The first group is the baseline highlight and adds 0 timeline steps; every later group adds 1 timeline step.

Code Step Behavior

  • Baseline: The first metadata group is active immediately, including at slide step 0 when the block is visible.
  • Non-cumulative: Only the lines specified for the active group are highlighted; previous highlights don't persist.
  • Dim approach: Non-highlighted lines are dimmed (controlled by --honeydeck-code-line-dim-opacity: 0.4).
  • Shiki at build time: No runtime JS cost for syntax highlighting.

Magic Code

Magic Code animates between multiple code states while keeping the same Honeydeck timeline model. It builds on Shiki Magic Code / Shiki Magic Move and uses build-time precompiled highlighting data.

````md magic-code {duration:500}
```ts
const count = 1
```

```ts
const count = 2
```
````
  • The canonical Honeydeck syntax is md magic-code.
  • md magic-move is accepted as a Slidev compatibility alias.
  • Use {duration:500} to override the animation duration for one block.
  • Set a deck-wide default with magicCodeDuration: 500 in deck-level frontmatter.
  • Content inside a Magic Code block that is not a fenced code block is ignored.
  • Magic Code looks and copies like a normal Honeydeck code block. Copying always copies the currently visible code text.

Inner code fences keep normal code step metadata:

````md magic-code
```ts {1|2}
const a = 1
const b = 2
```

```ts {all}
const sum = a + b
```
````

Timeline:

  1. First code state, line 1 highlighted
  2. First code state, line 2 highlighted
  3. Morph to second code state, all lines highlighted

Magic Code states may use different languages, but animations usually look best when all states use the same language.

Mixed Timeline Example

# Demo

```ts {2|4|all}
const a = 1
const b = 2
console.log(a + b)
```

<Reveal>Now notice the output</Reveal>

Timeline:

  1. Slide starts with code line 2 highlighted
  2. Code highlights line 4
  3. Code highlights all lines
  4. Reveal appears

PDF Export of Steps

Controlled by deck-level pdfSteps setting:

ValueBehavior
final (default)Each slide appears once with all reveals visible, code at final highlight
allEach step state becomes a separate PDF page

On this page