CSS Hover Effects — 10 Interactive Animations with Code
Build 10 CSS hover effects from scratch: fill-up button, underline slide, scale shadow, border draw, shine sweep, flip card, page curl, glitch, pulse, and slide background. Full copy-paste code.
// table of contents (21 sections)
Try them live — All 10 hover effects are available on CSSKit where you can customize parameters and copy the code. Source code on GitHub. See also Part 1 — Text Effects and Part 2 — More Text Effects.
After 20 text animations, it’s time to add interactivity. Hover effects are the backbone of modern UI feedback — they tell users “this element is clickable.” In this post, we’ll build 10 hover animations from scratch: buttons that fill, underlines that slide, cards that flip in 3D, and more. All pure CSS, no JavaScript.
21. Fill Up
Background color rises from the bottom on hover. A classic button hover effect.
How It Works
A ::before pseudo-element sits behind the text with position: absolute; bottom: 0; height: 0. On hover, height transitions to 100%, filling from the bottom up. The text color changes to white via a separate transition. The z-index: 1 on the button ensures text stays above the filling layer.
.fill-up {
--fill-color: #6366f1;
--fill-speed: 0.4s;
position: relative;
display: inline-block;
padding: 12px 32px;
font-size: 1.1rem;
font-weight: 600;
color: var(--fill-color);
border: 2px solid var(--fill-color);
background: transparent;
overflow: hidden;
cursor: pointer;
z-index: 1;
transition: color var(--fill-speed) ease;
}
.fill-up::before {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 0;
background: var(--fill-color);
z-index: -1;
transition: height var(--fill-speed) ease;
}
.fill-up:hover {
color: white;
}
.fill-up:hover::before {
height: 100%;
}
<button class="fill-up">Hover Me</button>
Tip: Change bottom: 0 to top: 0 to fill from top, or use width instead of height with left: 0 for a side fill.
22. Underline Slide
An animated underline slides in from the left on hover — perfect for navigation links.
How It Works
The ::after pseudo-element creates an underline bar with width: 0. On hover, width transitions to 100%, sliding the underline in from the left. The bottom: -4px positions it slightly below the text. By changing left: 0 to left: 50% and using transform: translateX(-50%) on both states, you can make it slide from center instead.
.underline-slide {
--ul-color: #f59e0b;
--ul-height: 3px;
--ul-speed: 0.3s;
position: relative;
display: inline-block;
font-size: 1.5rem;
font-weight: 600;
color: white;
text-decoration: none;
cursor: pointer;
}
.underline-slide::after {
content: "";
position: absolute;
bottom: -4px;
left: 0;
width: 0;
height: var(--ul-height);
background: var(--ul-color);
transition: width var(--ul-speed) ease;
}
.underline-slide:hover::after {
width: 100%;
}
<a class="underline-slide">Hover This Link</a>
Tip: Add width: 100% as the default and width: 0 on hover to make the underline slide out instead — great for exit animations.
23. Scale + Shadow
Element grows with an elevated shadow on hover — adds depth and interactivity to cards.
How It Works
Two properties transition simultaneously on hover: transform: scale() enlarges the element, while box-shadow adds a large diffuse shadow beneath it. The calc(var(--ss-shadow) * 2) makes the shadow blur proportional to the offset. Both use the same duration so they feel synchronized.
.scale-shadow {
--ss-scale: 1.08;
--ss-shadow: 20px;
--ss-color: #10b981;
--ss-speed: 0.3s;
display: inline-flex;
align-items: center;
justify-content: center;
width: 120px;
height: 120px;
background: var(--ss-color);
border-radius: 16px;
color: white;
font-weight: 700;
font-size: 1rem;
transition:
transform var(--ss-speed) ease,
box-shadow var(--ss-speed) ease;
cursor: pointer;
}
.scale-shadow:hover {
transform: scale(var(--ss-scale));
box-shadow: 0 var(--ss-shadow) calc(var(--ss-shadow) * 2) rgba(0, 0, 0, 0.3);
}
<div class="scale-shadow">Hover</div>
Tip: Keep the scale subtle (1.05-1.10) for production UI. Anything above 1.15 feels exaggerated.
24. Border Draw
Border draws itself around the element on hover — elegant and precise.
How It Works
Two pseudo-elements each handle two sides of the border. ::before draws the top and right edges, ::after draws bottom and left. Both start at width: 0; height: 0. On hover, ::before grows width first then height (using a transition delay on height), while ::after grows height first then width. This creates a sequential draw: top-right then bottom-left.
.border-draw {
--bd-color: #ec4899;
--bd-width: 3px;
--bd-speed: 0.4s;
position: relative;
display: inline-block;
padding: 14px 36px;
font-size: 1.1rem;
font-weight: 600;
color: white;
cursor: pointer;
}
.border-draw::before,
.border-draw::after {
content: "";
position: absolute;
width: 0;
height: 0;
border: var(--bd-width) solid transparent;
box-sizing: border-box;
}
.border-draw::before { top: 0; left: 0; }
.border-draw::after { bottom: 0; right: 0; }
.border-draw:hover::before,
.border-draw:hover::after {
width: 100%;
height: 100%;
}
.border-draw:hover::before {
border-top-color: var(--bd-color);
border-right-color: var(--bd-color);
transition:
width var(--bd-speed) ease,
height var(--bd-speed) ease var(--bd-speed);
}
.border-draw:hover::after {
border-bottom-color: var(--bd-color);
border-left-color: var(--bd-color);
transition:
width var(--bd-speed) ease var(--bd-speed),
height var(--bd-speed) ease;
}
<span class="border-draw">Hover Draw</span>
Key detail: The var(--bd-speed) delay on the second dimension creates the sequential draw effect. Without the delay, all sides would draw simultaneously.
25. Shine Sweep
A light sweep crosses the element on hover — adds polish to buttons and CTAs.
How It Works
A ::before pseudo-element is positioned off-screen at left: -100% with a transparent-to-white-to-transparent gradient. On hover, it slides to left: 150%, crossing the entire element like a light sweep. The overflow: hidden on the parent clips the shine at the edges. The width is only 50% of the element so the gradient stays narrow like a real light streak.
.shine-sweep {
--shine-color: rgba(255, 255, 255, 0.4);
--shine-speed: 0.6s;
--shine-bg: #374151;
position: relative;
display: inline-block;
padding: 14px 40px;
font-size: 1.1rem;
font-weight: 600;
color: white;
background: var(--shine-bg);
border-radius: 8px;
overflow: hidden;
cursor: pointer;
}
.shine-sweep::before {
content: "";
position: absolute;
top: 0;
left: -100%;
width: 50%;
height: 100%;
background: linear-gradient(
90deg,
transparent,
var(--shine-color),
transparent
);
transition: left var(--shine-speed) ease;
}
.shine-sweep:hover::before {
left: 150%;
}
<button class="shine-sweep">Shine Sweep</button>
Tip: Use rgba(255, 255, 255, 0.2) for a subtle sweep, or rgba(255, 255, 255, 0.6) for a more dramatic flash.
26. Flip Card
A full 3D card flip on hover — reveals a back face with different content.
How It Works
The outer container sets perspective: 800px to enable 3D space. The inner wrapper uses transform-style: preserve-3d so children live in the same 3D context. On hover, the inner rotates rotateY(180deg). Both faces use backface-visibility: hidden so only the face facing the viewer is visible. The back face starts pre-rotated at rotateY(180deg) so it’s initially hidden.
.flip-card {
--flip-speed: 0.6s;
--flip-bg-front: #6366f1;
--flip-bg-back: #8b5cf6;
perspective: 800px;
width: 200px;
height: 140px;
cursor: pointer;
}
.flip-card-inner {
position: relative;
width: 100%;
height: 100%;
transition: transform var(--flip-speed);
transform-style: preserve-3d;
}
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
.flip-card-front,
.flip-card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
color: white;
font-size: 1.1rem;
}
.flip-card-front { background: var(--flip-bg-front); }
.flip-card-back {
background: var(--flip-bg-back);
transform: rotateY(180deg);
}
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">Front</div>
<div class="flip-card-back">Back</div>
</div>
</div>
Tip: Use rotateX(180deg) instead of rotateY for a vertical flip. Set transform-origin: left on the inner for a book-opening effect.
27. Page Curl
A corner peel/curl effect on hover — simulates a physical page being lifted.
How It Works
Two pseudo-elements create the curl illusion. ::before draws the “underside” of the page using a diagonal gradient (linear-gradient(225deg, dark 50%, lighter 50%)) positioned at the bottom-right corner. ::after adds a subtle highlight on top of the curl. Both start at width: 0; height: 0 and grow to the curl size on hover. The z-index layering ensures the curl sits above the card content.
.hover-curl {
--hc-color: #1e293b;
--hc-size: 30px;
--hc-speed: 0.3s;
position: relative;
width: 220px;
height: 160px;
background: var(--hc-color);
border-radius: 10px;
overflow: hidden;
display: flex;
align-items: center;
justify-content: center;
font-family: system-ui, sans-serif;
color: #94a3b8;
font-size: 14px;
font-weight: 500;
}
.hover-curl::before {
content: '';
position: absolute;
bottom: 0;
right: 0;
width: 0;
height: 0;
background: linear-gradient(225deg, #050b18 50%, #334155 50%);
border-radius: 0 0 10px 0;
transition: all var(--hc-speed) ease;
z-index: 2;
}
.hover-curl::after {
content: '';
position: absolute;
bottom: 0;
right: 0;
width: 0;
height: 0;
background: linear-gradient(225deg, transparent 50%, rgba(255,255,255,0.08) 50%);
transition: all var(--hc-speed) ease;
z-index: 3;
}
.hover-curl:hover::before {
width: var(--hc-size);
height: var(--hc-size);
}
.hover-curl:hover::after {
width: calc(var(--hc-size) + 3px);
height: calc(var(--hc-size) + 3px);
}
<div class="hover-curl">Hover corner</div>
Tip: Increase --hc-size to 50px for a bigger curl, or change the gradient colors to match your page background for a realistic peel effect.
28. Hover Glitch
A quick color-split glitch on hover — punchy digital distortion.
How It Works
On hover, a @keyframes animation plays once using steps(2). Each frame applies a small translate() offset and red/blue text-shadow in opposite directions, creating the RGB split. The steps(2) timing makes it punchy rather than smooth — the glitch snaps between positions. Since the animation only triggers on :hover, it plays briefly each time you mouse over.
.hover-glitch {
--hg-color: #e2e8f0;
--hg-accent: #ef4444;
--hg-speed: 0.3s;
display: inline-block;
padding: 14px 32px;
border-radius: 10px;
background: #1e293b;
color: var(--hg-color);
font-weight: 600;
font-size: 15px;
font-family: system-ui, sans-serif;
position: relative;
cursor: pointer;
transition: background var(--hg-speed);
}
.hover-glitch:hover {
animation: hglitch var(--hg-speed) steps(2);
}
@keyframes hglitch {
0% { transform: translate(0); }
20% { transform: translate(-3px, 2px); text-shadow: 3px 0 var(--hg-accent), -3px 0 #3b82f6; }
40% { transform: translate(3px, -1px); text-shadow: -2px 0 var(--hg-accent), 2px 0 #3b82f6; }
60% { transform: translate(-2px, 1px); text-shadow: 2px 0 var(--hg-accent), -2px 0 #3b82f6; }
80% { transform: translate(1px, -1px); text-shadow: -1px 0 var(--hg-accent); }
100% { transform: translate(0); text-shadow: none; }
}
<div class="hover-glitch">Hover me</div>
Tip: Change the blue #3b82f6 to #22d3ee (cyan) for a more retro CRT monitor look.
29. Hover Pulse
A gentle breathing scale pulse on hover — subtle and elegant for CTAs.
How It Works
The simplest hover effect in this list. On hover, an infinite alternate animation scales between scale(1) and scale(var(--hp-scale)). The alternate direction makes it pulse back and forth, while ease-in-out keeps it smooth. A quick transition: transform 0.15s on the base state handles the initial scale-up when the mouse enters.
.hover-pulse {
--hp-color: #3b82f6;
--hp-scale: 1.08;
--hp-speed: 0.6s;
display: inline-block;
padding: 14px 32px;
border-radius: 12px;
background: var(--hp-color);
color: #fff;
font-weight: 600;
font-size: 15px;
font-family: system-ui, sans-serif;
cursor: pointer;
transition: transform 0.15s ease;
}
.hover-pulse:hover {
animation: hp-pulse var(--hp-speed) ease-in-out infinite alternate;
}
@keyframes hp-pulse {
from { transform: scale(1); }
to { transform: scale(var(--hp-scale)); }
}
<div class="hover-pulse">Pulse</div>
Tip: For a more dramatic heartbeat effect, use scale(1.15) with --hp-speed: 0.3s.
30. Slide Background
Background slides in from the left on hover, pushing the old color out.
How It Works
A ::before pseudo-element covers the full area with the new background color, but starts at scaleX(0) with transform-origin: left. On hover, it scales to scaleX(1), sliding in from the left edge. The text and border colors transition simultaneously. Using scaleX instead of width gives smoother performance since it’s GPU-accelerated.
.hover-slide-bg {
--hsb-from: #1e293b;
--hsb-to: #3b82f6;
--hsb-speed: 0.3s;
position: relative;
display: inline-block;
padding: 14px 32px;
border-radius: 10px;
font-weight: 600;
font-size: 15px;
font-family: system-ui, sans-serif;
color: #e2e8f0;
cursor: pointer;
z-index: 1;
overflow: hidden;
border: 1px solid #374151;
background: var(--hsb-from);
transition: color var(--hsb-speed) ease, border-color var(--hsb-speed) ease;
}
.hover-slide-bg::before {
content: '';
position: absolute;
inset: 0;
background: var(--hsb-to);
transform: scaleX(0);
transform-origin: left;
transition: transform var(--hsb-speed) ease;
z-index: -1;
}
.hover-slide-bg:hover {
color: #fff;
border-color: var(--hsb-to);
}
.hover-slide-bg:hover::before {
transform: scaleX(1);
}
<div class="hover-slide-bg">Slide In</div>
Tip: Change transform-origin: left to right to slide from the opposite side, or center to expand from the middle.
What’s Next?
That’s 10 hover effects covering the most common interaction patterns. But we’re only halfway through the hover category — CSSKit has 24 total. In the next post, we’ll cover 14 more hover effects including 3D tilt, border spin, backdrop blur, and icon swap.
Explore all 310 animations live at CSSKit or star the repo on GitHub.
You might also like
CSS Hover Effects Part 2 — 14 More Interactive Animations
14 more CSS hover effects with full code: text swap, zoom rotate, border spin, sink, color shift, underline grow, 3D tilt, sweep, shine line, expand border, backdrop, raise, skew, and icon swap.
CSS Attention Animations — 16 Effects with Code
Build 16 CSS attention animations (shake, pulse glow, rubber band, bounce, flash, heartbeat, jello, swing, pulse border, wiggle, breathe, radar, glow text, magnet, swing bounce, color flash) with live previews and copy-paste code.
CSS Background Animations — 10 Atmospheric Effects with Code
Build 10 CSS background effects from scratch: gradient shift, floating particles, aurora, ripple, bubbles, confetti, grid pulse, mesh gradient, matrix rain, and starfield. Full copy-paste code for each.
More Posts
API Gateway Patterns: The Front Door to Your Microservices
Web Components 2026: Building Framework-Agnostic UI Libraries
Building Autonomous AI Workflows with LangGraph: A Practical Guide
Building Type-Safe APIs with tRPC in 2026: Full-Stack TypeScript Without Schemas
Database Connection Pooling: Patterns for High-Performance Applications
Prompt Caching: Reduce LLM Costs by 90% with Smart Context Management
Enjoyed This Post?
Want to discuss the topic, have questions, or looking to collaborate on something similar? Drop a comment below or reach out directly.
