CSS Speech Bubble Generator

A chat bubble in one element, tail included. Move the tail along the edge, resize it, and copy the CSS - no pseudo-elements and no second box to keep in sync.

About the CSS speech bubble

The conventional speech bubble is two things pretending to be one: a rounded box, and a small triangle glued to its edge with ::after. It works, but the tail knows nothing about the box. Change the corner radius, the background, the border, or put a gradient behind it, and the tail has to be fixed separately - or it stays the old color and gives the trick away.

Clipping the whole bubble, tail included, from a single element solves that. The tail is part of the outline, so it inherits the background automatically. There is one element in the DOM, one background to change, and nothing to keep in sync.

The code

.shape {
  clip-path: polygon(
      0% 14%, 0.69% 9.67%, 2.67% 5.77%, 5.77% 2.67%, 9.67% 0.69%, 14% 0%,
      86% 0%, 90.33% 0.69%, 94.23% 2.67%, 97.33% 5.77%, 99.31% 9.67%,
      100% 14%, 100% 64%, 99.31% 68.33%, 97.33% 72.23%, 94.23% 75.33%,
      90.33% 77.31%, 86% 78%, 35% 78%, 28% 100%, 21% 78%, 14% 78%,
      9.67% 77.31%, 5.77% 75.33%, 2.67% 72.23%, 0.69% 68.33%, 0% 64%
  );
  width: 240px;
  height: 240px;
  background: #0d6efd;
}

Most of those points are the rounded corners - a curve in polygon() is just a lot of short straight edges, and the Roundness setting decides how many. The interesting part is the middle of the bottom edge: 35% 78%, 28% 100%, 21% 78%. That is the tail, dropping from the bubble's bottom edge at 78% of the height down to the very bottom and back up. Slide those three x-values together and the tail moves along the edge.

Keeping the text inside

This is the one thing to watch. clip-path clips children too, so text will disappear into the corners and the tail unless you leave room for it. Two habits cover almost every case:

  • Pad past the tail. If the tail takes the bottom 22% of the box, the bubble needs at least that much bottom padding - otherwise the last line of text hangs into it.
  • Let the bubble grow. Give it a min-height rather than a fixed height, so a long message makes it taller instead of clipping the text.

Which bubble for which job

  • Speech bubble - tail bottom-left, the classic incoming chat message. The one on this page.
  • Bubble right tail - the same shape mirrored, for the messages you sent.
  • Bubble tail up - tail on the top edge, for a dropdown or a popover hanging below its trigger.
  • Tail left and tail right - the tail on a side edge, for tooltips beside an element.
  • Square bubble - no corner rounding, a much shorter rule, and the right look for a terminal or code-style callout.

Frequently asked questions

Why is my last line of text cut off by the tail?

Because the tail is inside the element, not attached below it. The bubble's box includes the tail, so the bottom of the box is the tip of the tail. Add bottom padding at least as tall as the tail - with the default shape that is a little over a fifth of the height.

Can the bubble have a border?

Yes, from the Border control - not as a real border, which is clipped away with the rest of the box, but as two layers on one element: the bubble in the border color with the same bubble, inset, on a ::after above it. Watch the tail. It is much thinner than the body, so at a wide border width the fill stops before it and the whole tail turns the border color. That is what an inset outline does to a narrow feature; keep the border modest if you want a hollow tail.

How do I make the bubble grow with its content?

Do not set a height at all. A clipped element lays out exactly like any other, so with padding and a min-height the bubble grows down as text wraps, and the percentage-based tail scales with it. The tail does get proportionally taller in a tall bubble - if that bothers you, use a fixed-height footer row inside instead.

Is a clipped bubble better than the ::after triangle?

For anything with a gradient, an image or a changing background, yes: there is only one thing to color. The pseudo-element approach still wins on one point - the body of the bubble is an unclipped box, so it can have a real border and a real box-shadow. The tail then needs a second, slightly larger triangle behind the first in the border color, which is exactly as tedious as it sounds.

Will a screen reader announce the shape?

No, and it should not. clip-path is purely visual, so the bubble is still whatever element you put it on. Mark up a chat message as a list item or an article as usual, and the shape stays out of the accessibility tree entirely.

More CSS shapes