Utility · Announce

Announce

ARIA live-region service for screen-reader announcements. The component itself paints nothing — its job is to write text into hidden live regions so assistive tech reads it aloud. Two usage shapes: imperative (api.announce(msg, priority) from any descendant) and declarative (<Announce.Region message> bound to reactive state). Compose with Clipboard, DragDrop, form validation, chat logs.

parts{compiled.parts.order.length} events{events.length} history {history.length} region role {regionRole} timeout {defaultTimeout}ms
{#snippet children(snippetProps)} {(api = snippetProps, '')}
Imperative path · api.announce(msg, priority)
{ if (e.key === 'Enter') sendCustom(); }} placeholder="Type a message to announce…" style="flex: 1 1 18rem; padding: 0.375rem 0.625rem; border: 1px solid var(--color-border-default); border-radius: var(--radius-md); background: var(--color-surface-default); color: var(--color-content-primary); font: inherit;" /> {#each ['polite', 'assertive'] as p (p)} {/each}
{#each SCENARIOS as s (s.label)} {/each}
Live region inspector · what the screen reader currently sees
polite
{politeRegionText || '—'}
assertive
{assertiveRegionText || '—'}
Declarative path · <Announce.Region role message>
Log mode · role="log" — running history (chat / activity)
{ if (e.key === 'Enter') pushLog(); }} placeholder='Append a "chat" line…' style="flex: 1 1 auto; padding: 0.375rem 0.625rem; border: 1px solid var(--color-border-default); border-radius: var(--radius-md); background: var(--color-surface-default); color: var(--color-content-primary); font: inherit;" />
{#if logEntries.length === 0}
No entries yet — type and Enter to append.
{:else}
    {#each logEntries as line, i (i + line)}
  • {line}
  • {/each}
{/if}
history {#if history.length === 0} type a message and Enter, or pick a scenario — the live region inspector above shows what the AT receives {:else} {#each history.slice(0, 3) as h (h.at)} {h.priority} · "{h.msg}" {fmtTime(h.at)} {/each} {/if}
{/snippet}
{#if tab === 'live'}

Controls

The 4 ARIA region roles imply different semantics. status = polite info, alert = assertive (interrupts), log = running history, timer = time-sensitive (don't announce every tick).

soma behaviour
eidos visual · Announce + Region (imperative + declarative) svelte
{eidosSnippet}
{/if} {#if tab === 'api'}

API reference

2 parts: Provider · Region. Provider mounts 2 internal regions (polite + assertive) and exposes an imperative api via snippet props + via context. Region is for declarative purpose-specific live regions.

Provider (Announce)
PropTypeDefaultDescription
defaultTimeoutnumber1000ms before a posted message clears so the same text can be re-announced.
Region (declarative)
PropTypeDefaultDescription
role'status' | 'alert' | 'log' | 'timer''status'Implies polite/assertive + AT-specific handling semantics.
aria-live'polite' | 'assertive'from roleOverride the implicit urgency.
aria-atomicbooleantrueWhether AT reads the entire region on change (vs the diff only).
aria-relevantstring'additions text'Which mutations trigger an announcement.
messagestring—Bound reactive text — AT reads on change.
visuallyHiddenbooleantrueStandard sr-only technique. Set false for visible+announced banners.
Imperative API (Provider snippet props)
MethodSignatureDescription
announce(message, priority?, timeout?) => voidPost to the polite (default) or assertive internal region.
clear() => voidClear all internal live regions immediately.
{/if} {#if tab === 'morfo'}

morfo · declarative contract

Source: src/uix/morfo/components/announce.ts.

Parts
{#each partsList as part (part.kebab)} {/each}
PartMarkerElementRoleStatesOptional
{part.kebab} [{part.marker}] <{part.defaultElement}> {part.role ?? '—'} {part.states.length ? part.states.join(' | ') : '—'} {part.optional ? 'yes' : 'no'}
Events declaration
{#each events as action (action.name)} {@const sem = action.semantic} {@const intentDecl = 'intent' in sem ? sem.intent : undefined} {@const intentStr = typeof intentDecl === 'string' ? intentDecl : intentDecl ? `fromProp:${(intentDecl as { fromProp?: string }).fromProp ?? '?'}` : '—'} {/each}
namefamilyverbsequenceintenttarget
{action.name} {sem.family} {sem.verb ?? '—'} {sem.sequence ?? 'pre'} {intentStr} {action.target}
{/if} {#if tab === 'sema'}

sema · events

Announce raises commit-announce-polite / commit-announce-assertive on every message post, and commit-reset when regions are wiped. Use Play to replay an event against the Provider.

{#each events as action (action.name)} {@const intentDecl = 'intent' in action.semantic ? action.semantic.intent : undefined} {@const effectiveIntent = typeof intentDecl === 'string' ? intentDecl : undefined} {/each}
NameFamilyVerbSequenceIntentPlay
{action.name} {action.semantic.family} {action.semantic.verb ?? '—'} {action.semantic.sequence ?? 'pre'} {effectiveIntent ?? '—'}
{/if} {#if tab === 'recipe'}

Eidos recipe

Selectors at src/uix/eidos/components/announce/announce.css. The Provider is display: contents — no visual surface. Regions get the standard sr-only technique unless visuallyHidden={false}.

SelectorSourceWhat it paints
[data-announce]eidosdisplay: contents — invisible host.
[data-announce-region]morfoLive region — sr-only when visuallyHidden.
[data-announce-region]:not([data-visually-hidden])eidosCard chrome when the Region opts into visible UI.
{/if} {#if tab === 'a11y'}

Accessibility

Announce IS the perception channel for the screen reader audience — the component's whole purpose is to write text into aria-live regions. Without an active screen reader, the regions are functionally invisible (sr-only). The live region inspector above mirrors the region contents so you can verify behaviour without launching a real AT.

Role implications
roleImplicit aria-liveUse forExample message
statuspoliteNon-critical updates."Profile saved"
alertassertiveErrors, urgent notices."Connection lost"
logpoliteRunning history (chat, activity).New chat lines appended.
timerpoliteTime-sensitive info (don't announce every tick)."5 minutes remaining"
Best practices
{/if}