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/FadeGroupchild,TimelineStepsblock, code highlight group after the first, or Magic Code state after the first adds a step. RevealWithandFadeWithadd no steps. They watch existing steps bytargetor numericat.- 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, notdisplay: none). - Ephemeral when requested — add
ephemeralto render hidden content asnull, so it reserves no layout space. Presenter previews still show a muted ghost. - Default effect — fade-in. Customize with
classNameand Tailwind/CSS. - Optional
name="..."gives a reveal a slide-local target forRevealWith.
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"orat={n}. targetpoints to a same-slide<Reveal name="name">; forward references work.atis a 1-based slide-local step and can target any existing timeline step.name,target, andatmust 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 rendersnulland reserves no layout space. - Fade content must not contain nested timeline producers. Put
FadeinsideReveal, notRevealinsideFade.
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:
- Parent item appears
- Nested detail appears
- Code item appears with line 1 highlighted
- 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-moveis accepted as a Slidev compatibility alias.- Use
{duration:500}to override the animation duration for one block. - Set a deck-wide default with
magicCodeDuration: 500in 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:
- First code state, line 1 highlighted
- First code state, line 2 highlighted
- 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:
- Slide starts with code line 2 highlighted
- Code highlights line 4
- Code highlights all lines
- Reveal appears
PDF Export of Steps
Controlled by deck-level pdfSteps setting:
| Value | Behavior |
|---|---|
final (default) | Each slide appears once with all reveals visible, code at final highlight |
all | Each step state becomes a separate PDF page |