If you have used box-shadow, you know it supports an inset keyword that pushes the shadow to the inside of an element. It is natural to assume text-shadow works the same way. It does not. The text-shadow property has no inset keyword and never has.

This guide explains why, what inner text shadow effects actually look like, and how to simulate them using layered shadows, SVG filters, and the background-clip technique — with ready-to-copy code for each approach.

Why text-shadow has no inset

The CSS specification for text-shadow defines only four values: horizontal offset, vertical offset, blur radius, and color. There is no fifth value for inset. This is intentional — text glyphs are not boxes. A box has a clear inside and outside boundary that makes inset shadows straightforward to render. Text glyphs are irregular shapes with holes, curves, and variable stroke widths, so "inside" is not geometrically well-defined in the same way.

The CSS working group has discussed inner text shadow effects, but no specification has been finalised. Until it is, the only options are workarounds.

The short answer: Writing text-shadow: inset 2px 2px 4px black does nothing. The browser ignores the inset keyword entirely and the declaration is treated as invalid.

What does an inset text shadow look like?

An inner text shadow gives text a pressed, engraved, or embossed appearance — as if the letters are cut into or raised from a surface. It is popular for:

  • Embossed headings on textured backgrounds
  • Pressed or debossed button labels
  • Retro and letterpress typography effects
  • Neumorphic UI text treatments

The visual effect comes from placing a light shadow on one side and a dark shadow on the opposite side — or using a very dark shadow underneath lighter text to simulate depth. None of these require a true inset implementation.

Technique 1: Layered shadows to fake depth

The most widely supported approach is stacking multiple text-shadow values. By placing a dark shadow slightly offset in one direction and a light shadow in the opposite direction, you create the illusion of the text being pressed inward.

/* Pressed / debossed effect */
.pressed-text {
  color: #1a1d27;
  text-shadow:
    1px 1px 2px rgba(255, 255, 255, 0.4),   /* highlight top-left */
    -1px -1px 2px rgba(0, 0, 0, 0.6);         /* shadow bottom-right */
}

/* Embossed / raised effect (reversed) */
.embossed-text {
  color: #2d3148;
  text-shadow:
    -1px -1px 2px rgba(255, 255, 255, 0.3),  /* highlight top-left */
    1px 1px 3px rgba(0, 0, 0, 0.7);           /* shadow bottom-right */
}

Key principle: The pressed effect has the light highlight on top-left and dark shadow on bottom-right. The embossed effect reverses this. Which direction looks correct depends on your assumed light source — convention places light coming from the top-left.

Deep inset shadow with more layers

Adding more shadow layers increases the depth of the effect:

.deep-inset {
  color: #1a1d27;
  background: #2d3148;
  text-shadow:
    0 1px 0 rgba(255, 255, 255, 0.15),
    0 -1px 0 rgba(0, 0, 0, 0.5),
    1px 0 0 rgba(255, 255, 255, 0.1),
    -1px 0 0 rgba(0, 0, 0, 0.4);
}

Technique 2: Dark offset shadow on light text

A simpler approach that works well for large display text: use a single dark shadow with a small offset and tight blur. When text color is close to the background color, this creates a convincing sunken appearance.

/* Works best on light text with a mid-tone background */
.sunken-text {
  color: #94a3b8;
  background: #1a1d27;
  text-shadow: 2px 2px 0 rgba(0, 0, 0, 0.8);
}

/* Reverse — white text, shadow pushes down-right */
.inset-white {
  color: #f1f5f9;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.9);
}

Technique 3: SVG filter for true inner shadow

SVG filters are the closest you can get to a genuine inner shadow on text in the browser. The feComposite filter primitive lets you clip a drop shadow to the shape of the text itself, creating an effect that genuinely sits inside the letterforms.

/* Define the SVG filter — paste once in your HTML */
<svg width="0" height="0" style="position:absolute">
  <defs>
    <filter id="inner-shadow">
      <feFlood flood-color="rgba(0,0,0,0.8)" result="color"/>
      <feComposite in="color" in2="SourceGraphic" operator="in" result="shadow"/>
      <feGaussianBlur in="shadow" stdDeviation="3" result="blur"/>
      <feOffset dx="2" dy="2" result="offset"/>
      <feComposite in="SourceGraphic" in2="offset" operator="over"/>
    </filter>
  </defs>
</svg>

/* Apply the filter in CSS */
.svg-inner-shadow {
  filter: url(#inner-shadow);
  color: #7c6af7;
  font-size: 48px;
  font-weight: 800;
}

SVG filter limitation: This technique works best on large, bold text. At small font sizes the inner shadow becomes invisible because there is not enough space inside the letterforms for the blur to render correctly.

Technique 4: background-clip with gradient

The background-clip: text technique clips a background gradient to the shape of the text. By using a gradient that goes dark at one edge and light at the other, you simulate the depth of an inset shadow without using text-shadow at all.

.gradient-inset {
  background: linear-gradient(
    135deg,
    #4a4080 0%,    /* dark — simulates shadow corner */
    #9d8ffa 40%,   /* base color */
    #c4b8ff 100%   /* light — simulates highlight corner */
  );
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent; /* fallback */
  font-size: 64px;
  font-weight: 900;
}

/* Combine with text-shadow for more depth */
.gradient-inset-deep {
  background: linear-gradient(145deg, #2d2660, #7c6af7, #b4a7fb);
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  color: transparent;
  text-shadow: none; /* text-shadow does not render on clipped text */
}

Important: text-shadow does not render when -webkit-text-fill-color: transparent is set. You cannot combine the gradient clip technique with text-shadow on the same element. Use a wrapper element with a box-shadow if you need both.

Technique 5: Combining methods for letterpress effect

The classic letterpress text effect — used in retro and print-inspired designs — combines a dark text color, a background that closely matches the text, and a single light shadow offset upward. This is the most convincing inset simulation for body text and headings.

/* Classic letterpress — works on any background */
.letterpress {
  color: #1e2235;
  background: #2d3148;
  text-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);
  font-weight: 700;
}

/* Light theme letterpress */
.letterpress-light {
  color: #c8c0f0;
  background: #e8e4ff;
  text-shadow: 0 1px 0 rgba(255, 255, 255, 0.9);
  font-weight: 800;
}

/* Multi-shadow letterpress with more depth */
.letterpress-deep {
  color: #3d3670;
  background: #5a4fb5;
  text-shadow:
    0 -1px 0 rgba(0, 0, 0, 0.4),
    0 1px 0 rgba(255, 255, 255, 0.25);
}

Which technique should you use?

TechniqueBest forBrowser support
Layered shadowsAny size text, full control over feelAll browsers
Single dark shadowQuick effect, display headingsAll browsers
SVG filterTrue inner shadow, large bold textAll modern browsers
background-clip gradientColourful depth, large headingsAll modern browsers
LetterpressSubtle depth, body text and UI labelsAll browsers

For most use cases, layered shadows are the right choice. They work at any font size, require no SVG markup, and give you precise control. The SVG filter technique is worth the extra setup only when you need the shadow to genuinely sit inside the glyph at large sizes.

Common mistakes to avoid

Using inset keyword on text-shadow

As covered at the start — text-shadow: inset ... is invalid CSS. The whole declaration is ignored. If your inner shadow effect is not showing, check your DevTools to confirm the property is not crossed out as invalid.

Applying text-shadow and background-clip together

Once you set -webkit-text-fill-color: transparent for the background-clip technique, text-shadow no longer renders on that element. These two techniques are mutually exclusive on the same element. If you need both, wrap the element and apply each to a different layer.

Choosing the wrong shadow direction

The direction of your light and dark shadows must match. If your light shadow comes from the top-left but your dark shadow also comes from the top-left, the effect looks flat or wrong. The light and dark shadows should always come from opposite directions.

Too much blur on small text

High blur values on small text make the shadow bleed outside the letterforms and look more like a glow than an inset effect. For text under 24px, keep blur at 1–2px maximum. Save larger blur values for display-sized headings.

Frequently asked questions

Will CSS ever support inset text-shadow?

There is no finalised specification for inset text-shadow as of 2026. The CSS working group has discussed it, but given the technical complexity of defining "inside" for arbitrary glyph shapes, it has not been prioritised. The workarounds covered in this guide are the current standard approach.

Does the SVG filter work in all browsers?

SVG filters including feComposite and feGaussianBlur are supported in all modern browsers — Chrome, Firefox, Safari, and Edge. They are not supported in Internet Explorer, but IE has no meaningful market share in 2026.

Can I use these techniques on web fonts?

Yes. All five techniques work with any font — system fonts, Google Fonts, or self-hosted web fonts. The SVG filter technique looks best with thick-stroked web fonts where the inner area of each letter is large enough for the shadow to render visibly.

How do I make the effect visible on dark backgrounds?

On dark backgrounds, flip the shadow direction: use a light shadow offset upward and to the left, and keep the text color slightly darker than the background. The contrast between the highlight and the background creates the depth illusion rather than relying on a dark shadow underneath.

Generate CSS text shadows visually

Our CSS Text Shadow Generator lets you build and preview text shadow effects in real time — including layered shadows — and copy the exact code instantly.

Open Text Shadow Generator →