Action button
Botón con estado · components/buttons/action-button
Un botón que cuenta el resultado ahí mismo, sin mensajes afuera: "Guardar" se transforma en "Guardando…" con un spinner, después en "Guardado" con un tilde que se dibuja y el fondo en verde, o en "No se guardó" con una cruz y el fondo en rojo si la acción falló. Las letras que se repiten entre un texto y el siguiente se quedan y se deslizan a su lugar, las nuevas suben desde un desenfoque suave, y el ancho sigue al texto con un resorte para que nada alrededor salte. La flecha se va hacia donde apunta la acción y vuelve cuando el botón descansa, solo, pasados resetAfterMs.
Datos de contacto
Cambios sin guardar
Por qué entró
Muy simple, pero tiene movimiento y la acción ahí mismo, sin depender de un label o mensajes fuera del botón: guardar, guardando, guardado.
Sirve para
- guardar / guardando / guardado en formularios
- acciones cortas con resultado en el mismo botón
- publicar, enviar o confirmar algo que tarda un segundo y tiene que decir si salió
No sirve para
- mensajes largos: no entran en el botón
- acciones destructivas: ahí va confirm-morph o hold-to-confirm
Origen: Arc UI, porteado a nuestro stack y traducido. La licencia del sitio no está declarada: se usa en nuestras apps, no se publica.
Cómo se usa
import { ActionButton } from '@/components/ui/action-button/ActionButton'
<ActionButton label="Guardar" onAction={guardarPerfil} />
<ActionButton label="Publicar oferta" pendingLabel="Publicando…" successLabel="Publicada" errorLabel="No se publicó" onAction={publicar} onActionError={avisar} />
<ActionButton label="Guardar" onAction={guardar} resetAfterMs={0} />
label: la acción en reposo.pendingLabel("Guardando…"),successLabel("Guardado") yerrorLabel("No se guardó") son los otros tres textos; conviene que compartan letras conlabelpara que la transformación se vea.onAction: lo que hace. Si devuelve una promesa, el botón queda en "cargando" hasta que resuelva; si rechaza, pasa a error y llama aonActionError(error).resetAfterMs: cuánto dura el resultado antes de volver al reposo (2400 por defecto). Con0queda fijo hasta el próximo clic.- Acepta todo lo que acepta un
<button>. Mientras carga no se deshabilita: quedaaria-disabledyaria-busy, así quien navega con teclado no pierde el foco. El texto del estado se anuncia en unrole="status". - Con
prefers-reduced-motionlos textos cambian con un fundido seco y nada se desliza. TextMorph.tsx, en la misma carpeta, es la pieza que transforma el texto; se puede usar sola para otra palabra que cambie en el lugar (es decorativa: dale el texto plano aparte).
Qué toma de tu app
El botón es el acento de la app con su texto (--ui-accent, --ui-accent-ink), el radio
y la fuente de texto; el foco usa el acento y la sombra al pasar el mouse se deriva del
texto. "Guardado" es verde y "No se guardó" rojo en todas las apps: del acento toman sólo
la luz y la saturación. Las duraciones salen de --ui-dur y --ui-ease. Necesita motion
y lucide-react (la flecha).
Details
- Source
- Arc UI · Not declared
- Added
- October 2, 2026 · francovassallo
- Tags
- guardarestadocargandolistobotonfeedback
- Tokens used
--ui-accent--ui-dur--ui-ink--ui-radius--ui-font-text--ui-accent-ink--ui-ease--ui-radius-lg--ui-line--ui-surface--ui-ink-muted- Dependencies
motionlucide-react- Used in
- Not in any app yet