Skip to content

Landing: a11y, design-system conformance & performance - #3

Merged
prof-ramos merged 1 commit into
mainfrom
polish/landing-a11y-design-perf
Jul 31, 2026
Merged

Landing: a11y, design-system conformance & performance#3
prof-ramos merged 1 commit into
mainfrom
polish/landing-a11y-design-perf

Conversation

@prof-ramos

@prof-ramos prof-ramos commented Jul 31, 2026

Copy link
Copy Markdown
Owner

Consolida o trabalho não-commitado da landing em uma frente coesa. Hunks se sobrepõem entre page.tsx/layout.tsx/globals.css, então um split por área sem staging interativo quebraria a buildabilidade por commit.

Acessibilidade / UX

Skip link, foco 3px, prefers-reduced-motion, fieldset/legend, erros com role="alert", foco do menu mobile; eyebrow → brand-700 (contraste AA).

Conformidade com o design system (DESIGN.md)

  • Remove os 5 assets 3D .webp (azul/laranja fora da paleta, glossy) e o acervo Envato docs/uiux-assets (~413 MB).
  • Hero: sem o ingresso 3D flutuante; DashboardPreview é o visual único.
  • Fluxo operacional: terceira faixa brand-900 → seção clara com ícones line-art verdes, mantendo a numeração 01/02/03.
  • Público: render 3D → TicketLine line-art; painel de apoio brand-50ink-100 neutro (verde volta a ser recurso escasso).

Performance

  • Code-split do formulário abaixo da dobra (next/dynamic + ssr:false + IntersectionObserver, skeleton que reserva altura): tira zod + react-hook-form + radix do first-load da home (338 → 222 KB gzip).
  • Monta @vercel/speed-insights + @vercel/analytics para CWV de campo.

Fixes do code-review

  • Remove tokens --success/--warning não usados (paleta segue mono-verde).
  • Fonte única para cor de foco (--focus-ringvar(--ring), brand-600).
  • Fim do anúncio duplicado de erro de envio para leitores de tela.

Validação

pnpm build ✅ · typecheck ✅ · lint ✅ (0 erros) · Prettier ✅

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Novos Recursos

    • Adicionada uma seção com as etapas da operação: ingresso digital, check-in por QR Code e confirmação de entrada.
    • O formulário de candidatura agora é carregado conforme a aproximação à tela, melhorando o carregamento inicial.
    • Incluídos Analytics e Speed Insights.
  • Acessibilidade

    • Melhorada a navegação por teclado, incluindo foco no menu mobile e fechamento com Escape.
    • Adicionados link para pular ao conteúdo, foco visível, suporte a movimento reduzido e mensagens de erro anunciadas.
  • Melhorias

    • Formulário aprimorado com validações, mensagens de envio e foco automático no primeiro campo inválido.
    • Atualizados conteúdos e elementos visuais da landing page.

Bundles the uncommitted landing-page work into one coherent change —
hunks overlap across page.tsx/layout.tsx/globals.css, so a clean
per-area split without interactive staging wasn't possible.

Accessibility / UX
- Skip link, 3px focus ring, prefers-reduced-motion, fieldset/legend,
  role="alert" errors, mobile-menu focus handling; eyebrow -> brand-700
  for AA contrast.

Design-system conformance (DESIGN.md)
- Drop the 5 raster 3D .webp ticket assets (off-palette blue/orange,
  glossy) and the 413 MB docs/uiux-assets Envato archive.
- Hero: remove the floating 3D ticket; DashboardPreview is the sole visual.
- Operational flow: convert the third dark brand-900 band into a light
  section with green line-art icons, keeping the 01/02/03 sequence.
- Target audience: 3D concert render -> TicketLine line-art; support panel
  brand-50 -> neutral ink-100 so green stays a scarce resource.

Performance
- Code-split the below-the-fold pilot form (next/dynamic + ssr:false +
  IntersectionObserver, height-reserving skeleton): pulls zod +
  react-hook-form + radix out of the home first-load (338 -> 222 KB gzip).
- Mount @vercel/speed-insights + @vercel/analytics for field CWV.

Code-review fixes
- Remove unused --success/--warning tokens (palette stays single-green).
- Single-source the focus color (--focus-ring -> var(--ring), brand-600).
- Stop double-announcing submit errors to screen readers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
webingressos-page Ready Ready Preview Jul 31, 2026 4:23pm

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A landing page recebeu melhorias de acessibilidade, navegação móvel, conteúdo visual, validação do formulário, carregamento sob demanda, observabilidade e documentação técnica.

Changes

Acessibilidade e base da aplicação

Layer / File(s) Summary
Base global de acessibilidade e observabilidade
src/app/globals.css, src/app/layout.tsx, src/app/obrigado/page.tsx, src/app/privacidade/page.tsx
A aplicação adiciona foco visível, suporte a prefers-reduced-motion, espaçamento de rolagem, link de salto, Analytics, SpeedInsights e identificadores main-content.

Landing page e formulário

Layer / File(s) Summary
Navegação e composição visual da landing page
src/components/landing/header.tsx, src/components/landing/hero.tsx, src/components/landing/operational-flow.tsx, src/components/landing/pilot-band.tsx, src/components/landing/section.tsx, src/components/landing/target-audience.tsx, src/app/page.tsx
O menu móvel gerencia foco e Escape. O hero, as seções e o público-alvo recebem ajustes de conteúdo, contraste e layout. A página inicial renderiza OperationalFlow.
Carregamento e validação do formulário
src/components/landing/pilot-form-lazy.tsx, src/components/landing/pilot-form.tsx, src/components/ui/form.tsx
PilotFormLazy carrega PilotForm próximo à viewport. O formulário exibe erros de envio, foca o primeiro campo inválido, anuncia estados de submissão e usa agrupamento semântico.
Registro técnico e configuração de suporte
docs/uiux-analysis.md, .prettierignore, eslint.config.mjs
A análise documenta as alterações, validações e limitações. Prettier e ESLint ignoram docs/uiux-assets/**.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LandingPage
  participant PilotFormLazy
  participant IntersectionObserver
  participant PilotForm
  LandingPage->>PilotFormLazy: renderiza o formulário sob demanda
  PilotFormLazy->>IntersectionObserver: observa a aproximação da seção
  IntersectionObserver->>PilotFormLazy: informa entrada no limite de 600 px
  PilotFormLazy->>PilotForm: carrega dinamicamente sem SSR
  PilotForm-->>LandingPage: exibe o formulário
Loading

Possibly related issues

  • prof-ramos/zappin#70: Trata do mesmo objetivo de acessibilidade, com skip link e melhorias de foco e navegação.

Possibly related PRs

Poem

Foco no botão, menu em posição,
O formulário aguarda a ocasião.
QR Code guia a operação,
O erro recebe anunciação.
Analytics registra a missão.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed O título resume com clareza os principais objetivos: acessibilidade, conformidade com o design system e performance.
Description check ✅ Passed A descrição documenta as mudanças e a validação; as seções de riscos, rollback e evidências não foram preenchidas.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch polish/landing-a11y-design-perf

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Biome (2.5.5)
src/app/globals.css

File contains syntax errors that prevent linting: Line 7: Tailwind-specific syntax is disabled.; Line 62: Tailwind-specific syntax is disabled.; Line 111: Tailwind-specific syntax is disabled.; Line 192: Tailwind-specific syntax is disabled.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 43641622df

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const FLOW_STEPS = [
{
title: "Ingresso digital",
description: "A pessoa compra e acessa o ingresso pelo celular, sem fila e sem papel.",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reframe the operational flow as a pilot concept

This new present-tense flow says attendees buy and access mobile tickets and later promises real-time attendance tracking, but .agents/product-marketing.md states that the product has no functionality in production and must not be presented as completed. On the public landing, these statements—including the guaranteed outcome “sem fila”—turn validation hypotheses into availability and result claims, so describe them as proposed pilot behavior until they are verified.

AGENTS.md reference: AGENTS.md:L46-L48

Useful? React with 👍 / 👎.

Comment thread src/app/layout.tsx
Comment on lines +41 to +42
<SpeedInsights />
<Analytics />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Disclose telemetry before enabling it globally

When deployed, these components collect page-view and performance telemetry on every route, including from visitors who never submit the pilot form, while /privacidade still describes only information supplied through that form and lists only form-related purposes. This introduces undisclosed collection; update the privacy disclosure and applicable legal-basis/consent handling, or leave telemetry disabled until that is done.

Useful? React with 👍 / 👎.

@prof-ramos
prof-ramos merged commit 18801b8 into main Jul 31, 2026
3 of 4 checks passed
@prof-ramos
prof-ramos deleted the polish/landing-a11y-design-perf branch July 31, 2026 16:31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/uiux-analysis.md`:
- Around line 8-10: Atualize os trechos introdutórios e a seção que descreve os
assets em docs/uiux-analysis.md para refletirem o estado final registrado
posteriormente: não afirme que nenhuma dependência foi adicionada nem que cinco
arquivos WebP permanecem servidos. Preserve essas informações apenas se
explicitamente marcadas como histórico da primeira rodada; caso contrário,
substitua-as pelo estado final com as duas dependências adicionadas e os assets
removidos, mantendo o restante da análise consistente.
- Around line 96-98: Atualize a política descrita em docs/uiux-analysis.md para
cobrir o Analytics global carregado por Analytics em src/app/layout.tsx,
incluindo finalidade, dados tratados, retenção e formas de recusa. Não afirme
que o Analytics está coberto enquanto essas regras não estiverem incluídas em
src/app/privacidade/page.tsx.

In `@src/app/globals.css`:
- Line 158: Update the text-rendering declaration in the global stylesheet to
use the lowercase optimizelegibility value required by Stylelint’s
value-keyword-case rule, leaving the surrounding styling unchanged.

In `@src/app/layout.tsx`:
- Around line 41-42: Atualize a política de privacidade em
src/app/privacidade/page.tsx para documentar a telemetria de <SpeedInsights /> e
<Analytics />, incluindo dados tratados, finalidade, retenção e opção de recusa;
verifique também as versões instaladas para confirmar o comportamento atual. Se
essas versões exigirem consentimento, ajuste o fluxo em layout.tsx para
renderizar os componentes somente após o usuário consentir.
- Around line 33-35: Atualize o link de pulo no layout, identificado pelo
href="`#main-content`", para não remover o contorno global com
focus-visible:outline-none; alternativamente, substitua o anel sem opacidade por
ring-focus-ring. Preserve os demais estilos e garanta contraste de foco
compatível com WCAG.

In `@src/app/obrigado/page.tsx`:
- Around line 8-11: Enable programmatic focus on every main element with
id="main-content" by adding tabIndex={-1} in src/app/obrigado/page.tsx lines
8-11 and src/app/privacidade/page.tsx line 6; also apply the same change to the
corresponding main element in src/app/page.tsx as requested.

In `@src/components/landing/header.tsx`:
- Around line 76-104: Update the div#menu-principal className to include the
transition-discrete utility, preserving the existing opacity and translate
transitions so closing the menu animates before hidden applies display:none in
modern browsers.

In `@src/components/landing/pilot-band.tsx`:
- Line 9: Keep the scroll-mt-16 and sm:scroll-mt-20 values in the pilot-band
section unchanged; apply the consolidated deduplication adjustment to the
corresponding section wrapper in the Section component instead, reusing the
shared spacing values without altering header-aligned behavior.

In `@src/components/landing/pilot-form.tsx`:
- Around line 94-97: Remova as chamadas a toast.error no fluxo de erro de envio
do formulário, mantendo setSubmitError como único canal de anúncio acessível.
Aplique essa alteração tanto no catch próximo a setSubmitError quanto no fluxo
correspondente indicado na linha 133, preservando o banner role="alert" e o
restante do tratamento de erro.

In `@src/components/landing/section.tsx`:
- Around line 25-28: Centralize the shared scroll-mt-16/sm:scroll-mt-20 values
used by the landing Section component and pilot-band.tsx into a reusable styling
constant or utility. Update Section and pilot-band.tsx to consume that shared
definition, preserving the existing responsive scroll offset behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: c9d2a390-2841-40df-866d-5f082e15ccce

📥 Commits

Reviewing files that changed from the base of the PR and between db383e1 and 4364162.

📒 Files selected for processing (17)
  • .prettierignore
  • docs/uiux-analysis.md
  • eslint.config.mjs
  • src/app/globals.css
  • src/app/layout.tsx
  • src/app/obrigado/page.tsx
  • src/app/page.tsx
  • src/app/privacidade/page.tsx
  • src/components/landing/header.tsx
  • src/components/landing/hero.tsx
  • src/components/landing/operational-flow.tsx
  • src/components/landing/pilot-band.tsx
  • src/components/landing/pilot-form-lazy.tsx
  • src/components/landing/pilot-form.tsx
  • src/components/landing/section.tsx
  • src/components/landing/target-audience.tsx
  • src/components/ui/form.tsx
📜 Review details
🧰 Additional context used
🪛 LanguageTool
docs/uiux-analysis.md

[locale-violation] ~24-~24: “targets” é um estrangeirismo. É preferível dizer “objetivos” ou “alvos”.
Context: ...pais, labels associados e controles com targets de toque adequados. - O foco global usa...

(PT_BARBARISMS_REPLACE_TARGETS)


[style] ~26-~26: Três frases seguidas começam com a mesma palavra. Considere reformular a frase ou use um dicionário para encontrar um sinônimo.
Context: ...o continuam preservando seus estilos. - O FormControl mantém aria-describedby...

(PORTUGUESE_WORD_REPEAT_BEGINNING_RULE)


[style] ~47-~47: Três frases seguidas começam com a mesma palavra. Considere reformular a frase ou use um dicionário para encontrar um sinônimo.
Context: ...ráter de candidatura sem compromisso. - O formulário informa que a candidatura nã...

(PORTUGUESE_WORD_REPEAT_BEGINNING_RULE)


[style] ~47-~47: Pode omitir o pronome ‘que’ para tornar o texto mais fluido.
Context: ...orma que a candidatura não garante vaga e que o retorno ocorre em até 48 horas úteis,...

(SIMPLIFICAR_E_QUE_E)


[locale-violation] ~51-~51: “Performance” é um estrangeirismo. É preferível dizer “desempenho”, “atuação”, “apresentação”, “espetáculo” ou “interpretação”.
Context: ...ssistiva, além do toast existente. ### Performance - Os cinco PNGs selecionados foram con...

(PT_BARBARISMS_REPLACE_PERFORMANCE)


[uncategorized] ~61-~61: Sinal de pontuação isolado.
Context: ...ssets adicionados - ticket-event.webp: assinatura visual do hero. - `mobile-ti...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~62-~62: Sinal de pontuação isolado.
Context: ...a visual do hero. - mobile-ticket.webp, qr-code-ticket.webp e `checkmark-tick...

(UNLIKELY_OPENING_PUNCTUATION)


[misspelling] ~62-~62: Possível erro ortográfico.
Context: ...visual do hero. - mobile-ticket.webp, qr-code-ticket.webp e checkmark-ticket.webp:...

(PT_MULTITOKEN_SPELLING_HYPHEN)


[uncategorized] ~63-~63: Sinal de pontuação isolado.
Context: ...n e confirmação. - concert-ticket.webp: apoio visual da seção de público univer...

(UNLIKELY_OPENING_PUNCTUATION)


[misspelling] ~85-~85: Possível erro ortográfico.
Context: ... painel de apoio deixou de ser verde (bg-brand-50) e passou a neutro (bg-ink-100), ...

(PT_MULTITOKEN_SPELLING_HYPHEN)


[locale-violation] ~90-~90: “Performance” é um estrangeirismo. É preferível dizer “desempenho”, “atuação”, “apresentação”, “espetáculo” ou “interpretação”.
Context: ...ez a exposição à licença pendente. ### Performance e dependências Diferente da primeira r...

(PT_BARBARISMS_REPLACE_PERFORMANCE)


[locale-violation] ~92-~92: “performance” é um estrangeirismo. É preferível dizer “desempenho”, “atuação”, “apresentação”, “espetáculo” ou “interpretação”.
Context: ...dependências de runtime), esta etapa de performance **adicionou duas dependências de runtim...

(PT_BARBARISMS_REPLACE_PERFORMANCE)


[grammar] ~98-~98: Possível erro de concordância de número.
Context: ...caminho para obter Core Web Vitals de campo reais após o deploy. - O formulário do piloto...

(GENERAL_NUMBER_AGREEMENT_ERRORS)


[grammar] ~102-~102: Possível erro de concordância.
Context: ... gzip** no first-load da home (medido no build). ## Validação executada - `pnpm form...

(GENERAL_GENDER_AGREEMENT_ERRORS)


[uncategorized] ~106-~106: Sinal de pontuação isolado.
Context: ...lidação executada - pnpm format:check: passou. - pnpm lint: passou sem erros...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~107-~107: Sinal de pontuação isolado.
Context: ...npm format:check: passou. - pnpm lint`: passou sem erros; existe um warning pre...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~108-~108: Sinal de pontuação isolado.
Context: ...ber/tmp/last-ndc.ts. - pnpm typecheck: passou. - pnpm build`: passou; rotas e...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~109-~109: Sinal de pontuação isolado.
Context: ...pnpm typecheck: passou. - pnpm build: passou; rotas estáticas e `/api/subscri...

(UNLIKELY_OPENING_PUNCTUATION)


[uncategorized] ~110-~110: Sinal de pontuação isolado.
Context: ...piladas. - curl http://localhost:3000/: respondeu 200 com headers de seguranç...

(UNLIKELY_OPENING_PUNCTUATION)

🪛 Stylelint (17.14.1)
src/app/globals.css

[error] 158-158: Expected "optimizeLegibility" to be "optimizelegibility" (value-keyword-case)

(value-keyword-case)

🔇 Additional comments (21)
src/app/globals.css (2)

56-58: LGTM!

Also applies to: 79-79


150-157: LGTM!

Also applies to: 159-175, 177-188

src/app/layout.tsx (2)

3-4: LGTM!


33-38: 🎯 Functional Correctness

Sem alteração necessária.

docs/uiux-analysis.md (1)

1-7: LGTM!

Also applies to: 12-50, 67-95, 99-118

.prettierignore (1)

4-5: LGTM!

eslint.config.mjs (1)

8-8: LGTM!

src/components/landing/header.tsx (1)

13-30: LGTM!

Also applies to: 40-40, 62-62

src/components/landing/hero.tsx (1)

13-15: LGTM!

Also applies to: 44-53

src/components/landing/pilot-band.tsx (1)

13-13: LGTM!

src/components/landing/section.tsx (2)

25-28: Resumo da mudança não bate com o código.

O resumo dessa faixa de linhas diz que a classe lg:scroll-mt-12 foi adicionada. No código mostrado, essa classe não aparece — só existe scroll-mt-16 py-8 sm:scroll-mt-20 sm:py-10 lg:py-12. Não é um problema funcional: como o header não muda de altura em lg (continua sm:h-20), o valor sm:scroll-mt-20 cascateia corretamente até lg sem precisar de override. Reporto só para registro, comandante.


56-56: LGTM!

src/components/landing/target-audience.tsx (1)

1-36: LGTM!

src/app/page.tsx (2)

7-13: LGTM!

Also applies to: 19-27


18-18: 🗄️ Data Integrity & Integration

Skip link apontado para #main-content.

Tudo certo, Comandante. O link “Pular para o conteúdo” já tem href="#main-content" e o <main> usa id="main-content".

src/components/landing/operational-flow.tsx (1)

1-52: LGTM!

src/components/landing/pilot-form-lazy.tsx (1)

1-60: LGTM!

src/components/landing/pilot-form.tsx (3)

6-6: LGTM!

Also applies to: 60-60, 78-78, 297-297, 340-340


103-108: LGTM!


109-125: LGTM!

Also applies to: 134-286

src/components/ui/form.tsx (1)

151-151: LGTM!

Comment thread docs/uiux-analysis.md
Comment on lines +8 to +10
A landing já tinha uma base consistente: Next.js 16, React 19, Tailwind 4, HTML semântico básico, skip link, foco visível, formulário com React Hook Form + Zod, feedback via Sonner e ilustrações SVG leves.

O foco desta rodada foi conversão da candidatura, sem trocar a identidade visual nem adicionar dependências de runtime. A proposta continua usando evergreen/mint e o motivo de ingresso/perfuração como assinatura própria; cinco assets locais foram adicionados de forma pontual como apoio visual.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Corrija as afirmações sobre o estado final.

Line 8-10 afirma que esta rodada não adicionou dependências. Line 53-65 afirma que cinco WebP foram adicionados e servidos. Porém, Line 77-102 registra a remoção desses arquivos e a adição de duas dependências. Marque os primeiros trechos como histórico da primeira rodada ou substitua-os pelo estado final. Corrija isso antes do merge.

Also applies to: 51-65

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/uiux-analysis.md` around lines 8 - 10, Atualize os trechos introdutórios
e a seção que descreve os assets em docs/uiux-analysis.md para refletirem o
estado final registrado posteriormente: não afirme que nenhuma dependência foi
adicionada nem que cinco arquivos WebP permanecem servidos. Preserve essas
informações apenas se explicitamente marcadas como histórico da primeira rodada;
caso contrário, substitua-as pelo estado final com as duas dependências
adicionadas e os assets removidos, mantendo o restante da análise consistente.

Comment thread docs/uiux-analysis.md
Comment on lines +96 to +98
- `@vercel/speed-insights` e `@vercel/analytics`, montados em `src/app/layout.tsx`. Como
não há Google Chrome nesta máquina para rodar Lighthouse, o Speed Insights é o caminho
para obter Core Web Vitals de campo reais após o deploy.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 3 \
  'Analytics|SpeedInsights|cookies?|consentimento|consent|opt.?out|privacidade|Vercel' \
  src/app/layout.tsx src/app/privacidade/page.tsx src/components

Repository: prof-ramos/webingressos-page

Length of output: 3042


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- policy outline ---\n'
wc -l src/app/privacidade/page.tsx
printf '\n--- policy content ---\n'
cat -n src/app/privacidade/page.tsx

printf '\n--- layout content ---\n'
cat -n src/app/layout.tsx | sed -n '1,90p'

printf '\n--- broader privacy/analytics related terms ---\n'
rg -n -i 'analytics|speedinsights|cookie|cookies?|consentimento|consent|recusa|opt.?out|retenção|retencao|tratament|LGPD|Google|Vercel' src docs package.json | sed -n '1,240p'

Repository: prof-ramos/webingressos-page

Length of output: 257


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- policy size ---'
wc -l src/app/privacidade/page.tsx
printf '%s\n' ''
printf '%s\n' '--- policy content ---'
cat -n src/app/privacidade/page.tsx
printf '%s\n' ''
printf '%s\n' '--- layout content ---'
cat -n src/app/layout.tsx | sed -n '1,90p'
printf '%s\n' ''
printf '%s\n' '--- broader privacy/analytics related terms ---'
rg -n -i 'analytics|speedinsights|cookie|cookies?|consentimento|consent|recusa|opt.?out|retenção|retencao|tratament|LGPD|Google|Vercel' src docs package.json | sed -n '1,240p'

Repository: prof-ramos/webingressos-page

Length of output: 8167


Complete a cobertura do Analytics global na política.

O layout global carrega <Analytics />, mas src/app/privacidade/page.tsx continua restrita ao formulário de candidatura e não aborda finalidade, dados tratados, retenção nem recusa do Analytics; o texto final não deve dizer que o Analytics é coberto até incluir essas regras.

🧰 Tools
🪛 LanguageTool

[grammar] ~98-~98: Possível erro de concordância de número.
Context: ...caminho para obter Core Web Vitals de campo reais após o deploy. - O formulário do piloto...

(GENERAL_NUMBER_AGREEMENT_ERRORS)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/uiux-analysis.md` around lines 96 - 98, Atualize a política descrita em
docs/uiux-analysis.md para cobrir o Analytics global carregado por Analytics em
src/app/layout.tsx, incluindo finalidade, dados tratados, retenção e formas de
recusa. Não afirme que o Analytics está coberto enquanto essas regras não
estiverem incluídas em src/app/privacidade/page.tsx.

Comment thread src/app/globals.css
background-color: var(--color-background);
color: var(--color-foreground);
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Normalize o valor de text-rendering para passar no lint.

Stylelint sinaliza optimizeLegibility para a regra value-keyword-case. Use optimizelegibility nesta propriedade.

Correção
-    text-rendering: optimizeLegibility;
+    text-rendering: optimizelegibility;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
text-rendering: optimizeLegibility;
text-rendering: optimizelegibility;
🧰 Tools
🪛 Stylelint (17.14.1)

[error] 158-158: Expected "optimizeLegibility" to be "optimizelegibility" (value-keyword-case)

(value-keyword-case)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/globals.css` at line 158, Update the text-rendering declaration in
the global stylesheet to use the lowercase optimizelegibility value required by
Stylelint’s value-keyword-case rule, leaving the surrounding styling unchanged.

Source: Linters/SAST tools

Comment thread src/app/layout.tsx
Comment on lines +33 to +35
<a
href="#main-content"
className="sr-only focus-visible:not-sr-only focus-visible:fixed focus-visible:top-4 focus-visible:left-4 focus-visible:z-100 focus-visible:rounded-xl focus-visible:bg-brand-700 focus-visible:px-4 focus-visible:py-2 focus-visible:text-sm focus-visible:font-bold focus-visible:text-white focus-visible:ring-3 focus-visible:ring-ring/50 focus-visible:outline-none"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## candidate files"
git ls-files | rg '(^|/)(globals\.css|tailwind\.config\.(js|ts|cjs|mjs)|package\.json|src/app/layout\.tsx|styles\.css|app.*global.*\.css)$' || true

echo
echo "## layout snippet"
if [ -f src/app/layout.tsx ]; then
  nl -ba src/app/layout.tsx | sed -n '1,80p'
fi

echo
echo "## color/ring tailwind and CSS variables"
for f in $(git ls-files | rg '(\.css$|tailwind\.config\.(js|ts|cjs|mjs)$|globals\.css$|layout\.tsx$)'); do
  echo "--- $f"
  rg -n -- '--(ring|brand|background|primary|secondary|surface|focus|outline)|ring-focus-ring|ring-ring|focus-visible:ring|focus-visible:outline|outline-none|--brand-[0-9]+' "$f" || true
done

echo
echo "## exact class usage"
rg -n "ring-ring/50|ring-focus-ring|outline-none|focus-visible:outline" .

Repository: prof-ramos/webingressos-page

Length of output: 297


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## layout snippet"
cat -n src/app/layout.tsx | sed -n '1,80p'

echo
echo "## globals CSS and Tailwind config"
for f in src/app/globals.css package.json; do
  echo "--- $f"
  cat -n "$f"
done

echo
echo "## exact class usage"
rg -n "ring-ring/50|ring-focus-ring|outline-none|focus-visible:outline|--ring|--brand-[0-9]+" . || true

Repository: prof-ramos/webingressos-page

Length of output: 20130


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## contrast probe for focus styles"
python3 - <<'PY'
import math

def wcag_ratio(c1, c2):
    lum = []
    for c in [c1, c2]:
        srgb = [x / 12.92 if x <= 0.03928 else ((x + 0.055) / 1.055) ** 2.4 for x in c]
        lum.append(0.2126 * srgb[0] + 0.7152 * srgb[1] + 0.0722 * srgb[2])
    return (max(lum) + 0.05) / (min(lum) + 0.05)

brand600 = (27/255, 127/255, 82/255)
ink50 = (249/255, 250/255, 252/255)

def alpha_over(fg, bg, alpha):
    r = fg[0]*alpha + bg[0]*(1-alpha)
    g = fg[1]*alpha + bg[1]*(1-alpha)
    b = fg[2]*alpha + bg[2]*(1-alpha)
    return (r, g, b)

print(f"--ring/--focus-ring = var(--brand-600) = {brand600}")
print(f"light background = var(--ink-50) = {ink50}")
print(f"0d-ring vs light: {wcag_ratio(brand600, ink50):.2f}")
print(f"50%-alpha ring vs light: {wcag_ratio(alpha_over(brand600, ink50, 0.5), ink50):.2f}")
print(f"50%-alpha ring vs white: {wcag_ratio(alpha_over(brand600, (1,1,1), 0.5), (1,1,1)):.2f}")
print(f"ring-focus-ring vs light: {wcag_ratio(brand600, ink50):.2f}")
PY

echo
echo "## nearby generated CSS declarations if files exist"
rg -n "focus-visible:outline-none|focus-visible:ring-3|focus-visible:ring-ring|outline: 3px|focus-ring|focus-visible" node_modules/.vite 2>/dev/null || true

Repository: prof-ramos/webingressos-page

Length of output: 567


Passe o link de pulo para fora antes de esconder o contorno de foco.

focus-visible:outline-none mata a regra global de 3px solid var(--focus-ring). No fundo claro, ring-3 ring-ring/50 reduz o contraste do --brand-600 para cerca de 2:1, abaixo do 3:1 exigido pelo WCAG. Use ring-focus-ring sem opacidade no foco ou remova apenas outline-none para manter o contorno global.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/layout.tsx` around lines 33 - 35, Atualize o link de pulo no layout,
identificado pelo href="`#main-content`", para não remover o contorno global com
focus-visible:outline-none; alternativamente, substitua o anel sem opacidade por
ring-focus-ring. Preserve os demais estilos e garanta contraste de foco
compatível com WCAG.

Comment thread src/app/layout.tsx
Comment on lines +41 to +42
<SpeedInsights />
<Analytics />

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Tracked files matching layout/privacy/analytics/speedinsights/package:"
git ls-files | rg '(^|/)(layout|page\.tsx|package\.json|pnpm-lock\.yaml|yarn\.lock|package-lock\.json)$|SpeedInsights|Analytics|speedinsights|analsytics' || true

echo
echo "src/app/layout.tsx around SpeedInsights/Analytics:"
if [ -f src/app/layout.tsx ]; then
  nl -ba src/app/layout.tsx | sed -n '1,90p'
fi

echo
echo "Privacy page candidate:"
if [ -f src/app/privacidade/page.tsx ]; then
  nl -ba src/app/privacidade/page.tsx | sed -n '1,220p'
fi
if [ -f src/app/privacidade/page.ts ]; then
  echo "src/app/privacidade/page.ts exists"
fi

echo
echo "Search for consent / opt / Vercel policy / speed insights mentions:"
rg -n "SpeedInsights|Analytics|Vercel|tracking|privacy|privacidade|consent|consentimento|recusa|opt|analytics|telemetria|cookies" src package.json pnpm-lock.yaml yarn.lock package-lock.json 2>/dev/null || true

echo
echo "Dependency versions from package/lock:"
for f in package.json pnpm-lock.yaml yarn.lock package-lock.json; do
  [ -f "$f" ] && echo "--- $f ---" && rg -n "`@vercel/analytics`|next" "$f" -m 20 || true
done

echo
echo "Root files listing package and lock availability:"
git ls-files | rg '(^|/)(package\.json|pnpm-lock\.yaml|yarn\.lock|package-lock\.json)$'

Repository: prof-ramos/webingressos-page

Length of output: 430


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Checking whether Vercel/next package metadata is present in sandbox:"
if [ -f node_modules/@vercel/analytics/package.json ]; then
  cat node_modules/@vercel/analytics/package.json | jq '{version, main, repository}'
else
  echo "`@vercel/analytics` not installed in node_modules"
fi

if [ -f node_modules/next/package.json ]; then
  cat node_modules/next/package.json | jq '{version}'
else
  echo "next not installed in node_modules"
fi

Repository: prof-ramos/webingressos-page

Length of output: 410


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "src/app/layout.tsx:"
awk '{printf "%5d  %s\n", NR, $0}' src/app/layout.tsx

echo
echo "src/app/privacidade/page.tsx:"
awk '{printf "%5d  %s\n", NR, $0}' src/app/privacidade/page.tsx

echo
echo "package versions/snippets:"
node - <<'JS'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json','utf8'));
for (const k of ['dependencies','devDependencies']) {
  console.log(k, {next: pkg[k]?.next, '`@vercel/analytics`': pkg[k]?.['`@vercel/analytics`']});
}
JS

echo
echo "lock relevant entries:"
awk '/^$/ {next} /(`@vercel/analytics`|next@|next:|specifier): {print; for(i=0;i<8;i++){getline; if(/^$/) break; print}}' pnpm-lock.yaml

Repository: prof-ramos/webingressos-page

Length of output: 7182


Atualize a política antes de publicar a telemetria.

<SpeedInsights /> e <Analytics /> estão ativos em todo o site. A página src/app/privacidade/page.tsx só aborda os dados do formulário e não documenta a telemetria da Vercel, os dados tratados, a finalidade, a retenção nem a opção de recusa. Alinhe a política. Se o fluxo exigir consentimento, carregue os componentes somente após a escolha do usuário. Confirme o comportamento das versões instaladas antes do merge.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/layout.tsx` around lines 41 - 42, Atualize a política de privacidade
em src/app/privacidade/page.tsx para documentar a telemetria de <SpeedInsights
/> e <Analytics />, incluindo dados tratados, finalidade, retenção e opção de
recusa; verifique também as versões instaladas para confirmar o comportamento
atual. Se essas versões exigirem consentimento, ajuste o fluxo em layout.tsx
para renderizar os componentes somente após o usuário consentir.

Comment thread src/app/obrigado/page.tsx
Comment on lines +8 to +11
<main
id="main-content"
className="flex min-h-screen flex-col items-center justify-center px-5 py-16"
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files | rg '(^|/)src/app/(obrigado|privacidade)/page\.tsx$' || true

echo
echo "Relevant snippets:"
for f in src/app/obrigado/page.tsx src/app/privacidade/page.tsx; do
  echo "--- $f"
  wc -l "$f"
  sed -n '1,80p' "$f" | cat -n
done

echo
echo "Search for skip/main-content implementations:"
rg -n 'main-content|tabIndex=\{-1\}|skip|Skip|pular|Pular' src/app src/components src 2>/dev/null || true

Repository: prof-ramos/webingressos-page

Length of output: 7703


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "src/app/page.tsx:"
sed -n '1,80p' src/app/page.tsx | cat -n

echo
echo "src/app/layout skip-link region:"
sed -n '20,45p' src/app/layout.tsx | cat -n

echo
echo "All main-content usages:"
python3 - <<'PY'
from pathlib import Path
for path in [Path("src/app/page.tsx"), Path("src/app/obrigado/page.tsx"), Path("src/app/privacidade/page.tsx")]:
    lines = path.read_text().splitlines()
    for i, line in enumerate(lines, 1):
        if "main-content" in line or "tabIndex" in line:
            print(f"{path}:{i}:{line}")
PY

Repository: prof-ramos/webingressos-page

Length of output: 2948


🌐 Web query:

WCAG 2.4.1 Bypass blocks programmatically setting focus jump target tabindex

💡 Result:

To satisfy WCAG 2.4.1 (Bypass Blocks), implementing a "skip to main content" link is a highly effective and recommended technique [1][2]. A common technical challenge with this approach is ensuring that the keyboard focus actually moves to the target content when the link is activated, rather than just scrolling the page [1][3]. Setting tabindex="-1" on the target element is the standard and recommended practice to make this jump reliable [4][5][6]. Why tabindex="-1" is used: Standard HTML elements like

,
, or

are not natively focusable [5]. Browsers often fail to move focus to these elements during fragment navigation (when clicking an tag pointing to an ID) [7][6]. Adding tabindex="-1" allows an element to receive programmatic focus via JavaScript or the browser's internal focus management, without including it in the natural tab sequence of the page [1][4][6]. Implementation Best Practices: 1. Skip Link Placement: The skip link must be the very first focusable element in the DOM so that it is the first thing a user encounters when tabbing after page load [1][4][7]. 2. Target Configuration: Assign a unique ID to your main content container and add tabindex="-1" to it [1][5][6]. 3. Programmatic Focus: While many modern browsers handle focus movement to hash links automatically, relying solely on the href can be inconsistent [7][6]. It is best practice to attach a JavaScript click handler to the skip link that explicitly calls the.focus method on the target element [1][3][6]. 4. Visibility: Never hide the skip link using display: none or visibility: hidden, as these methods remove the element from the accessibility tree, making it invisible to screen readers and keyboard users [1][7][5]. Instead, use CSS to hide it visually (e.g., positioning it off-screen) and reveal it when it receives focus [1][4][3]. By following these steps, you ensure that the mechanism is robust, works across different browsers, and satisfies the requirements of WCAG 2.4.1 [5][6].

Citations:


Bora fazer o foco ir para o conteúdo pular o link.

O link “Pular para o conteúdo” só aponta para #main-content, e esses destinos não recebem foco programático. Adicione tabIndex={-1} em todos os <main id="main-content">:

  • src/app/obrigado/page.tsx#L8-L11
  • src/app/privacidade/page.tsx#L6
  • src/app/page.tsx#L18
📍 Affects 2 files
  • src/app/obrigado/page.tsx#L8-L11 (this comment)
  • src/app/privacidade/page.tsx#L6-L6
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/obrigado/page.tsx` around lines 8 - 11, Enable programmatic focus on
every main element with id="main-content" by adding tabIndex={-1} in
src/app/obrigado/page.tsx lines 8-11 and src/app/privacidade/page.tsx line 6;
also apply the same change to the corresponding main element in src/app/page.tsx
as requested.

Comment on lines +76 to +104
<div
id="menu-principal"
hidden={!isMenuOpen}
className="border-t border-border bg-white transition-[opacity,translate] duration-200 starting:-translate-y-2 starting:opacity-0 lg:hidden"
>
<Shell>
<nav aria-label="Navegação móvel" className="flex flex-col gap-1 py-4">
{NAV_LINKS.map((link, index) => (
<a
href={SITE_CONFIG.pilotFormUrl}
key={link.href}
ref={index === 0 ? firstMenuLinkRef : undefined}
href={link.href}
onClick={() => setIsMenuOpen(false)}
className="mt-2 inline-flex h-12 items-center justify-center gap-2 rounded-xl bg-brand-700 px-5 text-sm font-bold text-white transition-colors hover:bg-brand-800 sm:hidden"
className="rounded-xl px-3 py-3 text-base font-medium text-ink-700 transition-colors hover:bg-brand-50 hover:text-brand-700"
>
{SITE_CONFIG.ctaLabel}
<ArrowRight className="size-4" />
{link.label}
</a>
</nav>
</Shell>
</div>
) : null}
))}
<a
href={SITE_CONFIG.pilotFormUrl}
onClick={() => setIsMenuOpen(false)}
className="mt-2 inline-flex h-12 items-center justify-center gap-2 rounded-xl bg-brand-700 px-5 text-sm font-bold text-white transition-colors hover:bg-brand-800 sm:hidden"
>
{SITE_CONFIG.ctaLabel}
<ArrowRight className="size-4" />
</a>
</nav>
</Shell>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

MDN display property CSS transitions visibility hidden discrete discrete transition-discrete allow-discrete browser support

💡 Result:

In modern CSS, the transition-behavior property with the value allow-discrete enables transitions on properties that are typically animatable only in a discrete (all-or-nothing) manner, such as display and content-visibility [1][2][3]. By default, transition-behavior is set to normal, which prevents transitions for these discrete properties [4][2]. Key technical details: 1. Mechanism: When allow-discrete is used, the browser coordinates the discrete property change with other interpolable transitions (like opacity) [4][5]. For example, when animating from display: block to display: none, the browser maintains the block state throughout the animation duration and flips to none only at the end, allowing the other property transitions to complete [1][4][3]. 2. Implementation: You can apply this via the transition shorthand or the transition-behavior property [6][2]: transition: opacity 0.5s, display 0.5s allow-discrete; or transition-behavior: allow-discrete; 3. Starting State: When transitioning an element into the DOM or from display: none to a visible state, the @starting-style at-rule is often required to define the initial state, as transitions are not triggered on an element's first style update [1][7][4]. 4. Browser Support: Support for transition-behavior: allow-discrete is generally available in modern browsers, including Chrome (117+), Edge (117+), Firefox (129+), and Safari (17.4+) [8][9][10]. Unlike display, the visibility property is historically considered an interpolable property (though it animates discretely, switching to visible at 0% and hidden at the end) and does not strictly require allow-discrete to animate, though it is often used in conjunction with these modern techniques to manage accessibility and layout transitions [11]. [1][8][10]

Citations:


🌐 Web query:

developer.chrome blog exit-animations display transition-discrete allow-display Safari Firefox transition-discrete

💡 Result:

The CSS feature you are referring to is transition-behavior: allow-discrete [1][2]. It was introduced to solve the long-standing challenge of animating elements to and from display: none, which previously required JavaScript to manage the timing of DOM removal or style changes [1][3][4]. While some early documentation or discussions may have informally referred to a desired functionality as "allow-display," the official property name is transition-behavior with the value allow-discrete [1][5]. The term discrete refers to properties that cannot be interpolated (like display, visibility, or mix-blend-mode), meaning they typically flip instantly between values rather than transitioning smoothly [1][6][5]. Setting transition-behavior: allow-discrete allows the browser to perform a transition before switching the discrete property state, typically at the 50% mark of the transition duration [1][5][4]. Browser support for transition-behavior: allow-discrete is now broadly available in modern browsers [7]: - Chrome: Supported since version 117 [8][7]. - Firefox: Supported since version 129 [8][6]. - Safari: Supported since version 17.4 [8][6]. To create smooth entry and exit animations, this property is typically used in conjunction with the @starting-style at-rule [1][3]. The @starting-style rule defines the initial state for entry animations, preventing the browser from skipping the transition when an element is first added to the DOM or changes from display: none [1][2][4]. Without these combined features, animating an element out of display: none typically results in an immediate, jumpy appearance or disappearance [3][2]. For implementation, it is often recommended to use the transition shorthand and place the transition-behavior declaration after it, or to explicitly include allow-discrete within the shorthand transition definition [1][9][5]. Because older browsers will not support these properties, it is best practice to treat them as a progressive enhancement [3][4]. You can detect support using CSS feature queries like @supports (transition-behavior: allow-discrete) {... } [3].

Citations:


🌐 Web query:

"transition-discrete" display none Tailwind CSS

💡 Result:

The transition-discrete utility in Tailwind CSS is a wrapper for the CSS property transition-behavior: allow-discrete [1][2]. It is used to enable transitions for properties that are traditionally "discrete" and cannot be animated, most notably display [1][3]. Normally, when an element is toggled to display: none, it disappears instantly because the browser removes it from the layout, preventing any accompanying animations (like opacity) from playing [2][3]. By using transition-discrete, you instruct the browser to defer the display property change until the end of the transition timeline [2]. To effectively transition an element in and out of display: none, you must combine transition-discrete with other Tailwind utilities: 1. Transition Property: Ensure display is included in your transition properties. Tailwind's default transition utility now includes display by default [4][5]. 2. State Management: Toggle the element's visibility state (e.g., using hidden and block, or state-based variants) [1][2]. 3. Starting Style: To animate an element as it first appears (since transitions typically do not run on an element's first render or when switching from display: none to display: block), use the starting: variant [6][7]. This utilizes the CSS @starting-style rule [6][2]. Example pattern:

Content
In this example, transition-discrete ensures the exit animation (e.g., opacity fading out) completes before the element is set to display: none [2][3]. The starting: variant ensures a smooth fade-in when the element is first displayed [6][7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== candidate files =="
fd -a 'header\.tsx$|tailwind.*config|package.json|pnpm-lock.yaml|package-lock.json|yarn.lock' . | sed 's#^\./##' | head -80

echo
echo "== package-related tailwind versions =="
for f in $(fd 'package.json$' .); do
  echo "-- $f"
  rg -n '"tailwindcss"|`@tailwind`|scripts|postcss' "$f" || true
done

echo
echo "== header excerpt =="
f="$(fd 'header\.tsx$' . | head -1)"
if [ -n "$f" ]; then
  echo "FILE=$f"
  sed -n '1,180p' "$f" | nl -ba | sed -n '60,115p'
fi

echo
echo "== search for transition-discrete and display in Tailwind support =="
rg -n --glob '!node_modules' --glob '!dist' --glob '!build' 'transition-discrete|transition-behavior|display'|head -80 || true

Repository: prof-ramos/webingressos-page

Length of output: 561


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== header excerpt =="
sed -n '60,120p' src/components/landing/header.tsx

echo
echo "== transition/display CSS hints / Tailwind version =="
sed -n '1,70p' package.json
printf '\n'
rg -n 'transition-discrete|transition-behavior|display|starting:opacity|starting:-translate|starting:' src/pnpm-lock.yaml package.json || true

Repository: prof-ramos/webingressos-page

Length of output: 3932


Dê cobertura pra transição de fechamento.

O menu abre via display do atributo hidden, mas esse estado não é tratado como propriedade discreta. Quando isMenuOpen vira false, display: none é aplicado na hora e corta opacity/translate. Adicione transition-discrete no div#menu-principal para fechar com animação em browsers modernos.

🎖️ Ajuste sugerido
       <div
         id="menu-principal"
         hidden={!isMenuOpen}
-        className="border-t border-border bg-white transition-[opacity,translate] duration-200 starting:-translate-y-2 starting:opacity-0 lg:hidden"
+        className="border-t border-border bg-white transition-discrete transition-[opacity,translate] duration-200 starting:-translate-y-2 starting:opacity-0 lg:hidden"
       >
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/header.tsx` around lines 76 - 104, Update the
div#menu-principal className to include the transition-discrete utility,
preserving the existing opacity and translate transitions so closing the menu
animates before hidden applies display:none in modern browsers.

export function PilotBand() {
return (
<section id="programa-piloto" className="py-6 sm:py-8">
<section id="programa-piloto" className="scroll-mt-16 py-6 sm:scroll-mt-20 sm:py-8">

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Valor de scroll-mt duplicado — ver comentário consolidado.

Aqui o scroll-mt-16/sm:scroll-mt-20 está calibrado certinho com a altura do header (h-16/sm:h-20). Missão cumprida nesse ponto. Mas esse mesmo par de números está copiado em src/components/landing/section.tsx (Linha 27). Ver comentário consolidado ao final da revisão para o ajuste sugerido.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/pilot-band.tsx` at line 9, Keep the scroll-mt-16 and
sm:scroll-mt-20 values in the pilot-band section unchanged; apply the
consolidated deduplication adjustment to the corresponding section wrapper in
the Section component instead, reusing the shared spacing values without
altering header-aligned behavior.

Comment on lines +94 to 97
setSubmitError("Não foi possível enviar agora. Tente novamente em alguns minutos.")
toast.error("Erro ao enviar candidatura", {
description: "Tente novamente em alguns minutos ou entre em contato diretamente.",
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
fd -a 'pilot-form\.tsx$' . || true

echo "== file excerpt =="
if [ -f src/components/landing/pilot-form.tsx ]; then
  nl -ba src/components/landing/pilot-form.tsx | sed -n '1,180p'
fi

echo "== search toast/error usage in file =="
if [ -f src/components/landing/pilot-form.tsx ]; then
  rg -n "toast\.error|setSubmitError|aria-live|role=\"alert\"|role='alert'" src/components/landing/pilot-form.tsx || true
fi

echo "== package sonner references =="
rg -n "sonner|`@radix-ui`|toast" package.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null || true

Repository: prof-ramos/webingressos-page

Length of output: 300


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file excerpt with line numbers =="
awk '{printf "%6d\t%s\n", NR, $0}' src/components/landing/pilot-form.tsx | sed -n '1,180p'

echo "== relevant usages =="
grep -n 'toast\.error\|setSubmitError\|aria-live\|role="alert"\|role='"'"'alert'"'"'' src/components/landing/pilot-form.tsx || true

echo "== package/dependency references =="
grep -RlnE 'sonner|`@radix-ui/react-toast`|toast\.error' package.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null || true

Repository: prof-ramos/webingressos-page

Length of output: 8174


🌐 Web query:

Sonner Toast react announces error notify live region role status browser

💡 Result:

Sonner is designed to handle screen reader announcements using WAI-ARIA live regions, ensuring that toast notifications are accessible [1][2]. To properly announce toasts, the library utilizes a containing element as an ARIA live region rather than applying live region attributes to individual toast items [3]. Key accessibility details regarding Sonner's implementation include: * Live Region Strategy: The Toaster component maintains a persistent containing element (often a section) configured with aria-live, aria-relevant, and aria-atomic attributes [3]. This approach ensures that screen readers are aware of the container and can announce additions to it, solving issues where conditional rendering of individual toast elements caused some screen readers to fail to announce them [3][1]. * Configuration: Recent updates to Sonner have introduced custom ARIA announcement options [4]. Users can now configure aria-live, aria-atomic, and aria-relevant settings per toast, allowing for more granular control over notification urgency [4]. * Urgency Management: While the default behavior for general notifications is typically polite, you can use these custom options to set the aria-live attribute to assertive for urgent notifications, such as error messages, to ensure they are announced immediately [4][2]. * Historical Context: Early versions of Sonner experienced accessibility bugs related to placing role="status" directly on individual toast li elements, which led to inconsistent announcement behavior across different screen readers [3][1]. These issues were resolved by moving the live region attributes to the persistent parent container [3]. When implementing Sonner, ensure you are using a recent version that supports these configurable ARIA options to achieve optimal accessibility for error and status notifications [4].

Citations:


Escolha só um canal pra anunciar esse erro.

O mesmo catch aciona o banner role="alert" via setSubmitError(...) e dispara um toast.error(...) do Sonner, que também usa uma region viva para anunciar toasts. Isso faz o leitor de tela ouvir o mesmo erro duas vezes. Mantenha o banner e remova o toast.error desse fluxo, ou faça o feedback visual sem papel de status/alerta.

🎯 Ajuste sugerido mantendo só o banner
     } catch {
       setSubmitError("Não foi possível enviar agora. Tente novamente em alguns minutos.")
-      toast.error("Erro ao enviar candidatura", {
-        description: "Tente novamente em alguns minutos ou entre em contato diretamente.",
-      })
     } finally {

Também vale pra linha 133.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/pilot-form.tsx` around lines 94 - 97, Remova as
chamadas a toast.error no fluxo de erro de envio do formulário, mantendo
setSubmitError como único canal de anúncio acessível. Aplique essa alteração
tanto no catch próximo a setSubmitError quanto no fluxo correspondente indicado
na linha 133, preservando o banner role="alert" e o restante do tratamento de
erro.

Comment on lines +25 to +28
<section
id={id}
className={cn("scroll-mt-16 py-8 sm:scroll-mt-20 sm:py-10 lg:py-12", className)}
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Valor de scroll-mt duplicado entre section.tsx e pilot-band.tsx — ver comentário consolidado.

Esse componente compartilhado (Section) usa scroll-mt-16 sm:scroll-mt-20, calibrado para a altura do header (h-16/sm:h-20). O mesmo par de valores está repetido em src/components/landing/pilot-band.tsx (Linha 9), que não usa Section. Ver comentário consolidado ao final da revisão para a proposta de centralizar esse valor.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/landing/section.tsx` around lines 25 - 28, Centralize the
shared scroll-mt-16/sm:scroll-mt-20 values used by the landing Section component
and pilot-band.tsx into a reusable styling constant or utility. Update Section
and pilot-band.tsx to consume that shared definition, preserving the existing
responsive scroll offset behavior.

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.

1 participant