There are a couple of ways to do knockout text (text that appears cut out, such that you can see a background behind it) on the web. There is one fairly new way that has pretty decent browser support that is pretty interesting. But let’s cover all the ground.
Just Photoshop It Up
Like anything else, you can just fake it with a flat graphic. Sometimes it’s worth covering the obvious. The downsides are it’s not dynamic/hard to change. You can’t copy-and-paste the text. Images can be large in file size, especially as you fight for higher resolution. Etc etc. But, you can do it and it’ll work everywhere:
-webkit-background-clip: text;
This is a non-standardized property, but it was invented to do just this. The beauty is that you can pair it with -webkit-text-fill-color: transparent;
so that you’re only “hiding” the text in a browser that can do the clipping. Divya Manian documented this a few years back.
.clip-text-maybe {
/* if we can clip, do it */
-webkit-text-fill-color: transparent;
-webkit-background-clip: text;
/* what will show through the text
~ or ~
what will be the background of that element */
background: whatever;
/* fallback text color
~ or ~
the actual color of text, so it better work on the background */
color: red;
}
This is real web text, so with that comes all the accessibility and searchability and easy-to-change-ness and all that.
Here’s some fun examples of this method at work, by Jintos:
See the Pen -webkit-background-clip:text CSS effect by Jintos (@Jintos) on CodePen.
Like any other background, you can fix its position if that’s desireable. Here’s a demo of that by Richard Hayes:
See the Pen Webkit Clip with Fixed Background by Richard (@strawstack) on CodePen.
SVG <pattern> fill
SVG can also have real text, through it’s <text>
element. SVG text can be filled with whatever, including a pattern. And a pattern can use an image. So… knockout text!
An example of that would be like:
<svg>
<pattern id="pattern" patternUnits="userSpaceOnUse" width="750" height="800">
<image width="750" height="800" xlink:href="image.jpg"></image>
</pattern>
<text x="0" y="80" class="headline" style="fill:url(#pattern);">background-clip: text | Polyfill</text>
</svg>
Which you could use right in HTML anywhere. Here’s an example of that by C.Y. Park:
See the Pen SVG Text Clip with Gradient & GIF by C.Y. Park (@cypark) on CodePen.
Lea Verou also documented this technique.
Polyfill the CSS with SVG
Tim Pietrusky created a polyfill for -webkit-background-clip
that uses the SVG method as a fallback. The CSS version is certainly easier to write quickly than the SVG version, so it seems like this could be a good way to go while still getting better browser support.
See the Pen -webkit-background-clip: text Polyfill by Tim Pietrusky (@TimPietrusky) on CodePen.
mix-blend-mode: screen;
This is the new one I hadn’t seen before! CSS blending is fairly new. It allows you to blend elements on top of other elements like you would layer styles in Photoshop (e.g. “screen”, “multiply”, “lighten”, etc.)
Done just right, you can blend an image right on top of text without affecting the “real” background. I first saw Giana do it on CodePen:
See the Pen CSS Gradient Text in Firefox by Giana (@giana) on CodePen.
It works like this:
- Make the text black.
- Cover the entire text with the new text background and
mix-blend-mode: screen;
- Then cover that with a new copy of the same text (in white) and the same background as the page and
mix-blend-mode: multiply;
That will leave just the text with the “knockout” background visible. If you use pointer-events, you can leave the text clickable and selectable too, although it’s hard to see what you’re doing.
The advantage here is that this is a pure-CSS method that works in Firefox as well, not just -webkit-
stuff.
FWIW, there is an even better way to do it with photoshop. make sure the text layer is below the image, then hold Alt and click between the two layers.
Then you can change the text later, and the mask will always stay up to date.
https://helpx.adobe.com/photoshop/using/revealing-layers-clipping-masks.html
Thx for that Andrew,
Unfortunately if it doesn’t output accessible text, it could get businesses into hot water, and more importantly alienate potential users who have no way to access the content when presented in an image.
It is a cool link anyway, definitely the best way to knock-out around a graphic, or visual indicator that does not contain a text body.
Lewis, neither does the first option proposed via Photoshop. If you’re going to make an image rather than text, you might as well set a clipping mask as suggeste by Andrew.
Lewis, yep. Like Matth said: I’m just proposing workflow to make the worst solution slightly less painful.
In that same vein, if you do the Photoshop route, you’re likely saving out a lot of visually complex Png files. Remember to compress them, everyone! http://pngmini.com is just about the gold standard.
I don’t think Andrew was implying that his method is better than using CSS. He’s just saying that if you have to use photoshop his suggested method is preferable to the article’s example because it is non-destructive.
Anyway of accomplishing the reverse effect of that with CSS?
Reverse effect? You mean a photo background with white text on top?
Andrew, a quick mockup works better than words: https://cloudup.com/ctQdOssOX6h
The stipulation: Text should remain accessible, while flexible, and not be bound to
content
of a pseudo element.Scott,
I’m still not sure I see the difference.
I assume he idea is you’d want to stick the blue box with knocked out text over the top of an image. if that’s the case, it looks visually just like every example up there: solid color background, image clipped to within bounds of the text.
that said – if you really want the text to be transparent instead of a clipping mask (and again, I’m not sure how those would look different in the real world) then you might be able to do it with SVG. I don’t know how, that’s just thinking out loud.
Andrew, thanks for your response. Although the effect may appear similar to the original examples, what I’m trying to accomplish is quite different.
Looking closer at the first example, a background color needs to be applied to the clipping text (
clip-text_one
), and it needs to match the background color of the parent element, (body
) in order to pull the effect off. Correct?I forked the original pen where I applied a background image to the
body
and purposely set the clipping text to purple so it can be seen. I invite you to try and make the text knock out to reveal the parentbody
.Ah, I see.
I think what you’re asking for might not be possible. You might have to fake it, and accept the way it works (assuming it looks the same) in example one.
If you’re not a fan of the complexity, you could maybe make a sass mixin to handle it all for you?
No need to duplicate the text when using the blend mode method.
If you want the text on a white background:
mix-blend-mode: lighten
If you want the text on a black background:
mix-blend-mode: darken
Working in this pen.
A black or white background simplifies things considerably. There’s a comment in the CSS of the pen that touches on this, but by removing the pseudo-element text rather than the HTML text. The duplicated text is an ugly solution for a colored background. Love how concise your version is.
Notice that
-webkit-text-fill-color
isn’t necessary for the first technique, ascolor
would suffice.I suppose this has been done because if you want to render some outlined text with
-webkit-text-stroke
with a transparent filling, you wouldn’t see anything in browsers that doesn’t support it.So you’d want to use
-webkit-text-fill-color
instead, and fallback tocolor
for other browsers – which has less priority and this may end in a lot of headaches if-webkit-text-fill-color
isn’t used for its intended use (you can use!important
withcolor
and wouldn’t have any effect!).In the end, in browsers that support
@supports
,-webkit-text-fill-color
(and this includes Safari 9+) can be ditched altogether.I’m not sure I follow here. If you don’t set the text to transparent, you don’t see the knockout. you could use
color
to do that, but then the fallback is invisible text (bad).With SVG you can use clipPath and gradients and even get crazy and animate the darn thing! http://codepen.io/dennisgaebel/pen/xGMYJY
Hi all!
“-webkit-background-clip: text;” technique…
I do not understand how it works and whether the works: http://i.imgur.com/O4Ueykl.png
My browser – firefox 43.0a2.
A similar display in Firefox 41.0 and Rekonq.
The only browser in which this works is Chrome
Firefox doesn’t recognise most
-webkit
prefixes, and that’s pretty logical, since its prefix is-moz
.haroen,
So, you suggest to the property “background-clip” add the prefix “-moz”?
Property “background-clip” works fine in firefox without prefixes. But the final result does not give…
Quite a while ago I did a pen that recreates the knockout effect with an animated canvas as the background: http://codepen.io/noxoc/pen/tvArf
Fairly simple example, that’s probably why it went unnoticed. Might recreate with a more intriguing example. ;)