Skip to content

pesquisa componente stepper

Mateus Villain edited this page Aug 22, 2026 · 1 revision

Pesquisa de componente: Stepper

Premissa de escopo: "Stepper" é um nome ambíguo no mercado. Existem, na prática, dois componentes diferentes que disputam esse nome:

  1. Stepper de progresso / wizard — indicador visual de progresso do usuário através de uma sequência de etapas lógicas (ex.: checkout, onboarding, formulário multi-etapa como o AddressStepper).
  2. Stepper numérico (spin button) — campo de incremento/decremento de um valor numérico (ex.: quantidade de um produto).

Esta pesquisa trata do primeiro sentido (progresso multi-etapas), por ser o uso mais comum em design systems de produto e o mais relevante para documentação de fluxos como formulários e onboarding. A ambiguidade de nomenclatura é, aliás, um achado relevante desta pesquisa (ver seções 8 e 15): pelo menos três dos seis sistemas comparados usam "Stepper" para o componente numérico e um nome diferente para o componente de progresso — o que é uma armadilha de naming a evitar em um design system novo.


1. Definição e propósito

O Stepper (de progresso) é um componente que comunica visualmente em que ponto de um processo linear de múltiplas etapas o usuário se encontra, mostrando o que já foi concluído, o que está em andamento e o que ainda falta.

Seu trabalho principal não é decorativo — é reduzir a carga cognitiva de processos longos. Ao quebrar uma tarefa complexa (ex.: "cadastrar uma empresa") em etapas nomeadas e visíveis (ex.: "Dados da empresa → Endereço → Faturamento → Confirmação"), o Stepper permite que o usuário construa um modelo mental do esforço total antes de começar, evitando a sensação de "formulário infinito" que é uma das principais causas de abandono em fluxos longos.

Deve ser usado quando:

  • O processo é linear e dividido em etapas discretas e nomeáveis (checkout, onboarding, configuração inicial, formulários longos de cadastro).
  • Vale a pena mostrar ao usuário o esforço total do processo antes/durante a execução.
  • Existe benefício em permitir voltar a etapas anteriores para revisão.

Não deve ser usado quando:

  • O processo tem duração/andamento indeterminado (ex.: um upload, um processamento em background) — nesse caso o componente correto é um Progress Bar (Carbon e Atlassian tratam isso como dois componentes distintos: Progress Indicator/Tracker vs. Progress Bar).
  • Existem apenas 2 estados (ex.: "antes/depois") — um Toggle ou um simples texto de status resolve com menos peso visual.
  • A tarefa não é sequencial e o usuário pode/deve executar as etapas em qualquer ordem sem relação de dependência — nesse caso, Tabs ou uma lista de tarefas (checklist) comunicam melhor a ausência de ordem obrigatória.
  • Há apenas 1 etapa — não há progresso a comunicar.

Alternativas e quando preferi-las:

Alternativa Prefira quando
Progress bar (barra contínua) O processo é medido em % ou tempo, não em etapas nomeadas
Tabs As seções são independentes e não sequenciais
Accordion Todas as seções cabem na mesma tela e não há navegação forçada entre etapas
Breadcrumbs O objetivo é mostrar hierarquia de navegação, não progresso de uma tarefa
Checklist / lista de tarefas As etapas podem ser feitas em qualquer ordem e o valor está em marcar "concluído", não em posição sequencial

2. Modelo mental do usuário

O usuário que vê um Stepper espera, por convenção consolidada em checkouts de e-commerce e wizards de instalação (padrão desde os anos 2000, reforçado por Material Design, formulários de bancos e gateways de pagamento):

  • Saber quantas etapas existem no total — isso define a expectativa de esforço. Ocultar o número total de etapas (ex.: "próxima etapa" sem indicar quantas faltam) é uma quebra de expectativa que aumenta ansiedade e abandono.
  • Saber em qual etapa está agora — geralmente comunicado por destaque visual forte (cor, preenchimento, negrito) na etapa atual.
  • Poder voltar a etapas já concluídas para revisar ou corrigir dados — a maioria dos usuários espera clicar em uma etapa anterior e voltar a ela. Quando isso não é permitido (Stepper linear estrito), o usuário espera pelo menos um botão "Voltar" explícito.
  • Não conseguir pular para uma etapa futura sem completar as anteriores, quando o processo é linear — isso é esperado e não gera frustração, ao contrário de bloquear a volta.

Relação com padrões de SO/apps populares: o padrão mais forte vem de checkouts (Amazon, gateways de pagamento) e de wizards de instalação de sistema operacional (instalação do Windows/macOS, configuração inicial de apps mobile). Isso molda a expectativa de orientação horizontal no desktop (etapas em sequência da esquerda para a direita) e compactação vertical no mobile (rótulo do passo atual + "Passo 2 de 5", sem os círculos de todas as etapas visíveis simultaneamente).

Quebrar essa convenção (por exemplo, ordenar da direita para a esquerda em contextos LTR, ou não indicar o total de etapas) tem custo cognitivo real e só vale a pena se houver um ganho de UX comprovado — por exemplo, ocultar o total de etapas pode ser aceitável em fluxos adaptativos onde o número de etapas muda dinamicamente conforme as respostas do usuário (nesse caso, ainda assim vale comunicar "algumas etapas a mais" textualmente).


3. Anatomia

Elemento Tipo Função
Container do Stepper Obrigatório Agrupa todas as etapas; define orientação (horizontal/vertical)
Indicador da etapa (step indicator) Obrigatório Círculo/marcador visual que representa a etapa — contém número, ícone ou marca de conclusão
Rótulo da etapa (step label) Obrigatório Texto curto que nomeia a etapa (ex.: "Dados pessoais")
Descrição da etapa (step description) Opcional Texto auxiliar sob o rótulo, explica brevemente o conteúdo da etapa quando o rótulo sozinho não é suficiente
Linha conectora (connector) Auxiliar Linha entre indicadores consecutivos; comunica visualmente que as etapas formam uma sequência e, ao mudar de cor/preenchimento, reforça quanto já foi percorrido
Ícone de estado Auxiliar Substitui o número dentro do indicador em estados como concluído (check) ou erro (alerta)
Conteúdo da etapa (step content/panel) Depende do padrão Em steppers "verticais com conteúdo embutido" (ex.: Material UI vertical stepper), cada etapa expande para revelar seu formulário; em steppers "de navegação pura" (ex.: Carbon, Atlassian), o conteúdo fica fora do Stepper, em outra área da página
Botões de navegação (Próximo/Voltar) Auxiliar, geralmente externo ao Stepper Controlam o avanço; tecnicamente pertencem ao formulário/wizard que envolve o Stepper, não ao componente em si — mas fazem parte do fluxo

Estruturalmente, de fora para dentro: container → lista ordenada de etapas → (indicador + rótulo + descrição opcional) por etapa → conector entre etapas consecutivas.


4. Estados

Estado Quando ocorre Comportamento Representação visual
Not started / upcoming Etapas futuras, ainda não alcançadas Não interativas (ou interativas apenas se o Stepper for não-linear) Indicador vazio/contorno neutro, rótulo com cor de menor contraste (secundária)
Current A etapa em que o usuário está agora Recebe foco visual forte; conteúdo correspondente é exibido Indicador preenchido com cor de destaque (geralmente cor primária), rótulo em peso maior (bold)
Completed Etapas já concluídas com sucesso Geralmente clicável, permitindo voltar para revisão Indicador preenchido com ícone de check, cor de sucesso ou neutra escura, conector até ela também preenchido
Error Etapa concluída com dado inválido, ou etapa que falhou em validação de submissão Deve ser possível navegar de volta a ela para corrigir Indicador com cor de erro e ícone de alerta; disponibilizar mensagem textual (não só cor)
Disabled Etapa que não pode ser alcançada no momento (ex.: depende de decisão em etapa anterior que ainda não foi tomada) Não clicável, sem foco de teclado Indicador e rótulo com opacidade reduzida/cor neutra apagada
Hover Mouse sobre uma etapa clicável (geralmente etapas concluídas, se navegação livre for permitida) Feedback de que o item é interativo Leve mudança de fundo/cor no indicador ou rótulo
Focus / focus-visible Etapa recebeu foco via teclado (focus-visible) ou via qualquer meio (focus) Indica onde o foco do teclado está Contorno de foco visível (outline), nunca apenas mudança de cor de fundo — contraste mínimo de 3:1 contra o fundo adjacente (WCAG 2.2, critério 2.4.11)
Optional Etapa marcada como não-obrigatória para concluir o fluxo Pode ser pulada sem bloquear o avanço Rótulo acompanhado de texto auxiliar "(opcional)"

Distinção importante: disabled (etapa inacessível por regra de negócio, não deveria nem receber foco) é diferente de uma etapa apenas not started mas alcançável (não deveria ter aparência de desabilitada, pois isso sugere ao usuário que algo está quebrado). Confundir os dois é um erro comum: uma etapa futura em um Stepper linear não é "desabilitada", é apenas "ainda não alcançada" — a diferença importa tanto visualmente quanto semanticamente (aria-disabled só deve ser usado no primeiro caso).


5. Interações

Mouse:

  • Clique em uma etapa concluída (se o Stepper permitir navegação livre) leva de volta a ela.
  • Clique em uma etapa futura normalmente não faz nada, ou é bloqueado silenciosamente em Steppers estritamente lineares — o ideal é que o indicador nem pareça clicável (cursor padrão, não pointer) para etapas futuras não alcançáveis.
  • Hover em etapas clicáveis fornece feedback visual sutil.

Touch:

  • Área de toque de cada indicador deve respeitar o mínimo de 24×24px CSS (WCAG 2.2, critério 2.5.8), com recomendação de 44×44px para maior conforto (Apple HIG / Material).
  • Em telas estreitas, o padrão comum é comprimir o Stepper horizontal em uma versão compacta ("Passo 2 de 5") — ver seção 10.

Teclado: este é o ponto mais frequentemente mal implementado, porque não existe um padrão ARIA nativo para o Stepper de progresso (ver seção 6). As implementações de referência (Carbon, e o guia da Compound Design System) convergem para:

  • Quando o Stepper é interativo/navegável: as etapas clicáveis formam um conjunto de elementos focáveis por Tab/Shift+Tab (se implementado como lista de links/botões) — ou, alternativamente, um único tab-stop com roving tabindex e navegação por setas entre etapas, padrão que o Carbon adota ao permitir que usuários naveguem entre etapas pressionando as teclas de seta, com a primeira etapa selecionada por padrão.
  • Enter/Espaço ativa (navega para) a etapa focada, quando ela é uma etapa alcançável.
  • Etapas disabled não devem ser incluídas na ordem de tabulação.
  • Quando o Stepper é apenas informativo (não interativo, comum quando o wizard já tem seus próprios botões "Próximo/Voltar"), ele não precisa estar na ordem de tabulação — nenhum atributo ARIA precisa ser aplicado ao Stepper puramente decorativo, e a navegação real do fluxo acontece pelos botões de ação, que devem vir logo após o Stepper na ordem lógica de foco.

6. Acessibilidade

Este é o ponto mais delicado do componente: não existe um padrão dedicado ao "Stepper de progresso" no WAI-ARIA Authoring Practices Guide (APG). O único padrão de "stepper" documentado pelo W3C é o Spinbutton — um widget de input que restringe seu valor a um conjunto ou intervalo de valores discretos, tipicamente com um campo de texto e botões de incremento/decremento, que é o componente numérico, não o de progresso. Isso confirma a ambiguidade de nomenclatura descrita na seção 1 e significa que qualquer implementação do Stepper de progresso é, na prática, uma composição de padrões existentes (navegação + lista ordenada), não um padrão ARIA único e oficial. Esse é, em si, um achado a documentar explicitamente para quem for implementar o componente: não confiar em "o padrão ARIA do stepper" porque ele não existe para este sentido do componente.

Na ausência de um padrão oficial, a implementação de referência mais citada (adotada por bibliotecas como KendoReact) segue esta composição:

Semântica HTML e roles:

  • O container do Stepper deve ser um elemento <nav> ou ter role="navigation", já que ele existe para navegar entre etapas de um processo — o componente Stepper é um elemento de landmark <nav> ou um elemento com role="navigation", contendo uma lista ordenada de itens de navegação, cada um contendo um link.
  • As etapas devem ser marcadas como uma lista ordenada (<ol>), pois a sequência é semanticamente relevante — a ordem importa para o entendimento do processo.
  • Cada etapa clicável deve ser um elemento nativamente focável e ativável: <a> (se navega via URL) ou <button> (se apenas dispara mudança de estado em JS) — nunca uma <div> com onClick.

ARIA — roles, states e properties:

  • aria-current="step" na etapa atual — este é o atributo mais importante e específico do padrão, existindo justamente para indicar "etapa atual dentro de um processo por etapas" (valor step do atributo aria-current, que também aceita page, location, date, time, true).
  • aria-disabled="true" em etapas não alcançáveis (não disabled no sentido de remover do DOM/foco, se a etapa ainda precisa ser lida por leitor de tela como parte da sequência).
  • aria-label em cada indicador quando o conteúdo visual (número/ícone) sozinho não é suficiente como nome acessível — ex.: um indicador que mostra apenas um ícone de check precisa de um nome acessível como "Dados pessoais, concluído".
  • Se o Stepper for puramente decorativo (sem interação, com navegação feita só pelos botões do wizard), nenhum atributo ARIA extra é necessário além de landmarks básicos — não adicionar role="progressbar" nesse caso, pois esse role implica em aria-valuenow/aria-valuemin/aria-valuemax, que descrevem progresso contínuo, não discreto por etapas nomeadas.
  • Mudanças de etapa (avançar/voltar) devem ser anunciadas via uma região aria-live="polite" — idealmente anunciando número e nome da etapa nova (ex.: "Etapa 2 de 5: Endereço"), para que usuários de leitor de tela percebam a transição mesmo sem ver o Stepper diretamente na tela (esse padrão é reforçado por guias de acessibilidade de progress steps, como o da Compound Design System).

Gerenciamento de foco:

  • Ao avançar/voltar de etapa, o foco deve mover-se para o início do conteúdo da nova etapa (geralmente o heading da etapa ou o primeiro campo do formulário) — não deixar o foco "perdido" no botão que disparou a navegação nem retido no Stepper.
  • Se o Stepper usa roving tabindex com navegação por setas, apenas a etapa atualmente focável tem tabindex="0"; as demais têm tabindex="-1".

Leitores de tela:

  • O nome acessível de cada etapa deve comunicar três coisas: posição ("Etapa 2 de 5"), rótulo ("Endereço") e estado ("concluído" / "atual" / "com erro") — muitas implementações erram ao comunicar só o rótulo, deixando o usuário de leitor de tela sem saber a posição/status.

Outros critérios WCAG relevantes:

  • Contraste mínimo de 3:1 para elementos gráficos não-textuais (o indicador da etapa) contra o fundo adjacente (WCAG 1.4.11).
  • Não usar apenas cor para diferenciar estados (concluído/atual/erro) — sempre complementar com ícone, formato ou texto (WCAG 1.4.1), já que aproximadamente 1 em 12 homens tem alguma forma de daltonismo.
  • Área mínima de toque de 24×24px CSS (WCAG 2.2, critério 2.5.8) para cada indicador clicável.
  • Em zoom de até 400% / reflow (WCAG 1.4.10), o Stepper horizontal deve poder quebrar linha ou se transformar em versão vertical/compacta sem perda de conteúdo ou funcionalidade.

7. Validação e feedback

O Stepper participa indiretamente da validação de formulários multi-etapa, mesmo não sendo ele mesmo um campo de input:

  • Quando validar: o padrão mais equilibrado é validar on blur dentro da etapa atual (campo a campo) e on submit da etapa (ao clicar "Próximo"), bloqueando o avanço se houver erro — validar apenas no fim do wizard inteiro (última etapa) é frustrante porque obriga o usuário a voltar várias etapas para corrigir algo que poderia ter sido sinalizado antes.
  • Ao detectar um erro em uma etapa já concluída (ex.: uma validação assíncrona no backend que só retorna depois), o Stepper deve refletir isso mudando o estado daquela etapa para error (seção 4) e, idealmente, permitir clicar diretamente nela para corrigir, sem obrigar o usuário a navegar sequencialmente por todas as etapas intermediárias.
  • Preserve o que o usuário já digitou ao voltar para uma etapa anterior — nunca limpar campos ao navegar entre etapas do mesmo fluxo.
  • Comunique o erro tanto na etapa afetada (ícone + cor, seção 4) quanto no conteúdo daquela etapa (mensagem específica junto ao campo problemático, com aria-invalid="true" e aria-describedby apontando para a mensagem de erro — relação direta com a seção 6).

8. Conteúdo e UX Writing

  • Rótulos de etapa devem ser curtos (1 a 3 palavras) e descrever o resultado/conteúdo da etapa, não um verbo genérico de progresso.
    • ✅ "Endereço", "Pagamento", "Confirmação"
    • ❌ "Etapa 2", "Continuar", "Processando"
  • Quando um rótulo sozinho não é suficientemente claro, uma descrição curta abaixo ajuda — mas evite duplicar a mesma informação em rótulo e descrição.
    • ✅ Rótulo: "Configurar IdP" — Descrição: "Conecte seu provedor de identidade"
    • ❌ Rótulo: "Configuração" — Descrição: "Configure algumas coisas"
  • Para etapas opcionais, sinalize isso explicitamente no texto, não apenas visualmente (leitores de tela e usuários que escaneiam rapidamente o texto precisam da palavra "opcional").
  • Evite termos vagos como "Processando" como rótulo de etapa — eles não descrevem o que o usuário vai realizar ali, apenas o estado do sistema.
  • Mensagens de erro em uma etapa devem ser específicas e acionáveis, e devem indicar onde dentro da etapa está o problema, não apenas que "algo deu errado":
    • ✅ "CEP não encontrado. Verifique os números digitados."
    • ❌ "Erro na etapa 2."

9. Variantes

Variante Quando usar Quando não usar / risco
Horizontal Desktop, poucas etapas (tipicamente até 5–6), rótulos curtos Muitas etapas ou rótulos longos quebram o layout; nesse caso prefira vertical ou versão compacta
Vertical Mobile, ou quando cada etapa expõe um formulário extenso logo abaixo do seu indicador (padrão "vertical stepper com conteúdo embutido" do Material UI) Ocupa mais espaço vertical; evite em telas onde o usuário precisa ver as etapas seguintes sem rolar
Linear Processos com dependência real entre etapas (ex.: dados de uma etapa alimentam a seguinte) Não force linearidade se as etapas são, de fato, independentes — isso é um atrito artificial
Não-linear Usuário pode navegar livremente entre etapas concluídas ou mesmo futuras Cuidado ao permitir pular etapas com dependência de dados; valide integridade ao permitir avanço fora de ordem
Com número Padrão default; comunica quantidade total de forma imediata —
Com ícone Quando o ícone comunica melhor o conteúdo da etapa do que um número (ex.: um ícone de cartão para "Pagamento") Ícones sozinhos podem ser ambíguos — combine com rótulo textual, nunca substitua o texto pelo ícone
Alinhamento de rótulo (esquerda vs. centralizado) Centralizado quando há muitas etapas e espaço é limitado; esquerda é o padrão default e geralmente mais legível —
Compacta / "Passo X de Y" Mobile ou espaço muito restrito Perde a visão do todo (não mostra nomes das etapas futuras); use só quando espaço realmente não permite a versão completa

Sobre a proliferação de variantes: o conjunto essencial é orientação (horizontal/vertical) × linearidade (linear/não-linear) × densidade (completa/compacta) — as demais (alinhamento, número vs. ícone) são modificadores visuais de baixo custo de manutenção porque não mudam o comportamento, só a aparência. Evite criar variantes de "clicável" vs. "não clicável" como uma prop separada de "linear" — normalmente a interatividade decorre diretamente da linearidade e do estado da etapa (concluída = clicável; futura = não clicável, salvo Stepper explicitamente não-linear), então uma prop adicional só duplica a fonte de verdade.


10. Responsividade

  • Desktop: Stepper horizontal com todas as etapas visíveis e rótulos completos é o padrão. Há espaço para descrição opcional abaixo do rótulo.
  • Tablet: geralmente mantém o horizontal, mas os rótulos podem truncar com reticências e tooltip, ou omitir a descrição opcional.
  • Mobile: a transformação de padrão mais comum é o Stepper horizontal completo virar uma versão compacta — mostrando apenas a etapa atual em destaque (ex.: "Passo 2 de 5: Endereço") com uma barra de progresso fina abaixo, ou uma versão vertical enxuta com apenas a etapa atual expandida e as demais colapsadas em uma linha só com o rótulo. Essa mudança acontece porque a área de toque mínima (24–44px) de cada indicador, multiplicada pelo número de etapas, frequentemente não cabe na largura de uma tela mobile sem comprometer a legibilidade dos rótulos.
  • Em qualquer breakpoint, a densidade de informação (quantas etapas + rótulos + descrições simultaneamente visíveis) deve diminuir conforme o espaço diminui, mas o número total de etapas e a etapa atual nunca devem deixar de ser comunicados — é a informação mínima indispensável do componente.

11. Dependências e composição

  • Classificação: o Stepper é um componente composto (compound component) — ele é montado a partir de um componente-base (o indicador/marcador de etapa, que por sua vez usa primitivos como Ícone e Texto/Label) repetido em coleção, conectado por uma linha/conector.
  • Depende de: Ícone (para checks e estados de erro), Texto/Label (tipografia do design system), Tokens de cor de estado (sucesso, erro, neutro, primário) e Tokens de espaçamento.
  • Compõe-se com: o conteúdo de cada etapa geralmente é um Form (formulário) do design system; os controles de navegação são Buttons; mensagens de erro por etapa reutilizam o padrão de Inline Message/Helper Text usado em outros componentes de formulário.
  • Padrão maior: o Stepper raramente é usado sozinho — ele é a peça visual de um padrão (pattern) de nível mais alto, o "Wizard" ou "Formulário multi-etapa", que também inclui gerenciamento de estado entre etapas, validação, e navegação — esse padrão é o que caberia documentar separadamente como "Wizard" ou "Multi-step form" no nível de patterns do design system, com o Stepper como um dos seus blocos.

12. API e implementação

Proposta de API pensando em uma biblioteca React moderna, com composição controlada (o estado de "etapa atual" fica com quem consome o componente — abordagem controlada é preferível aqui porque o wizard que envolve o Stepper quase sempre precisa saber e reagir à etapa atual para renderizar o conteúdo correspondente):

type StepStatus = 'not-started' | 'current' | 'completed' | 'error' | 'disabled';

interface StepperStep {
  id: string;
  label: string;
  description?: string;
  status: StepStatus;
  optional?: boolean;
}

interface StepperProps {
  steps: StepperStep[];
  currentStepId: string;
  orientation?: 'horizontal' | 'vertical';
  interactive?: boolean; // permite clicar em etapas concluídas/anteriores
  onStepChange?: (stepId: string) => void;
  'aria-label': string; // nome do processo, ex. "Cadastro de empresa"
}

// Uso
<Stepper
  aria-label="Cadastro de empresa"
  orientation="horizontal"
  interactive
  currentStepId="address"
  steps={[
    { id: 'company', label: 'Dados da empresa', status: 'completed' },
    { id: 'address', label: 'Endereço', status: 'current' },
    { id: 'billing', label: 'Faturamento', status: 'not-started', optional: true },
    { id: 'confirm', label: 'Confirmação', status: 'not-started' },
  ]}
  onStepChange={(id) => goToStep(id)}
/>

Decisões de API comentadas:

  • status como enum explícito por etapa, em vez de o componente inferir o status a partir de comparar índices com currentStepId — isso é proposital: permite representar error em uma etapa que já foi "passada" (índice menor que a atual), o que uma inferência puramente posicional não conseguiria expressar. O trade-off é que quem consome o componente precisa manter esse array sincronizado, mas isso já seria necessário de qualquer forma para saber se uma etapa está válida.
  • interactive como boolean simples, e não um enum de "modos" — cobre o caso mais comum (permitir voltar a etapas concluídas) sem exigir configuração granular por etapa; navegação para etapas futuras continua bloqueada implicitamente por regra de negócio (só etapas com status: 'completed' ficam clicáveis quando interactive é true).
  • onStepChange dispara apenas quando o usuário interage com o Stepper diretamente (clique numa etapa concluída) — não deve ser confundido com os handlers de "Próximo/Voltar" do wizard, que ficam fora do componente (ver seção 11).
  • aria-label obrigatório no nível do componente para nomear o processo como um todo — sem isso, um usuário de leitor de tela que navega direto para a landmark <nav> do Stepper (seção 6) não sabe do que se trata.

13. Casos extremos e edge cases

Caso Comportamento esperado
Muitas etapas (10+) em orientação horizontal Migrar para versão compacta/scroll horizontal com indicação visual de mais conteúdo, ou reconsiderar se o processo não deveria ser dividido em "seções" com sub-steppers, já que mais de ~6–7 etapas visíveis simultaneamente prejudica a leitura
Rótulo de etapa muito longo Truncar com reticências + title/tooltip com o texto completo; nunca deixar o texto quebrar o layout do Stepper de forma imprevisível
Etapa sem rótulo (só número) Evitar — sempre fornecer ao menos um aria-label textual, mesmo que visualmente omitido, para que a etapa tenha nome acessível
Erro simultâneo em múltiplas etapas Cada etapa com erro mostra seu próprio indicador de erro; se o usuário está em uma etapa posterior à primeira com erro, considerar um aviso agregado ("2 etapas anteriores precisam de atenção") com link direto para a primeira
Etapa disabled + tentativa de navegação via URL direta (deep link) Redirecionar para a última etapa válida alcançável, nunca renderizar uma etapa cujas dependências (dados de etapas anteriores) não foram satisfeitas
Processo que muda de número de etapas dinamicamente (ex.: uma resposta em uma etapa adiciona/remove uma etapa seguinte) Comunicar isso textualmente ("mais uma etapa foi adicionada") e, se possível, animar a inserção/remoção do indicador correspondente em vez de apenas recalcular silenciosamente — mudanças silenciosas na contagem total confundem o modelo mental construído na seção 2
disabled + error simultâneos (estado conflitante) Não deveria ocorrer logicamente — uma etapa desabilitada não deveria carregar um estado de validação. Se surgir por bug de estado, disabled deve prevalecer visualmente (a etapa continua não-interativa), mas o erro subjacente deve ser logado/reportado, pois indica inconsistência no estado do wizard
Etapa concluída cujo conteúdo foi invalidado por uma mudança em etapa anterior Reverter seu status para not-started ou error (nunca deixar como completed desatualizado) — essencial para não passar uma falsa sensação de segurança ao usuário

14. Boas práticas

Faça:

  • Sempre comunique o número total de etapas e a posição atual, mesmo em versões compactas ("Passo 2 de 5").
  • Combine cor com ícone/forma para diferenciar estados (nunca só cor).
  • Permita voltar a etapas concluídas para revisão, salvo razão de negócio explícita para impedir (ex.: pagamento já processado).
  • Mova o foco para o conteúdo da nova etapa ao navegar.
  • Use aria-current="step" na etapa atual.

Evite:

  • Rótulos genéricos como "Etapa 1", "Etapa 2" sem nome descritivo do conteúdo.
  • Misturar, na mesma tela, um Stepper de progresso com um Stepper numérico sem deixar claríssimo (por nome de componente/prop) qual é qual — a colisão de nomenclatura já confunde times inteiros de design system, como mostra a seção 15.
  • Depender só de posição/índice para saber se uma etapa tem erro — trate erro como um estado explícito e independente da posição.

Não faça:

  • Não use <div>s com onClick sem semântica de link/botão para etapas clicáveis — isso quebra navegação por teclado e leitura por leitores de tela.
  • Não valide o formulário inteiro apenas na última etapa, obrigando o usuário a voltar várias etapas para corrigir algo sinalizável antes.
  • Não deixe uma etapa "concluída" com aparência de válida depois que uma mudança em etapa anterior invalidou seus dados.

15. Análise comparativa de mercado

A pesquisa nos seis design systems revelou um padrão relevante: metade dos sistemas não tem, hoje, um componente de Stepper de progresso maduro e oficialmente publicado — apesar de "Stepper" ser um dos componentes mais pedidos pela comunidade em pelo menos dois deles.

Sistema Nome do componente Status Observações
Material Design (Google) "Steppers" Descontinuado na especificação oficial O componente existe/existiu na versão arquivada do Material Design (M2), mas não faz parte da especificação atual do Material Design 3 — o M3 documenta apenas "Progress indicators", que são indicadores de carregamento contínuo/circular, não um stepper de etapas nomeadas. O Material UI (MUI), biblioteca React não-oficial, mantém seu próprio Stepper legado, hoje descrito como não mais documentado nas diretrizes oficiais do Material Design, mas ainda suportado pela biblioteca.
Carbon (IBM) "Progress indicator" Maduro e bem documentado Possui sete estados: completed, current, not started, error, disabled, hover e focus. Suporta navegação por setas do teclado entre etapas quando interativo. Carbon distingue explicitamente Progress Indicator (por etapas nomeadas) de Progress Bar (percentual contínuo) — dois componentes separados, o que evita a ambiguidade encontrada em outros sistemas.
Atlassian Design System "Progress tracker" (+ "Progress indicator" como variante mais simples) Maduro, com dois níveis de complexidade O ProgressTracker usa uma API de status por etapa (disabled, visited, current) e percentageComplete, com recomendações de acessibilidade específicas como manter rótulos curtos (1–2 palavras). Atlassian também mantém "Progress bar" como componente separado para progresso contínuo — mesmo padrão de separação do Carbon.
Shopify Polaris "Stepper" (nome documentado, componente nunca existiu) Não implementado Achado notável: a documentação do Polaris chegou a referenciar um componente Stepper, mas o componente nunca foi de fato implementado no repositório — havia documentação parcial sem código correspondente, confirmado por uma issue pública no repositório. O único "Stepper" que de fato existe no ecossistema Shopify é o numérico, usado em extensões de checkout e de POS para campos de quantidade — reforçando a colisão de nomenclatura descrita na seção 1.
Adobe Spectrum "StepList" (distinto de "Stepper", que é o numérico) Em desenvolvimento (alpha) Adobe evita a ambiguidade pelo nome: Stepper, em Spectrum, é exclusivamente o campo numérico de incremento/decremento (usado, por exemplo, dentro do NumberField). O componente de progresso por etapas foi proposto e está sendo construído sob o nome StepList, com pacotes já publicados no npm em versão alpha (@react-spectrum/steplist), seguindo a Collections API do React Spectrum (mesma API usada por Menu, ListBox etc.) e um prop lastCompletedStep que separa "etapa atual" de "até onde o usuário pode avançar novamente".
Fluent UI / Fluent 2 (Microsoft) — Não implementado Não existe um componente Stepper/Wizard oficial no Fluent UI. Há pelo menos duas issues públicas pedindo o componente — uma aberta em 2020, fechada por inatividade, e outra reaberta em 2024 argumentando explicitamente que o ProgressBar existente "não serve para esse caso de uso" — ambas sem componente entregue até o momento desta pesquisa. Equipes que usam Fluent UI recorrem a bibliotecas de terceiros ou implementações internas.

Síntese — diferenças, semelhanças e aprendizados:

  1. Naming é o maior risco de confusão do componente. Três dos seis sistemas (Shopify, Adobe, e o próprio ecossistema React em geral) usam "Stepper" primariamente ou exclusivamente para o componente numérico. Um design system novo deveria considerar nomear o componente de progresso de forma menos ambígua desde o início — "Progress Tracker" (Atlassian), "Progress Indicator" (Carbon) ou "Step List" (Adobe) evitam a colisão. Se o nome "Stepper" for mantido por familiaridade de mercado, vale documentar explicitamente a diferença logo na primeira linha da página do componente.
  2. Os sistemas maduros (Carbon, Atlassian) tratam progresso "por etapas nomeadas" e progresso "contínuo/percentual" como dois componentes separados, nunca um único componente com muitas props tentando cobrir os dois casos. Essa separação é uma decisão de arquitetura recomendada (ver seção 16).
  3. Não existe um padrão ARIA oficial e universalmente citado para este componente (seção 6) — cada sistema construiu sua própria composição de nav + lista ordenada + aria-current, convergindo informalmente para o mesmo resultado sem uma fonte normativa comum. Isso é evidência de que a acessibilidade deste componente exige atenção redobrada durante a implementação, já que não há um "gabarito oficial" a seguir cegamente.
  4. O suporte a navegação por teclado com setas (Carbon) é o padrão mais explícito e citável encontrado; sistemas que não documentam isso claramente (Atlassian, Adobe) deixam a responsabilidade maior para quem implementa.
  5. Dois dos seis sistemas (Polaris, Fluent) simplesmente não têm o componente, apesar de demanda documentada da comunidade — reforçando que este não é um componente trivial de acertar de primeira, e que investir tempo na composição correta (seção 3–6) compensa.

16. Recomendações para Design Systems

UX:

  • Adote a separação Stepper (etapas nomeadas) vs. Progress Bar (percentual/tempo contínuo) como dois componentes distintos desde o início, seguindo o padrão validado por Carbon e Atlassian — evita uma API inchada tentando cobrir dois modelos mentais diferentes.
  • Priorize, por padrão, permitir voltar a etapas concluídas (interactive: true como default), só restringindo quando houver razão de negócio explícita (ex.: pagamento já processado) — é o comportamento que o modelo mental do usuário já espera (seção 2).

Acessibilidade:

  • Trate a composição nav + ol + aria-current="step" como o padrão de referência interno do design system, documentando-o explicitamente na página do componente — já que não existe uma referência oficial única do W3C para apontar (seção 6), a documentação interna precisa preencher essa lacuna sozinha.
  • Exija, na revisão de qualquer implementação, que mudanças de etapa: (1) movam o foco para o conteúdo novo, e (2) sejam anunciadas via região aria-live.

Arquitetura:

  • Modele o Stepper como componente controlado (estado de etapa atual e status por etapa vivem em quem consome), pois o wizard ao redor quase sempre precisa reagir à etapa ativa de qualquer forma (seção 12).
  • Trate status como um valor explícito por etapa (não inferido por posição), para representar corretamente estados de erro em etapas já visitadas (seção 12).

Implementação:

  • Nomeie o componente evitando colisão com um eventual Stepper numérico futuro no mesmo sistema (ex.: "ProgressSteps", "StepTracker") — ou, se "Stepper" for mantido, garanta que o Stepper numérico (se existir) tenha um nome claramente diferente (ex.: "NumberStepper", "QuantitySelector").
  • Escreva testes de acessibilidade automatizados focados em: presença de aria-current, nome acessível de cada etapa (rótulo + posição + estado), e ordem de tabulação coerente entre Stepper e os botões de navegação do wizard.

Documentação:

  • Documente explicitamente, na primeira seção da página do componente, que "Stepper" aqui se refere ao indicador de progresso — não ao input numérico — dado o histórico de ambiguidade encontrado nesta pesquisa (seção 15). Essa única frase evita boa parte da confusão observada em outros ecossistemas.
  • Inclua, na documentação, um exemplo explícito de estado de erro em etapa já concluída (edge case da seção 13), já que é o cenário mais frequentemente esquecido em implementações reais.

Referências

WAI-ARIA / Acessibilidade:

Design systems:

Referência adicional: