Site sempre responsivo
Uma página só está finalizada quando passa por python3 scripts/check_responsive.py (sem rolagem horizontal, nada vazando da tela, alvos de toque grandes o suficiente) nos 6 tamanhos de referência. Essa verificação também roda na CI: um PR que quebra o responsivo não pode ser implantado.
Larguras de referência (testar todas)
| Largura | Caso |
|---|---|
| 320 px | Android pequeno, iPhone SE com zoom |
| 375 px | iPhone padrão (alvo nº 1: a audiência vem do TikTok) |
| 414 px | Telefone grande |
| 768 px | Tablet em retrato |
| 1024 px | Tablet em paisagem / notebook pequeno |
| 1440 px | Desktop |
Tamanhos testados pelo script: 320×640, 375×667, 414×896, 768×1024, 1024×600 (tela baixa), 1440×900.
Regras CSS
- Mobile first: estilos base para 320 px, depois
@media (min-width: …)para ampliar. Pontos de quebra:600px,900px,1200px(sem valores mágicos em outros lugares; um componente pode usar@container). - Sem largura fixa maior que 280 px sem
min()/max-width: 100%:width: min(520px, 100%). - Tipografia fluida com
clamp(): títulosclamp(32px, 7vw, 72px), texto corrente mínimo de 16 px (nunca abaixo disso no mobile, senão o iOS dá zoom nos campos). - Alturas:
min-height: 100svh(não100vh, que fica por baixo da barra de endereço mobile). Nunca usarheightfixo em um bloco de texto. - Margens laterais: calha de 16 px no mobile (
space-4), +env(safe-area-inset-left/right)para telas com notch (o viewport já temviewport-fit=cover). - Texto longo:
overflow-wrap: anywhereem URLs, comandos e nomes de arquivos; blocos de código comoverflow-x: autodentro do bloco, nunca na página. - Grades:
grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr))para os cards de tutoriais. - Elementos decorativos que vazam (blocos
byte/spark, canvas):position: fixedou pai comoverflow: clip, e tamanhos emmin(vw, px). - Tátil: botões e links isolados ≥ 44 × 44 px (24 px = mínimo bloqueante do script, WCAG 2.2), 8 px de espaçamento mínimo entre dois alvos. Nenhuma informação disponível apenas ao passar o mouse (
:hover): prever:focus-visiblee o estado tátil. - Vídeos e iframes (YouTube):
aspect-ratio: 16 / 9; width: 100%; height: auto;— vídeos do TikTok em9 / 16commax-width: 360px.
Imagens
- Sempre
widtheheightno HTML (evita deslocamento de layout, CLS) +max-width: 100%; height: autono CSS. - Formatos: SVG para logotipos e ícones, WebP/AVIF para capturas, com
srcset+sizes(ex. 480w, 960w, 1600w). loading="lazy"edecoding="async"abaixo da dobra; nunca na imagem principal (LCP), que usafetchpriority="high".- Miniaturas de tutoriais:
aspect-ratio: 16 / 9; object-fit: cover.
Viewport (já implementado, não remover)
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
Proibido: maximum-scale=1, user-scalable=no (bloqueia o zoom, ruim para acessibilidade).
Animação e desempenho mobile
prefers-reduced-motionrespeitado (regra do projeto).- Canvas: limitar
devicePixelRatioa 2 e reduzir a densidade abaixo de 600 px (já feito noapp.js, manter esse princípio). - Sem
backdrop-filterpesado empilhado no mobile.
Verificar
# com o contêiner de check-site rodando em :8088, ou sem argumento (servidor local em site/)
python3 scripts/check_responsive.py # todas as páginas de site/
python3 scripts/check_responsive.py --shots /tmp/rs # + capturas PNG por largura
O script falha se scrollWidth > largura, se um elemento visível ultrapassar a borda, se um alvo tátil tiver menos de 24 px (links dentro de uma frase são isentos) ou se o texto corrente tiver menos de 16 px. Olhe as capturas em 320 e 375 px antes de enviar.