33 CSS Card Hover Effects26 / 33

Pure CSSMIT licensed

Grain Texture

A vintage film card where animated SVG noise grain breathes faster and stronger on hover over a sepia, vignetted frame. Suited to photography, film, archival, and any brand chasing an analog, celluloid mood.

Published Updated

Live Demo
Try it

The code

<div class="card-26">
  <article class="card-26__card">
    <div class="card-26__overlay"></div>
    <div class="card-26__inner">
      <span class="card-26__meta">SUPER 8 · 1974</span>
      <h2 class="card-26__title">Golden<br>Hour Reel</h2>
      <p class="card-26__desc">A scanned film transfer. Hover and the grain breathes, like light running through celluloid.</p>
    </div>
  </article>
</div>
@import url('https://fonts.googleapis.com/css2?family=Cormorant:wght@500;600&family=Fragment+Mono&display=swap');

.card-26,
.card-26 *,
.card-26 *::before,
.card-26 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.card-26 {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: #16130f;
  font-family: 'Fragment Mono', monospace;
  padding: 2rem;
}

.card-26__card {
  position: relative;
  width: 330px;
  height: 440px;
  border-radius: 8px;
  cursor: pointer;
  overflow: hidden;
  background: linear-gradient(160deg, #3a2e22, #1b140d);
  box-shadow: 0 20px 45px rgba(0,0,0,0.5);
}

.card-26__card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 35%, rgba(210,170,110,0.35), transparent 60%), linear-gradient(160deg, #5a4732, #241a10);
}

.card-26__overlay {
  position: absolute;
  inset: -50%;
  z-index: 2;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.12;
  mix-blend-mode: overlay;
  pointer-events: none;
  transition: opacity 0.5s ease;
  animation: card-26-move 0.6s steps(4) infinite;
}

.card-26__card:hover .card-26__overlay {
  opacity: 0.42;
  animation-duration: 0.25s;
}

@keyframes card-26-move {
  0% {
    transform: translate(0,0);
  }

  25% {
    transform: translate(-8%, 5%);
  }

  50% {
    transform: translate(6%, -8%);
  }

  75% {
    transform: translate(-5%, -4%);
  }

  100% {
    transform: translate(7%, 6%);
  }
}

.card-26__card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  box-shadow: inset 0 0 90px rgba(0,0,0,0.7);
  pointer-events: none;
}

.card-26__inner {
  position: relative;
  z-index: 4;
  height: 100%;
  padding: 2.4rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  color: #f0e2cc;
}

.card-26__meta {
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  color: #c9a877;
}

.card-26__title {
  font-family: 'Cormorant', serif;
  font-weight: 600;
  font-size: 3rem;
  line-height: 0.95;
  font-style: italic;
}

.card-26__desc {
  font-size: 0.82rem;
  line-height: 1.65;
  color: #c4b39a;
}

@media (prefers-reduced-motion: reduce) {
  .card-26__overlay {
    animation: none !important;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Card Hover Effect from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Grain Texture
Source: https://codefronts.com/motion/css-card-hover-effects/grain-texture/

A vintage film card where animated SVG noise grain breathes faster and stronger on hover over a sepia, vignetted frame. Suited to photography, film, archival, and any brand chasing an analog, celluloid mood.
## HTML
```html
<div class="card-26">
  <article class="card-26__card">
    <div class="card-26__overlay"></div>
    <div class="card-26__inner">
      <span class="card-26__meta">SUPER 8 · 1974</span>
      <h2 class="card-26__title">Golden<br>Hour Reel</h2>
      <p class="card-26__desc">A scanned film transfer. Hover and the grain breathes, like light running through celluloid.</p>
    </div>
  </article>
</div>
```
## CSS
```css
@import url('https://fonts.googleapis.com/css2?family=Cormorant:wght@500;600&family=Fragment+Mono&display=swap');

.card-26,
.card-26 *,
.card-26 *::before,
.card-26 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.card-26 {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: #16130f;
  font-family: 'Fragment Mono', monospace;
  padding: 2rem;
}

.card-26__card {
  position: relative;
  width: 330px;
  height: 440px;
  border-radius: 8px;
  cursor: pointer;
  overflow: hidden;
  background: linear-gradient(160deg, #3a2e22, #1b140d);
  box-shadow: 0 20px 45px rgba(0,0,0,0.5);
}

.card-26__card::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at 50% 35%, rgba(210,170,110,0.35), transparent 60%), linear-gradient(160deg, #5a4732, #241a10);
}

.card-26__overlay {
  position: absolute;
  inset: -50%;
  z-index: 2;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.12;
  mix-blend-mode: overlay;
  pointer-events: none;
  transition: opacity 0.5s ease;
  animation: card-26-move 0.6s steps(4) infinite;
}

.card-26__card:hover .card-26__overlay {
  opacity: 0.42;
  animation-duration: 0.25s;
}

@keyframes card-26-move {
  0% {
    transform: translate(0,0);
  }

  25% {
    transform: translate(-8%, 5%);
  }

  50% {
    transform: translate(6%, -8%);
  }

  75% {
    transform: translate(-5%, -4%);
  }

  100% {
    transform: translate(7%, 6%);
  }
}

.card-26__card::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  box-shadow: inset 0 0 90px rgba(0,0,0,0.7);
  pointer-events: none;
}

.card-26__inner {
  position: relative;
  z-index: 4;
  height: 100%;
  padding: 2.4rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  color: #f0e2cc;
}

.card-26__meta {
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  color: #c9a877;
}

.card-26__title {
  font-family: 'Cormorant', serif;
  font-weight: 600;
  font-size: 3rem;
  line-height: 0.95;
  font-style: italic;
}

.card-26__desc {
  font-size: 0.82rem;
  line-height: 1.65;
  color: #c4b39a;
}

@media (prefers-reduced-motion: reduce) {
  .card-26__overlay {
    animation: none !important;
  }
}
```

How this works

A pseudo-element covers the card face with a repeating radial-gradient of tiny noise dots — the visual grain. At rest, the grain sits at low opacity for subtle texture.

On :hover, a keyframes animation shifts the grain's background-position subtly so the noise reads as “breathing” — not a static pattern but a living surface. Speed and offset are small enough that individual dots don't visibly move; the eye sees the overall texture pulsing.

mix-blend-mode: overlay lets the grain interact with the card's colour rather than sitting flat on top — a warm card gets warmer grain; a cool card gets cool grain.

Make it yours

  • Tune grain density by adjusting the radial-gradient stops — small tight radials for fine noise, larger radials for chunky.
  • Change grain colour from --grain on .card-26 — desaturated pastels read best.
  • Adjust breathing amplitude — 1px offset for subtle, 4px for pronounced texture pulse.
  • Change blend mode — overlay for balanced, multiply for darker paper-like, soft-light for cinema film.
  • Layer two grains with different densities and blend modes for a stacked-medium film feel.

Gotchas — read before shipping

  • Breathing keyframes must respect prefers-reduced-motion — freeze at initial position under reduced motion.
  • mix-blend-mode requires the parent to have an explicit background.
  • The grain overlay must have pointer-events: none.
  • Test on OLED — deep-composited noise patterns can vibrate visually; add a subtle blur to soften.
  • Screen readers should not announce the grain — ensure the pseudo-element has no accessible name (which is default).

Browser support

ChromeSafariFirefoxEdge
45+9+16+45+

Repeating radial gradients, mix-blend-mode, and keyframes are universally supported.

Techniques used in this demo

Search CodeFronts

Loading…