Recipeaction

Status Button

Stable

Recipe do Button oficial que mostra o andamento de uma ação assíncrona onde a pessoa já está olhando: spinner enquanto roda, check quando termina e de volta ao rótulo, sem mudar de largura, com cada passo anunciado por uma região live. Preserva variantes, tamanhos e comportamento do primitive; enquanto ocupado continua focável — é aria-disabled, não disabled — e ignora novos cliques. Funciona sem controle, por um `onClick` assíncrono, ou controlado por `status`, como num formulário com `useActionState`.

Instalação

npx up-web-ds add status-button

Gera check para a biblioteca de ícones do projeto (components.json → iconLibrary).

Preview

Basic

Interaction

States

Success Label

Variants

Sizes

Failure

Form Action

Disabled

All states

Basic

Interaction

States

Success Label

Variants

Sizes

Failure

Form Action

Disabled

Usage

@/components/ui/status-button
import { StatusButton } from '@/components/ui/status-button'
Status Button
<StatusButton status="loading">Salvar alterações</StatusButton>

Props

Props de StatusButton
PropTypeDefaultDescription
children, obrigatóriaReact.ReactNodesem valor padrão
loadingAnnouncementstring"Carregando"Announced while loading.
onClick((event: React.MouseEvent<HTMLButtonElement>) => void | Promise<void>)sem valor padrãoUncontrolled mode: loading while the returned promise is pending, then success. A rejection returns the button to idle. Omit it when a form owns the submission.
onError((error: unknown) => void)sem valor padrãoReceives a rejection from `onClick`, after the button is back to idle. Without it the rejection is rethrown, so an error is never swallowed.
onStatusChange((status: StatusButtonStatus) => void)sem valor padrãoAlso called for the automatic return to idle after `successDuration`.
size"default" | "sm" | "xs" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | nullsem valor padrão
statusStatusButtonStatussem valor padrãoControlled mode, e.g. derived from `useActionState`.
successAnnouncementstring"Concluído"Announced on success when there is no `successLabel`.
successDurationnumber1500How long the success state stays before returning to idle, in ms.
successLabelReact.ReactNodesem valor padrãoShown next to the check, e.g. "Enviado" for an "Enviar" button, and announced. Without it the check stands alone and `successAnnouncement` is announced instead.
variant"link" | "destructive" | "primary" | "outline" | "secondary" | "ghost" | nullsem valor padrão
classNamestring | ((state: ButtonState) => string | undefined)sem valor padrãoCSS class applied to the element, or a function that returns a class based on the component's state.
focusableWhenDisabledbooleanfalseWhether the button should be focusable when disabled.
nativeButtonbooleantrueWhether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`).
renderReact.ReactElement<unknown, string | React.JSXElementConstructor<any>> | ComponentRenderFn<HTMLProps, ButtonState>sem valor padrãoAllows you to replace the component's HTML element with a different tag, or compose it with another component. Accepts a `ReactElement` or a function that returns the element to render.
styleReact.CSSProperties | ((state: ButtonState) => React.CSSProperties | undefined)sem valor padrãoStyle applied to the element, or a function that returns a style object based on the component's state.

Estende Omit< ButtonProps, 'onClick' | 'onError' | 'children' >. Aceita também os atributos padrão de um elemento React (282 não listados).

prop obrigatória.

Metadata

Notas de acessibilidade

Verificado em tests/e2e/status-button.spec.ts no Chromium desktop e mobile, em light e dark, com Axe sem violações WCAG 2.2 AA. Teclado verificado em navegador: o botão entra na sequência de Tab com foco visível, Enter dispara a ação, e durante a ação o foco permanece nele — ele fica aria-disabled em vez de disabled, então o foco não é devolvido à página no meio de um envio — e um segundo acionamento é ignorado, inclusive o submit de formulário. Leitor de tela verificado pela árvore de acessibilidade do navegador: o nome é o rótulo quando ocioso, "Carregando" enquanto roda e o successLabel (ou "Concluído") no sucesso. O primeiro spec mostrou que o Chrome não tira nome de dentro de um role="status", e o botão ficava sem nome enquanto carregava; agora os estados desenhados são aria-hidden, o nome vem de um span sr-only e o anúncio de uma região aria-live="polite" sempre montada, vazia quando ociosa, para que voltar ao rótulo não seja anunciado. aria-busy saiu, porque num ancestral da região ele pode segurar o anúncio que deveria sair. Os textos anunciados são props para produtos em outro idioma. A largura não muda em nenhum quadro do ciclo. Falha: com `onError` o erro é entregue à página e o botão volta a idle; sem ele o erro é relançado, para nunca ser engolido. Formulário com useActionState verificado em navegador. Touch verificado em 320px com emulação de toque: os alvos de estados, variantes, tamanhos e formulário atendem ao piso de 24x24 e o toque percorre o ciclo inteiro. Reduced motion verificado em navegador com prefers-reduced-motion: reduce — a troca vira um cross-fade sem deslocamento nem blur, o pulo do check some (useReducedMotion) e o giro do spinner, que é ambiente, para (motion-safe:animate-spin), restando o arco e o anúncio; o ciclo se completa igual. O upstream escalonava dez barras com um keyframe que nenhum projeto tem; o giro nativo do Tailwind dispensou uma capability para um spinner só.

Classificação

Layer
Recipe
Domínio
Domain-free (compartilhado)
Categoria
action
Status
Stable

Origem e distribuição

Fonte
Derived from ncdai / status-button
Licença
MIT
Distribuição
Interna

Acessibilidade

  • Teclado: revisado
  • Leitor de tela: revisado
  • Touch: revisado
  • Reduced motion: revisado

Dependências

Pacotes
motion
Ícones
check
Construído sobre
Button
Usado por
Nenhum ainda