Making links slide with vertical motion
A tiny CSS trick makes links slide smoothly on hover, giving your UI a lively feel without using any JavaScript.
Ever scroll past links that feel flat and lifeless? Sometimes all it takes is a tiny nudge to make them pop. In this little experiment, I put together a quick JSFiddle that gives links a smooth vertical motion when you hover over them.
The concept
Each link has two states stacked vertically: the normal state and the active :active state. By default, the active state is hidden just above or below the link. When you hover over the link, it slides into view, replacing the original text.
--- in view
MY LINK
--- out of view
MY LINK (:active state)
hover:
--- out of view
MY LINK
--- in view
MY LINK (:active state)
It’s a neat trick that turns an ordinary link into something that feels alive, without adding heavy JavaScript or complex animations.
Demo
If all you want is a demo, you can see the effect in action in this JSFiddle link.
Or check it embedded below:
HTML structure
The HTML is simple: a list of links, each with a data-label attribute holding the active state text.
<h1>Your projects</h1>
<ul>
<li>
<a href="#" data-label="Morning Coffee Routine">
<span>Morning Coffee Routine</span>
</a>
</li>
<li>
<a href="#" data-label="Grocery Shopping List">
<span>Grocery Shopping List</span>
</a>
</li>
<li>
<a href="#" data-label="Weekly Exercise Schedule">
<span>Weekly Exercise Schedule</span>
</a>
</li>
<li>
<a href="#" data-label="Dinner Recipes To Try">
<span>Dinner Recipes To Try</span>
</a>
</li>
<li>
<a href="#" data-label="Evening Relaxation Activities">
<span>Evening Relaxation Activities</span>
</a>
</li>
</ul>
The span holds the visible text, while data-label provides the text that slides in on hover. You could easily avoid the content duplication in your markup by relying on a short JavaScript function that would fetch the content of the span and inject it in the data-label attribute.
But if you know me, you know I wouldn’t consider JavaScript for this. Although extremely easy to put together, you need to remember it’s yet one extra bit you will have to maintain down the road. Especially if you consider that content usually comes from a CMS of some sort, this approach just means you would output the same variable twice. That’s it.
But this is the least interesting bit. Let’s actually get to the most interesting part now.
CSS magic
CSS handles all the movement with transforms and transitions. Besides, it will grab the content inside the data-label attribute and place it inside a pseudo element so that it doesn’t bloat the markup and avoids extra content for screen readers.
h1 {
font-size: 0.6rem;
font-weight: normal;
text-transform: uppercase;
letter-spacing: 3px;
color: #666;
width: 100%;
margin-left: 1rem;
margin-bottom: 1rem;
}
ul {
list-style: none;
padding: 0;
margin: 0;
}
a {
font-weight: bold;
font-size: 1.5rem;
text-decoration: none;
padding: 0.5rem 1rem;
display: flex;
flex-direction: column;
color: #282828;
position: relative;
overflow: hidden;
}
a span {
transition: translate 150ms ease-in-out;
}
a::after {
content: attr(data-label);
position: absolute;
top: 3rem;
left: 1rem;
transition: translate 150ms ease-in-out;
color: #666600;
}
a:hover,
a:focus {
background-color: #eeeedd;
}
a:hover::after,
a:focus::after,
a:hover span,
a:focus span {
translate: 0 -2.5rem;
}
The trick is stacking the two states and translating them vertically on hover. overflow: hidden ensures the sliding text only appears within the link boundaries.
Why this matters
Small UI interactions like this make a website feel polished and intentional. Solving effects like this purely with CSS is far better than relying on JavaScript: it’s lightweight, requires no extra scripts to maintain, and keeps the markup clean.
Using JavaScript for something this simple often means duplicating content or adding extra elements, which can clutter your HTML and potentially confuse screen readers or other assistive technologies. CSS pseudo-elements, on the other hand, can handle the visual effect without touching the DOM or introducing redundant content.
This approach is not only more maintainable, but it also ensures that the interaction remains performant and accessible, while still giving users a playful and engaging experience.