16 CSS Fade In Animation Designs 05 / 16
Scroll-Triggered Observer Fade
An activity feed list where items observe viewport entry via IntersectionObserver and acquire a .fi-05--visible class, triggering CSS transitions to slide and fade in staggered.
The code
<div class="fi-05" id="fi-05-root">
<div class="fi-05__title">Activity Feed</div>
<div class="fi-05__items">
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">Deployment successful</div><div class="fi-05__meta">production · 2 min ago</div></div><span class="fi-05__badge">Live</span></div>
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">Pull request merged</div><div class="fi-05__meta">feature/auth · 14 min ago</div></div><span class="fi-05__badge">Done</span></div>
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">Build failed on main</div><div class="fi-05__meta">CI pipeline · 1 hr ago</div></div><span class="fi-05__badge">Error</span></div>
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">New user signup spike</div><div class="fi-05__meta">analytics · 3 hr ago</div></div><span class="fi-05__badge">Alert</span></div>
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">Database backup complete</div><div class="fi-05__meta">storage · 6 hr ago</div></div><span class="fi-05__badge">Done</span></div>
</div>
</div> <div class="fi-05" id="fi-05-root">
<div class="fi-05__title">Activity Feed</div>
<div class="fi-05__items">
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">Deployment successful</div><div class="fi-05__meta">production · 2 min ago</div></div><span class="fi-05__badge">Live</span></div>
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">Pull request merged</div><div class="fi-05__meta">feature/auth · 14 min ago</div></div><span class="fi-05__badge">Done</span></div>
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">Build failed on main</div><div class="fi-05__meta">CI pipeline · 1 hr ago</div></div><span class="fi-05__badge">Error</span></div>
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">New user signup spike</div><div class="fi-05__meta">analytics · 3 hr ago</div></div><span class="fi-05__badge">Alert</span></div>
<div class="fi-05__item"><div class="fi-05__dot"></div><div class="fi-05__body"><div class="fi-05__name">Database backup complete</div><div class="fi-05__meta">storage · 6 hr ago</div></div><span class="fi-05__badge">Done</span></div>
</div>
</div>.fi-05{
--bg:#0b1120;--sky:#38bdf8;--emerald:#34d399;--rose:#fb7185;--text:#f0f9ff;
font-family:'Inter',sans-serif;
min-height:360px;border-radius:20px;
padding:32px;overflow:hidden;
}
.fi-05 *,.fi-05 *::before,.fi-05 *::after{box-sizing:border-box;margin:0;padding:0}
.fi-05 ::selection{background:var(--sky);color:#000}
.fi-05__title{
font-size:.75rem;font-weight:600;letter-spacing:.15em;text-transform:uppercase;
color:rgba(240,249,255,.35);margin-bottom:24px;
}
.fi-05__items{display:flex;flex-direction:column;gap:14px}
/* Items start hidden; JS adds .fi-05--visible via IntersectionObserver */
.fi-05__item{
display:flex;gap:16px;align-items:center;
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);
border-radius:12px;padding:16px 20px;
opacity:0;transform:translateX(-24px);
transition:opacity .6s cubic-bezier(.16,1,.3,1),transform .6s cubic-bezier(.16,1,.3,1);
}
.fi-05__item.fi-05--visible{opacity:1;transform:translateX(0)}
.fi-05__item:hover{background:rgba(255,255,255,.06);border-color:rgba(56,189,248,.2)}
.fi-05__dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}
.fi-05__item:nth-child(1) .fi-05__dot{background:var(--sky);box-shadow:0 0 8px rgba(56,189,248,.5)}
.fi-05__item:nth-child(2) .fi-05__dot{background:var(--emerald);box-shadow:0 0 8px rgba(52,211,153,.5)}
.fi-05__item:nth-child(3) .fi-05__dot{background:var(--rose);box-shadow:0 0 8px rgba(251,113,133,.5)}
.fi-05__item:nth-child(4) .fi-05__dot{background:var(--sky);box-shadow:0 0 8px rgba(56,189,248,.5)}
.fi-05__item:nth-child(5) .fi-05__dot{background:var(--emerald);box-shadow:0 0 8px rgba(52,211,153,.5)}
.fi-05__body{flex:1}
.fi-05__name{font-size:.9rem;font-weight:600;color:var(--text);margin-bottom:3px}
.fi-05__meta{font-size:.75rem;color:rgba(240,249,255,.4)}
.fi-05__badge{
font-size:.68rem;font-weight:700;padding:3px 10px;border-radius:8px;
}
.fi-05__item:nth-child(1) .fi-05__badge{background:rgba(56,189,248,.15);color:var(--sky)}
.fi-05__item:nth-child(2) .fi-05__badge{background:rgba(52,211,153,.15);color:var(--emerald)}
.fi-05__item:nth-child(3) .fi-05__badge{background:rgba(251,113,133,.15);color:var(--rose)}
.fi-05__item:nth-child(4) .fi-05__badge{background:rgba(56,189,248,.15);color:var(--sky)}
.fi-05__item:nth-child(5) .fi-05__badge{background:rgba(52,211,153,.15);color:var(--emerald)}
@media(prefers-reduced-motion:reduce){
.fi-05__item{transition:none;opacity:1;transform:none}
} .fi-05{
--bg:#0b1120;--sky:#38bdf8;--emerald:#34d399;--rose:#fb7185;--text:#f0f9ff;
font-family:'Inter',sans-serif;
min-height:360px;border-radius:20px;
padding:32px;overflow:hidden;
}
.fi-05 *,.fi-05 *::before,.fi-05 *::after{box-sizing:border-box;margin:0;padding:0}
.fi-05 ::selection{background:var(--sky);color:#000}
.fi-05__title{
font-size:.75rem;font-weight:600;letter-spacing:.15em;text-transform:uppercase;
color:rgba(240,249,255,.35);margin-bottom:24px;
}
.fi-05__items{display:flex;flex-direction:column;gap:14px}
/* Items start hidden; JS adds .fi-05--visible via IntersectionObserver */
.fi-05__item{
display:flex;gap:16px;align-items:center;
background:rgba(255,255,255,.03);border:1px solid rgba(255,255,255,.07);
border-radius:12px;padding:16px 20px;
opacity:0;transform:translateX(-24px);
transition:opacity .6s cubic-bezier(.16,1,.3,1),transform .6s cubic-bezier(.16,1,.3,1);
}
.fi-05__item.fi-05--visible{opacity:1;transform:translateX(0)}
.fi-05__item:hover{background:rgba(255,255,255,.06);border-color:rgba(56,189,248,.2)}
.fi-05__dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}
.fi-05__item:nth-child(1) .fi-05__dot{background:var(--sky);box-shadow:0 0 8px rgba(56,189,248,.5)}
.fi-05__item:nth-child(2) .fi-05__dot{background:var(--emerald);box-shadow:0 0 8px rgba(52,211,153,.5)}
.fi-05__item:nth-child(3) .fi-05__dot{background:var(--rose);box-shadow:0 0 8px rgba(251,113,133,.5)}
.fi-05__item:nth-child(4) .fi-05__dot{background:var(--sky);box-shadow:0 0 8px rgba(56,189,248,.5)}
.fi-05__item:nth-child(5) .fi-05__dot{background:var(--emerald);box-shadow:0 0 8px rgba(52,211,153,.5)}
.fi-05__body{flex:1}
.fi-05__name{font-size:.9rem;font-weight:600;color:var(--text);margin-bottom:3px}
.fi-05__meta{font-size:.75rem;color:rgba(240,249,255,.4)}
.fi-05__badge{
font-size:.68rem;font-weight:700;padding:3px 10px;border-radius:8px;
}
.fi-05__item:nth-child(1) .fi-05__badge{background:rgba(56,189,248,.15);color:var(--sky)}
.fi-05__item:nth-child(2) .fi-05__badge{background:rgba(52,211,153,.15);color:var(--emerald)}
.fi-05__item:nth-child(3) .fi-05__badge{background:rgba(251,113,133,.15);color:var(--rose)}
.fi-05__item:nth-child(4) .fi-05__badge{background:rgba(56,189,248,.15);color:var(--sky)}
.fi-05__item:nth-child(5) .fi-05__badge{background:rgba(52,211,153,.15);color:var(--emerald)}
@media(prefers-reduced-motion:reduce){
.fi-05__item{transition:none;opacity:1;transform:none}
}(function(){
const items = document.querySelectorAll('#fi-05-root .fi-05__item');
const obs = new IntersectionObserver((entries)=>{
entries.forEach(e=>{
if(e.isIntersecting){
const el = e.target;
const idx = [...items].indexOf(el);
setTimeout(()=>el.classList.add('fi-05--visible'), idx * 100);
obs.unobserve(el);
}
});
},{threshold:0.2});
items.forEach(el=>obs.observe(el));
})(); (function(){
const items = document.querySelectorAll('#fi-05-root .fi-05__item');
const obs = new IntersectionObserver((entries)=>{
entries.forEach(e=>{
if(e.isIntersecting){
const el = e.target;
const idx = [...items].indexOf(el);
setTimeout(()=>el.classList.add('fi-05--visible'), idx * 100);
obs.unobserve(el);
}
});
},{threshold:0.2});
items.forEach(el=>obs.observe(el));
})();How this works
Each .fi-05__item starts with opacity: 0; transform: translateX(-24px) and two CSS transition properties on opacity and transform. JavaScript creates an IntersectionObserver with threshold: 0.2 — the callback fires when 20% of the element enters the viewport. Inside the callback, a setTimeout indexed by the item's position in the NodeList adds 100ms × index before appending .fi-05--visible, which sets opacity: 1; transform: translateX(0).
This approach is superior to pure CSS animation for scroll content: animations only trigger when the user actually scrolls to the element, and each item animates exactly once (the observer calls unobserve after triggering). Without JS, items remain at opacity: 0 — to degrade gracefully, add a .no-js .fi-05__item { opacity: 1; transform: none } rule.
Customize
- Reduce the
thresholdinIntersectionObserverfrom0.2to0.05to trigger earlier. - Increase the per-item stagger from
100msto150msfor a more dramatic cascade. - Add a reverse animation on
IntersectionObserverisIntersecting: falseto re-hide items on scroll-out. - Change the transition direction from
translateX(-24px)totranslateY(20px)for a bottom-up reveal.
Watch out for
- If the component mounts outside the viewport (e.g. in a modal),
IntersectionObserverwon't fire until the modal opens and the element enters the viewport — this is the desired behaviour, but test for it. - The
!importantoverride on hover requires the hover rule to be more specific than the animation's fill-mode, or use a JS class toggle instead. - Without calling
obs.unobserve(el)after triggering, the observer fires again on every scroll intersection, re-triggering the animation each time the element enters and leaves the viewport.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 58+ | 12.1+ | 55+ | 58+ |
IntersectionObserver requires polyfill for IE11 and older Safari