Skip to content

Legible per-source error banner - #68

Open
trevormunoz wants to merge 1 commit into
mainfrom
worktree-element-error-panel
Open

trevormunoz wants to merge 1 commit into
mainfrom
worktree-element-error-panel

Conversation

@trevormunoz

Copy link
Copy Markdown
Member

What

The player's failure banner rendered the raw technical error.message (e.g. HLS error: networkError, or a bare fetch error) for every error. During a real upstream outage that reads as noise — a viewer can't tell the source is unreachable. This replaces it with a short, source-keyed message.

  • Source on state. A setError(error, source) funnel on PlayerStateManager records the error source alongside the error. Every existing error site routes through it — Root (manifest/canvas/auth/media), Viewer (native/HLS/DASH), PlayerState (playback) — with no change to the iiif-player-error / errorCallback payloads.
  • Friendly copy. A pure resolveErrorMessageKeys(source) maps the source to new, translatable TermKeys: manifest/canvas → "This item couldn't be loaded.", media → "This media couldn't be played.", auth → "Sign-in required." — each with a neutral cause line (no "temporarily" claim, since failures include permanent ones). Non-fatal errors (e.g. a blocked play()) still render, with a generic friendly message.
  • Presentation. The banner is role="status" (polite) with part="error". The compound Root stays visually unstyled; the element gives the part a default-styled panel via ::part(error) and --iiif-player-error-fg / --iiif-player-error-bg.

Behavior change (observable)

  • Banner role: alert → status.
  • Banner text: raw error.message → friendly per-source copy.

A host that drove its own error UI off the old banner can hide the default with iiif-transcript-player::part(error) { display: none } and keep using iiif-player-error / errorCallback. Documented in the README.

Accessibility

  • Default banner colors are contrast-checked (#b91c1c on #fbeae8 = 5.45:1) and added to the TOKEN_CONTRAST_PAIRS guard. An earlier draft used opacity: 0.85 on the detail line — that computed to 4.44:1 and the axe suite caught it; opacity removed.
  • role="status" chosen over alert: a failed load is informational, not an interrupt.

Out of scope

No retry button (recovery is replacing the element, as documented); no raw/HTTP detail line in the default (a host can render it via ::part(error)); no auth login flow (a host adds a sign-in link).

Testing

  • New unit test for resolveErrorMessageKeys; new PlayerStateManager setError/clearError test.
  • New ::part(error) reachability test; element-errors / Root banner tests updated for role="status" and the friendly copy.
  • Full suite green (736 tests); typecheck, lint, and the element build's custom-elements-manifest + contrast guards all pass. Introduced in 07aac69.

Relates to the <iiif-transcript-player> element surface (#61).

🤖 Generated with Claude Code

https://claude.ai/code/session_01D2MtErpTjB6bWPrn3EnCgV

The player's failure banner showed the raw technical error.message
(e.g. "HLS error: networkError") for every error, so a viewer had no
idea a fatal load failure — like an upstream manifest/media 5xx — came
from the source being unreachable. It now shows a short, source-keyed
message instead.

- Thread the error source onto player state via a setError(error, source)
  funnel on PlayerStateManager; every error site (Root manifest/canvas/
  auth/media, Viewer native/HLS/DASH, PlayerState playback) sets it.
- resolveErrorMessageKeys maps the source to new, translatable TermKeys:
  manifest/canvas -> "This item couldn't be loaded." media -> "This media
  couldn't be played." auth -> "Sign-in required." — each with a neutral
  cause line that makes no (mis)claim about the failure being temporary.
  Non-fatal errors still render, with a generic friendly message.
- The banner is role="status" (polite), not assertive alert, and carries
  part="error"; the element gives it a default-styled panel via
  ::part(error) and --iiif-player-error-fg/-bg (contrast 5.45:1, added to
  the TOKEN_CONTRAST_PAIRS guard). The compound Root stays unstyled.

Behavior change: the banner's role (alert -> status) and copy (raw ->
friendly) are observable. Hosts that drove their own error UI can hide
the panel with `::part(error){display:none}` and keep using
iiif-player-error / errorCallback.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D2MtErpTjB6bWPrn3EnCgV

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant