CSS Speech Bubble Generator
Generate a CSS speech bubble with a tail pointing in any direction — built with pure CSS using ::after pseudo-element.
CSS generated is free to use — no attribution required.
How do you use the CSS speech bubble generator?
Set the bubble color and corner radius, then choose which side the tail sits on and how far along that side it points. The preview is a live element, so the bubble grows with its content the way it will in a page. The generated CSS includes the pseudo-element rule for the tail.
How do CSS speech bubbles work?
A speech bubble is one element plus one pseudo-element. The element itself is just a box with padding, a background and a border radius. The tail is an ::after with content: "", absolutely positioned against the bubble and shaped with the border triangle trick.
Because the tail lives on a pseudo-element, the markup stays a single semantic element — no extra wrapper divs, and nothing for a screen reader to announce.
How do you position the tail?
Give the bubble position: relative so the tail has something to anchor to, then place the pseudo-element with position: absolute. For a tail below the bubble, top: 100% puts it directly under the bottom edge and left: 32px slides it along.
To center a tail, use left: 50% plus transform: translateX(-50%). Positioning by percentage alone leaves the tail off-center by half its own width, which is the most common reason a bubble looks subtly wrong.
How do you match the tail to the bubble color?
The tail’s colored border must be the same color as the bubble background, and only the border facing the bubble is colored — the others stay transparent. For a downward tail that means a colored border-top with transparent left and right borders.
Because the tail does not inherit the parent background, any rule that changes the bubble color on hover or in a dark theme has to change the tail’s border color too. Driving both from a single custom property is the maintainable way to do it.
How do you add a border around a speech bubble with a tail?
This is the hard part, and it needs two pseudo-elements. The ::before draws a slightly larger triangle in the border color, and the ::after draws a slightly smaller one in the background color, offset by the border width so it sits on top.
The two stacked triangles produce the illusion of an outlined tail. Get the offset wrong by a pixel and a hairline of the border color shows through the middle, so nudge the inner triangle until the seam disappears at the zoom levels you care about.
How do you make the bubble fit its content?
A bubble should size itself to its text. display: inline-block or a flex parent lets it shrink-wrap, and a max-width in ch units keeps long messages readable — somewhere around 60 characters is a comfortable line length.
Watch the interaction with the tail: if the bubble can become narrower than the tail’s offset, a tail positioned at a fixed left will hang off the end. A percentage position or a min-width avoids that.
Should you use a speech bubble as a tooltip?
Only carefully. A bubble that appears on hover alone is unreachable by keyboard and invisible on touch, so any content that matters must not live there exclusively.
If the bubble is a tooltip, make it appear on :focus-visible as well as :hover, and connect it to its trigger with aria-describedby so it is announced. If it is a chat message rather than a tooltip, it is ordinary content and needs none of that — just make sure the color contrast holds.
Is there a modern alternative to the pseudo-element tail?
Yes — clip-path can cut the bubble and its tail from a single element as one polygon, which avoids the two-pseudo-element dance entirely and animates cleanly.
The trade-off is that a clipped shape cannot have a border or a box-shadow, both of which get clipped away with everything else. If the bubble needs an outline or a drop shadow, the pseudo-element approach is still the practical one, with filter: drop-shadow() for the shadow.
What is the browser compatibility for CSS speech bubbles?
Everything the technique relies on — pseudo-elements, absolute positioning and border triangles — is universally supported and needs no fallback. clip-path, for the single-element variant, works in all current browsers, and an unsupported clip-path degrades to an unclipped rectangle rather than a broken layout.