Ir al contenido

Componentes · Ola 2 · Estados

Toast

Aviso breve que confirma una acción y se va solo, sin interrumpir. Se monta un <Toaster /> una vez, en el layout raíz del producto, y cada toast se muestra con toast() desde cualquier parte. Aparece abajo a la derecha, o arriba en un móvil.

import { Toaster, toast } from '@archeblack/ui';

Ejemplos

Variantes

success, info, warning, danger y neutral (por defecto), cada una con su ícono. Pasa el puntero por encima para detener el tiempo.

Svelte
<script lang="ts">
	import { Button, toast } from '@archeblack/ui';
</script>

<!-- El <Toaster /> está montado una vez en el layout; toast() funciona desde cualquier parte. -->
<div style:display="flex" style:flex-wrap="wrap" style:gap="var(--arche-spacing-2)">
	<Button
		size="sm"
		onclick={() =>
			toast({
				title: 'Cambios guardados',
				description: 'Tu proyecto se actualizó.',
				variant: 'success'
			})}
	>
		Éxito
	</Button>
	<Button
		size="sm"
		onclick={() =>
			toast({
				title: 'Nueva versión disponible',
				description: 'Se instala al recargar.',
				variant: 'info'
			})}
	>
		Información
	</Button>
	<Button
		size="sm"
		onclick={() => toast({ title: 'Te queda el 12 % de la cuota', variant: 'warning' })}
	>
		Advertencia
	</Button>
	<Button
		size="sm"
		onclick={() =>
			toast({
				title: 'No pudimos desplegar',
				description: 'El build falló en el paso 3.',
				variant: 'danger'
			})}
	>
		Error
	</Button>
	<Button size="sm" onclick={() => toast('Enlace copiado')}>Neutral</Button>
</div>

Con acción

action suma un botón debajo de la descripción. Al usarlo, el toast se cierra.

  • informe-q3.pdf
  • logo.svg
  • notas.md
Svelte
<script lang="ts">
	import { Button, toast } from '@archeblack/ui';

	let files = $state(['informe-q3.pdf', 'logo.svg', 'notas.md']);

	function remove(name: string) {
		const before = files;
		files = files.filter((file) => file !== name);
		// Con acción dura 10 s por defecto: hay que tener tiempo de llegar al botón.
		toast({
			title: 'Archivo borrado',
			description: name,
			action: { label: 'Deshacer', onClick: () => (files = before) }
		});
	}
</script>

<ul style:display="grid" style:gap="var(--arche-spacing-2)" style:margin="0" style:padding="0">
	{#each files as file (file)}
		<li
			style:display="flex"
			style:align-items="center"
			style:justify-content="space-between"
			style:gap="var(--arche-spacing-4)"
			style:list-style="none"
		>
			<code>{file}</code>
			<Button size="sm" variant="ghost" onclick={() => remove(file)}>Borrar</Button>
		</li>
	{:else}
		<li style:list-style="none" style:color="var(--arche-color-text-muted)">Sin archivos.</li>
	{/each}
</ul>

Cerrar desde el código

toast() devuelve un id; toast.dismiss(id) cierra ese toast. Con duration: Infinity queda hasta entonces.

Svelte
<script lang="ts">
	import { Button, toast } from '@archeblack/ui';

	let id = $state<string>();

	function start() {
		// Infinity: queda hasta que el código (o la persona) lo cierre.
		id = toast({ title: 'Subiendo 3 archivos…', duration: Infinity });
	}

	function finish() {
		if (id) toast.dismiss(id);
		id = undefined;
		toast({ title: 'Archivos subidos', variant: 'success' });
	}
</script>

<div style:display="flex" style:flex-wrap="wrap" style:gap="var(--arche-spacing-2)">
	<Button size="sm" onclick={start} disabled={!!id}>Empezar la subida</Button>
	<Button size="sm" onclick={finish} disabled={!id}>Terminar</Button>
</div>

Props

toast()

Funciones de toast
PropDescripción
toast(options)? (options: ToastOptions | string) => string Muestra un toast y devuelve su id. En el servidor no hace nada: un toast es siempre la respuesta a algo que pasó en el navegador.
toast.dismiss(id?)? (id?: string) => void Cierra el toast con ese id o, sin id, todos, también los que esperan su turno. Útil al cambiar de página o en los tests. Si el foco estaba en ese toast, pasa como con el botón de cerrar: al toast que queda o adonde estaba antes.

Opciones de toast()

Opciones de toast()
PropDescripción
title string Qué pasó, en una línea y en texto fuerte. toast("…") con un texto es lo mismo.
description? string Una oración debajo del título, en text-muted.
variant? 'neutral' | 'success' | 'warning' | 'danger' | 'info' Por defecto 'neutral'El ícono del estado (los mismos de Alert) y cómo se anuncia: danger y warning con role="alert"; los demás con role="status".
action? { label: string; onClick: () => void } Una acción con aspecto de enlace debajo de la descripción. Al hacer clic se llama a onClick y el toast se cierra.
duration? number Por defecto 6000Milisegundos visible: 6000 por defecto y 10000 con action. Infinity (o un valor de 0 o menos) lo deja hasta que se cierre. El tiempo se detiene con el puntero o el foco encima y con la pestaña oculta.

Toaster

Props de Toaster
PropDescripción
limit? number Por defecto 3Cuántos toasts se ven a la vez. Los demás esperan en orden de llegada y aparecen cuando se cierra uno.
label? string Por defecto 'Notificaciones'Nombre de la región de los toasts; con hotkey, se le suma el atajo («Notificaciones (F8)»).
dismissLabel? string Por defecto 'Cerrar'Nombre accesible del botón de cerrar de cada toast.
hotkey? string | null Por defecto 'F8'Tecla (KeyboardEvent.key) que lleva el foco a los toasts para llegar a su acción sin recorrer la página. null la desactiva.
class? ClassValue Clases del producto; se suman a arche-toaster, la <section> fija.
...rest HTMLAttributes<HTMLElement> Cualquier otro atributo va a la <section>.

Accesibilidad

  • El Toaster monta desde el principio dos regiones vivas vacías, una status y una alert: los lectores de pantalla solo anuncian los cambios de una región que ya estaba en la página. Cada toast escribe su título y su descripción en la que le toca; la acción y el botón de cerrar quedan fuera del anuncio.
  • neutral, success e info se anuncian con status (esperan a que el lector termine); danger y warning con alert (interrumpen). Las regiones llevan aria-atomic="false": un toast nuevo no vuelve a leer los que ya estaban.
  • Nunca roba el foco. F8 (o la tecla de hotkey) lleva el foco a los toasts; Tab recorre sus acciones y Escape cierra el que tiene el foco (o el más nuevo, con el foco en la sección). Al cerrar un toast con foco, por el botón, Escape o toast.dismiss(), el foco pasa al botón de cerrar del toast que queda en su lugar y, si era el último, vuelve adonde estaba (o queda en la sección, si ese elemento ya no existe o se deshabilitó): nunca queda en el <body>.
  • El tiempo se detiene mientras el puntero o el foco están sobre los toasts, y con la pestaña oculta (WCAG 2.2.1). Un toast con acción dura 10 s por defecto.
  • El botón de cerrar es un Button de solo ícono con aria-label («Cerrar») y mide 28 px.
  • El ícono es decorativo: el título nombra el estado. Escríbelo para que se entienda sin el color.
  • Con movimiento reducido el toast aparece sin desplazarse.
  • En colores forzados, la elevación (una sombra) desaparece y el toast lleva un borde.

Qué evitar

  • Un toast para un error que bloquea o que hay que corregir en un formulario. Un Alert junto a lo que falló, o el error de Field: quedan en la página.
  • La única forma de hacer algo dentro de un toast («Confirmar»). Una acción que también esté en la página, o un Dialog si hay que decidir.
  • Un toast por cada cosa que pasa (cada archivo de una subida). Uno que resuma («3 archivos subidos»), cerrando el anterior con toast.dismiss(id).
  • danger o warning para avisos que pueden esperar. neutral, success o info: no interrumpen al lector de pantalla.
  • Montar un <Toaster /> en cada página o componente. Uno solo, en el layout raíz del producto.