diff --git a/src/lib/components/Toggle.svelte b/src/lib/components/Toggle.svelte index 9335c91..6a5627b 100644 --- a/src/lib/components/Toggle.svelte +++ b/src/lib/components/Toggle.svelte @@ -17,6 +17,10 @@ // Kept separate from the `loading` prop so callers can also force loading externally. let pending = $state(false); + // Briefly true after an async onChange rejects, so screen readers hear that + // the action failed rather than silently inferring it from the snap-back. + let snapBack = $state(false); + // Effective busy state covers both the externally-controlled prop and our internal pending flag. let busy = $derived(loading || pending); @@ -41,7 +45,11 @@ await result; checked = next; } catch (_) { - // Swallow; the toggle simply stays at its previous value. + // Swallow; the toggle stays at its previous value. Flag the snap-back + // briefly so assistive tech announces aria-invalid="true" and screen + // reader users hear that the action failed. + snapBack = true; + setTimeout(() => { snapBack = false; }, 1500); } finally { pending = false; } @@ -68,6 +76,7 @@ aria-label={label} aria-disabled={disabled ? "true" : undefined} aria-busy={busy ? "true" : undefined} + aria-invalid={snapBack ? "true" : undefined} >