Never time out a snackbar that carries an action

M3's accessibility page forbids it outright: an actioned snackbar has to wait
for the person to read it and reach its action. An entry with an action and no
timeout of its own is now untimed, the close button it already draws being the
way out; a timeout written out still wins. Plan step 11, actions.md ACT-03.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qwx5USif3wFFmxtHg5U1g9
This commit is contained in:
Andreas Reinhold
2026-09-14 05:54:42 +02:00
co-authored by Claude Opus 5
parent 04b655fa9b
commit 6e24ff2cf8
3 changed files with 13 additions and 6 deletions
+8 -2
View File
@@ -3,7 +3,8 @@
*
* One snackbar at a time, as M3 shows them. Each waits its turn, stays for its timeout (paused
* while hovered or focused, so it is never pulled away from someone reading or reaching for its
* action) and is replaced by the next.
* action) and is replaced by the next. A snackbar carrying an action has no timeout at all —
* M3's accessibility page forbids one — unless the caller writes a timeout out.
*
* A `sticky` toast ("A new version is ready" with a Reload action) stays until it is answered, but
* never holds the queue up: it is kept aside rather than queued, a toast that arrives while it shows
@@ -52,12 +53,17 @@ document.addEventListener('alpine:init', () => {
const toast = Array.isArray(detail) ? detail[0] : detail
const sticky = toast.sticky === true
// M3 forbids a snackbar with an action from auto-dismissing: it has to wait for the
// person to read it and reach it. A timeout written out still wins, for a caller who
// means it; a missing one no longer falls back to the default.
const untimed = sticky || toast.timeout === 0 || toast.timeout === null || (toast.action != null && toast.timeout === undefined)
const entry = {
id: ++sequence,
type: toast.type ?? null,
title: toast.title ?? '',
description: toast.description ?? null,
timeout: sticky || toast.timeout === 0 || toast.timeout === null ? 0 : (toast.timeout ?? DEFAULT_TIMEOUT_MS),
timeout: untimed ? 0 : (toast.timeout ?? DEFAULT_TIMEOUT_MS),
action: toast.action ?? null,
sticky,
}