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:
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>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;
}
}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;
}
}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;
}
}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?