Learn how to make a clean, scalable speech bubble with CSS using two reliable techniques. This tutorial includes a live preview, copy‑paste CSS, and tips to center content inside the bubble. Examples below use an accessible default color.
Live Preview: Speech Bubble
Method 1: Using Borders
/* Bubble with rounded box + triangle tail (bottom-left) */
.speech-bubble {
--bg: #b03030; /* darker for better contrast with white text */
--radius: 12px;
display: inline-block;
max-width: 28ch;
padding: 12px 16px;
background: var(--bg);
color: #fff;
border-radius: var(--radius);
position: relative;
}
/* Tail (fill) */
.speech-bubble::after {
content: "";
position: absolute;
left: 24px; /* move left/right to reposition the tail */
bottom: -12px; /* places the tail under the bubble */
width: 0;
height: 0;
border-left: 12px solid transparent;
border-right: 12px solid transparent;
border-top: 12px solid var(--bg); /* points tail downward */
}
/* Optional: add a border that also outlines the tail
- Set a border on the bubble box
- Use a second pseudo-element behind the fill triangle */
.speech-bubble {
--border: #7a1f1f;
--border-w: 2px;
border: var(--border-w) solid var(--border);
z-index: 0; /* create stacking context for z-index layering below */
}
.speech-bubble::before {
content: "";
position: absolute;
left: 24px;
bottom: calc(-12px - var(--border-w));
width: 0;
height: 0;
border-left: calc(12px + var(--border-w)) solid transparent;
border-right: calc(12px + var(--border-w)) solid transparent;
border-top: calc(12px + var(--border-w)) solid var(--border);
z-index: 0; /* sits beneath the fill triangle */
}
.speech-bubble::after {
z-index: 1; /* keep the fill triangle on top */
}
/* Optional: outline the whole shape (box + tail) with a drop shadow.
Note: drop-shadow is not a true border but can add contrast. */
.speech-bubble {
filter: drop-shadow(0 0 1px rgba(0,0,0,.25));
}
Method 2: Using clip-path (Single Element)
/* One element speech bubble with a built-in tail (bottom-left) */
.speech-bubble-clip {
width: 150px;
height: 110px;
background: #b03030;
color: #fff;
/* Rectangle with a small triangular tail carved at bottom-left */
clip-path: polygon(
0% 0%,
100% 0%,
100% 70%,
38% 70%,
30% 100%,
27% 70%,
0% 70%
);
/* Optional: rounding for the main box (tail stays sharp in most browsers) */
border-radius: 12px;
/* Optional: add contrast around the shape */
filter: drop-shadow(0 0 1px rgba(0,0,0,.25));
}
/* Note: you can't apply a true CSS border that matches a custom clip-path.
Options:
- Layer a second element/pseudo-element behind with the same clip-path
and slightly larger box to simulate a border.
- Use filter: drop-shadow() for an outline-like effect.
- Switch to SVG if you need a real stroked (bordered) shape. */
How This Works
The border method draws a normal rounded rectangle for the bubble and uses a pseudo-element whose borders form a CSS triangle for the tail. Coloring only one border and making the others transparent creates a triangular point that you can position anywhere around the bubble.
The clip-path method cuts the element itself into a custom polygon that combines the bubble and tail in one layer. It’s concise and scalable but offers less control over rounded corners on the tail and may require modern browser support.
How to Center Content Inside a Speech Bubble
The easiest way is to turn the bubble into a Flexbox or Grid container so its children are perfectly centered both horizontally and vertically.
Method 1: Use CSS Flexbox
.speech-bubble,
.speech-bubble-clip {
display: flex;
justify-content: center;
align-items: center;
text-align: center; /* helps for multi-line text */
}
Method 2: Use CSS Grid
.speech-bubble,
.speech-bubble-clip {
display: grid;
place-items: center;
text-align: center;
}
When to Use Each Shape Method
Use the border triangle method when you need a responsive bubble with rounded corners, text padding, and broad browser support. Use clip-path for a single-element solution, precise custom shapes, or decorative bubbles where you don’t need a separate tail element or rounded tail corners.
Quick Customizations
- Change color via the background or CSS variable (e.g., –bg).
- Reposition the tail by tweaking left/right on the pseudo-element (border method) or adjusting the polygon coordinates (clip-path).
- To add a border/outline:
- Border method: create a bordered tail by stacking a larger “border” triangle behind the filled tail with a second pseudo-element, or add a subtle filter: drop-shadow() to outline the whole shape.
- clip-path method: you can’t apply a true CSS border that follows the custom shape. Use an extra layered element/pseudo-element with the same clip-path, filter: drop-shadow(), or switch to SVG for a stroked shape.
- Increase border-radius for softer corners.
Accessibility & SEO
Provide sufficient color contrast between the bubble and text. The original example (#cd5454 with white) is about 4.2:1 and fails WCAG AA for normal text. Fix by either darkening the background (e.g., #b03030 is ~6.3:1 with white and passes AA for normal text), using black text, or ensuring large text (≥24px regular or ≥18.66px bold) if you keep a lighter background. Also use semantic HTML (e.g., a p or blockquote inside the bubble).