Toast — auto-dismiss via onMount setTimeout, manual dismiss action, aria-live polite status region.
propstateactiononMountonDisposeshowon:clickinterpolationThis recipe doesn't have a hydrated island in the demo gallery yet. An in-browser, WASM-compiled playground (edit this source and re-render live) is planned as a follow-up — read the real source below.
// toast — prop message + action dismiss, auto-dismiss via onMount setTimeout
@state {
const message = prop({ default: 'Something happened.' })
const duration = prop({ default: 3000 })
let visible = state(false)
let _timerId: number | null = null
const dismiss = action(() => {
visible = false
if (_timerId !== null) { clearTimeout(_timerId); _timerId = null }
})
onMount(() => {
visible = true
_timerId = setTimeout(() => { visible = false }, duration) as unknown as number
})
onDispose(() => {
if (_timerId !== null) { clearTimeout(_timerId); _timerId = null }
})
}
@template {
<div
class={['toast', visible && 'toast-visible']}
role="status"
aria-live="polite"
show={visible}
>
<span class="toast-msg">{message}</span>
<button class="toast-close" on:click={dismiss} aria-label="Dismiss">×</button>
</div>
}
@style {
.toast { display: flex; align-items: center; gap: 0.75rem; padding: 0.6rem 1rem; background: var(--toast-bg, #333); color: var(--toast-fg, #fff); border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.2); min-width: 16rem; max-width: 90vw; }
.toast-msg { flex: 1; font-size: 0.9rem; }
.toast-close { background: none; border: none; color: inherit; font-size: 1.2rem; cursor: pointer; opacity: 0.7; line-height: 1; padding: 0; }
.toast-close:hover { opacity: 1; }
[hidden] { display: none; }
}statea11y