
How to Add Text Shadows Without Hurting Readability?
- Sajjad
- Typography
- 01 Oct, 2026
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);
}
| Component | Meaning | Notes |
|---|---|---|
offset-x | Horizontal shift; positive moves right | Required |
offset-y | Vertical shift; positive moves down | Required |
blur-radius | How soft the edge is; 0 is a hard copy | Optional, defaults to 0, cannot be negative |
color | Shadow colour | Optional, 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:
- Large offsets. A
4px 4pxshadow on 16px text creates a visible double image. Offsets beyond about 10% of the font size start to look like a misprint. - 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.
- Coloured shadows. Shadows in a saturated colour clash with the text colour and create vibration at the edges, especially red against blue.
- Shadows on body text. Any shadow on long passages adds fatigue. Readability depends on clean, consistent letterforms over hundreds of words.
- 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:
| Technique | How it works | Reliability |
|---|---|---|
| Gradient scrim | A dark gradient overlay behind the text area | High |
| Solid overlay | A semi-transparent tint over the whole image | High |
| Text box | A solid or translucent panel behind the text | Very high |
| Image choice and crop | Choose images with calm areas where text sits | Depends on content |
| Halo text-shadow | Tight multi-layer shadow around letters | Medium |
| Offset drop shadow | A single offset shadow | Low |
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-shadowon a single element is fine; animating it on dozens of elements while scrolling is not. Respectprefers-reduced-motionfor 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
- Is the shadow necessary, or would a scrim, overlay, or text panel work better?
- Is the offset at most about 10% of the font size, ideally zero for halos?
- Is the shadow colour neutral and at least as dark (or light) as needed for contrast?
- Does the text pass WCAG contrast at its worst position, at every breakpoint?
- Is the text still readable with shadows removed, as in forced colours mode?
- Is body text free of shadows?
- 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:
- MDN Web Docs: text-shadow — syntax, multiple shadows, and browser compatibility.
- W3C WAI: Understanding SC 1.4.3 Contrast (Minimum) — contrast requirements for normal and large text, including text over images.
- MDN Web Docs: forced-colors — which properties browsers override in forced colours mode.
- Tailwind CSS: text-shadow — the built-in text shadow utilities added in Tailwind v4.1.
- CSS-Tricks: text-shadow almanac entry — examples of common shadow effects.


