Component
Persistent Toast
Toast that stays open until explicitly dismissed, with a shrinking progress bar showing the remaining time. The bar pauses on hover and resumes on leave. Ideal for upload results and important notices that shouldn't be missed.
- Component
- Tailwind
- HTML
- MIT
What it does
Capabilities
- Countdown progress bar
- Stays open until dismissed
- Pause on hover
- Resume on leave
- Close button
- Role status
- Aria-live polite
Preview
Live component
9:41 100%
devsnips.site/library/tailwind/components/toasts/persistent-toast/ fluid · no fixed width 100%
Install
Add to your project
npx devsnips add Tailwind/Components/Toasts/persistent-toast Tailwind/Components/Toasts/persistent-toast <!--
Snippet Name: Persistent Toast
Description: Toast that stays open until explicitly dismissed, with a shrinking progress bar showing the remaining time. Great for upload/download progress or important notices.
Author: DevSnips Contributors
Usage Example: Call showToast() for a non-ignorable confirmation that still times out.
-->
<div
id="persistent-toast-region"
class="pointer-events-none fixed bottom-4 right-4 z-50 flex w-full max-w-sm flex-col items-end gap-3"
role="region"
aria-label="Notifications"
aria-live="polite"
data-toast-region
></div>
<!-- Demo trigger button -->
<button onclick="showToast()" class="inline-flex items-center gap-2 rounded-xl bg-blue-600 px-5 py-2.5 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-blue-700 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3 3-3"/></svg>
Upload file
</button>
<script>
(function () {
const root = document.currentScript.closest('[data-toast-region]');
if (!root) return;
const DURATION = 6000;
function dismiss(toast) {
if (!toast || toast.dataset.dismissed === 'true') return;
toast.dataset.dismissed = 'true';
toast.classList.add('translate-x-2', 'opacity-0');
toast.addEventListener('transitionend', () => toast.remove(), { once: true });
setTimeout(() => toast.remove(), 400);
}
function showToast() {
const toast = document.createElement('div');
toast.className = 'toast-item pointer-events-auto relative w-full overflow-hidden rounded-xl border border-gray-200 bg-white shadow-lg ring-1 ring-black/5 transition duration-300 ease-out';
toast.setAttribute('role', 'status');
toast.innerHTML = `
<div class="flex items-start gap-3 p-4">
<span class="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-blue-100 text-blue-600">
<svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3 3-3"/></svg>
</span>
<div class="min-w-0 flex-1">
<p class="text-sm font-semibold text-gray-900">Upload complete</p>
<p class="mt-0.5 text-sm text-gray-500">design-system.fig was uploaded successfully.</p>
</div>
<button type="button" class="toast-close shrink-0 rounded-md p-1 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 focus:outline-none focus-visible:ring-2 focus-visible:ring-gray-400" aria-label="Dismiss notification">
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</div>
<div class="toast-progress h-1 w-full origin-left scale-x-100 bg-blue-500 transition-transform ease-linear" style="transform: scaleX(1)"></div>`;
root.appendChild(toast);
const bar = toast.querySelector('.toast-progress');
const closeBtn = toast.querySelector('.toast-close');
// Start the countdown bar on the next frame so the transition runs
function startBar() {
requestAnimationFrame(() => {
bar.style.transitionDuration = DURATION + 'ms';
bar.style.transform = 'scaleX(0)';
});
}
startBar();
const timer = setTimeout(() => dismiss(toast), DURATION);
function pause() { clearTimeout(timer); bar.style.transitionDuration = '0ms'; }
function resume() {
const rect = bar.getBoundingClientRect();
const remaining = rect.width > 0 ? DURATION * (rect.width / bar.offsetWidth) : DURATION;
bar.style.transitionDuration = remaining + 'ms';
bar.style.transform = 'scaleX(0)';
}
closeBtn.addEventListener('click', () => { clearTimeout(timer); dismiss(toast); });
toast.addEventListener('mouseenter', () => pause());
toast.addEventListener('mouseleave', () => resume());
}
window.showToast = showToast;
})();
</script> # Persistent Toast
Persistent toast that stays until dismissed.
## Usage
```bash
npx devsnips add Tailwind/Components/Toasts/persistent-toast
```
Copy from `code.html`.
## File Structure
`code.html` · `preview.html` · `metadata.json` · `README.md` 85 lines UTF-8 · LF · Spaces: 2
DevSnips AI ready Claude · Cursor · Windsurf
Agent skill & Model Context Protocol
Pass this component straight into your AI workflow. DevSnips exposes each resource as a structured, addressable registry entry, so your agent installs the real files instead of approximating them.
devsnips://components/toasts/persistent-toast I want to use this DevSnips resource in production: Page: https://devsnips.site/library/tailwind/components/toasts/persistent-toast/ Install: npx devsnips add Tailwind/Components/Toasts/persistent-toast This is the Persistent Toast — Toast that stays open until explicitly dismissed, with a shrinking progress bar showing the remaining time. The bar pauses on hover and resumes on leave. Ideal for upload results and important notices that shouldn't be missed. Use this DevSnips resource as the starting point for my project. Adapt it to my project's stack and conventions, fix any issues, and make it production-ready while preserving its intended design and behavior.
Paste into ChatGPT, Claude or your agent — it already carries the page URL and the exact install path.
Resource URI
devsnips://components/toasts/persistent-toast