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.
Installation
quarto add EmilHvitfeldt/quarto-revealjs-chat-bubblesUsage
Enable the plugin in your presentation front matter:
format:
revealjs: default
revealjs-plugins:
- chat-bubblesThen 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}

:::
::: {.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.