Toast stack: drop role=status from the global live region
All checks were successful
CD / Build and push images (push) Successful in 1m10s
CD / Deploy to Test (push) Successful in 10s
CD / Smoke tests against Test (push) Successful in 1m9s
CD / Promote to Int (push) Successful in 11s
CI / Lint, typecheck, test (push) Successful in 4m24s
CI / Build container images (push) Has been skipped
CI / Auth e2e pack (push) Successful in 6m32s
CI / Import/export fidelity gate (push) Successful in 49s
Release / Build release images and notes (push) Successful in 1m9s
Release / Release-candidate operations QA (push) Successful in 42s
Prod deploy / Deploy the released images to Prod (push) Successful in 15s

The always-mounted toast container carried role="status", so every
page-scoped getByRole('status') locator suddenly resolved to two
elements — legal.spec failed CI with a strict-mode violation. The
region keeps aria-live="polite" (announcements work the same); the
status role stays with the per-page elements that had it before.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fb2VzvcoBPHkjh8bZ6PzQn
This commit is contained in:
Claude Fable 5 2026-07-16 12:16:20 +02:00
parent 6c98a71d34
commit 4f79a816e4

View File

@ -3,7 +3,7 @@ import { createContext, useCallback, useContext, useRef, useState } from 'react'
/** /**
* App-wide toast notifications (issue #130). One provider near the root owns * App-wide toast notifications (issue #130). One provider near the root owns
* the stack; `useToast()` hands out a fire-and-forget `showToast`. Toasts are * the stack; `useToast()` hands out a fire-and-forget `showToast`. Toasts are
* announced politely (`role="status"` on the stack container), stack bottom * announced politely (`aria-live` on the stack container), stack bottom
* center above the footer, and dismiss themselves clicking one dismisses * center above the footer, and dismiss themselves clicking one dismisses
* it early. The plugin host's `ui.toast` capability feeds the same stack. * it early. The plugin host's `ui.toast` capability feeds the same stack.
*/ */
@ -49,8 +49,11 @@ export function ToastProvider({ children }: { children: React.ReactNode }): Reac
{children} {children}
{/* The live region exists permanently, so screen readers pick up {/* The live region exists permanently, so screen readers pick up
toasts added to it; individual toasts must not carry their own toasts added to it; individual toasts must not carry their own
role="status" (a region appearing WITH its content isn't read). */} role="status" (a region appearing WITH its content isn't read).
<div className="toast-stack" role="status" aria-live="polite"> Deliberately aria-live WITHOUT role="status": the region is
global and always mounted, and a second status role broke every
page-scoped getByRole('status') locator (legal.spec, CI). */}
<div className="toast-stack" aria-live="polite">
{toasts.map((toast) => ( {toasts.map((toast) => (
<div <div
key={toast.id} key={toast.id}