-
Notifications
You must be signed in to change notification settings - Fork 3
pesquisa componente stepper
Premissa de escopo: "Stepper" é um nome ambíguo no mercado. Existem, na prática, dois componentes diferentes que disputam esse nome:
-
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). - 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.
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 |
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).
| 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.
| 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).
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çoativa (navega para) a etapa focada, quando ela é uma etapa alcançável. - Etapas
disablednã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.
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 terrole="navigation", já que ele existe para navegar entre etapas de um processo — o componente Stepper é um elemento de landmark<nav>ou um elemento comrole="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>comonClick.
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" (valorstepdo atributoaria-current, que também aceitapage,location,date,time,true). -
aria-disabled="true"em etapas não alcançáveis (nãodisabledno sentido de remover do DOM/foco, se a etapa ainda precisa ser lida por leitor de tela como parte da sequência). -
aria-labelem 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 emaria-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êmtabindex="-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.
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"earia-describedbyapontando para a mensagem de erro — relação direta com a seção 6).
-
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."
| 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.
- 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.
- 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.
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:
-
statuscomo enum explícito por etapa, em vez de o componente inferir o status a partir de comparar índices comcurrentStepId— isso é proposital: permite representarerrorem 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. -
interactivecomo 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 comstatus: 'completed'ficam clicáveis quandointeractiveétrue). -
onStepChangedispara 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-labelobrigató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.
| 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 |
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 comonClicksem 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.
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:
- 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.
- 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).
-
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. - 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.
- 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.
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: truecomo 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
statuscomo 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.
WAI-ARIA / Acessibilidade:
- WAI-ARIA Authoring Practices Guide — Spinbutton Pattern: https://www.w3.org/WAI/ARIA/apg/patterns/spinbutton/
- WAI-ARIA Authoring Practices Guide (visão geral): https://www.w3.org/WAI/ARIA/apg/
- WCAG 2.2 Quick Reference: https://www.w3.org/WAI/WCAG22/quickref/
- Compound Design System — Progress Steps, Accessibility: https://compound.thephoenixgroup.com/latest/components/components/progress-steps/accessibility-4hixvdZY-4hixvdZY
- KendoReact — Stepper WAI-ARIA Support: https://www.telerik.com/kendo-react-ui/components/layout/stepper/accessibility/wai-aria-support
Design systems:
- Material Design — Steppers (arquivado, M2): https://djibe.github.io/material/docs/4.6/material/steppers/
- Material Design 3 — Progress indicators: https://m3.material.io/components/progress-indicators
- Material UI (MUI) — React Stepper: https://mui.com/material-ui/react-stepper/
- Carbon Design System — Progress indicator (uso): https://carbondesignsystem.com/components/progress-indicator/usage/
- Carbon Design System — Progress indicator (acessibilidade, v10): https://v10.carbondesignsystem.com/components/progress-indicator/accessibility/
- Atlassian Design — Progress tracker: https://atlassian.design/components/progress-tracker/
- Atlassian Forge — Progress tracker: https://developer.atlassian.com/platform/forge/ui-kit/components/progress-tracker/
- Shopify Polaris — issue confirmando ausência do componente Stepper: https://github.com/Shopify/polaris-react/issues/2274
- Adobe React Spectrum — issue de implementação do StepList: https://github.com/adobe/react-spectrum/issues/1012
- Adobe React Spectrum — pacote
@react-spectrum/steplist: https://www.npmjs.com/package/@react-spectrum/steplist - Adobe Spectrum CSS — Stepper (numérico): https://opensource.adobe.com/spectrum-css-vr-results/site-897dc97-2021-04-21_23-58-10/dist/docs/stepper.html
- Fluent UI — issue "Stepper / Wizard Component": https://github.com/microsoft/fluentui/issues/31558
- Fluent UI — issue "Stepper component" (2020, fechada): https://github.com/microsoft/fluentui/issues/14299
Referência adicional:
- Component Gallery — Progress indicator (também conhecido como Stepper): https://component.gallery/components/progress-indicator/