A hover effect is a small piece of feedback that tells a visitor "this is interactive" before they even click. Done well, these micro-interactions make a site feel responsive and alive; done poorly (or skipped entirely), buttons and links can feel static and unclear. The good news is that most convincing hover effects are just :hover combined with a CSS transition — no JavaScript required.
The core idea is always the same: define the "at rest" state normally, define the "hovered" state under a :hover selector, and add a transition so the browser animates between the two instead of snapping instantly.
Pattern 1: Lift on Hover
.card {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
transform: translateY(-6px);
box-shadow: 0 12px 20px rgba(0, 0, 0, 0.15);
}
The card rises 6 pixels and gains a larger shadow at the same time, which together simulate the card physically lifting toward the viewer. This is one of the most common patterns for product cards, blog post previews, and pricing tiers.
Pattern 2: Underline Grow
<a class="link-underline" href="#">Read more</a>
.link-underline {
position: relative;
text-decoration: none;
}
.link-underline::after {
content: "";
position: absolute;
left: 0;
bottom: -2px;
width: 0%;
height: 2px;
background: currentColor;
transition: width 0.25s ease;
}
.link-underline:hover::after {
width: 100%;
}
An empty ::after pseudo-element starts at zero width, sitting just below the text. On hover, its width animates from 0% to 100%, drawing an underline that appears to grow from left to right rather than just blinking on. This reads as noticeably more polished than the browser's default underline.
Pattern 3: Color Shift
.btn {
background: #2563eb;
color: white;
padding: 0.6em 1.4em;
border-radius: 8px;
border: none;
transition: background-color 0.2s ease;
}
.btn:hover {
background: #1d4ed8;
}
A subtle shift to a darker shade of the same color on hover is the simplest possible hover effect, but it's effective precisely because it's subtle — it confirms interactivity without distracting from the button's label.
Keeping Effects Tasteful
A good rule of thumb: the transition duration for a hover effect should usually sit between 150ms and 300ms. Much faster and it feels like it didn't animate at all; much slower and the interface starts to feel sluggish and unresponsive. It's also worth pairing any hover effect with a matching :focus-visible style, so keyboard users who tab through your page get the same visual feedback that mouse users get for free.
