quarto-revealjs-chat-bubbles

Chat conversations for Quarto Reveal.js presentations

Add chat conversations to your Quarto Reveal.js presentations, styled after iMessage, Slack, Discord, or Microsoft Teams. Messages appear one at a time as fragments, and long conversations scroll automatically to keep the latest message in view.

GitHub


Installation

quarto add EmilHvitfeldt/quarto-revealjs-chat-bubbles

Usage

Enable the plugin in your presentation front matter:

format:
  revealjs: default
revealjs-plugins:
  - chat-bubbles

Then write a conversation using .chat and speaker classes. Add .fragment to any message you want to reveal on a keypress:

::: {.chat}
::: {.speaker-1}
Hey, are you coming tonight?
:::

::: {.fragment .speaker-2}
Yeah! What time does it start?
:::

::: {.fragment .speaker-1}
Doors open at 7
:::
:::

Themes

Set theme on the .chat container. Themes are per conversation, so one deck can mix platforms.

Theme Layout Looks like
imessage (default) Alternating sides, tailed bubbles iOS Messages
slack Single column, avatar and name Slack
discord Single column, dark surface Discord
teams Alternating bubbles with avatars Microsoft Teams

Themes that show names need a roster. names maps positionally onto the speaker slots:

::: {.chat theme="slack" names="Jo,Meg,Amy"}
::: {.speaker-1}
I shall write a splendid book someday.
:::

::: {.fragment .speaker-2}
We are proud of you now, Jo.
:::

::: {.fragment .speaker-3}
Will there be any fashionable people in it?
:::
:::

Avatars default to the speaker’s initial on a coloured background; pass avatars="a.png,b.png" to use images instead. Consecutive messages from the same speaker are grouped automatically.


Speaker classes

Class Color
.speaker-1 Blue
.speaker-2 Gray (slate for avatars)
.speaker-3 Green
.speaker-4 Purple

What a slot looks like depends on the theme: in iMessage it decides the side and the bubble colour, in Slack and Discord it colours the avatar and name.

.speaker-1 is the sender by default, which in the alternating themes is the person on the right. Point that at another slot with self="2" on the container.

The original class names (.bubble-right, .bubble-left, .bubble-left-2, .bubble-left-3) still work and map to .speaker-1 through .speaker-4, so existing decks need no changes.


Group chats

Use all four slots together for multi-person conversations:

::: {.chat}
::: {.speaker-1}
Alright who's bringing what to the potluck?
:::

::: {.fragment .speaker-2}
I can do dessert
:::

::: {.fragment .speaker-3}
I'll bring drinks
:::

::: {.fragment .speaker-4}
Can I just bring chips
:::
:::

Typing indicator

Add .typing to any .fragment message for a two-step reveal: the first keypress shows an animated three-dot typing indicator, and the next keypress expands the bubble and reveals the message text.

::: {.chat}
::: {.speaker-1}
Are you still there?
:::

::: {.fragment .speaker-2 .typing}
Sorry, was typing a really long reply.
:::
:::

Images and plots

Messages can contain images or R/Python plot output. Place them inside the message div as usual:

::: {.chat}
::: {.speaker-1}
![](photo.gif)
:::

::: {.fragment .speaker-2}
Haha love it
:::
:::

Code chunk output works the same way:

::: {.chat}
::: {.speaker-2}
Can you show me that plot?
:::

::: {.fragment .speaker-1}
```{r}
plot(mpg ~ disp, data = mtcars)
```
:::
:::

Customizing colors

Override the CSS custom properties in your own stylesheet:

:root {
  --bubble-blue:   #FF3B30;  /* speaker 1 */
  --bubble-gray:   #E9E9EB;  /* speaker 2 */
  --bubble-green:  #34C759;  /* speaker 3 */
  --bubble-purple: #AF52DE;  /* speaker 4 */

  --bubble-slate:  #5A6B7B;  /* speaker 2's avatar and name */
}

--bubble-slate exists because speaker 2’s near-white default reads well as a bubble fill but disappears when the same colour has to fill a small avatar or colour a username.