The Times You Need A Custom @property Instead Of A CSS Variable — Smashing Magazine
circle()
’s radius is set to the registered custom property, --circleSize
, which is then independently changed on hover using a transition
. The result is something close to Material Design’s ripple effect, and we can do it because we’ve told CSS to treat the custom property as a percentage value rather than a string:
Here’s an idea I have that uses the same basic idea as the ripple, only it chains multiple custom properties together that are formatted as colors, lengths, and angle degrees for a more complex animation where text slides up the container as the text changes colors.
Let’s use this demo as an exercise to learn more about defining custom properties with the @property
at-rule, combining what we just saw in the ripple with the concept of interpolating gradient values.
The HTML
The HTML contains Chinese characters we’re going to animate. These Chinese characters are marked up with tags so that their English translations can be supplied in
tags. The idea is that
.scrolling-text
is the component’s parent container and, in it, is a child element holding the sliding text characters that allow the characters to slide in and out of view.
Vertical Sliding
In CSS, let’s make the characters slide vertically on hover. What we’re making is a container with a fixed height we can use to clip the characters out of view when they overflow the available space.
.scrolling-text
syntax: "
.text-container:has(:hover, :focus) .text ";
inherits: false;
initial-value: 5px;
.text ";
inherits: false;
initial-value: 5px;
Setting the .scrolling-text
container’s width to min-content
gives the characters a tight fit, stacking them vertically in a single column. The container’s height is set 1lh
. And since we’ve set overflow: hidden
on the container, only one character is shown in the container at any given point in time.
Tip: You can also use the HTML
element or either the
white-space
ortext-wrap
properties to control how text wraps.
On hover, the text moves -2lh
, or double the height of a single text character in the opposite, or up, direction. So, basically, we’re sliding things up by two characters in order to animate from the first character to the third character when the container holding the text is in a hovered state.
Applying Gradients To Text
Here’s a fun bit of styling:
.text ";
inherits: false;
initial-value: 5px;
How often do you find yourself using repeating gradients in your work? The fun part, though, is what comes after it. See, we’re setting a transparent
color on the text and that allows the repeating-linear-gradient()
to show through it. But since text is a box like everything else in CSS, we clip the background at the text itself to make it look like the text is cut out of the gradient.
Pretty neat, right? Now, it looks like our text characters have a striped pattern painted on them.
Animating The Gradient
This is where we take the same animated gradient concept covered in other tutorials and work it into what we’re doing here. For that, we’ll first register some of the repeating-linear-gradient()
values as custom properties. But unlike the other implementations, ours is a bit more complex because we will animate several values rather than, say, updating the hue.
Instead, we’re animating two colors, a length, and an angle.
@property --c1 {
syntax: "";
inherits: false;
initial-value: rgb(224, 236, 236);
}
@property --c2 {
syntax: "";
inherits: false;
initial-value: rgb(92, 198, 162);
}
@property --l {
syntax: " | ";
inherits: false;
initial-value: 5px;
}
@property --angle {
syntax: "";
inherits: false;
initial-value: 180deg;
}
.text {
background: repeating-linear-gradient(
var(--angle),
var(--c1),
var(--c1) 5px,
var(--c2) var(--l),
var(--c2) 6px);
}
We want to update the values of our registered custom properties when the container that holds the text is hovered or in focus. All that takes is re-declaring the properties with the updated values.
.text-container:has(:hover, :focus) .text {
--c1: pink;
--c2: transparent;
--l: 100%;
--angle: 90deg;
background-size: 50% 100%;
transform: translateY(-2lh);
}
To be super clear about what’s happening, these are the custom properties and values that update on hover:
--c1
: Starts with a color value ofrgb(224, 236, 236)
and updates topink
.--c2
: Starts with a color value ofrgb(92, 198, 162)
and updates totransparent
.--l
: Starts with length value5px
and updates to100%
.--a
: Starts with an angle value of180deg
and updates to90deg
.
So, the two colors used in the gradient transition into other colors while the overall size of the gradient increases and rotates. It’s as though we’re choreographing a short dance routine for the gradient.
Refining The Transition
All the while, the .text
element containing the characters slides up to reveal one character at a time. The only thing is that we have to tell CSS what will transition
on hover, which we do directly on the .text
element:
.text {
transition: --l, --angle, --c1, --c2, background-size, transform 2.4s ease-in-out;
transition-duration: 2s;
}
Yes, I could just as easily have used the all
keyword to select all of the transitioning properties. But I prefer taking the extra step of declaring each one individually. It’s a little habit to keep the browser from having to watch for too many things, which could slow things down even a smidge.
Final Demo
Here’s the final outcome once again:
I hope this little exercise not only demonstrates the sorts of fancy things we can make with CSS custom properties but also helps clarify the differences between custom properties and standard variables. Standard variables are excellent placeholders for more maintainable code (and a few fancy tricks of their own) but when you find yourself needing to update one value in a property that supports multiple values — such as colors in a gradient — the @property
at-rule is where it’s at because it lets us define variables with a custom specification that sets the variable’s syntax, initial value, and inheritance behavior.
That’s why @property
is a useful CSS standard to keep in mind and keep ready to use when you are thinking about animations that involve isolated value changes.
Further Reading On SmashingMag
(gg, yk)