Fade out then remove
Click to dismiss. The element fades and shrinks, then is removed from the DOM only after the transition ends.
CSSJavaScriptBeginner
<div class="wrap" id="wrap">
<div class="alert"><p>Click dismiss to see me leave.</p><button>Dismiss</button></div>
</div>
.wrap{display:grid;justify-items:center}
.alert{display:flex;gap:16px;align-items:center;width:min(380px,90vw);padding:16px 18px;border-radius:12px;background:#2a1513;border:1px solid #4a2e2a;
transition:opacity .35s ease,transform .35s ease}
.alert.leaving{opacity:0;transform:scale(.94) translateY(-8px)}
.alert p{margin:0;flex:1}
button{font:inherit;background:#f08a5d;color:#270100;border:0;border-radius:8px;padding:8px 14px;font-weight:600;cursor:pointer}
.again{background:none;color:#cfc3c1;border:1px solid #4a2e2a;margin-top:16px}
const wrap = document.getElementById('wrap');
function bind(alert) {
alert.querySelector('button').onclick = () => {
alert.classList.add('leaving');
const done = () => { alert.remove(); offerBack(); };
alert.addEventListener('transitionend', done, { once: true });
setTimeout(() => alert.isConnected && done(), 600); // safety net
};
}
function offerBack() {
const b = document.createElement('button');
b.className = 'again';
b.textContent = 'Bring it back';
b.onclick = () => {
b.remove();
const a = document.createElement('div');
a.className = 'alert';
a.innerHTML = '<p>Click dismiss to see me leave.</p><button>Dismiss</button>';
wrap.prepend(a);
bind(a);
};
wrap.append(b);
}
bind(document.querySelector('.alert'));
<!doctype html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Fade out</title>
<style>
*{box-sizing:border-box}
html,body{margin:0;height:100%}
body{display:grid;place-items:center;background:#140807;color:#fffffc;font:16px/1.5 system-ui,-apple-system,"Segoe UI",sans-serif}
.wrap{display:grid;justify-items:center}
.alert{display:flex;gap:16px;align-items:center;width:min(380px,90vw);padding:16px 18px;border-radius:12px;background:#2a1513;border:1px solid #4a2e2a;
transition:opacity .35s ease,transform .35s ease}
.alert.leaving{opacity:0;transform:scale(.94) translateY(-8px)}
.alert p{margin:0;flex:1}
button{font:inherit;background:#f08a5d;color:#270100;border:0;border-radius:8px;padding:8px 14px;font-weight:600;cursor:pointer}
.again{background:none;color:#cfc3c1;border:1px solid #4a2e2a;margin-top:16px}
</style></head>
<body>
<div class="wrap" id="wrap">
<div class="alert"><p>Click dismiss to see me leave.</p><button>Dismiss</button></div>
</div>
<script>
const wrap = document.getElementById('wrap');
function bind(alert) {
alert.querySelector('button').onclick = () => {
alert.classList.add('leaving');
const done = () => { alert.remove(); offerBack(); };
alert.addEventListener('transitionend', done, { once: true });
setTimeout(() => alert.isConnected && done(), 600); // safety net
};
}
function offerBack() {
const b = document.createElement('button');
b.className = 'again';
b.textContent = 'Bring it back';
b.onclick = () => {
b.remove();
const a = document.createElement('div');
a.className = 'alert';
a.innerHTML = '<p>Click dismiss to see me leave.</p><button>Dismiss</button>';
wrap.prepend(a);
bind(a);
};
wrap.append(b);
}
bind(document.querySelector('.alert'));
</script>
</body></html>
Good for
Dismissible alerts, chips, notifications and list items.
Watch out for
Never remove the node immediately, or the exit is skipped. Wait for transitionend, with a timeout as a safety net.
Accessibility
Wrap motion in @media (prefers-reduced-motion: reduce) and switch it off or shorten it for people who ask for less movement.