Building a WhatsApp chat demo for a site that lives in WhatsApp

April 2026·7 min read·source gist

Text Ari is an AI career assistant that lives inside WhatsApp. No app to install, no site to log into. You text it like you text anyone else and it sends back job matches.

Which makes marketing it a problem. The product doesn’t have any screens to screenshot. A static mockup looks like nothing. A video loop feels like advertising. What you actually need on the landing page is the thing itself, running, on loop, so someone can watch a fake user get matched without signing up.

Here’s the demo:

A

Text Ari

online

Message

It loops. Conversation plays, ends, waits 3.5 seconds, plays again. Everything you see is React components rendered inside a rectangle on the marketing site. None of it is WhatsApp’s real app. The trick is that it needs to feel like it is.

Six decisions did most of the work.

1. Steal the palette exactly

There is no “close enough” grey here. People have a WhatsApp shape in their head and they will clock a wrong colour even if they can’t articulate it. The palette I used matches WhatsApp’s dark mode:

  • #0b141a canvas background
  • #1f2c34 incoming bubble, header, input
  • #005c4b outgoing bubble (the green)
  • #00a884 success/receipt accent (score pill)
  • #e9edef primary text
  • #8696a0 secondary text, placeholder

Inline as style attributes, not Tailwind classes, because these aren’t design-system values. They’re specific to this one rectangle pretending to be another app.

2. The bubble tail is the whole message

WhatsApp bubbles have an asymmetric corner, where one radius is smaller than the others. That corner points at the sender. It’s the cheapest directional cue in messaging UI and doing without it costs more than you’d think. Toggle it below:

Bubble tail

Hey! How’s it going?
Good. You?
All good here.

One corner per direction: 2px radius instead of 8px. The bubble points at its sender.

With the tail off, the conversation reads like two columns of text that happen to alternate sides. With it on, each message is pointing at whoever said it. Same words, same position. The only change is one border-radius value per side dropping from 8px to 2px.

That’s the kind of detail design engineers get to notice: the UI primitive is asymmetric for a reason, and matching the reason is cheaper than matching the look.

3. The typing indicator does most of the acting

Three dots, bouncing, staggered. That’s it. The specifics matter though:

  • Dot size: 6px circles at 40% opacity baseline.
  • Cycle: 1.2s per dot.
  • Stagger: 200ms between dots, so the wave runs across without overlapping.
  • Easing: 0%, 60%, 100% at rest; 30% is the peak (translateY(-3px), 100% opacity). 60% of the cycle is idle.

That long rest period is what makes it feel patient rather than jittery. If the dots were jumping constantly, it would feel like loading. The rest tells you someone is thinking.

4. The pacing is the script

The conversation is driven by an array of delays:

const DELAYS = [600, 500, 1200, 2800, 600, 1100, 1000, 1000];

Eight numbers. The 2800ms in the middle is the one to notice. That’s where the user is “typing” their CV summary. If you play that beat at 600ms like the rest, the demo feels like a ticker. At 2.8 seconds, it feels like the user is composing something long. Same messages, different story.

The bot delays are shorter (500 to 1100ms) because the bot is an assistant responding to input. It shouldn’t pretend to deliberate. The typing indicator buys the credibility that the speed would otherwise undercut.

5. Cards still live in bubbles

When Ari returns job matches, each one is a little card with a title, category, salary, and a score badge. The temptation is to break out of the chat layout for rich content: new row, grid, buttons. Don’t.

The card stays inside a message bubble. Same background colour. Same rounded corners. Same directional tail. Inside it’s a mini layout: title on top, meta underneath, score pill floating right. From the outside it still reads as “Ari just sent you something.” That consistency is what keeps the demo feeling like a real conversation instead of a product page pretending to be a chat.

6. Let it loop

After the final card, the demo waits 3.5 seconds and restarts. No replay button. The demo is running in the background of your browsing, and if you look back at it, it’s doing something again.

The auto-scroll to the bottom on every new message is the other part of that. Without it the demo silently fails once the conversation exceeds the visible area. With it, you can glance at the component any time and see the latest message regardless of how many have come before.

The demo is the marketing page

If your product is conversational, you don’t describe it. You let someone watch a fake conversation play out. The entire pitch is in the demo: the tone, the speed, the job-match output, the fact that this is a person texting a thing and getting useful answers. Put a headline on top and a sign-up underneath and the page is done.

The source is this gist. Drop it in any React app, swap the messages, and you have a WhatsApp demo in your own rectangle.