.scale-background-on-hover {
position: relative;
isolation: isolate;
}
.scale-background-on-hover::after {
background-color: pink;
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
transition: transform 250ms;
}
.scale-background-on-hover:hover::after {
transform: scale(1.1);
}
Link to this headingDemo
Link to this headingContext
When a user hovers over something actionable, like a button or a link, it can be useful to indicate to the user that the item can be interacted with. Typically this is done with a color change or an underline, but we can be more creative than that!
A favourite trick of mine is to scale up the item on hover, and add a brief transition
. That way, it appears to grow under the cursor:
The problem with this effect is that text sometimes looks wonky when scaled up, especially on non-retina monitors. The effect isn't always crisp.
A fancier way to do this would be to only scale the background when we hover:
This effect is a bit more quirky and eye-catching, and you get to avoid all the funkiness with text crispness.
Link to this headingUsage
Link to this headingWith vanilla HTML/CSS
<!--
Add the class wherever it's needed!
Be sure to remove any background from the element itself.
-->
<button class="btn scale-background-on-hover">
Hover over me!
</button>
Link to this headingWith React
We can create a utility component for this effect:
const ScaleBackground = ({
color,
transitionDuration,
children,
style = {},
...delegated,
}) => {
return (
<Wrapper
style={{
'--color': color,
'--transition-duration': transitionDuration,
...style,
}}
{...delegated}
>
{children}
</Wrapper>
);
}
const Wrapper = styled.div`
position: relative;
display: inline-block;
&::after {
background-color: var(--color);
transition: transform var(--transition-duration);
content: "";
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: -1;
}
&:hover::after {
transform: scale(1.1);
}
`;
We add display: inline-block
to the Wrapper
so that the container shrinks down to cover the children, without expanding to fill the available horizontal space.
Link to this headingExplanation
The trick to this effect is animating the pseudo-element.
We definitely want to use a transform: scale
transition, since it's both more performant and more smooth than trying to animate width
or height
, or fussing with background-size
.
If we wanted, we could also add a blank <div>
instead of using a pseudo-element… but it feels way nicer to use a pseudo-element, since it means a style concern is encapsulated entirely within our CSS.
Link to this headingAdditional Exploration
There's so much more you can do with this concept. Here are some suggestions:
- Experiment with other transforms (rotation, skew, translate…).
- Use opacity to pronounce the hover even further (say, going from 0.8 opacity to 1).
- Give other easing timing functions a shot.
- Use a keyframe animation to include several "steps" in the transition.
- Use
:active
(on a button or link) to have a different effect when the user is interacting with the element. - Apply a slightly different transition to the element itself, so that both the element and the
::after
pseudo-element move independently (but in a synchronized way). - Use spring physics to produce a more natural effect.
Have fun!
Last updated on
May 13th, 2020