make toasts accessible: ARIA live region, dismiss button, pause on hover

This commit is contained in:
Your Name
2026-02-19 19:44:06 +02:00
parent b1e0114483
commit 19f794bc45
2 changed files with 66 additions and 6 deletions

View File

@@ -1,4 +1,5 @@
import { AnimatePresence, motion } from "framer-motion";
import { X } from "lucide-react";
import { springs } from "@/lib/motion";
import { useToastStore } from "@/stores/toast-store";
@@ -10,9 +11,17 @@ const TYPE_STYLES = {
export function ToastContainer() {
const toasts = useToastStore((s) => s.toasts);
const removeToast = useToastStore((s) => s.removeToast);
const pauseToast = useToastStore((s) => s.pauseToast);
const resumeToast = useToastStore((s) => s.resumeToast);
return (
<div className="pointer-events-none fixed bottom-4 right-4 z-[100] flex flex-col gap-2">
<div
className="pointer-events-none fixed bottom-4 right-4 z-[100] flex flex-col gap-2"
role="status"
aria-live="polite"
aria-atomic="true"
>
<AnimatePresence>
{toasts.map((toast) => (
<motion.div
@@ -21,9 +30,20 @@ export function ToastContainer() {
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 20, scale: 0.9 }}
transition={springs.wobbly}
className={`pointer-events-auto rounded-lg border px-4 py-2 text-sm shadow-md ${TYPE_STYLES[toast.type]}`}
className={`pointer-events-auto flex items-center gap-2 rounded-lg border px-4 py-2 text-sm shadow-md ${TYPE_STYLES[toast.type]}`}
onMouseEnter={() => pauseToast(toast.id)}
onMouseLeave={() => resumeToast(toast.id)}
onFocus={() => pauseToast(toast.id)}
onBlur={() => resumeToast(toast.id)}
>
{toast.message}
<span className="flex-1">{toast.message}</span>
<button
onClick={() => removeToast(toast.id)}
className="shrink-0 rounded p-0.5 transition-opacity hover:opacity-70"
aria-label="Dismiss notification"
>
<X className="size-3.5" />
</button>
</motion.div>
))}
</AnimatePresence>