What is “CSS text outline”?
CSS doesn’t have a single, universal standard property called text-outline.
In real projects, “outlined text” usually means one of two things:
-
Fake an outline using
text-shadowby stacking multiple shadows around the text. This is widely supported and surprisingly powerful. -
Use a real stroke with
-webkit-text-stroke. It’s widely implemented in modern browsers, but it’s still vendor-prefixed, so you should plan a graceful fallback.
This tutorial focuses on those two techniques, because they cover 99% of “text outline” needs: badges, hero headlines, logos, “sticker” text, and dramatic UI labels.
Text outline shadow basics
The text-shadow property draws a shadow of the text.
The syntax is: x-offset y-offset blur color.
You can also provide multiple shadows, separated by commas.
.title {
text-shadow: 8px 8px 0 rgba(0, 0, 0, 0.25);
}
.title {
text-shadow: 0 10px 20px rgba(0, 0, 0, 0.35);
}
.title {
text-shadow:
2px 2px 0 rgba(0, 0, 0, 0.35),
-2px -2px 0 rgba(255, 255, 255, 0.35);
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 220px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: linear-gradient(135deg, #f4f4f4, #e9e9e9);
}
.title {
font-family: ui-sans-serif, system-ui, sans-serif;
font-size: 56px;
line-height: 1;
margin: 0;
letter-spacing: 0.02em;
color: #111;
}
Outline-ish
On its own, a single shadow doesn’t create a true outline. For an outline, we need to put shadows on multiple sides of the text.
CSS text outline stroke using stacked text-shadows
The classic trick: place sharp shadows (blur = 0) around the text. Four shadows (top-left, top-right, bottom-left, bottom-right) gives a simple outline. Eight shadows (including straight up/down/left/right) looks much more like a stroke.
.outlined {
color: #fff;
text-shadow:
-2px -2px 0 #111,
2px -2px 0 #111,
-2px 2px 0 #111,
2px 2px 0 #111;
}
.outlined {
color: #fff;
text-shadow:
-2px -2px 0 #111,
0px -2px 0 #111,
2px -2px 0 #111,
-2px 0px 0 #111,
2px 0px 0 #111,
-2px 2px 0 #111,
0px 2px 0 #111,
2px 2px 0 #111;
}
.outlined {
color: #111;
text-shadow:
-2px -2px 0 #00c2ff,
0px -2px 0 #00c2ff,
2px -2px 0 #00c2ff,
-2px 0px 0 #00c2ff,
2px 0px 0 #00c2ff,
-2px 2px 0 #00c2ff,
0px 2px 0 #00c2ff,
2px 2px 0 #00c2ff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 240px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: radial-gradient(circle at 30% 20%, #ffe08a, #ff9bc8 55%, #7b7bff);
}
.outlined {
font-family: ui-sans-serif, system-ui, sans-serif;
font-size: 64px;
line-height: 1;
margin: 0;
letter-spacing: 0.02em;
}
Sticker Text
Make outline thickness adjustable
Writing 8 shadows is already a lot. Now imagine doing it again for a thicker outline.
Instead, we’ll store thickness in a CSS variable (--t) and reuse it in each shadow.
.outlined {
--t: 4px;
text-shadow:
calc(var(--t) * -1) calc(var(--t) * -1) 0 #111,
0 calc(var(--t) * -1) 0 #111,
var(--t) calc(var(--t) * -1) 0 #111,
calc(var(--t) * -1) 0 0 #111,
var(--t) 0 0 #111,
calc(var(--t) * -1) var(--t) 0 #111,
0 var(--t) 0 #111,
var(--t) var(--t) 0 #111;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 260px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: linear-gradient(135deg, #f6f6f6, #e8fff0);
}
.outlined {
font-family: ui-sans-serif, system-ui, sans-serif;
font-size: 72px;
line-height: 1;
margin: 0;
letter-spacing: 0.02em;
color: #fff;
}
Adjust Me
CSS text outline color
With the text-shadow approach, the outline “color” is simply the shadow color(s).
You can use any color format: hex, rgb/rgba, hsl/hsla, and even modern color spaces if you’re feeling fancy.
.outlined {
color: #fff;
text-shadow:
-3px -3px 0 #111,
0px -3px 0 #111,
3px -3px 0 #111,
-3px 0px 0 #111,
3px 0px 0 #111,
-3px 3px 0 #111,
0px 3px 0 #111,
3px 3px 0 #111;
}
.outlined {
color: #111;
text-shadow:
-3px -3px 0 rgba(255, 255, 255, 0.9),
0px -3px 0 rgba(255, 255, 255, 0.9),
3px -3px 0 rgba(255, 255, 255, 0.9),
-3px 0px 0 rgba(255, 255, 255, 0.9),
3px 0px 0 rgba(255, 255, 255, 0.9),
-3px 3px 0 rgba(255, 255, 255, 0.9),
0px 3px 0 rgba(255, 255, 255, 0.9),
3px 3px 0 rgba(255, 255, 255, 0.9);
}
.outlined {
color: #fff;
text-shadow:
-3px -3px 0 #00c2ff,
0px -3px 0 #00c2ff,
3px -3px 0 #00c2ff,
-3px 0px 0 #00c2ff,
3px 0px 0 #00c2ff,
-3px 3px 0 #00c2ff,
0px 3px 0 #00c2ff,
3px 3px 0 #00c2ff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 240px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: conic-gradient(from 230deg, #ffdf7d, #ff8ec8, #8fa7ff, #7dffd9, #ffdf7d);
}
.outlined {
font-family: ui-sans-serif, system-ui, sans-serif;
font-size: 64px;
line-height: 1;
margin: 0;
letter-spacing: 0.02em;
}
Color Pop
Learn more about text-shadow in the CSS Text Shadow Interactive Tutorial.
Real outlines with -webkit-text-stroke
If you want a true “vector-like” stroke around text, -webkit-text-stroke is the go-to:
you can set the stroke width and stroke color.
It’s vendor-prefixed, so treat it like a progressive enhancement and keep a fallback ready.
CSS text outline stroke width
.stroked {
-webkit-text-stroke-width: 4px;
-webkit-text-stroke-color: #111;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 260px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: linear-gradient(135deg, #f7f7f7, #e9f0ff);
}
.stroked {
font-family: sans-serif, arial;
font-size: 72px;
line-height: 1;
margin: 0;
letter-spacing: 0.02em;
color: #fff;
}
Real Stroke
CSS text outline color with -webkit-text-stroke
Stroke color is set with -webkit-text-stroke-color. Here are a few common combos:
.stroked {
color: #fff;
-webkit-text-stroke: 4px #111;
}
.stroked {
color: #111;
-webkit-text-stroke: 4px #fff;
}
.stroked {
color: #fff;
-webkit-text-stroke: 4px #00c2ff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 240px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: radial-gradient(circle at 30% 20%, #fff0a8, #ff9bd0 55%, #8ea4ff);
}
.stroked {
font-family: sans-serif, arial;
font-size: 68px;
line-height: 1;
margin: 0;
letter-spacing: 0.02em;
}
Stroke Color
You will notice that -webkit-text-stroke is the shorthand for setting both the stroke width and color in one declaration.
CSS text outline only
“Outline only” means the fill becomes invisible, but the stroke stays.
With -webkit-text-stroke, it’s straightforward: set color: transparent.
With a text-shadow outline, you can also set color: transparent,
but the result can look a bit rougher (still useful as a fallback).
.outline-only {
color: transparent;
-webkit-text-stroke: 4px #111;
}
.outline-only {
color: transparent;
text-shadow:
-2px -2px 0 #111,
0px -2px 0 #111,
2px -2px 0 #111,
-2px 0px 0 #111,
2px 0px 0 #111,
-2px 2px 0 #111,
0px 2px 0 #111,
2px 2px 0 #111;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 260px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: linear-gradient(135deg, #f6fff3, #f3f6ff);
}
.outline-only {
font-family: ui-sans-serif, system-ui, sans-serif;
font-size: 78px;
line-height: 1;
margin: 0;
letter-spacing: 0.02em;
}
Hollow
CSS text outline effect and “outside” looks
People often say “outside outline” when they want the outline to feel like it sits around the text (a sticker/halo vibe), or when they want a soft edge. Two easy recipes:
-
Hard sticker outline: stacked
text-shadow(sharp, blur 0). - Soft halo: add an extra blurred shadow behind the hard outline.
.halo {
--t: 3px;
--blur: 16px;
color: #fff;
-webkit-text-stroke: 3px #111;
text-shadow:
calc(var(--t) * -1) calc(var(--t) * -1) 0 #111,
0 calc(var(--t) * -1) 0 #111,
var(--t) calc(var(--t) * -1) 0 #111,
calc(var(--t) * -1) 0 0 #111,
var(--t) 0 0 #111,
calc(var(--t) * -1) var(--t) 0 #111,
0 var(--t) 0 #111,
var(--t) var(--t) 0 #111,
0 0 var(--blur) rgba(0, 0, 0, 0.45);
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 280px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background-image: url("https://picsum.photos/1200/601");
background-size: cover;
background-position: center;
}
.stage::before {
content: "";
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.15);
}
.stage {
position: relative;
isolation: isolate;
}
.halo {
position: relative;
font-family: sans-serif, arial;
font-size: 74px;
line-height: 1;
margin: 0;
letter-spacing: 0.03em;
}
Halo
CSS text outline shadow variants
Once you accept that “outline via shadows” is really just “a lot of shadows”, you can build fun effects: inner-ish glow, neon, or thick comic text. The key idea is layering: multiple shadows are drawn front-to-back.
.neon {
color: #fff;
text-shadow:
0 0 2px #fff,
0 0 10px #00c2ff,
0 0 20px #00c2ff,
0 0 40px rgba(0, 194, 255, 0.7),
-2px -2px 0 #111,
2px -2px 0 #111,
-2px 2px 0 #111,
2px 2px 0 #111;
}
.comic {
color: #ffe08a;
text-shadow:
-3px -3px 0 #111,
0px -3px 0 #111,
3px -3px 0 #111,
-3px 0px 0 #111,
3px 0px 0 #111,
-3px 3px 0 #111,
0px 3px 0 #111,
3px 3px 0 #111,
10px 10px 0 rgba(0, 0, 0, 0.25);
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 280px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: radial-gradient(circle at 20% 20%, #1c1c1c, #0b0b0b 65%);
}
.neon,
.comic {
font-family: ui-sans-serif, system-ui, sans-serif;
font-size: 72px;
line-height: 1;
margin: 0;
letter-spacing: 0.03em;
}
Neon
Comic
CSS text outline gradient
“Gradient outline” can mean two different things, so we’ll do both:
- Gradient-looking outline by layering colored shadows (works everywhere, but it’s an illusion).
-
Gradient fill + real stroke using
background-clip: textplus-webkit-text-stroke(very clean).
Gradient outline using layered text-shadows
We can “fake” a vertical gradient outline by putting warm shadows on top and cool shadows on bottom. It’s not mathematically perfect, but visually it reads as a gradient edge.
.grad-outline {
color: #111;
text-shadow:
-2px -2px 0 #ff4d9d,
0px -2px 0 #ff4d9d,
2px -2px 0 #ff4d9d,
-2px 0px 0 #8a5cff,
2px 0px 0 #8a5cff,
-2px 2px 0 #00c2ff,
0px 2px 0 #00c2ff,
2px 2px 0 #00c2ff;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 260px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: #f6f6f6;
}
.grad-outline {
font-family: ui-sans-serif, system-ui, sans-serif;
font-size: 74px;
line-height: 1;
margin: 0;
letter-spacing: 0.02em;
}
Gradient Edge
Gradient fill plus stroke using background-clip text
This is the “poster headline” combo:
a gradient fill (via background-clip: text) and a crisp stroke (via -webkit-text-stroke).
The fill becomes transparent so the background gradient shows through the letters.
.hero {
background: linear-gradient(90deg, #ff4d9d, #8a5cff, #00c2ff);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
-webkit-text-stroke: 4px #111;
}
*,
::before,
::after {
box-sizing: border-box;
}
.stage {
min-height: 280px;
display: grid;
place-items: center;
padding: 24px;
border: 3px solid #111;
background: linear-gradient(135deg, #fff7cf, #ffd6ef 55%, #d7e6ff);
}
.hero {
font-family: sans-serif, arial;
font-size: 80px;
line-height: 1;
margin: 0;
letter-spacing: 0.02em;
}
Gradient Fill
When should you use text-shadow vs -webkit-text-stroke?
-
Use
text-shadowwhen you need broad compatibility, you want glow/soft effects, or you like the “sticker outline” style. -
Use
-webkit-text-strokewhen you want the cleanest outline, especially for large type and logos.
Debugging: why your text outline isn’t working
- You outlined the wrong element: outlines apply to the element’s text, not its container. Make sure the class is on the element containing the text node.
-
Your outline is hidden by background: if you used
color: transparent, remember you need a stroke or shadow to actually see anything. -
Your outline looks blurry: for sharp outlines with
text-shadow, keep blur at 0. - Too thin to notice: small fonts need smaller offsets; large fonts often need thicker strokes.
-
Performance: dozens of layered shadows on huge text can be expensive.
If you need a very thick outline, prefer
-webkit-text-stroke.
Accessibility tips for outlined text
- Contrast still matters: outlines help readability, but don’t rely on them to “fix” low-contrast color choices.
- Avoid very-thin strokes on busy photos: use a thicker stroke or add a soft halo layer for separation.
- Keep the text selectable: these techniques keep your text as real text (good for copy/paste and screen readers).
Wrap-up
You now have two reliable toolkits for “CSS text outline”:
stacked text-shadow for flexible, creative effects, and -webkit-text-stroke for clean, true strokes.
