15 CSS Chat Bubbles

6 Pure CSS9 Light JSPublished

15 hand-coded CSS chat bubbles for messaging apps, AI assistant interfaces, support widgets, and testimonial sections. Covers the pure-CSS bubble with a pseudo-element tail, iMessage and WhatsApp Web styles, a ChatGPT-style response bubble, animated typing indicators, image attachments, voice messages with a scrubbable waveform, and a full chat screen on flexbox. Messages use a real ordered list with <time> metadata rather than div soup. Scoped under .cb-NN for no-collision pasting, prefers-reduced-motion guarded, framework-agnostic.

Related25 CSS Blockquotes25 CSS Testimonials & Reviews34 CSS Floating Buttons30 CSS Notifications22 CSS Avatars

Course pickPartner: Zero to Mastery

Affiliate link

Want to build production-ready CSS layouts, animations and UI from scratch?

You just browsed 15 chat bubbles. Learn the Flexbox, Grid and animation thinking behind them, so the next component starts in your head, not in a copy-paste.

10% off at checkout with code
Check ZTM Course

Every ZTM course in one membership: $25/month, paid yearly at $299 · 30‑day money-back guarantee

The CSS Bootcamp. We may earn a commission, at no extra cost to you.

01. Pure CSS Chat Bubble With Arrow (opens in a new tab)

Published

Pure CSS
Six tail silhouettes drawn with zero SVG and zero extra markup, across three techniques: two ::after border-triangle variants (a baseline wedge and a centred arrow), three clip-path polygons (straight wedge, concave curl, pixel staircase), and the real curved iOS-style curl built from two pseudo-elements. Flip between them with the segmented control — the switch is a radio group read by :has(), so the whole demo is genuinely stylesheet-only, including the light/dark toggle.

02. iMessage CSS Chat Bubbles (opens in a new tab)

Published

Light JS
An iOS-accurate message thread: true curved tails (not triangles), bubble grouping where only the last message in a run wears a tail, a blue↔green channel switch for iMessage vs SMS, long-press tapback reactions that bloom out of the bubble, and a Delivered → Read receipt that updates in the live region. The composer sends real messages.

03. ChatGPT Response Bubble CSS (opens in a new tab)

Published

Light JS
The layout every AI product converges on: the user prompt is a bubble, the assistant answer is not. Prompts sit right-aligned in a tinted capsule; the response flows full-width as real Markdown — headings, lists, inline code, a syntax-tinted code block with a copy button — under a small model badge. Streaming is animated with a blinking block caret, there is a Stop control mid-stream, and the feedback row only appears once the answer is complete.

04. Tailwind CSS Chat Bubble (opens in a new tab)

Published

Light JS
A chat card written the way you would actually write it in a Tailwind project — utilities on every element, dark: variants for the full colour scheme, group-hover: timestamps, peer state on the composer, and arbitrary-value utilities for the tail geometry. Ships with a scoped vanilla fallback so it renders identically here; delete that block in a real Tailwind app and the class names carry over untouched.

05. CSS Chat Bubble Typing Indicator Animation (opens in a new tab)

Published

Pure CSS
Four production typing indicators in one switchable demo — bounce dots, a travelling wave, a soundwave meter and a shimmer skeleton — all pure CSS, all built from the same three-span markup, all staggered by a single inline --i custom property instead of four hand-written delay rules. Each one respects prefers-reduced-motion by falling back to a non-animated resting state that still reads as "waiting".

06. CSS Speech Bubble With Avatar (opens in a new tab)

Published

Pure CSS
Three avatar layouts for the same thread, switched with a pure-CSS radio group: beside (the classic 40 px circle next to the bubble), overlap (avatar notched into the bubble corner), and stacked (avatar and name above the message, the layout Slack-style products use). Repeated avatars inside a run are hidden but keep their space, presence is a ring rather than a dot on top of the face, and timestamps reveal on hover and focus.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

07. Live Support Widget Chat Bubble CSS (opens in a new tab)

Published

Light JS
A floating support launcher that expands into a real conversation card — built on the Popover API, so the browser handles the top layer, light-dismiss, Escape, and focus return with no JavaScript at all. @starting-style gives it an origin-anchored open animation, an unread badge counts waiting replies, quick-reply chips send canned questions, and the agent answers with a typing pause first.

08. WhatsApp Web Chat Bubble CSS (opens in a new tab)

Published

Light JS
The message-thread mechanics that make a mobile-messenger layout feel right: a timestamp and tick pair that floats inside the bubble and lets the last line of text wrap around it, tinted outgoing bubbles with clip-path tails, a sticky date divider, a quoted-reply block, and a receipt that walks sent → delivered → read. Send a message and watch all four states run.

09. CSS Speech Bubble With Border (opens in a new tab)

Published

Pure CSS
The hardest bubble to get right: an outlined one where the tail's stroke joins the border seamlessly. Three solutions side by side — the classic two stacked triangles, a single clip-path wedge outlined with filter: drop-shadow(), and and a nested-box stroke that can carry a gradient outline. Move the tail to any of the four sides with the pure-CSS control and watch all three follow.

10. CSS Chat Bubble Image Attachment (opens in a new tab)

Published

Light JS
Five media bubbles that cover almost every attachment a messenger has to render: a single photo with an overlaid caption, a mosaic grid that collapses extra images into a +N tile, a video preview with duration and play affordance, a file card with a conic progress ring, and a link preview. Clicking any photo opens it in a native <dialog> lightbox with Escape, backdrop dismiss and focus return handled by the platform.

11. Responsive Full Chat Screen Flexbox CSS (opens in a new tab)

Published

Light JS
A complete two-pane messenger shell that becomes a single-pane mobile app without a media query — the breakpoint is a container query, so the same component works full-screen, in a split view or inside a 380 px sidebar. Scrollable history with a pinned composer, sticky date headers, an auto-growing textarea, unread pills, three switchable conversations and a back button that only exists in the narrow layout.

12. CSS Comic Book Speech Bubble (opens in a new tab)

Published

Pure CSS
A four-panel comic strip with the whole balloon vocabulary drawn in CSS: a rounded speech balloon, a starburst shout cut with a 24-point clip-path, a dashed whisper, and a narration caption. Halftone dots come from a two-layer radial-gradient, panel art is duotoned with filter + mix-blend-mode, and three ink palettes swap with a pure-CSS control.

13. CSS Voice Message Chat Bubble (opens in a new tab)

Published

Light JS
A voice-note bubble with a real scrubbable waveform — 44 bars, no canvas, no library. The played portion is revealed by a second bar layer clipped to the progress width, so the fill animates on the compositor. Click or drag anywhere on the wave to seek, use the arrow keys on the slider, switch playback speed, and open the transcript in a <details> that animates with ::details-content.

14. Thought Bubble CSS Pure Code (opens in a new tab)

Published

Pure CSS
A cloud-shaped thought bubble with descending circles, built three ways: gradient cloud (the whole shape painted with layered radial-gradients), masked cloud (the same geometry used as a mask so the bubble can be filled with a photo or gradient), and outline cloud (the masked shape stroked with drop-shadow). The bubble and its trailing circles drift on independent, staggered loops.
Newsletter

Enjoying these demos? Get new ones by email.

A short email when a new collection ships. Unsubscribe any time.

at

Almost there: check your inbox to confirm.Manage

15. Testimonial Card Speech Bubble CSS (opens in a new tab)

Published

Light JS
Testimonials as speech bubbles that point down at the person who said them — semantic <blockquote> + <cite>, an accessible star rating that reads as text, and a scroll-snap carousel you can drive with the arrows, the dots, a swipe or the keyboard. Active state comes from an IntersectionObserver, so the dots stay honest even when you scroll the track by hand.
FAQ

Frequently asked questions

How do I add the pointed tail to a chat bubble?
Three techniques, and the right one depends on what the bubble sits on. The border trick is the classic: a zero-size ::after with transparent borders on three sides and a coloured border on the fourth produces a triangle — border: 9px solid transparent; border-inline-end-color: var(--bubble-bg) pinned to the bubble's edge with position: absolute. It is universally supported and costs nothing, but the triangle is flat, so it cannot follow a gradient or a border. The second option is a rotated square: a small element with transform: rotate(45deg) tucked half-behind the bubble, which inherits the bubble's background including a gradient, and can carry the same border for outlined styles. The third is clip-path: polygon() on the bubble itself, cutting the tail out of one shape — elegant, but remember that clip-path also clips the element's own pseudo-elements, so any decorative extras have to move to a wrapper. Two details that separate a convincing tail from an obvious one: the tail should sit flush with the bubble's bottom edge for a chat log rather than centred, and only the last bubble in a run gets one — repeating the tail on every grouped message is what makes hand-built chat UIs look wrong.
How do I build the iMessage and WhatsApp bubble styles accurately?
Both are recognisable from three properties, and getting them slightly wrong is more noticeable than not attempting them. iMessage: outgoing bubbles use a blue gradient (roughly #0b93f6 to #0a7cff) with white text, incoming use #e9e9eb with near-black text, both at a border radius around 18px on a ~17px font. The tail is a rounded shape rather than a hard triangle, which is why a plain border triangle reads as "nearly iMessage". WhatsApp Web: outgoing is the pale green #d9fdd3 on a patterned background, incoming is white, radius is tighter at about 7.5px, and the timestamp plus delivery ticks sit inline at the bottom-right inside the bubble, floated so the text wraps around them. That inline metadata is the detail most recreations miss. In both apps, consecutive messages from the same sender group with a small gap and only the final bubble keeps its tail. Demos 02 and 08 implement both, and the copy is generic sample text rather than anything resembling either company's branding.
How do I make an animated typing indicator?
Three dots with staggered animation delays, and the whole thing is about eight lines. Each dot animates opacity or translate on a loop, offset by roughly 160ms: .dot:nth-child(2) { animation-delay: .16s } .dot:nth-child(3) { animation-delay: .32s }. Animate transform and opacity only — animating height or margin forces layout on every frame for what is a purely decorative loop. The part people skip is the accessibility. A typing indicator is a live status, so it belongs in a region with aria-live="polite" and a visually-hidden text equivalent like "Priya is typing" — three animated dots convey nothing to a screen reader. Equally important, it must stop under prefers-reduced-motion: reduce, and the honest fallback is the static text rather than three frozen dots, which look like a rendering bug. Finally, do not leave it running indefinitely: an indicator that never resolves is worse than none, so clear it on a timeout as well as on message arrival.
What is the correct semantic markup for a chat log?
A chat log is a list of messages, so it should be an <ol> — ordered, because sequence is meaningful — with each message an <li>. Inside, the sender and timestamp belong in structured markup rather than floating divs: an <article> per message with a <header> carrying the sender name and a real <time datetime="2026-08-12T14:08"> is the shape that survives both screen readers and copy-paste. Do not rely on left/right alignment alone to convey who spoke — that is visual-only information, so each message needs the sender's name in the accessible name even when it is visually hidden for grouped runs. For a live conversation, wrap the log in aria-live="polite" so new messages are announced, but scope it carefully: marking the entire scrolling history as live means every re-render re-announces everything. The usual approach is a live region containing only the newest message, with the historical log outside it.
How do I build a chat screen that scrolls correctly?
The layout is straightforward — a flex column with the header and composer fixed and the message list taking the remaining space — but two behaviours are where chat UIs usually break. First, new messages should pin to the bottom, and the modern way is flex-direction: column-reverse on the scroll container with the messages in reverse order, which makes the browser anchor scrolling to the bottom natively. Alternatively, keep normal order and use overflow-anchor, or set scrollTop = scrollHeight after append. Second, do not force-scroll when the user has deliberately scrolled up to read history — check whether they are near the bottom first, and if not, show a "new messages" affordance instead of yanking them down. On mobile, the composer needs padding-block-end: env(safe-area-inset-bottom) or the home indicator overlaps it, and the container should use 100dvh rather than 100vh so the collapsing browser chrome does not leave a gap. Demo 11 implements the full screen.
How do I display a voice message with a waveform?
The waveform is a row of bars whose heights come from real amplitude data — an array of values between 0 and 1, one per bar, written into each bar as a custom property and read by block-size: calc(8% + var(--h) * 92%). That keeps all the layout in CSS and the data in one small array. Progress is the interesting part: rather than redrawing bars as playback advances, layer a second identical waveform in the accent colour and clip it with clip-path: inset(0 calc(100% - var(--p)) 0 0), so a single custom property drives the fill. One trap this collection hit directly: do not give the bars a min-width. With 44 bars at 3px plus gaps, that is a 218px hard floor, and a chat bubble on a 320px phone has roughly 150-190px available — the row refuses to shrink and pushes the page sideways. Let the bars compress with flex: 1 1 0; min-inline-size: 0 instead. For accessibility, the waveform is decorative and should be aria-hidden; the real control is a labelled play button plus a native <input type="range"> for scrubbing.
Are these chat bubbles accessible?
The bubbles themselves are simple; the accessibility work is in the conversation around them. Colour contrast is the first risk — the pale green and light grey that messaging apps use sit close to the 4.5:1 floor, so any custom palette needs checking rather than eyeballing, and a white-on-light-blue outgoing bubble frequently fails. Sender identity must not depend on alignment or colour alone, since neither is available to a screen reader user: the name belongs in the markup even when visually hidden in grouped runs. Timestamps use <time datetime> so the machine-readable value is unambiguous regardless of how it is formatted visually. Every interactive element — play buttons, reaction triggers, expand toggles — is a real <button> with a visible :focus-visible ring at 2px minimum and a 44 by 44 CSS pixel hit target. Animated indicators respect prefers-reduced-motion and fall back to text rather than freezing. And any auto-scrolling log needs to be interruptible, since content that moves under a reader is a WCAG 2.2.2 problem as much as a usability one.
How do these chat bubbles port to React, Vue, Svelte and Tailwind?
The CSS ports unchanged; what changes is that a chat log is data-driven, so the natural shape is a <Message> component taking sender, body, timestamp and a variant, rendered from an array. Two structural notes matter more than the syntax. Message grouping — only the last bubble in a run gets a tail, and repeated senders collapse their header — is derived state, so compute it while mapping (compare each message's sender to the previous one) rather than storing a isLastInGroup flag that can drift out of sync. And auto-scroll belongs in a layout effect rather than a normal one: React's useLayoutEffect runs before paint, so the jump to the bottom happens without a visible flash, whereas useEffect shows one frame at the old position. Vue's nextTick and Svelte's afterUpdate serve the same purpose. In the Next.js App Router the log itself can render on the server while only the composer and live updates need 'use client'. For Tailwind, demo 04 is already written as utilities, with the tail kept as a small piece of real CSS since a pseudo-element triangle has no clean utility equivalent.

Related collections

30 CSS Badges preview

30 CSS Badges

30 hand-coded CSS badges for status indicators, notifications, membership tiers, live-data displays, and SEO / DevOps / financial dashboards — upload progress, typing indicator, transit line status, Core Web Vitals, ECG heartbeat, CI/CD build pipeline, countdown ring, live price ticker, keycap shortcut, wax seal, conference lanyard, and holographic collectibles. Copy-paste HTML and CSS, WCAG 2.2 accessible, MIT licensed.

20 CSS Banners & Alerts preview

20 CSS Banners & Alerts

20 hand-coded CSS banner and alert bars for e-commerce storefronts, SaaS dashboards, marketing sites, and compliance-bound products. Covers GDPR cookie consent with a real preferences panel, promo bars with countdown timers, free-shipping threshold progress, app-install smart banners, maintenance and incident status bars, newsletter signup bars, age verification gates, limited-stock urgency banners, live event announcements, browser upgrade notices, geo and currency switchers, sticky top announcement bars, bottom cookie bars, inline form validation alerts, icon-aligned alert banners, left-border accent alerts, diagonal stripe promos, full-width hero banners, animated gradient border alerts, and a text-wrapping laboratory that shows five strategies for the unbreakable string that breaks more banners than anything else. Every bar is scoped under a .ba-NN prefix for no-collision pasting, guards prefers-reduced-motion, carries the correct aria-live and role semantics, and ports unchanged to React, Vue, Svelte, Astro, Next.js and Tailwind.

25 CSS Blockquotes preview

25 CSS Blockquotes

25 CSS blockquotes for article pull quotes, testimonial cards, docs callout admonitions, statement banners, and social-post embeds. Covers giant decorative quotation marks, minimalist hairline and classic left borders, animated gradient and glowing neon borders, glassmorphism and aurora backgrounds, brutalist cards, speech bubbles, thick accent rails, highlighted mark text, newspaper multi-column layouts, an expandable read-more disclosure, a scroll-snap testimonial slider, and one-click copy.

Search CodeFronts

Loading…