2 Pure CSS5 with JSPublished 2026-10-05Free · no sign-up
Seven fade-ins for everyday interface moments: content that arrives in sequence on page load, a native popover that fades, paragraphs that fade in as you scroll, an image that only appears once loaded, a dialog with a fading backdrop, a cross-fading tip and tab panels that fade.
Header, text and button fade in one after another when the page first paints, a gentle start that guides the eye. Each element carries an index for its delay, and the fade starts from a nearly finished state so slow devices never show blank space for long.
View code
<section class="fi1"><p class="k" style="--i:0">New season</p><h3 style="--i:1">Light layers for warm days</h3><a href="#shop" style="--i:2">Shop the edit</a></section>
A native popover that fades and scales in when opened and fades out when closed. The popover attribute gives light dismiss and Escape for free, @starting-style supplies the opening frame, and allow-discrete keeps it visible until the fade-out finishes.
Paragraphs fade in as they enter the scrolling panel and then stay. It is the plain fade without movement, which is the safest scroll effect for motion-sensitive readers, and it still switches off under reduced motion.
View code
<div class="fi3" tabindex="0" role="region" aria-label="Article, scroll"><p class="sp">Scroll ↓</p><p class="f">The kiln reaches 1,240 degrees on a long firing.</p><p class="f">Glazes shift colour as the temperature climbs.</p><p class="f">Every bowl comes out a little different.</p></div>
var box = document.querySelector('.fi3'); box.classList.add('js');
var io = new IntersectionObserver(function (es) { es.forEach(function (e) { if (e.isIntersecting) { e.target.classList.add('in'); io.unobserve(e.target); } }); }, { root: box, threshold: .5 });
box.querySelectorAll('.f').forEach(function (p) { io.observe(p); });
04. Image Fades In When Loaded
FadeImages
An image that fades in only once it has actually loaded, so people never see it paint in strips. The img starts transparent only after script runs, the load event adds a class, and an already-cached image is handled by checking complete.
View code
<figure class="fi4"><img width="240" height="160" alt="A soft gradient standing in for a photo" src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 240 160'%3E%3Cdefs%3E%3ClinearGradient id='g' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%2306b6d4'/%3E%3Cstop offset='1' stop-color='%237c3aed'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='240' height='160' fill='url(%23g)'/%3E%3C/svg%3E"><figcaption>Fades in on load</figcaption></figure>
var f = document.querySelector('.fi4'), img = f.querySelector('img'); f.classList.add('js');
function done() { img.classList.add('ok'); }
if (img.complete) requestAnimationFrame(done); else img.addEventListener('load', done);
05. Dialog with Fading Backdrop
FadeDialog
A confirmation dialog whose box and dimmed backdrop both fade in together. The native dialog element handles focus and Escape; the ::backdrop pseudo-element gets its own transition so the page darkens smoothly rather than snapping.
View code
<div class="fi5"><button class="open">Delete project</button><dialog aria-labelledby="fi5h"><h3 id="fi5h">Delete this project?</h3><p>This removes 14 files. It cannot be undone.</p><div><button class="no">Cancel</button><button class="yes">Delete</button></div></dialog></div>
var d = document.querySelector('.fi5 dialog');
document.querySelector('.fi5 .open').onclick = function () { d.showModal(); };
d.querySelectorAll('button').forEach(function (b) { b.onclick = function () { d.close(); }; });
06. Cross-Fading Quote Swap
FadeContent
Pressing Next fades the current tip out and the next one in, rather than replacing the text abruptly. The text is swapped at the midpoint of a two-part transition, and the paragraph is a live region so each new tip is announced.
View code
<div class="fi6"><p aria-live="polite">Name your colours by role, not by hue.</p><button>Next tip</button></div>
var p = document.querySelector('.fi6 p'), tips = ['Name your colours by role, not by hue.', 'Space with a scale, not ad-hoc pixels.', 'Test focus states before shipping.'], k = 0;
document.querySelector('.fi6 button').onclick = function () { p.classList.add('hide'); setTimeout(function () { k = (k + 1) % tips.length; p.textContent = tips[k]; p.classList.remove('hide'); }, 250); };
07. Tab Panels That Fade
FadeTabs
Switching tabs fades the new panel in instead of snapping, using a keyframe animation on the panel that becomes visible. The tabs follow the ARIA tabs pattern with roving tabindex and arrow keys, so the fade sits on a fully accessible component.
var tabs = document.querySelectorAll('.fi7 [role=tab]');
function sel(t) { tabs.forEach(function (x) { var on = x === t; x.setAttribute('aria-selected', on); x.tabIndex = on ? 0 : -1; document.getElementById(x.getAttribute('aria-controls')).hidden = !on; }); t.focus(); }
tabs.forEach(function (t, i) { t.onclick = function () { sel(t); }; t.onkeydown = function (e) { if (e.key === 'ArrowRight' || e.key === 'ArrowLeft') { e.preventDefault(); sel(tabs[(i + (e.key === 'ArrowRight' ? 1 : tabs.length - 1)) % tabs.length]); } }; });
About these CSS Fade In Animations
Fade, the gentlest motion
A fade changes only opacity, with no movement, which makes it the safest animation for people sensitive to motion. It still switches off under prefers-reduced-motion in these demos, but it is a good default.
Fading things in from display: none
Elements that appear, such as popovers and dialogs, need @starting-style for their first frame and allow-discrete on display and overlay so they stay visible while fading out.
Wait for images to load
Fading in an image before it has loaded just fades in an empty box. Listen for the load event, and check img.complete for images already in the cache.
Never trap content at zero opacity
Set the hidden starting state from script or a supported feature check. If the page loads without JavaScript, everything should simply be visible.
Frequently asked questions
What duration should a fade use?
Around 200 to 400ms for interface elements, up to 800ms for large images.
Do popovers need JavaScript?
No. popovertarget opens and closes them, including light dismiss and Escape.
Does fading hurt Largest Contentful Paint?
Fading the main hero image delays when it counts as painted; fade secondary content instead.