CSS Snippets

Pure CSS Hover Effects

A collection of reusable, dependency-free hover effects built with plain CSS transitions and transforms. Every card previews the effect live with a real pointer interaction, then shows the exact stylesheet you can copy into any project. No JavaScript, no build step: the :hover pseudo-class, the transition property and a few keyframes do the work.

The nine patterns cover underline slide, lift, scale, border glow, gradient sweep, shrink, rotate, pulse and shine.

Underline Slide

A single underline slides in from the right while the label tints toward the accent color.

Hover me
CSS
/* Underline slide */
.ddk-hover-underline {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 22px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1;
  color: #E4E4E7;
  background: #27272A;
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  transition: color 0.3s ease;
}
.ddk-hover-underline::after {
  content: '';
  position: absolute;
  left: 14px;
  right: 14px;
  bottom: 6px;
  height: 2px;
  background: #A35FFF;
  border-radius: 1px;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}
.ddk-hover-underline:hover {
  color: #C4B5FD;
}
.ddk-hover-underline:hover::after {
  transform: scaleX(1);
  transform-origin: left center;
}

Lift + Shadow

The element floats upward and casts a soft, accent-tinted shadow beneath it.

Hover me
CSS
/* Lift + shadow */
.ddk-hover-lift {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 22px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1;
  color: #E4E4E7;
  background: #27272A;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
  transition: transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
}
.ddk-hover-lift:hover {
  transform: translateY(-6px);
  border-color: rgba(163, 95, 255, 0.6);
  box-shadow: 0 14px 30px -8px rgba(163, 95, 255, 0.45);
}

Scale

Grows slightly with a tactile zoom and a deeper accent glow around the edges.

Hover me
CSS
/* Scale */
.ddk-hover-scale {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 22px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1;
  color: #FFFFFF;
  background: #A35FFF;
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  box-shadow: 0 4px 14px -4px rgba(163, 95, 255, 0.5);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.ddk-hover-scale:hover {
  transform: scale(1.08);
  box-shadow: 0 8px 24px -6px rgba(163, 95, 255, 0.65);
}

Border Glow

The border ignites into an accent outline with a soft outer halo, like a neon edge.

Hover me
CSS
/* Border glow */
.ddk-hover-glow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 22px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1;
  color: #E4E4E7;
  background: #27272A;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  transition: border-color 0.3s ease, box-shadow 0.3s ease;
}
.ddk-hover-glow:hover {
  border-color: #A35FFF;
  box-shadow: 0 0 0 1px #A35FFF, 0 0 24px rgba(163, 95, 255, 0.55);
}

Gradient Sweep

A purple gradient sweeps across the background while the label brightens to white.

Hover me
CSS
/* Gradient sweep */
.ddk-hover-sweep {
  position: relative;
  z-index: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 12px 22px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1;
  color: #E4E4E7;
  background: #27272A;
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  transition: color 0.35s ease;
}
.ddk-hover-sweep::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(120deg, #A35FFF 0%, #7C3AED 100%);
  transform: translateX(-101%);
  transition: transform 0.4s ease;
}
.ddk-hover-sweep:hover {
  color: #FFFFFF;
}
.ddk-hover-sweep:hover::before {
  transform: translateX(0);
}

Shrink

Compresses inward on hover to feel like a tactile press against the surface.

Hover me
CSS
/* Shrink */
.ddk-hover-shrink {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 22px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1;
  color: #FFFFFF;
  background: #A35FFF;
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  box-shadow: 0 4px 14px -4px rgba(163, 95, 255, 0.5);
  transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.ddk-hover-shrink:hover {
  transform: scale(0.92);
  box-shadow: 0 1px 4px -2px rgba(163, 95, 255, 0.4);
}

Rotate

Tilts and scales with a subtle overshoot for a playful, springy nudge.

Hover me
CSS
/* Rotate */
.ddk-hover-rotate {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 22px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1;
  color: #FFFFFF;
  background: #A35FFF;
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  box-shadow: 0 4px 14px -4px rgba(163, 95, 255, 0.5);
  transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.ddk-hover-rotate:hover {
  transform: rotate(6deg) scale(1.06);
}

Pulse

A continuous heartbeat while hovered, pulsing scale and glow in a smooth loop.

Hover me
CSS
/* Pulse */
.ddk-hover-pulse {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 22px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1;
  color: #FFFFFF;
  background: #A35FFF;
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  box-shadow: 0 4px 14px -4px rgba(163, 95, 255, 0.5);
}
.ddk-hover-pulse:hover {
  animation: ddk-hover-pulse 0.8s ease-in-out infinite;
}
@keyframes ddk-hover-pulse {
  0%,
  100% {
    transform: scale(1);
    box-shadow: 0 4px 14px -4px rgba(163, 95, 255, 0.5);
  }
  50% {
    transform: scale(1.07);
    box-shadow: 0 0 28px rgba(163, 95, 255, 0.7);
  }
}

Shine Sweep

A glossy light streak sweeps diagonally across the surface on hover.

Hover me
CSS
/* Shine sweep */
.ddk-hover-shine {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  padding: 12px 22px;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1;
  color: #FFFFFF;
  background: #A35FFF;
  border-radius: 8px;
  cursor: pointer;
  user-select: none;
  box-shadow: 0 4px 14px -4px rgba(163, 95, 255, 0.5);
}
.ddk-hover-shine::after {
  content: '';
  position: absolute;
  top: 0;
  left: -80%;
  width: 50%;
  height: 100%;
  background: linear-gradient(
    120deg,
    transparent,
    rgba(255, 255, 255, 0.45),
    transparent
);
  transform: skewX(-20deg);
  transition: left 0.55s ease;
}
.ddk-hover-shine:hover::after {
  left: 130%;
}

How these hover effects work

Every effect in this collection starts from the same CSS building blocks. The :hover pseudo-class detects the interaction. As MDN defines it, :hover matches when the user interacts with an element with a pointing device, such as a mouse cursor. The state applies automatically while the pointer sits over the element, with no JavaScript listening for mouseenter or mouseleave events. The browser does the hit testing for you, and the same rule works for links, buttons and any other element you attach the class to.

The motion comes from the transition property. When a property listed in transition changes between the default state and the :hover state, the browser interpolates the intermediate values over the declared duration instead of switching instantly. The underline slide animates transform: scaleX() from 0 to 1 along its own easing curve while the lift and scale cards animate transform and box-shadow together, because each transition declaration can list several properties separated by commas. Rotate uses the cubic-bezier() timing function with a slight overshoot, and the sweep effects move a gradient layer with translateX(). These are all examples of interpolating between two computed values, which is the core mechanism behind every preview on this page. The easing function controls how fast the value changes across the duration, and that curve is what makes a lift feel springy rather than abrupt.

The pulse card is the exception. It runs a CSS animation driven by @keyframes, looping for 0.8s while hovered.

Edge cases and browser behavior

Touch devices have no hover state in the mouse sense. MDN notes that most touchscreen browsers only support :hover partially, some keeping the style stuck after a tap until another element is touched. A hover-only effect can feel random on a phone, so pair the :hover rule with :focus-visible for keyboard and switch users, and treat the animation as a desktop enhancement rather than the only feedback channel.

Motion sensitivity is a second boundary. WCAG 2.2 includes a success criterion for animation from interactions, expecting that non-essential movement can be turned off. The prefers-reduced-motion media query is the standard way to honor that preference: wrap the pulse loop or any strong displacement in a query that disables it. Color shifts also matter, since WCAG 2.2's contrast criteria apply to text and to meaningful graphical states.

Common mistakes and alternatives

The most frequent mistake is toggling a property that cannot interpolate. Display, for example, has no intermediate value, so the browser snaps between none and block with no transition at all. The second mistake is forgetting that :hover alone never fires for keyboard users, and the tab key then produces no feedback. The third issue is animating box-shadow on large surfaces, where repainting every frame is heavier than moving a transform. MDN's animation guide recommends favoring transform and opacity for smoother animation.

Alternatives abound. Replace translateY with scale for a press feel, or fade the label color instead of moving geometry. Short transitions, roughly 150 to 400ms, match the timing users expect.

FAQ

Do these hover effects work on touch devices?

Hover states on touch devices are limited: an element may keep the style briefly after a tap. Pair the hover style with :focus-visible so keyboard and touch users see the same cue.

Can I use these snippets with a framework like Tailwind or React?

Yes. The styles are plain CSS and depend on no library, so Tailwind and React both accept them as-is.

Why do some effects animate box-shadow instead of transform?

box-shadow creates the glow and depth look that transform cannot, and it suits small elements like buttons. For larger surfaces, prefer transform and opacity.

What does the cubic-bezier() value in the Underline Slide do?

cubic-bezier() defines a custom easing curve. The rotate card uses 0.34, 1.56, 0.64, 1, which overshoots slightly.

The hover state and transition references behind these snippets

The behavior these snippets rely on is defined by the CSS specifications below.

  • MDN: the :hover pseudo-class defines when the hover state matches and how touch browsing behaves, which backs the state detection notes above.
  • MDN: CSS transitions documents the property, duration and timing functions that drive the interpolated motion in these cards.
  • MDN: box-shadow specifies the shadow syntax used by the lift, glow and pulse effects.
  • MDN: Using CSS animations explains the @keyframes rules and the animation shorthand behind the pulse loop.
  • WCAG 2.2 provides the contrast criteria and the animation-from-interactions requirement that apply to hover feedback and reduced-motion users.