Skip to content

fix: aria-current="page" nos itens de navegação ativos (closes #118) - #135

Draft
Guiroos wants to merge 3 commits into
mainfrom
claude/quirky-johnson-qkjbyt
Draft

fix: aria-current="page" nos itens de navegação ativos (closes #118)#135
Guiroos wants to merge 3 commits into
mainfrom
claude/quirky-johnson-qkjbyt

Conversation

@Guiroos

@Guiroos Guiroos commented Aug 31, 2026

Copy link
Copy Markdown
Owner

O que mudou

Nenhum dos 3 sites que marcam o item de navegação ativo (Sidebar.tsx, BottomNav.tsx) expunha isso a leitor de tela — o estado active/isActive(href) alimentava só className. Adicionado aria-current="page" (via expressão, nunca literal fixo) nos três:

  1. components/layout/Sidebar.tsx:69NavItem (11 links da sidebar, 12 com admin)
  2. components/layout/BottomNav.tsx:66NavItem (3 links da barra inferior)
  3. components/layout/BottomNav.tsx:202 — links inline do dialog "Menu" (7 links, única via para 7 das 12 rotas no mobile)

Mudança puramente aditiva: aria-current={active ? 'page' : undefined} (ou isActive(href) no site 3) — sem alterar className, estrutura JSX, roteamento ou isActive/pendingHref.

Por que dessa forma

Segui a proposta da issue à risca, inclusive a escolha do token:

  • 'page', não 'true''page' é o token ARIA específico para "este link aponta para a página em que você está" (o que os leitores anunciam como "página atual"); 'true' é o fallback genérico para quando não há token que descreva a relação, e aqui há.
  • ? 'page' : undefined, não {active}aria-current={active} renderiza aria-current="false" no HTML quando inativo, e ARIA trata isso como o estado false (presente), não como ausência do atributo — React só omite aria-* para undefined/null. Isso deixaria a marcação sintaticamente presente e semanticamente inerte.

Não toquei isActive/pendingHref (o cálculo do estado já está correto) nem os 4 <nav> sem aria-label do shell — a issue registra isso como achado adjacente, deliberadamente fora deste escopo.

Como testei

  • npm run lint && npm run format:check && npm run typecheck && npm test && npm run build — todos verdes (530 testes, 35 arquivos).
  • Novo arquivo __tests__/unit/a11y-pagina-atual.test.ts, seguindo o precedente de a11y-estado-selecao.test.ts ([a11y] Chip e Segment não expõem qual opção está selecionada — o seletor de tipo do formulário de lançamento é um grupo de botões sem estado para leitor de tela #107): asserções ancoradas na expressão de estado (aria-current={active ? 'page' : undefined}), não em toMatch(/aria-current/) genérico — as duas correções erradas mais prováveis ('page' fixo, ou {active} sem o ternário) passariam nesse gate genérico e não neste.
  • ds-reviewer rodado sobre as duas mudanças (hook PostToolUse:Edit exige a cada edição): confirmou que aria-current não introduz violação de DS (não é classe Tailwind, não é valor arbitrário, não substitui primitivo). Reportou 4 violações pré-existentes nos mesmos arquivos, sem relação com esta mudança (confirmado via diff) — ver seção abaixo.

Risco e o que NÃO foi coberto

Arquivos tocados

  • components/layout/Sidebar.tsx
  • components/layout/BottomNav.tsx
  • __tests__/unit/a11y-pagina-atual.test.ts (novo)

Generated by Claude Code

Sidebar, BottomNav e os links inline do dialog "Menu" marcavam o item
ativo só via className, sem nada programaticamente determinável para
leitor de tela — os 11+ links se anunciavam idênticos.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1hGTYJx2gfTHSDdWFTWTa

@Guiroos Guiroos left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revisão do head 9665b8f. Os 3 sites da #118 estão fechados por inteiro (exigência 8): Sidebar.tsx:69, BottomNav.tsx:66 e BottomNav.tsx:202grep -rn "aria-current" components app devolve exatamente essas três ocorrências, e grep por isActive|usePathname não revela um quarto site de link de navegação. A escolha de 'page' sobre 'true' e do ternário com undefined sobre {active} está correta e justificada, e o escopo adjacente (as 4 <nav> sem aria-label) ficou de fora como a issue pediu.

Também conferi que a mudança não expõe dois marcadores de "página atual" ao mesmo tempo: Sidebar é hidden … lg:flex e o <nav> da BottomNav é lg:hidden, então em cada breakpoint só um dos dois chega à árvore de acessibilidade; e com o dialog "Menu" aberto o Radix marca o resto como aria-hidden, deixando os 7 links do site 3 como a informação inteira — que é o caso que a issue apontou como o que fecha sozinho.

Um achado, não-bloqueante: o gate novo passa verde com a correção inteira revertida por comentário. Verificado rodando tsc, lint e vitest sobre o fonte mutado, não deduzido. Detalhe e a correção de três linhas no comentário inline.


Generated by Claude Code

Comment thread __tests__/unit/a11y-pagina-atual.test.ts Outdated
toMatch(/aria-current=.../) sem âncora passava com o atributo comentado,
deixando os 22 links de navegação voltar a se anunciar idênticos sem
que o gate acusasse — mesmo modo de falha que a11y-estado-selecao.test.ts
(#107) já evita para aria-pressed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1hGTYJx2gfTHSDdWFTWTa

@Guiroos Guiroos left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Revisão do head f50ed9e (a anterior foi no 9665b8f). O achado da rodada passada está fechado: as três asserções passaram a ancorar na linha inteira (^\s*…$ com flag m), e comentar a linha do fix agora deixa o gate vermelho — mutei para conferir, não deduzi.

Reconferi por conta própria que não há um quarto site, já que a exigência 8 é o eixo do achado original: usePathname fora de Sidebar/BottomNav só aparece em ArchivedFilterChip.tsx e MonthSelector.tsx, que são filtros de URL, não links de navegação. Os 3 sites da #118 seguem sendo a lista inteira.

Rodei os gates nesta sessão, sobre este head: npm ci e npm test verdes (35 arquivos, 530 testes).

Um achado, nit — sobre o alcance do gate, não sobre o fix. A âncora nova é da linha, não do elemento: mover o mesmo aria-current={…} para um filho do <Link> mantém as três asserções verdes com o <a> sem aria-current na árvore. Verifiquei rodando nos dois arquivos (<div> do BottomNav, <Icon> do Sidebar), e é a mesma classe de correção errada que a família #126/#127/#128 nomeia ("aria-label no ícone em vez do botão"). A correção soma a âncora de elemento à de linha sem perder nenhuma das duas — reprodução, o diff das três asserções e a bateria de mutação nos quatro estados no comentário inline, incluindo por que a variante intuitiva [^>]* reprova o fonte correto.


Generated by Claude Code

Comment thread __tests__/unit/a11y-pagina-atual.test.ts Outdated
A âncora de linha (f50ed9e) protegia contra o atributo comentado, mas
não amarrava aria-current ao <Link> — o mesmo atributo movido para um
filho (a <div>/<Icon> que já carrega o destaque visual do item ativo)
mantinha as três asserções verdes com o <a> sem aria-current na árvore,
a mesma classe de correção errada que a família #126/#127/#128 nomeia.

[^<]*, não [^>]*: o <Link> do dialog "Menu" tem onClick={() => {...}}
na tag de abertura, e a seta => contém um > que faria [^>]* parar cedo
demais e reprovar o fonte correto.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1hGTYJx2gfTHSDdWFTWTa
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants