0002 — Zoom automático no mobile ao focar qualquer campo¶
| Status | CORRIGIDO |
| Branch | fix/0002-zoom-automatico-no-mobile |
| Aberto em | 2026-08-03 |
| Severidade | ALTA |
| Apps afetados | api (sem impacto) · web · e2e |
Status:
RASCUNHO→DIAGNOSTICADO→EM CORREÇÃO→CORRIGIDO
1. Sintoma¶
No celular, tocar em qualquer campo faz a tela ampliar sozinha. A página fica maior que a viewport, o teclado cobre metade do que sobrou, e é preciso apertar os dedos para voltar ao tamanho normal — a cada campo. Preencher um formulário de cadastro, que tem dez campos, significa dez ampliações e dez correções manuais. O relato do usuário é direto: "quebra a utilização".
Não há mensagem de erro; nada aparece no console. É comportamento do navegador, não do app — o que explica por que nenhum teste pegou.
2. Reprodução¶
- Abrir o app num iPhone (ou no Safari com iOS emulado), autenticado.
- Ir a Inquilinos → Novo inquilino.
- Tocar no campo "Nome".
A tela amplia no instante do foco. Repete em qualquer campo de qualquer tela: login, busca das listas, formulário de imóvel, análise de crédito.
- Ambiente: dev-remote (e local — não depende de dados).
- Superfície: mobile, especificamente iOS/iPadOS. O Android Chrome não amplia ao focar, então quem testa em Android não vê o defeito. Não é questão de breakpoint: o gatilho é o tamanho da fonte do campo, que é o mesmo em qualquer largura.
- PWA: não agrava nem alivia. O comportamento é do motor de renderização, e vale igual na aba do Safari e na tela instalada.
- Dados/pré-condições: nenhuma. Vale para qualquer papel e tenant, inclusive na tela de login, antes de autenticar.
3. Esperado × Atual¶
- Esperado: tocar num campo posiciona o cursor e abre o teclado. A escala da página não muda. O usuário continua podendo ampliar por conta própria, se quiser.
- Atual: tocar num campo amplia a página automaticamente, e ela permanece ampliada até o usuário desfazer com o gesto.
4. Causa raiz¶
O Safari do iOS amplia a página quando o campo que recebe foco tem font-size menor que 16px. É comportamento deliberado do navegador — a suposição é que texto pequeno demais para ler precisa de aproximação — e não há como desligá-lo a não ser satisfazendo o limite, ou proibindo o zoom por completo.
Os campos do app estão todos abaixo do limite. Medido com iPhone 13 emulado:
| Tela | Campos | Tamanho medido |
|---|---|---|
| Login | 2 | 14px |
| Novo inquilino | 10 | 14px (8 simples + 2 multilinha) |
| Busca das listas | 1 | 14px |
A origem é única: MuiOutlinedInput.input define fontSize: 14 no tema do @garantora/design-system (dist/theme/components.js:37). Todo campo do app nasce dali — TextField, Autocomplete, Select, busca das listas.
Há um segundo tamanho na medição, 13,3px, que não é gatilho: é o MuiSelect-nativeInput, o input oculto que o MUI mantém para submissão de formulário. Ele nunca recebe foco, então não provoca ampliação. Registrado aqui para não virar caça ao fantasma depois.
Não é regressão. O valor 14px existe desde que o app adotou o tema da marca; o defeito sempre esteve lá e só agora foi relatado. O viewport do index.html está correto (width=device-width, initial-scale=1, sem maximum-scale) — ele não bloqueia o zoom do usuário, o que é o comportamento acessível desejado.
5. Correção¶
Decidido: os campos vão a 16px em todas as telas, desktop e mobile — uma medida só, em vez de um valor por breakpoint —, e a mudança é feita no @garantora/design-system, não no apps/web.
apps/api¶
Sem impacto.
apps/web¶
A triagem previa que o trabalho aqui seria "só subir o pino". Ao implementar, isso se mostrou insuficiente — o pino sozinho não conserta (ver Registro):
contexts/color-mode— o merge decomponentsera spread raso, e o app sobrescreveMuiInputBaseeMuiNativeSelect, exatamente as duas chaves que a 2.9.2 usa como rede de segurança. Passou adeepmerge(@mui/utils, forma que otheme/README.mddo pacote recomenda), para as duas coisas coexistirem.components/shared/DocumentsPanel— dois campos de observação encolhidos porsxlocal (13px e 12px).sxde página vence o tema, então a correção do pacote não os alcançava. Os encolhimentos saíram.package.json— pino em#v2.9.2e@mui/utils@^6.4.9, casando a major do@mui/material6.5.0 (opnpm addsem versão trouxe a 9.x, muito à frente).
@garantora/design-system — entregue na 2.9.2¶
O pacote resolveu, e de forma mais completa que o pedido: MuiOutlinedInput.input de 14 para 16, mais MuiInputBase.input e MuiNativeSelect.select como rede de segurança, o token --field-font-size e um piso em tokens/base.css (input,select,textarea{font-size:16px}). Rótulos, dicas e erros ficaram na escala de sempre — como o pedido pedia.
apps/e2e¶
- Teste de regressão: varredura que reprova se algum campo focável ficar abaixo de 16px, em viewport mobile. Diferente dos outros testes desta suíte, ele não exercita fluxo — afirma uma propriedade do CSS, que é onde o bug vive. Sem ele, um valor reintroduzido em qualquer componente volta a ampliar a tela sem ninguém notar.
6. Risco / impacto colateral¶
- Densidade das telas de formulário no desktop. 14 → 16px é ~14% de aumento no texto de todo campo. O
OutlinedInputtem altura fixa de 36px no tema; 16px caibem, mas o respiro vertical diminui. Precisa de olhada nas telas mais densas (análise de crédito, parâmetros) depois do bump. - Alinhamento com rótulo e helper text.
InputLabelfica em 13px; a diferença com o valor digitado passa de 1px para 3px. Não quebra, mas muda a hierarquia visual. - Nada de dinheiro. Não toca pricing, billing nem sinistro — é tipografia de controle de formulário.
- Sem risco de dado. Nenhuma migration, nenhum contrato de API.
7. Critérios de aceite¶
- [x] CA1. Seguindo os passos da seção 2 num iOS (real ou emulado), tocar num campo não altera a escala da página.
- [x] CA2. Todo campo focável (
input,textarea) mede ≥ 16px em viewport mobile, em login, busca de lista e formulários de inquilino, imóvel e análise. - [x] CA3. O usuário continua podendo ampliar por gesto — o
viewportsegue semmaximum-scalenemuser-scalable=no. - [x] CA4. No desktop, os formulários não ganham rolagem nem quebra de alinhamento com o campo 2px maior.
- [x] CA5. O teste de regressão reprova se algum campo voltar a ficar abaixo de 16px.
8. Plano¶
- [x] Reproduzir o bug e medir os tamanhos reais (iPhone 13 emulado)
- [x] Localizar a causa raiz no tema do pacote
- [x]
@garantora/design-system— corrigido na 2.9.2 pelo mantenedor - [x]
apps/web— pino em#v2.9.2 - [x]
apps/web—deepmergeno tema (sem ele o pino perdia a rede de segurança) - [x]
apps/web— encolhimentos locais doDocumentsPanelremovidos - [x]
apps/e2e— teste de regressão do tamanho mínimo de campo - [x] Validado no browser com iPhone 13 emulado
- [x]
tsclimpo, ESLint no mesmo baseline de 53.make testnão se aplica — nada de API
9. Decisões em aberto¶
Nenhuma pendente de decisão — as duas foram resolvidas na triagem:
- Como corrigir: 16px em todas as telas (não só no mobile, e sem bloquear o zoom no
viewport, que seria regressão de acessibilidade — WCAG 1.4.4). - Onde corrigir: no
@garantora/design-system, com o app aguardando a publicação.
Dependência externa que bloqueia o fechamento: o pacote precisa publicar a mudança. Enquanto isso, este fix fica em DIAGNOSTICADO e o mobile segue com o defeito — o que é consequência assumida da decisão, não descuido.
10. Registro¶
2026-08-03 — corrigido, e o pino sozinho não bastava.
A 2.9.2 saiu com a correção completa. Mas a triagem havia previsto que o trabalho no app seria "só subir o pino", e isso estava errado por dois motivos que apareceram ao medir.
1. O merge de tema era raso, e comia a rede de segurança. buildTheme fazia {...base.components, ...appOverrides(mode)}. Spread substitui a chave inteira: como o app sobrescreve MuiInputBase (cor do placeholder) e MuiNativeSelect (ícone do caret), o fontSize: 16 que a 2.9.2 pôs nessas duas chaves era apagado. O comentário no próprio arquivo já avisava do risco — "usar a mesma chave num merge raso o apagaria" — escrito quando o pacote ainda não usava MuiInputBase. Agora usa.
Medindo, a perda era parcial, não total: o login continuou a 16px mesmo com o spread, porque aqueles campos passam por MuiOutlinedInput, que o app não sobrescreve. Ou seja, o bug principal ficaria corrigido e a rede de segurança silenciosamente ausente — o pior tipo de defeito, o que não aparece hoje. Trocado por deepmerge.
2. Dois campos encolhidos por sx local. No DocumentsPanel, as observações de upload e de edição estavam em 13px e 12px via sx de página, que vence o tema. A correção do pacote não os alcançava. Encontrados varrendo MuiInputBase-input e MuiInputBase-root com fontSize, e removidos. A varredura ampliada mostrou que eram os únicos campos editáveis encolhidos; o único outro fontSize em campo é o OTP do verify-email, a 24px, acima do piso.
Um terceiro encolhimento ficou de propósito: o Select de tipo do mesmo painel, a 13px. Select do MUI não é campo digitável — o elemento que recebe foco é um input oculto que o iOS ignora —, então não dispara ampliação.
Medição depois da correção (iPhone 13 emulado): login 2 campos a 16px; /renters e /properties 1 cada a 16px; /renters/create 9 a 16px; /settings 9 a 16px. Nenhum campo focável abaixo de 16px em nenhuma tela varrida.
Teste de regressão: apps/e2e/tests/e2e/mobile-zoom.spec.ts, 4 casos, 4 passando no chromium. Ele não exercita fluxo — afirma a propriedade do CSS, porque é onde o bug vive e porque nenhum navegador de teste reproduz a ampliação do WebKit móvel. Um dos casos guarda o outro lado da moeda: o viewport não pode ganhar user-scalable=no nem maximum-scale=1, senão corrigir o zoom automático custaria o zoom intencional (WCAG 1.4.4).
Lacuna declarada: os dois campos do DocumentsPanel não têm caso automatizado. A linha de envio do painel só é montada sob condições que não reproduzi de fora — configurar tipo de documento na área ADDITIONAL não bastou. Preferi declarar a lacuna a deixar teste vermelho ou skip silencioso. A correção neles está verificada por leitura e pela medição do tema; falta o caso que a defende de regressão.
Sobre o CA4 (densidade no desktop): os campos ficaram 2px maiores, dentro dos 36px de altura do OutlinedInput. Sem rolagem nova nem quebra de alinhamento nas telas medidas.
Suíte completa executada. Primeira rodada: 253 passaram, 3 falharam — e nenhuma das três era o fix.
- Duas no
credit-analysis-documents(chromium): resíduo meu. Depurando o teste de regressão, tentei montar a linha de envio do painel com umPUT /settings/credit-analysis-documentsmarcando "Comprovante de Renda" comoADDITIONAL/OPTIONAL— no tenant compartilhado do e2e, e sem desfazer. O checklist passou a mostrar duas entradas do mesmo tipo ("Falta" e "(opcional) Falta") e ogetByTestIddo spec bateu em dois elementos. Limpei a área que sujei; o spec voltou a 12/12. Os specs da casa mutam configuração dentro detry/finally— o meu era código de depuração solto, que é pior. - Uma no
renters-properties-list(webkit): corrida no teste da spec 0013. O CA2 lia a contagem depois de esperar só o chip do filtro, que vem do estado do React no instante do clique; a contagem só muda quando a resposta da API chega. No navegador mais lento, lia o número antigo —891 < 891. Trocado porexpect.poll. Esse teste já estava namain: passou no merge da spec 0013 por sorte de tempo. Corrigido aqui em vez de deixar instável.
Segunda rodada, depois das duas correções: 256 passaram, 6 skipped, 0 falhas (12,3 min, chromium + firefox + webkit).
O que a suíte não prova: ela roda em chromium, firefox e no webkit desktop, e o defeito é do WebKit móvel. Nenhum deles reproduz a ampliação. A suíte confirma a propriedade (nenhum campo abaixo de 16px) e a ausência de regressão nos fluxos; a prova final é abrir num iPhone de verdade.