Skip to content

Chat Bubbles with Typing Indicator

Pure CSS chat window with grouped incoming and outgoing message bubbles, an animated three-dot typing indicator and a working composer.

Live preview

Open in new tab

Code

A chat window with incoming and outgoing message bubbles and an animated three-dot typing indicator, built with pure CSS and a few lines of vanilla JavaScript. Use it for support widgets, messenger mockups, onboarding demos or any page that needs a lively conversation preview.

How it works

The outer .tb-wrap paints the pastel gradient page and holds all custom properties. Inside it, section.tb-chat is a rounded card with three parts: the .tb-header (avatar with a green .tb-presence dot, name, the .tb-state line and a video call button), the scrollable .tb-body and the .tb-composer form.

Messages live in the ol.tb-log list. Each li.tb-msg gets .tb-msg--in or .tb-msg--out, which aligns it left or right. Incoming .tb-bubble elements use a soft lavender fill, outgoing ones a violet gradient, and one squared-off corner on each side acts as the tail. When the sender changes, an adjacent sibling selector adds extra space so messages read as groups. New messages fade in with the tb-pop keyframes.

The .tb-typing row sits under the log: a small avatar plus the .tb-dots bubble with three .tb-dot spans. They share the tb-bounce keyframes, and the second and third dots are delayed by a fraction of --tb-speed, which creates the wave. The indicator is pure CSS and shows on load.

The script runs on DOMContentLoaded for every .tb-chat. On submit it calls preventDefault(), appends your text as an outgoing bubble with the current time, scrolls .tb-body down and shows the indicator while .tb-state switches to "typing...". After REPLY_DELAY a reply from the REPLIES array is added and the indicator is hidden with the hidden attribute.

Customization

  • --tb-out-1 and --tb-out-2 on .tb-wrap: the gradient of outgoing bubbles, the send button and the typing status color.
  • --tb-in-bg: the background of incoming bubbles and the typing bubble.
  • --tb-dot-size, --tb-bounce and --tb-speed: the size of each dot, how high it jumps and the length of one wave.
  • --tb-radius: the roundness of all bubbles; the tail corner stays at 6px.
  • height of .tb-body: the visible message area, 340px on phones and 420px from 640px.
  • REPLY_DELAY and REPLIES in script.js: how long the indicator shows and the simulated answers. Replace the timeout with your WebSocket or server-sent events.

Accessibility

The message list has role="log" with aria-live="polite", so new messages are announced. The typing row is a role="status" region with the visually hidden text "Maya is typing", while the dots are aria-hidden. The scroll area is focusable with an aria-label, the input has a hidden label, and icon buttons have aria-labels and a visible focus outline. With prefers-reduced-motion: reduce the bounce becomes a gentle opacity fade and the pop-in and smooth scrolling are turned off.

Usage notes

The avatar photo comes from Unsplash; replace it with your own image. The input id tb-input must be unique if you place two chats on one page.

Requirements

No dependencies. Pure HTML, CSS and JavaScript.

Chat Bubbles with Typing Indicator – Pure CSS chat widgets component screenshot
Chat Bubbles with Typing Indicator · Pure CSS Chat Widgets

More Chat Widgets

Chat Widget – Tailwind CSS chat widgets preview

Chat Widget

Tailwind CSS support chat widget with floating launcher, unread badge, message bubbles, suggested replies, typing indicator and Enter-to-send input.