Skip to content

UI/Mobile: editor nasce em 15px e o iOS dá zoom automático ao focar (CodeEditor.tsx:38) — piso de 16px no touch #53

Description

@caioross

Contexto

O editor da corrida nasce com fonte de 15px — abaixo do limiar de 16px que faz o
Safari/iOS dar zoom automático ao focar um campo de texto. Na prática: o jogador de
celular toca no editor, a página dá um zoom que ele não pediu, e a corrida começa com a
viewport deslocada (o código-alvo sai de vista). O zoom acontece no primeiro toque
aumentar a fonte depois pelo menu de opções não desfaz o estrago.

Origem: [Feedback] O Mobile · 2026-07-18 (Discussion #45), fricção nº2 — ICE ≈ 75
(Impacto 3 × Confiança 5 × Facilidade 5), candidata nº1 do [Plano] W30 (#47).

Evidência no código (origin/main):

  • src/components/CodeEditor.tsx:38const [fontSize, setFontSize] = useState(15);
  • src/components/CodeEditor.tsx:15-16MIN_FONT = 12 / MAX_FONT = 20 (o piso de 12
    permite o jogador descer abaixo do limiar de zoom manualmente, no mobile)
  • src/components/CodeEditor.tsx:45lineHeight = Math.round(fontSize * 1.7), e
    :217 — a gutter compartilha fontSize/lineHeight, então o alinhamento
    textarea ↔ overlay colorido ↔ gutter depende dos três casarem.

A spec já pede isso: docs/UI-AAA-OVERHAUL.md §III, "Responsivo (mobile — crítico, hoje
frágil)" → "Fonte do código maior (16px+) para o teclado virtual".

Acceptance criteria

  • Em viewport touch/estreita (ex.: (pointer: coarse) ou largura < 768px), o editor
    nasce com fontSize >= 16; em desktop o default de 15px pode permanecer.
  • No mesmo contexto, o piso do controle "tamanho da fonte" é 16 — não é possível
    descer abaixo do limiar de zoom pelo menu de opções (desktop mantém MIN_FONT = 12).
  • O trio textarea + <pre> espelho + gutter continua alinhado char-a-char em
    qualquer tamanho válido (nenhum drift visual no realce da linha ativa, :277+).
  • Nenhuma mudança na lógica de digitação: handleInput/comparação char-a-char, WPM,
    precisão e anti-cheat intactos — node scripts/validate-metrics.mjs verde.
  • prefers-reduced-motion inalterado; nenhum trabalho novo por keystroke (área
    sagrada
    : a decisão de tamanho é resolvida no mount/resize, nunca no onChange).
  • Gate do HANDBOOK §6 verde (typecheck + build + Vitest).

Dica de abordagem

Fatia de uma rodada. Resolver o default no mount (e em resize, com debounce) via
window.matchMedia("(pointer: coarse)") — e derivar MIN_FONT efetivo do mesmo sinal, em
vez de espalhar condicional pela UI. Cuidado com hidratação: matchMedia não existe no
servidor; inicialize com o valor de desktop e ajuste num useEffect (o primeiro paint com
15px é aceitável — o zoom do iOS só dispara no foco). Evite user-scalable=no no viewport:
mata o pinch-zoom e é problema de acessibilidade — a correção certa é a fonte.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Prioridade normalarea:uiInterface, design system, animação, game feel

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions