Type something to search...
How to Add Text Shadows Without Hurting Readability?

How to Add Text Shadows Without Hurting Readability?

The client's new homepage has a full-bleed photo of a sunlit beach with a white headline set right across the sky. On the designer's mock-up the sky is deep blue and the headline is crisp. On the live site, the image crops differently on mobile, the headline lands over bright sand and white clouds, and half the letters vanish. Someone adds text-shadow: 2px 2px 8px black and now the headline looks smudged, like it is printed on a fogged-up window. The text is technically more visible, but it is harder to read than before.

Text shadows can rescue legibility or ruin it. This article explains how text-shadow works, what makes a shadow help or hurt, the specific recipes that work for text on images, halos, and outlines, how shadows relate to WCAG contrast requirements, and the accessibility and performance details to check before you ship.

What Is text-shadow?

text-shadow paints one or more copies of the text's glyphs behind the text itself, each offset, blurred, and coloured independently. The syntax for one shadow is:

text-shadow: <offset-x> <offset-y> <blur-radius> <color>;
.headline {
  text-shadow: 0 1px 2px rgb(0 0 0 / 0.4);
}
ComponentMeaningNotes
offset-xHorizontal shift; positive moves rightRequired
offset-yVertical shift; positive moves downRequired
blur-radiusHow soft the edge is; 0 is a hard copyOptional, defaults to 0, cannot be negative
colorShadow colourOptional, defaults to currentColor

You can list multiple shadows separated by commas. The first one is painted on top. Unlike box-shadow, text-shadow has no spread value and no inset keyword.

Why Shadows Often Hurt Readability

Readers recognize words by their shapes, and those shapes depend on crisp edges and clear counters (the enclosed spaces in letters like a, e, and o). A shadow adds a second set of edges next to the real ones. When that second set is too close in tone, too far offset, or too blurry, the eye has to work harder to separate the letter from its ghost.

The common failure patterns:

  1. Large offsets. A 4px 4px shadow on 16px text creates a visible double image. Offsets beyond about 10% of the font size start to look like a misprint.
  2. Heavy blur at low contrast. A wide, soft shadow behind light text on a mid-tone background smears the letter edges without adding real contrast.
  3. Coloured shadows. Shadows in a saturated colour clash with the text colour and create vibration at the edges, especially red against blue.
  4. Shadows on body text. Any shadow on long passages adds fatigue. Readability depends on clean, consistent letterforms over hundreds of words.
  5. Glow in dark mode. A light glow around light text on a dark background makes the text look blurry and reduces perceived sharpness. Dark interfaces need contrast from the text colour itself, not a halo.

The underlying principle is that a shadow should either be barely noticeable (adding a touch of separation) or functional (creating a solid backing that increases contrast). Anything in between usually makes text worse.

Recipes That Help

Subtle Lift for Display Text

On a plain background, a very small, soft, low-opacity shadow can give a headline a sense of depth without affecting legibility:

.hero-title {
  text-shadow: 0 1px 1px rgb(0 0 0 / 0.15);
}

Keep the offset at 1–2px, the blur at 1–3px, and the opacity at 10–25%. If a reader can consciously see the shadow, it is probably too strong.

Contrast Halo for Text on Images

When text sits on a busy or unpredictable image, a tight, dark halo creates a thin band of contrast around each letter. The trick is to use zero offset and stack a sharp shadow with a soft one:

.image-caption {
  color: #fff;
  text-shadow:
    0 0 1px rgb(0 0 0 / 0.9),
    0 0 4px rgb(0 0 0 / 0.6),
    0 0 12px rgb(0 0 0 / 0.4);
}

The first layer sharpens the edge, the second forms a narrow halo, and the third darkens the area just around the letters. This works much better than a single offset shadow because the backing is even on every side.

Outlined Text

Several hard shadows offset in different directions produce an outline:

.outlined {
  color: #fff;
  text-shadow:
    -1px -1px 0 #111,
    1px -1px 0 #111,
    -1px 1px 0 #111,
    1px 1px 0 #111;
}

This is the classic subtitle and meme style. It is legible on almost any background but looks heavy, and the diagonals leave small gaps at larger sizes. For a cleaner outline on large display text, combine -webkit-text-stroke with paint-order so the stroke sits behind the fill instead of eating into it:

.stroked {
  color: #fff;
  -webkit-text-stroke: 3px #111;
  paint-order: stroke fill;
}

-webkit-text-stroke works in all current major browsers despite its prefix. Support for paint-order on HTML text is newer than its SVG support, so check that it applies in every engine you target; without it, the stroke is centered on the outline and thins the letters.

Letterpress and Embossed Effects

A one-pixel light shadow below dark text on a mid-tone background imitates letterpress. It was popular in skeuomorphic design and still appears in some interfaces:

.letterpress {
  color: #4b5563;
  text-shadow: 0 1px 0 rgb(255 255 255 / 0.6);
}

Use it only at large sizes on solid backgrounds. On small text, it blurs the baseline.

Better Alternatives for Text on Images

A text shadow is often not the best way to make text readable on a photo. These options give more reliable contrast:

TechniqueHow it worksReliability
Gradient scrimA dark gradient overlay behind the text areaHigh
Solid overlayA semi-transparent tint over the whole imageHigh
Text boxA solid or translucent panel behind the textVery high
Image choice and cropChoose images with calm areas where text sitsDepends on content
Halo text-shadowTight multi-layer shadow around lettersMedium
Offset drop shadowA single offset shadowLow

A gradient scrim is usually the most effective and least visible solution:

.hero {
  position: relative;
  isolation: isolate;
}

.hero img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
}

.hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to top, rgb(0 0 0 / 0.75) 0%, rgb(0 0 0 / 0.35) 45%, transparent 75%);
}

.hero h1 {
  color: #fff;
  text-shadow: 0 1px 3px rgb(0 0 0 / 0.35);
}

The scrim does the heavy lifting for contrast. The small shadow on the heading only smooths out any remaining bright spots. This combination holds up across crops and screen sizes.

Shadows and WCAG Contrast

WCAG 1.4.3 requires a contrast ratio of at least 4.5:1 for normal text and 3:1 for large text (24px and above, or 18.66px and above when bold). WCAG 1.4.11 separately requires 3:1 for meaningful graphics and UI components.

WCAG does not provide a formula for text shadows. Contrast is measured between the text colour and the colour directly behind it. In practice:

  • A solid, unblurred outline or a dense halo effectively becomes the background the letters sit on. If the halo is dark and opaque enough, you can measure the text colour against the halo colour.
  • A soft, low-opacity shadow does not reliably change the background. Measure against the underlying image or colour as if the shadow were not there.
  • Text over images must pass at its weakest point. Find the brightest area of the image that the text can overlap at any breakpoint, and measure there.

Contrast checkers that sample a single pixel will not catch problems over photos. Use a tool that lets you pick the background colour from the actual rendered area, and check every breakpoint, because object-fit: cover crops differently on narrow screens. For the detailed rules, see WCAG contrast requirements for text.

Accessibility Considerations Beyond Contrast

Forced Colours Mode

In Windows Contrast Themes and other forced colours modes, browsers remove text shadows and replace text and background colours with the user's palette. That is good for readers, but it means your shadow cannot be the only thing keeping text visible. If the design depends on a shadow to separate white text from a white area of an image, test what happens when the shadow is removed. Background images are typically kept in forced colours mode, so add a fallback backing:

@media (forced-colors: active) {
  .hero h1 {
    background-color: Canvas;
    color: CanvasText;
    padding-inline: 0.25em;
  }
}

Low Vision and Magnification

People using screen magnifiers see shadows enlarged too. A soft 8px blur becomes a big grey smear at 400% zoom, which makes edges harder to find. Tight shadows and scrims scale more gracefully. The guide on how to test typography for low-vision users covers magnifier testing in detail.

Dyslexia and Visual Stress

Some readers with dyslexia or visual stress find double edges and glows particularly tiring. Keep shadows off running text entirely, and keep display shadows subtle. Overall guidance on accessible type is in how to make website typography accessible.

Shadows in Light and Dark Themes

A shadow that works in a light theme rarely works in a dark one. Define shadows as custom properties so each theme can set its own, or remove them:

:root {
  --text-shadow-display: 0 1px 2px rgb(0 0 0 / 0.15);
}

@media (prefers-color-scheme: dark) {
  :root {
    --text-shadow-display: none;
  }
}

.display {
  text-shadow: var(--text-shadow-display);
}

In dark mode, rely on text colour contrast instead of glows. Light shadows around light text reduce sharpness. If you are tuning type for dark backgrounds, see how to choose fonts for dark mode.

Text Shadows in Tailwind CSS v4

Tailwind v4.1 added built-in text shadow utilities: text-shadow-2xs, text-shadow-xs, text-shadow-sm, text-shadow-md, text-shadow-lg, and text-shadow-none, along with colour utilities like text-shadow-black/40.

<section class="relative isolate">
  <img src="/images/hero.webp" alt="" class="absolute inset-0 -z-20 h-full w-full object-cover" />
  <div class="absolute inset-0 -z-10 bg-linear-to-t from-black/75 via-black/30 to-transparent"></div>
  <h1 class="text-5xl font-bold text-white text-shadow-sm text-shadow-black/30">Explore the coast</h1>
</section>

You can define custom shadows as theme tokens in your CSS entry file:

@import "tailwindcss";

@theme {
  --text-shadow-halo: 0 0 1px rgb(0 0 0 / 0.9), 0 0 4px rgb(0 0 0 / 0.6), 0 0 12px rgb(0 0 0 / 0.4);
}

That generates a text-shadow-halo utility.

Performance and Print

Text shadows are rendered on the GPU in modern browsers and are cheap for static text. Costs rise in a few cases:

  • Large blur radii on large areas of text, such as a 40px blur on a full-screen headline, increase paint time.
  • Animated shadows repaint on every frame. Animating text-shadow on a single element is fine; animating it on dozens of elements while scrolling is not. Respect prefers-reduced-motion for any shadow animation.
  • Many stacked layers. Ten-layer "3D" text effects multiply paint work and are hard to read anyway.

For print, shadows usually look muddy and waste toner. Remove them:

@media print {
  * {
    text-shadow: none !important;
  }
}

A Quick Checklist

  1. Is the shadow necessary, or would a scrim, overlay, or text panel work better?
  2. Is the offset at most about 10% of the font size, ideally zero for halos?
  3. Is the shadow colour neutral and at least as dark (or light) as needed for contrast?
  4. Does the text pass WCAG contrast at its worst position, at every breakpoint?
  5. Is the text still readable with shadows removed, as in forced colours mode?
  6. Is body text free of shadows?
  7. Does the dark theme use different or no shadows?

Text Shadow FAQ

Only in specific cases. A tight, dark halo can improve legibility of light text on busy images, and a very subtle shadow can add depth to headlines. Large offsets, heavy blurs, and coloured shadows usually make text harder to read by blurring letter edges.

WCAG does not define a calculation for shadows. A solid, dense outline or halo can reasonably be treated as the background the text sits on, but a soft or semi-transparent shadow should not be relied on. Measure contrast against the underlying background at its worst point.

Use a zero offset halo made of two or three stacked shadows, one sharp and dark, one slightly blurred, and one wider and lighter. Better still, add a dark gradient scrim behind the text and use only a small shadow to smooth out bright spots.

Usually not. Glows around light text on dark backgrounds reduce sharpness and make text look blurry. Get contrast from the text and background colours instead, and remove or replace light-theme shadows in your dark theme.

In forced colours modes such as Windows Contrast Themes, browsers remove text shadows and apply the user's chosen colours. Make sure text remains readable without the shadow, especially when it sits over a background image.

For large display text, a stroke combined with paint order set to stroke before fill produces a cleaner, more even outline than stacked shadows. For small text, outlines of any kind hurt readability, so avoid them.

Conclusion

A text shadow is a tool for separating letters from their background, and it only works when it does that cleanly. Subtle shadows on display text add depth, tight halos make light text survive busy images, and hard outlines suit subtitles and bold display styles. Big offsets, wide blurs, coloured shadows, glows in dark mode, and shadows on body text all add visual noise that slows reading.

When text sits over photography, solve contrast with a scrim, overlay, or text panel first and use the shadow as a finishing touch. Then verify the result: measure contrast at the worst point of the image on every breakpoint, check that the text survives forced colours mode with no shadow at all, and keep running text shadow-free. Your headlines will look polished without asking readers to squint.

Here are some useful references for going deeper on text shadows and readability:

  1. MDN Web Docs: text-shadow — syntax, multiple shadows, and browser compatibility.
  2. W3C WAI: Understanding SC 1.4.3 Contrast (Minimum) — contrast requirements for normal and large text, including text over images.
  3. MDN Web Docs: forced-colors — which properties browsers override in forced colours mode.
  4. Tailwind CSS: text-shadow — the built-in text shadow utilities added in Tailwind v4.1.
  5. CSS-Tricks: text-shadow almanac entry — examples of common shadow effects.
Share :

Related Posts

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

Ascenders, Descenders, and Baselines: The Anatomy of a Letterform

You align an icon next to a button label and it looks a pixel or two too high, no matter how you adjust vertical-align. You set overflow: hidden

Continue Reading
Are Google Fonts GDPR-Compliant?

Are Google Fonts GDPR-Compliant?

In late 2022, thousands of small business owners in Germany and Austria opened letters demanding a few hundred euros in "damages" because their websi

Continue Reading
How to Audit Web Font Performance with Lighthouse?

How to Audit Web Font Performance with Lighthouse?

A client sends you a screenshot of their PageSpeed Insights report: performance score 61, LCP 3.9 seconds, and a vague list of warnings. They want to

Continue Reading