Exercise: Creating Pure CSS Speech Bubbles

From time to time in my career, I’ve been asked to make a web UI that looks like a chat app - iMessage, WhatsApp, those kinds of conventions.

The actual layout is fairly simple, but creating a speech-bubble effect using pure CSS requires some neat tricks using pseudo-elements and the triangular border trick we looked at earlier.

We’re going to end up with something that looks like this:

speech-bubbles.html

Basic Speech Bubbles

There’s no specific HTML element for conversation - it’s actually covered in the WHATWG’s HTML living spec section 4.14.3 Conversations as an example of a scenario that isn’t covered by any specific elements.

<!DOCTYPE html>
<html lang="en">
<head>
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Speech Bubbles</title>
</head>
<body>
	<p class="left">
		The numbers all go to eleven. Look, right across the board, eleven, eleven,
		eleven and...
	</p>
	<p class="right">
		Oh, I see. And most amps go up to ten?
	</p>
	<p class="left">
		Exactly.
	</p>
	<p class="right">
		Does that mean it's louder? Is it any louder?
	</p>
	<p class="left">
		Well, it's one louder, isn't it? It's not ten. You see, most blokes, you
		know, will be playing at ten. You're on ten here, all the way up, all the
		way up, all the way up, you're on ten on your guitar. Where can you go from
		there? Where?
	</p>
</body>
</html>
speech01.html

First, we’ll use the left and right classes to apply a border, background colour, and horizontal alignment to the lines of dialogue:

	p {
		border-radius: 1em;
		border: 3px solid #000;
		padding: 1em;
		margin: 1em;
		box-sizing: border-box;
		&.right {
			margin-left: 8em;
		}
		&.left {
			margin-right: 8em;
		}
	}
speech02.html

Next, we’re going to use a ::before pseudo-element to add the first part of the “tail”. We’ll use the same rule for both left and right classes, only overriding it for the specific details which actually differ between the two.

Notice that we’ve also added position: relativeto the p rule for the speech bubbles, so that we can then use absolute positioning on the pseudo-elements to line them up with the border of the paragraph:

	p {
		border-radius: 1em;
		border: 3px solid #000;
		padding: 1em;
		margin: 1em;
		box-sizing: border-box;

		position: relative;

		&.right {
			margin-left: 8em;

			&::before {
				right: 1.5em;
				border-right-width: 2em;
				border-right-color: black;
			}
		}

		&.left {
			margin-right: 8em;

			&::before {
				left: 1.5em;
				border-left-width: 2em;
				border-left-color: black;
			}
		}

		&::before {
			content: ' ';
			position: absolute;
			bottom: -2.5em;
			border-width: 0 0 2.5em 0;
			border-color: transparent;
			border-style: solid;
		}
	}
speech03.html

Finally, we’ll use an ::after pseudo-element to draw another triangle; this one’s going to be white, and gives the effect of a hollow tail with an outline around it. We’ll also give the body element 4em of bottom padding, so the final tail doesn’t overflow the viewport and force a scrollbar:

	body { padding-bottom: 4em; }

	p {
		border-radius: 1em;
		border: 3px solid #000;
		padding: 1em;
		margin: 1em;
		box-sizing: border-box;

		position: relative;

		&.right {
			margin-left: 8em;

			&::before {
				right: 1.5em;
				border-right-width: 2em;
				border-right-color: black;
			}

			&::after {
				right: 1.7em;
				border-right-width: 1.6em;
				border-right-color: white;
			}
		}

		&.left {
			margin-right: 8em;

			&::before {
				left: 1.5em;
				border-left-width: 2em;
				border-left-color: black;
			}

			&::after {
				left: 1.7em;
				border-left-width: 1.6em;
				border-left-color: white;
			}
		}

		&::before {
			content: ' ';
			position: absolute;
			bottom: -2.5em;
			border-width: 0 0 2.5em 0;
			border-color: transparent;
			border-style: solid;
		}

		&::after {
			content: ' ';
			position: absolute;
			bottom: -2em;
			border-width: 0 0 2em 0;
			border-color: transparent;
			border-style: solid;
		}
	}
speech04.html

Exercises

Colour Coding

Colour the dialogue, so that each participant has their own colour.

  • How would you do this for a group chat with more than two participants?

Consecutive Comments

Imagine the same person posts multiple comments: how would you only display the speech bubble tail on the final comment in a group?

Avatars

How would you extend this example to include an avatar image next to each participant in a group chat?


This site uses Just the Docs, a documentation theme for Jekyll.