16 CSS Fade In Animation Designs 13 / 16

Greyscale to Color Saturate Fade

Three profile cards transition from filter: saturate(0) brightness(0.7) to full color, creating a greyscale-to-vivid reveal with cascading animation delays.

Pure CSS MIT licensed
Live Demo Open in tab
Open in playground

The code

<div class="fi-13">
  <div class="fi-13__photo">
    <div class="fi-13__dot"></div>
    <div class="fi-13__overlay">
      <div class="fi-13__pname">Alex Chen</div>
      <div class="fi-13__prole">Lead Designer</div>
    </div>
  </div>
  <div class="fi-13__photo">
    <div class="fi-13__dot"></div>
    <div class="fi-13__overlay">
      <div class="fi-13__pname">Mia Torres</div>
      <div class="fi-13__prole">Engineering Lead</div>
    </div>
  </div>
  <div class="fi-13__photo">
    <div class="fi-13__dot"></div>
    <div class="fi-13__overlay">
      <div class="fi-13__pname">Sam Osei</div>
      <div class="fi-13__prole">Product Manager</div>
    </div>
  </div>
</div>
.fi-13{
  --bg:#111;--text:#fafafa;
  font-family:'Plus Jakarta Sans',sans-serif;
  min-height:340px;border-radius:20px;
  display:flex;align-items:center;justify-content:center;gap:16px;flex-wrap:wrap;
  padding:40px;overflow:hidden;
}
.fi-13 *,.fi-13 *::before,.fi-13 *::after{box-sizing:border-box;margin:0;padding:0}
.fi-13 ::selection{background:#888;color:#fff}

/* Greyscale → color: filter:saturate(0) opacity:0 → saturate(1) opacity:1 */
.fi-13__photo{
  position:relative;border-radius:16px;overflow:hidden;
  flex:1;min-width:140px;max-width:170px;aspect-ratio:3/4;
  opacity:0;filter:saturate(0) brightness(.7);
  animation:fi-13-color-in 1s ease-out forwards;
  cursor:pointer;
}
.fi-13__photo:nth-child(1){--d:.1s}
.fi-13__photo:nth-child(2){--d:.3s}
.fi-13__photo:nth-child(3){--d:.5s}
.fi-13__photo{animation-delay:var(--d)}
.fi-13__photo:hover{filter:saturate(1.2) brightness(1.05)!important;transform:scale(1.03);transition:all .3s}

/* Colorful gradient "photos" */
.fi-13__photo:nth-child(1){background:linear-gradient(160deg,#f97316,#ec4899,#8b5cf6)}
.fi-13__photo:nth-child(2){background:linear-gradient(160deg,#06b6d4,#3b82f6,#a855f7)}
.fi-13__photo:nth-child(3){background:linear-gradient(160deg,#10b981,#14b8a6,#0ea5e9)}

.fi-13__overlay{
  position:absolute;inset:0;background:linear-gradient(to top,rgba(0,0,0,.7) 0%,transparent 60%);
  display:flex;flex-direction:column;justify-content:flex-end;padding:16px;
}
.fi-13__pname{font-size:.85rem;font-weight:700;color:#fff;margin-bottom:3px}
.fi-13__prole{font-size:.7rem;color:rgba(255,255,255,.6)}
.fi-13__dot{
  position:absolute;top:14px;right:14px;
  width:8px;height:8px;border-radius:50%;background:#4ade80;
  box-shadow:0 0 6px #4ade80;
}

@keyframes fi-13-color-in{to{opacity:1;filter:saturate(1) brightness(1)}}
@media(prefers-reduced-motion:reduce){
  .fi-13 *{animation:none!important;opacity:1!important;filter:none!important}
}

How this works

Each photo element starts at opacity: 0; filter: saturate(0) brightness(.7). The keyframe fi-13-color-in transitions to opacity: 1; filter: saturate(1) brightness(1). CSS can animate multiple filter functions in a single filter property as long as both the from and to states declare the same function list in the same order — mismatched lists cause an abrupt jump rather than an interpolation.

The greyscale-to-color effect reads as a meaningful narrative: the UI presents a world in greyscale that gradually gains colour as it loads, evoking a film developing or a scene coming to life. Each card is delayed 200ms from its predecessor. Hover boosts saturation beyond 1.0 (saturate(1.2)) and adds a small scale, providing an active state that contrasts with both the initial greyscale and the settled colour.

Customize

  • Adjust color transition speed: change duration from 1s to 1.5s for a slower, more dramatic color emergence.
  • Add filter: contrast(1.1) to the final state for photos that 'pop' with extra vibrancy.
  • Trigger the animation on hover instead of load: move the animation to a :hover rule for on-demand colorization.
  • Change cascade stagger from .2s to .4s between items for a more deliberate reveal sequence.

Watch out for

  • filter functions must match exactly between keyframe states — saturate(0) and brightness(.7) in from must both appear in to as saturate(1) and brightness(1). Mismatched function lists cause an instant jump rather than smooth interpolation.
  • Hover applies filter: saturate(1.2) — this overrides the animation's forwarded state. If the user mouses over during the animation, the filter snaps to the hover value. Prevent this by only applying hover after a animation-duration delay via pointer-events: none that you remove in JS.
  • Animating filter is composited on the GPU but still creates a new stacking context — elements inside a filtered ancestor cannot appear above siblings outside it using z-index.

Browser support

ChromeSafariFirefoxEdge
56+ 9+ 35+ 56+

filter: saturate() + brightness() combined in one property interpolates correctly in all evergreen browsers

Search CodeFronts

Loading…