Part of the appeal of the web as a design medium is the movement. The animations, transitions, and interactivity. Text can be done in any medium, but text that reveals itself over time in an interesting way? That’s great for the web. Think posters, galleries, banners, advertisements, or even something as small as spoiler-reveal effects, “reveal” animations can be entirely delightful. In this article, we’ll look at one fairly simple method for doing this with CSS.
Here’s the outcome, incorporating a play button to show you it can be done via user interaction:
This is achieved with the help of “masking” and perfectly placed conic-gradient()
. The browser support for conic gradients is fine, but we’re also going to be using CSS’ @property
here which isn’t in stable Firefox yet, but is in Firefox Nightly.
Note: Masking is a graphic technique where portion of a graphic or image is hidden based on the graphic or image layered over and/or under it, based on either the alpha channel or the light/darkness of masking image.
In our example, the masking is done using CSS Blend Mode. I’ll mention later why this is the method I’d chosen instead of CSS Mask (the mask-image
property).
Let’s get started with the HTML. Some black text on a white background is a good place to start. This is technically our “mask”.
<p>Marasmius rotula is... </p>
Code language: HTML, XML (xml)
p {
background: white;
font-size: 34px;
line-height: 42px;
text-align: justify;
}
Code language: CSS (css)
A container element is added around the text to serve as the graphic to be shown through the mask/text. Also, a CSS variable is used in the container element to assign the line-height
. This variable will later be used in the gradient.
<section class="text">
<p>Marasmius rotula...</p>
</section>
Code language: HTML, XML (xml)
section.text {
width: 420px;
--lH: 42px; /* line height */
p {
background: white;
font-size: 34px;
line-height: var(--lH);
text-align: justify;
}
}
Code language: CSS (css)
Now, we write up a conic-gradient()
as the background
for the <section>
container, with the gradient’s height same as the para’s line-height
, and set to repeat for each line of text. The gradient should look like an arrow (triangular at the tip) passing through the text.
section.text {
width: 420px;
--lH: 42px; /* line height */
background: repeat-y left/100% var(--lH) conic-gradient(white 265deg, red 269deg 271deg, white 275deg), white;
p {
background: white;
font-size: 34px;
line-height: var(--lH);
text-align: justify;
}
}
Code language: CSS (css)
We won’t see anything yet since the “black text with white background”, <p>
, is blocking the gradient behind it. However, the gradient looks like this:
We’ll now turn the gradient into an animation, where it grows from zero width to however much width is needed for it to cover the entire text. For the moment, let’s make this transition animation to take place when we hover
the cursor on the text.
@property --n {
syntax: "<length-percentage>";
inherits: false;
initial-value: 0%;
}
section.text {
width: 420px;
--lH: 42px; /* line height */
background:
repeat-y left/var(--n) var(--lH) conic-gradient(white 265deg, red 269deg 271deg, white 275deg), white;
p {
background: white;
font-size: 34px;
line-height: var(--lH);
text-align: justify;
}
}
section.text:hover {
--n: 340%;
transition: --n linear 2s;
}
Code language: CSS (css)
The --n
custom property is used to assign the size of the gradient. Its initial value is 0%
, which increases with a transition
effect when the text is hovered, and hence the gradient grows in width.
We still haven’t masked our example. So, once again, only the text will be visible. Let me show you how the gradient animates, separately, below:
Note: @property
creates a custom property that has a known type, hence the property value can be animated. The custom property may not have been able to be animated otherwise.
Let’s now drop the blend mode (the mix-blend-mode
property) into the <p>
element to finally see the effect.
@property --n {
syntax: "";
inherits: false;
initial-value: 0%;
}
section.text {
width: 420px;
--lH: 42px; /* line height */
background:
repeat-y left/var(--n) var(--lH) conic-gradient(white 265deg, red 269deg 271deg, white 275deg), white;
p {
mix-blend-mode: screen;
background: white;
font-size: 34px;
line-height: var(--lH);
text-align: justify;
}
}
section.text:hover {
--n: 340%;
transition: --n linear 2s;
}
Code language: CSS (css)
For the sake of operability, instead of on text hover, I’ll move the animation to take place with user controls, Play and Reset. Here’s the final output:
The reason we didn’t use mask-image
, as I mentioned before is because Safari doesn’t render the output if I use multiple gradient images (on top of the conic-gradient()
), and also has a blotchy implementation of box-decoration-break
during animation, both of which are important to work correctly for the effect I wanted to achieve.
That said, here’s a Pen that uses mask-image
and box-decoration-break
, in case you want to learn how to go about it and get some ideas on approaching any alternative methods. At the time of writing this article, it’s best to view that in Chrome.
Here’s another example that shows off how this effect might be used in a real-world context, revealing the text of different “tabs” as you navigate between tags.
For design variants, play with different colors, and number and kind of gradients. Let me know what you come up with!
But why does the mixed blend mode screen work?