blog

Knockout text in CSS

A long time ago (in 2015.) I was working on a project that never saw the light of day. What I remember from that project is the design for 404 and 500 pages that required knockout text. The little twist was that text was filled with a colored version of the background image that was otherwise in displayed black and white.

Sadly, I don’t have screenshots of the original design but I did save the code snippet which, after light cleanup, still works!

Here is the final result:

See the Pen Knockout text by Teo Dragovic (@teodragovic) on CodePen.

I managed to refactor the code so it comes down to a single class. Here is the full code in Sass:

$image: 'beach.jpg'; // put path to the background image here

@mixin absolute($top: auto, $right: auto, $bottom: auto, $left: auto) {
position: absolute;
top: $top;
right: $right;
bottom: $bottom;
left: $left;
}

.c-knockout-text {
@include absolute(0, 0, 0, 0);
padding: 20px;
display: grid;
place-content: center;
text-align: center;

font-size: calc(50px + 20vw);
line-height: 0.8;
font-weight: 900;
text-shadow: 0 0 1px rgba(0, 0, 0, 0.2);

background-image: url($image);
background-position: center top;
background-repeat: no-repeat;
background-size: cover;
filter: contrast(160%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
// Fallback
color: #0066a4;

&:before {
@include absolute(0,0,0,0);
pointer-events: none;
z-index: -1;
content: "";
background-image: url($image);
background-position: center top;
background-repeat: no-repeat;
background-size: cover;
filter: grayscale(100%);
opacity: 20%;
}
}

For the knockout effect I’m using combination of -webkit-background-clip: text; and -webkit-text-fill-color: transparentl. Already well documented technique.

The other part is using pseudo-element to position the second image exactly behind knocked out one (all background-* properties need to have same values) and use CSS grayscale filter to make it black and white. Using CSS filters I can modify the same image so I don’t have to load separate versions of the same resources. It’s a performance win.

I also used opacity, text-shadow and contrast filter to tweak contrast between background and foreground to my liking.