In this tutorial, you’ll learn how to create dynamic comic book speech bubbles using just CSS. We’ll explore techniques like using shapes, borders, and shadows to mimic the look of speech bubbles straight out of a comic. Whether you’re building a playful website or adding unique touches to your UI, these easy-to-implement designs will bring your text to life with a fun and engaging comic book style.
<!DOCTYPE html>
<html lang="en">
<head>
<style>
@import url('https://fonts.googleapis.com/css2?family=Bangers&family=Bowlby+One+SC&family=Luckiest+Guy&family=Sigmar&display=swap');
:root {
--font-bowlby: "Bowlby One SC", sans-serif;
--font-bangers: "Bangers", sans-serif;
--font-luckiest: "Luckiest Guy", sans-serif;
--font-sigmar: "Sigmar", sans-serif;
--black: #222;
}
body {
margin: 0;
padding: 0;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: radial-gradient(circle at 51% 0%, #8BC34A80 60%, #2196F380), #fff;
background: var(--black);
}
body * {
box-sizing: border-box;
}
body:before {
bottom: 5vmin;
color: #fff2;
font-family: Arial, Helvetica, sans-serif;
font-size: 1.5vmin;
width: 100%;
text-align: center;
filter: none;
}
*:before,
*:after {
content: "";
position: absolute;
box-sizing: border-box;
}
.wrapper {
display: grid;
grid-gap: 1.1vmin;
grid-template-columns: repeat(2, 1fr);
grid-auto-rows: 1fr;
width: 80vmin;
height: 80vmin;
}
.item {
width: auto;
min-height: 0;
perspective: 80vmin;
}
.item:nth-child(1) {
grid-column: 1 / span 2;
grid-row: 1 / 25;
clip-path: polygon(0 0, 100% 0, 100% 72%, 0 100%);
background:
radial-gradient(circle at 60% 50%, #ff4a4a 0 4vmin, #fff0 10vmin 100%),
repeating-conic-gradient(from 0deg at 60% 50%, #ff4a4a 0 6deg, #ed3535 0 12deg);
}
.item:nth-child(2) {
grid-column: 1 / span 1;
grid-row: 22 / 45;
clip-path: polygon(0 15%, 0 100%, 88.5% 87%, 100% 2%);
background: linear-gradient(45deg, #2196F3, #65b9fb);
}
.item:nth-child(3) {
grid-column: 2 / span 1;
grid-row: 18 / 45;
clip-path: polygon(15% 17%, 4% 98%, 100% 100%, 100% 4%);
margin-left: -15%;
background:
radial-gradient(circle at 100% 100%, #8BC34A 0 4vmin, #fff0 10vmin 100%),
repeating-conic-gradient(from 0deg at 100% 100%, #8BC34A 0deg 6deg, #6e9b3a 6.1deg 12deg, #8BC34A 12.1deg), #8BC34A;
}
.item:nth-child(4) {
grid-column: 1 / span 2;
grid-row: 70 / 41;
clip-path: polygon(43.25% 6%, 42.65% 14%, 100% 16%, 100% 100%, 0 100%, 0 16%);
background: #ffa700;
}
.item:before {
width: 100%;
height: 100%;
}
.item:nth-child(2):before {
background:
linear-gradient(-60deg, #777, #000),
repeating-linear-gradient(30deg, #000, #999, #000 5%),
repeating-linear-gradient(-60deg, #000, #999, #000 5%);
background-blend-mode: screen, difference;
filter: contrast(20) blur(0.5px);
mix-blend-mode: soft-light;
}
.item:nth-child(3):before {
background: repeating-conic-gradient(#fff7 0%, #fff0 .0002%, #fff0 .004%, #fff0 .007%);
filter: blur(0.75px)
}
.item:nth-child(4):before {
background-blend-mode: overlay;
filter: contrast(20) blur(2px);
mix-blend-mode: screen;
background:
linear-gradient(0deg, #999, #000),
repeating-conic-gradient(#ddd, #000 12.5% 37.5%, #ddd 50%) 0 / 0.5em 0.5em;
}
.bubble {
background: #fff;
width: 30vmin;
height: 15vmin;
border-radius: 1vmin;
margin-left: 3vmin;
margin-top: 5vmin;
border: 0.4vmin solid var(--black);
transform: rotate(2deg);
position: absolute;
}
.bubble:after {
background: radial-gradient(circle at 140% 35%, #fff0 3vmin, var(--black) calc(3vmin + 1px) calc(3.35vmin), #fff calc(3.35vmin + 1px) 100%);
width: 4vmin;
height: 4vmin;
bottom: -3.65vmin;
left: 2vmin;
border-radius: 0 0 0% 100%;
border-left: 0.4vmin solid var(--black);
border-bottom: 0.4vmin solid var(--black);
border-right: 1px solid #fff0;
border-top: 1px solid #fff0;
background-repeat: no-repeat;
}
.item:nth-child(2) .bubble {
border-radius: 100%;
width: 23vmin;
height: 17vmin;
margin-left: 14vmin;
margin-top: 4vmin;
}
.item:nth-child(2) .bubble:after {
transform: rotateY(0deg) rotate(37deg);
left: 0vmin;
border-radius: 0;
border-bottom: 0;
border-right: 0;
background: linear-gradient(125deg, #fff 0 2.5vmin, var(--black) calc(2.5vmin + 1px) 2.8vmin, #fff0 calc(2.8vmin + 1px) 100%);
bottom: -2.5vmin;
width: 5vmin;
height: 5vmin;
}
.item:nth-child(3) .bubble {
border-radius: 1%;
width: 36vmin;
height: 14vmin;
margin-top: 8vmin;
transform: rotateY(-45deg) skewY(-5deg);
border-left-width: 0.6vmin;
}
.item:nth-child(3) .bubble:after {
border-radius: 0;
border-bottom: 0;
border-right: 0;
background: linear-gradient(125deg, #fff 0 2.5vmin, var(--black) calc(2.5vmin + 1px) 2.8vmin, #fff0 calc(2.8vmin + 1px) 100%);
right: 5vmin;
bottom: -4.75vmin;
width: 5vmin;
height: 5vmin;
left: inherit;
}
.item:nth-child(4) .bubble {
border-radius: 100%;
top: 4vmin;
left: 1.25vmin;
transform: rotate(-4deg);
width: 32vmin;
height: 15vmin;
}
.item:nth-child(4) .bubble:after,
.item:nth-child(4) .bubble:before {
width: 8vmin;
margin-left: 28vmin;
bottom: -1.5vmin;
background: radial-gradient(circle at 105% 25%, #fff0 3vmin, var(--black) calc(3vmin + 1px) calc(3.3vmin), #fff calc(3.3vmin + 1px) 100%);
border-top: 1px solid #fff0;
border-right: 1px solid #fff0;
transform: rotate(-20deg);
background-repeat: no-repeat;
}
.item:nth-child(4) .bubble:nth-child(1):after {
display: none;
}
.item:nth-child(4) .bubble:before {
transform: rotateY(0deg) rotate(12deg);
left: -22vmin;
border-radius: 0;
border-bottom: 0;
border-left: 0.35vmin solid var(--black);
border-right: 0;
background: linear-gradient(125deg, #fff 0 3vmin, var(--black) calc(3vmin + 1px) 3.35vmin, #fff0 calc(3.35vmin + 1px) 100%);
bottom: -5.25vmin;
width: 6vmin;
height: 6vmin;
}
.item:nth-child(4) .bubble + .bubble {
width: 42vmin;
height: 20vmin;
left: 34.5vmin;
}
.item:nth-child(4) .bubble + .bubble:before {
transform: rotate(15deg);
bottom: -3vmin;
left: -23vmin;
}
.item:nth-child(4) .bubble + .bubble:after {
left: -35vmin;
bottom: 9.5vmin;
background: radial-gradient(circle at 80% -45%, #fff0 4vmin, var(--black) calc(4vmin + 1px) calc(4.3vmin), #fff calc(4.3vmin + 1px) 100%);
border-right: 0 solid #fff0;
border-top: 0 solid #fff0;
background-repeat: no-repeat;
border: 0;
border-bottom: 0.35vmin solid var(--black);
border-left: 0.35vmin solid var(--black);
border-right: 1px solid #fff0;
border-top: 1px solid #fff0;
}
.splash {
width: 28vmin;
height: 16vmin;
left: 42vmin;
top: 2.9vmin;
position: relative;
display: flex;
align-items: center;
justify-content: center;
filter: drop-shadow(0 0 0.1vmin var(--black)) drop-shadow(0 0 0.1vmin var(--black)) drop-shadow(0 0 0 var(--black)) drop-shadow(0 0 0 var(--black)) drop-shadow(0 0 0 var(--black)) drop-shadow(0 0 0 var(--black)) drop-shadow(0 0 0 var(--black));
}
.splash:before {
width: 100%;
height: 100%;
background: #fff;
clip-path: polygon(0% 0%, 27% 24%, 28% 0%, 46% 24%, 60% 0%, 71% 28%, 99% 1%, 83% 30%, 99% 28%, 84% 40%, 100% 51%, 82% 60%, 100% 69%, 78% 73%, 100% 100%, 66% 79%, 61% 100%, 45% 78%, 33% 100%, 28% 81%, 0% 100%, 21% 65%, 0% 61%, 24% 47%, 0% 32%, 15% 28%);
}
.splash:after {
right: -14vmin;
top: 1.25vmin;
width: 80%;
height: 70%;
transform: rotate(-14deg);
background: #fff;
clip-path: polygon(0% 35%, 53% 33%, 44% 51%, 100% 32%, 23% 73%, 33% 45%, 0% 50%);
}
input[type="text"] {
border: 0;
width: 100%;
height: 100%;
font-family: var(--font-luckiest);
font-size: 10vmin;
letter-spacing: -0.2vmin;
margin-top: -1vmin;
background: #fff0;
outline: none;
color: #9b63ff;
text-align: center;
text-shadow: 0 0 0.25vmin var(--black), 0.25vmin 0.25vmin 0 var(--black), -0.25vmin 0.25vmin 0 var(--black), -0.25vmin -0.25vmin 0 var(--black), 0.25vmin -0.25vmin 0 var(--black);
display: flex;
align-items: center;
justify-content: center;
}
.splash input {
z-index: 2;
font-size: 9vmin;
margin-top: 1vmin;
color: #ffc274;
letter-spacing: 0;
text-shadow: 0.25vmin 0.25vmin 0 var(--black), -0.25vmin 0.25vmin 0 var(--black), -0.25vmin -0.25vmin 0 var(--black), 0.25vmin -0.25vmin 0 var(--black);
font-family: var(--font-bangers);
}
.item:nth-child(1) .bubble input {
margin-top: 0vmin;
}
.item:nth-child(2) .bubble input {
color: #8bc34a;
font-family: var(--font-bowlby);
font-size: 9vmin;
margin-top: 0vmin;
text-shadow:
0 0 0.5vmin var(--black),
0.25vmin 0.25vmin 0 var(--black),
-0.25vmin 0.25vmin 0 var(--black),
-0.25vmin -0.25vmin 0 var(--black),
0.25vmin -0.25vmin 0 var(--black),
0.5vmin 0.25vmin 0.125vmin var(--black);
}
.item:nth-child(3) .bubble input {
color: #ff4a4a;
font-family: var(--font-luckiest);
text-shadow:
1vmin 1vmin 0.1vmin #2223,
0 0 0.25vmin var(--black),
0.25vmin 0.25vmin 0 var(--black),
-0.25vmin 0.25vmin 0 var(--black),
-0.25vmin -0.25vmin 0 var(--black),
0.25vmin -0.25vmin 0 var(--black);
font-size: 12vmin;
}
.item:nth-child(4) .bubble input {
font-size: 13vmin;
line-height: 20vmin;
font-family: var(--font-bangers);
color: #55ceff;
text-shadow:
0.5vmin .5vmin 0.1vmin #222,
0 0 0.25vmin var(--black),
0.25vmin 0.25vmin 0 var(--black),
-0.25vmin 0.25vmin 0 var(--black),
-0.25vmin -0.25vmin 0 var(--black),
0.25vmin -0.25vmin 0 var(--black);
}
.item:nth-child(4) .bubble + .bubble input {
color: #e75dff;
font-family: var(--font-luckiest);
font-style: italic;
}
</style>
</head>
<body>
<div class="wrapper">
<div class="item">
<div class="bubble"><input type="text" value="Hey!"></div>
<div class="splash"><input type="text" value="Share!"></div>
</div>
<div class="item">
<div class="bubble"><input type="text" value="This"></div>
</div>
<div class="item">
<div class="bubble"><input type="text" value="With!"></div>
</div>
<div class="item">
<div class="bubble"><input type="text" value="Your!"></div>
<div class="bubble"><input type="text" value="Gang!"></div>
</div>
</div>
</body>
</html>
Credit: https://codepen.io/josetxu/pen/eYoKONP