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-shadow by 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: text plus -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-shadow when you need broad compatibility, you want glow/soft effects, or you like the “sticker outline” style.
  • Use -webkit-text-stroke when 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.