Inner text shadow with CSS

Solution 1:

Here's a little trick I discovered using the :before and :after pseudo-elements:

.depth {
    color: black;
    position: relative;
}
.depth:before, .depth:after {
    content: attr(title);
    color: rgba(255,255,255,.1);
    position: absolute;
}
.depth:before { top: 1px; left: 1px }
.depth:after  { top: 2px; left: 2px }

The title attribute needs to be the same as the content. Demo: http://dabblet.com/gist/1609945

Solution 2:

You should be able to do it using the text-shadow, erm somethink like this:

.inner_text_shadow
{
    text-shadow: 1px 1px white, -1px -1px #444;
}

here's an example: http://jsfiddle.net/ekDNq/

Solution 3:

Here's my best try:

    .inner_shadow {
    color:transparent;
    background-color:white;
    text-shadow: 0 0 20px rgba(198,28,39,0.8), 0 0 0 black;
    font-family:'ProclamateHeavy';  // Or whatever floats your boat
    font-size:150px;
    }
   
    <span class="inner_shadow">Inner Shadow</span>
   

The problem is how to clip the shadow that bleeds around the edges!!! I tried in webkit using background-clip:text, but webkit renders the shadow above the background so it doesn't work.

Making a Text Mask with CSS?

Without a top mask layer it is impossible to do a true inner shadow on text.

Perhaps someone should recommend that the W3C add background-clip: reverse-text, that would cut a mask through the background instead of cutting the background to fit inside the text.

Either that or render the text-shadow as part of the background and clip it with background-clip: text.

I tried absolutely positioning an identical text element above it, but the problem is background-clip: text crops the background to fit inside the text, but we need the reverse of that.

I tried using text-stroke: 20px white; on both this element and the one above it, but the text stroke goes in as well as out.

Alternate Methods

Since there is currently no way to make an inverted-text mask in CSS, you could turn to SVG or Canvas and make a text replacement image with the three layers to get your effect.

Since SVG is a subset of XML, SVG text would still be select-able and searchable, and the effect can be produced with less code than Canvas.

It would be harder to achieve this with Canvas because it doesn't have a dom with layers like SVG does.

You could produce the SVG either server-side, or as a javascript text-replacement method in the browser.

Further Reading:

SVG versus Canvas:

http://dev.opera.com/articles/view/svg-or-canvas-choosing-between-the-two/

Clipping and Masking with SVG Text:

http://www.w3.org/TR/SVG/text.html#TextElement

Solution 4:

An elegant example without the need for a positioned wrapper or duplicative content in the markup:

:root {
  background: #f2f2f2;
}
h1 {
  background-color: #565656;
  font: bold 48px 'Futura';
  color: transparent;
  text-shadow: 0px 2px 3px rgba(255, 255, 255, 0.8);
  -webkit-background-clip: text;
  -moz-background-clip: text;
  background-clip: text;
}
<center>
  <h1>Text With Inner Shadow</h1>
</center>

Looks good on dark backgrounds as well:

:root {
  --gunmetal-gray: #2a3439;
  background: var(--gunmetal-gray);
}

h1[itemprop="headline"] {
  font-family: 'Futura';
  font-size: 48px;
  padding-bottom: 0.35rem;
  font-variant-caps: all-small-caps;
  background-color: var(--gunmetal-gray);
  color: transparent;
  text-shadow: 0px 2px 3px rgba(255, 255, 255, 0.1);
  -webkit-background-clip: text;
  -moz-background-clip: text;
  background-clip: text;
  filter: brightness(3);
}
<center>
  <h1 itemprop="headline">Text With Inner Shadow</h1>
</center>

AND ME LINK AND ME2 LINK

Solution 5:

More precise explanation of the CSS in kendo451's answer.

There's another way to get a fancy-hacky inner shadow illusion,
which I'll explain in three simple steps. Say we have this HTML:

<h1>Get this</h1>

and this CSS:

h1 {
  color: black;
  background-color: #cc8100;
}

It's a good slogan

Step 1

Let's start by making the text transparent:

h1 {
  color: transparent;
  background-color: #cc8100;
}

It's a box

Step 2

Now, we crop that background to the shape of the text:

h1 {
  color: transparent;
  background-color: #cc8100;
  background-clip: text;
}

The background is the text!

Step 3

Now, the magic: we'll put a blurred text-shadow, which will be in front of the background, thus giving the impression of an inner shadow!

h1 {
  color: transparent;
  background-color: #cc8100;
  background-clip: text;
  text-shadow: 0px 2px 5px #f9c800;
}

We got it! Yay!

See the final result.

Downsides?

  • Only works in Webkit (background-clip can't be text).
  • Multiple shadows? Don't even think.
  • You get an outer glow too.