Qualidade de Frontend
Barra de qualidade para qualquer UI web voltada ao usuário. Requisitos duros (a11y, perf, mobile-first), não preferências estilísticas. Revisões detectam os bugs que importam para usuários reais — páginas lentas, navegação por teclado quebrada, layouts que mudam durante o carregamento.
Roteamento de Modelo
Use a preferência de frontend do operador quando um modelo precisar criticar ou reescrever UI, UX, hierarquia visual, texto de acessibilidade ou detalhes de interação:
source "$WALTER_OS_HOME/scripts/walter/lib/model-router.sh"
frontend_model=""
walter_model_resolve frontend frontend_model
A preferência padrão é Claude. O operador pode apontar o alias para outra rota do LiteLLM em ~/.config/walter-os/overlay/personal.env.
Esta skill aciona automaticamente em diffs que tocam frontend. Para uma landing page construída do zero, use landing-page-fast (que inclui estas regras mas fornece scaffolding).
Por que isso importa especificamente para este operador
- [Projeto A] usuários são fornecedores SMB argentinos — muitos em telefones, frequentemente em 3G/4G com conexão intermitente. Site lento = usuários vão embora. WCAG importa porque alguns usuários têm baixa alfabetização tecnológica + visão prejudicada.
- [Projeto B] usuários incluem pacientes com deficiências. WCAG não é "bom ter" — é um requisito de usabilidade.
- Documentação da [Empresa] é lida por devs sofisticados que pulam fora instantaneamente em sites lentos ou com teclado quebrado.
Acessibilidade (WCAG 2.2 AA — obrigatório)
Teclado
- Cada elemento interativo alcançável por Tab. Botões, links, campos de formulário, controles personalizados. Execute uma navegação manual por Tab; se algum controle for pulado, BLOQUEANTE.
- Foco visível. O contorno
:focus-visibledeve ser óbvio. Não definaoutline: nonesem uma alternativa. - Ordem de tabulação lógica. A ordem de leitura corresponde à ordem do DOM; evite valores
tabindex> 0 (eles quebram o fluxo natural). - Link de pular. O primeiro elemento focalizável em cada página é "Pular para o conteúdo principal".
- Escape fecha modais. Prenda o foco dentro do modal até fechar.
HTML Semântico
<button>para ações,<a>para navegação. Nunca<div onClick>para nenhum dos dois.- Títulos hierárquicos: um
<h1>por página;<h2>...<h6>não pulam níveis. - Marcos:
<header>,<nav>,<main>,<footer>. Roles ARIA apenas quando nenhum elemento semântico se encaixa. - Listas são
<ul>/<ol>. Não finja listas com<div>. - Controles de formulário têm
<label>. O atributoforcorresponde aoid.
Cor e Contraste
- Contraste mínimo WCAG AA: 4.5:1 para texto corporal, 3:1 para texto grande (18pt+ ou 14pt+ negrito), 3:1 para componentes de UI e gráficos.
- Cor não é o único indicador: estados de erro mostram ícone + texto + cor, não apenas borda vermelha.
- Testado com daltonismo simulado. Use Stark, axe DevTools ou simuladores de visão de cores das ferramentas de desenvolvedor do navegador.
Formulários
- Cada entrada tem um rótulo visível. Placeholder NÃO é um rótulo.
- Mensagens de erro são associadas programaticamente via
aria-describedby. - Campos obrigatórios marcados. Tanto visualmente (
*ou "obrigatório") QUANTO programaticamente (aria-required="true"ourequired). - Erros de validação não desaparecem ao focar. Persistem até corrigidos ou descartados.
- Estados de sucesso confirmados. Após o envio, usuários de leitores de tela sabem que funcionou.
Imagens e Mídia
- Atributo
altem cada<img>. Vazio (alt="") para decorativas; descritivo para conteúdo. Imagens geradas por IA: descreva o que realmente está na imagem, não o prompt. - Legendas/transcrições para vídeo/áudio.
- Sem reprodução automática com som.
- Animações respeitam
prefers-reduced-motion:@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } }
Ferramentas
@axe-core/reactno modo dev — registra problemas de a11y no console.eslint-plugin-jsx-a11y— detecta em tempo de build.- Teste manual com leitor de tela antes do lançamento: VoiceOver (Mac), NVDA (Windows), TalkBack (Android).
- Pontuação de a11y do Lighthouse ≥ 95 no CI.
Desempenho — Core Web Vitals
As 3 métricas nas quais o Google ranqueia. Passe EM TODAS AS TRÊS para estar "Bom":
LCP (Largest Contentful Paint) — velocidade de carregamento
Alvo: < 2,5 s em 3G. No mundo real para [Projeto A] é mais agressivo já que as redes móveis argentinas são mais lentas que a mediana.
Como:
- Pré-carregue a imagem LCP:
<link rel="preload" as="image" href="...">. - Sem recursos que bloqueiam renderização: adie JS não crítico, inclua CSS crítico inline.
- Fontes auto-hospedadas com
font-display: swapou use pilha de fontes do sistema. - CDN para ativos estáticos: Vercel, Cloudflare R2, Bunny.net.
- HTTP/2 ou HTTP/3: padrão Vercel/Cloudflare.
- Comprima imagens: WebP/AVIF, dimensionadas para a viewport.
- Otimize a imagem hero especificamente: é quase sempre a LCP.
INP (Interaction to Next Paint) — responsividade
Alvo: < 200 ms p75. Substituiu FID em 2024.
Como:
- Sem tarefas longas > 50 ms na thread principal durante a interação.
- Adie a hidratação (ilhas Astro, streaming Next.js, qwik).
- Web Workers para computação pesada.
- Debounce/throttle em manipuladores de entrada.
- Evite
dangerouslySetInnerHTMLcom strings grandes — o parsing bloqueia. useMemo/memoapenas onde medido, não preventivamente.
CLS (Cumulative Layout Shift) — estabilidade visual
Alvo: < 0,1.
Como:
- Tags de imagem e vídeo têm
width+height(navegador reserva espaço). - Reserve espaço para anúncios/embeds com min-height.
- Não insira conteúdo acima do existente: banners de notificação vão abaixo da dobra ou empurram para baixo via transform, não inserindo no DOM acima.
- Fontes web: use
font-display: swap+size-adjustpara corresponder às métricas, OUfont-display: optionalse possível. - Telas de esqueleto dimensionadas para corresponder ao conteúdo real.
Tamanho do Bundle
- Orçamento inicial de JS: < 150 KB gzip para landing pages, < 250 KB para shells de aplicativo.
- Divisão de código por rota (Next.js, Astro, SvelteKit fazem isso por padrão).
- Importações dinâmicas para dependências pesadas não críticas (gráficos, editores).
- Audite dependências:
bundle-analyzerounext-bundle-analyzer. Livre-se da biblioteca de UI de 10 MB da qual você importou um componente. - Tree-shaking funcionando: importe apenas o que precisa (
import { thing } from 'lib'nãoimport lib from 'lib').
Padrões de Carregamento
- Pré-conexão com origens necessárias:
<link rel="preconnect" href="...">. - Pré-busca de páginas prováveis ao passar o mouse/focar (Next.js faz isso).
- Carregamento lento de imagens abaixo da dobra:
loading="lazy". - Carregamento lento de iframes:
loading="lazy". - Service Worker para offline-first (onde faz sentido — [Projeto A] se beneficia, páginas de marketing não).
Estados — todo componente lida com todos os cinco
Para qualquer UI orientada a dados, BLOQUEANTE se algum estado não for tratado:
- Carregando — esqueleto ou spinner. NÃO tela em branco. NÃO mudança de layout.
- Vazio — estado de primeira vez, sem dados. Com CTA para popular.
- Erro — mensagem clara, botão de repetir, sem stack trace para o usuário. Registrado no Sentry.
- Parcial / desatualizado — quando os dados vêm do cache ou de busca parcial.
- Sucesso — o caminho feliz.
Exemplo para uma lista:
if (isLoading) return <ListSkeleton />
if (error) return <ErrorState onRetry={refetch} />
if (data.length === 0) return <EmptyState cta={...} />
return <List items={data} />
Se seu componente tem apenas o estado de sucesso mostrado a mim no diff, pergunte: onde vivem os outros 4?
Mobile-first
- Projete para 360 px de largura primeiro, escale para cima.
- Alvos de toque ≥ 44×44 px (Apple HIG) ou 48×48 px (Material).
- Sem interações apenas em hover: qualquer coisa disponível em hover também tem um caminho de foco/toque.
- Teste em dispositivo real ou chrome devtools com throttle: rede 3G, desaceleração de CPU 4×.
- Formulários funcionam sem zoom: input
font-size: 16px+(caso contrário iOS amplia ao focar).
Referência de Ferramentas
CI / pré-merge:
- Lighthouse CI com orçamentos por métrica.
@axe-core/cliou subconjunto de a11y do Lighthouse.bundlewatchpara reprovar PRs que cresçam o bundle além do orçamento.- Regressão visual: Chromatic (Storybook), Percy ou capturas de tela do Playwright verificadas.
Local:
- Chrome DevTools Painéis Performance + Lighthouse.
unlighthousepara auditoria de site completo.@unjs/check-vitalspara Web Vitals rápido.
Formato de Saída
**[BLOQUEANTE|AVISO|NIT] [FE-N] <uma linha>**
Onde: `caminho/para/Componente.tsx:42-58`
Categoria: <a11y | perf | estados | mobile | semântico>
Impacto: <qual usuário / cenário / métrica>
Correção: <mudança específica, código se couber>
Verificação: <como confirmar — execução do Lighthouse / teste manual / etc.>
Severidade:
- BLOQUEANTE: Violação de WCAG AA, fluxo principal quebrado, Web Vitals falhando na faixa "Ruim". Deve corrigir.
- AVISO: Lacuna de melhor prática, regressão de desempenho que ainda não é visível ao usuário.
- NIT: Oportunidade de melhoria (bundle menor, esqueleto melhor).
Regras Duras
- Sem
<div onClick>para botões. Sempre. - Sem imagens sem
alt. Sempre. - Sem mudança de layout por conteúdo real. Sempre reserve espaço.
- Sem reprodução automática com som. Sempre.
prefers-reduced-motionrespeitado. Sempre.
O que esta skill NÃO cobre
- Backend (use
web-security-baseline). - Schema / consultas de BD (use
data-migration-safety). - Identidade de marca / design visual (use
brand-creation). - Construção de nova landing page (use
landing-page-fastque faz scaffolding).
Referências
- Referência rápida WCAG 2.2
- Documentação web.dev Core Web Vitals
- Trilha de aprendizado de desempenho web.dev
- Componentes Inclusivos por Heydon Pickering
- Checklist do The A11y Project