motionlint
Pegue animações ruins antes que elas sejam lançadas. Auditoria de movimento determinística + revisão de design com visão-LLM.
Documentação
MotionLint
Avalie a qualidade de animação de qualquer página com um único comando. Sem chave de API, sem configuração.
npx motionlint audit http://localhost:3000 --open
Determinístico — medido a partir da página ao vivo, sem envolvimento de LLM. Pré-requisito único: npx playwright install chromium.
O MotionLint mede o movimento que seu aplicativo realmente entrega — durações, curvas de easing, intervalos de stagger, timing de saída, suporte a reduced-motion — e o pontua contra um conjunto publicado de padrões de animação. Ease-in em um dropdown, um modal de 600ms, um card que escala de 0, movimento de hover que dispara no toque: tudo é detectado, com o valor medido e uma correção concreta.
A auditoria é gratuita e offline. Adicione uma chave de API e o MotionLint também faz revisão de design com vision-LLM — capturas de tela em múltiplos viewports e rajadas de frames de 50ms de jornadas reais de usuário, avaliadas por um modelo e devolvidas ao seu agente de codificação como descobertas classificadas. Ele roda como um servidor MCP dentro do Claude Code e do Cursor.
Por que isso existe
Agentes de codificação de IA leem JSX, HTML e CSS — eles são cegos para o que o usuário realmente vê, clica e assiste animar. Regras em um prompt dizem ao agente o que deveria acontecer; nada verifica o que aconteceu. Modais que deveriam deslizar simplesmente aparecem; estados de carregamento são omitidos; anéis de foco desaparecem. A revisão de código não consegue detectar nada disso antes do merge, porque nada disso é visível no diff.
O MotionLint fecha esse ciclo: ele mede o aplicativo em execução e devolve o veredito.
Como é diferente
| MotionLint | Ferramentas de regressão visual (Percy, Chromatic, Playwright snapshots) | Geradores de design com IA (v0, Galileo, Claude Design, Stitch) | |
|---|---|---|---|
| Auditoria de movimento determinística | 13 verificações, medidas na página ao vivo — sem chave de API, $0 | ✗ | ✗ |
| Revisão de UX em múltiplos viewports | descobertas classificadas em 12 dimensões | apenas diffs de pixels | gera novos layouts a partir de prompts |
| Revisão de animação | Rajadas de frames de 50ms via screencast CDP → folha de contato → LLM | ✗ | ✗ |
| Ajuste de animação ao vivo | Pré-visualizações Shadow-DOM + sliders + exportação para Claude Code | ✗ | gera novo movimento, não ajusta o que já existe |
| Servidor MCP nativo | ✓ MCP stdio para Claude Code / Cursor | ✗ | varia |
| Gate de CI | ✓ SARIF + códigos de saída para varredura de código | ✓ limites de diff de imagem | ✗ |
| Qualidade validada | 100% de recall em um teste de estresse com 24 fixtures, em 5 modelos de fronteira | n/a | n/a |
A lacuna conceitual que o MotionLint fecha: ferramentas de regressão visual detectam o que mudou, mas não se os novos pixels são bons; ferramentas de design com IA geram do zero, mas não revisam o que já está em execução. O MotionLint revisa o comportamento ao vivo com um vision LLM e devolve o veredito ao ciclo de codificação.
Comece aqui — sem necessidade de chave de API
npx playwright install chromium # one-time per machine (~300MB)
npx motionlint audit http://localhost:3000 --open
Essa é toda a configuração para a auditoria. É determinística, roda offline, não custa nada e funciona em qualquer URL que você consiga carregar — seu servidor de desenvolvimento, um deploy de staging ou o site de outra pessoa. Requer Node 18+.
As regras que ele verifica estão publicadas em docs/STANDARDS.md — leia-as antes de instalar qualquer coisa.
Depois: revisão de design com LLM
Defina uma chave de API (ANTHROPIC_API_KEY, OPENAI_API_KEY ou GOOGLE_API_KEY — ou execute o Ollama localmente de graça) e mais três comandos são desbloqueados:
npm install -g motionlint
# Multi-viewport UX review of a page → ranked findings across 12 dimensions.
motionlint review http://localhost:3000
# Animation review of a scripted user journey → frame contact sheet + report.
motionlint flow --spec flows/signup.json
# Interactive HTML tuner — every animation on the page, with live sliders.
motionlint tune http://localhost:3000
Dentro do Claude Code / Cursor
claude mcp add motionlint -- npx -y motionlint mcp
Superfície completa de flags — gates de CI, descoberta de rotas, Storybook, modo escuro, baselines
# CI mode — non-zero exit on critical issues, SARIF output for code scanning.
motionlint review https://staging.acme.dev --ci --threshold critical --format sarif -o ux.sarif
# Polished, shareable HTML review with embedded screenshots + before/after fixes.
motionlint review http://localhost:3000 --format html -o review.html
# Review every route the site knows about (sitemap.xml + Next.js app/ directory).
motionlint review http://localhost:3000 --discover-routes
# Storybook mode — discover stories from /index.json, review each story iframe as its own route.
motionlint review http://localhost:6006 --storybook
# Color-scheme sweep — light and dark modes, plus Windows High Contrast.
motionlint review http://localhost:3000 --schemes --forced-colors --format html -o review.html
# Interaction affordances — grid each element's default/hover/focus/active states.
motionlint review http://localhost:3000 --state-grid
# Agent focus — keep only the top 5 findings, and only ones not seen in prior runs.
motionlint review http://localhost:3000 --max-findings 5 --new-only
# Before/after comparison — PR preview vs. production baseline.
motionlint review https://pr-123.preview.example.com --against https://prod.example.com
# Reviewer focus — cap the SARIF upload at 10 annotations per report.
motionlint review https://staging.acme.dev --format sarif -o ux.sarif --max-pr-annotations 10
# Pick a provider explicitly (auto-detect picks the first reachable one).
motionlint review http://localhost:3000 --provider anthropic --model claude-sonnet-5
# Track provider quality across runs + teach the reviewer from eval misses.
motionlint eval --provider anthropic --evolve
Pacote no npm: motionlint.
Exemplo de saída de terminal para uma revisão de fluxo:
$ motionlint flow --spec flows/signup.json --provider anthropic
→ Running flow "signup-happy-path" against http://localhost:3000/signup (11 steps, 50ms intervals × 750ms window)
provider: anthropic (claude-sonnet-5)
capturing flow…
✓ step 1: 16 frames ✓ step 2: 16 frames ✓ step 3: 16 frames …
captured 176 frames in 31s
contact sheet → .motionlint/flows/signup-happy-path-…png
analyzing flow…
report → .motionlint/flows/signup-happy-path.md
Score: 4/10 · 3 critical findings
[critical] interaction — input focus rings missing across steps 2/4/6
[critical] interaction — submit button has no pressed state
[critical] loading_state — 1.4s wait with no spinner during submit
Experimente a demo
Um showcase de animações TS com múltiplas rotas é incluído em demo/ — cobrindo Motion One, GSAP, anime.js, @formkit/auto-animate e lottie-web — incluindo uma one-pager com tema de gato que exercita todos os recursos do MotionLint em uma única URL:
node demo/server.mjs # http://localhost:4173
motionlint review http://localhost:4173/cat --record --embed
motionlint flow --spec flows/signup.json
motionlint tune http://localhost:4173/dashboard
Rotas disponíveis: /, /pricing, /signup, /dashboard, /loading, /cat. Relatórios vão para .motionlint/reports/, capturas de tela para .motionlint/screenshots/, vídeos para .motionlint/videos/.
Configuração
Chaves de API
O MotionLint carrega automaticamente um arquivo .env do diretório de trabalho na inicialização:
# .env (gitignored)
ANTHROPIC_API_KEY=sk-ant-...
# or
OPENAI_API_KEY=sk-...
# or
GOOGLE_API_KEY=...
# or run a local Ollama (no key needed) — auto-detected on http://localhost:11434
Variáveis de ambiente reais têm precedência sobre .env. Sem chave definida e sem Ollama em execução, o MotionLint recorre a um provedor mock determinístico para que o pipeline completo (captura → análise → relatório) ainda rode de ponta a ponta em testes de fumaça.
Detecção automática de provedor
O MotionLint detecta automaticamente nesta ordem: Ollama (local) → Anthropic → OpenAI → Google. O primeiro com uma chave de API funcional (ou serviço em execução) vence. Substitua com --provider <name> e --model <id>. Veja Provedores em profundidade para o scorecard de qualidade por provedor e como escolher.
Tudo abaixo é para leitores que querem entender como o MotionLint funciona por baixo dos panos, escolher o provedor certo para seu fluxo de trabalho ou integrá-lo ao CI.
Qualidade validada entre provedores
O pipeline de revisão de fluxo foi testado sob estresse em 12 padrões populares de animação de web apps × 2 variantes (24 fixtures no total) — entradas escalonadas, hover/press/focus, entradas de modal, skeletons de carregamento, erros de formulário, toasts, rampas de contador, dashboards com múltiplas animações, stagger de modal com conteúdo, feedback rico de formulário (focus + press + spinner + sucesso) e animações orientadas por scroll (barra de progresso + revelação com IntersectionObserver + parallax).
Executado em 2026-07-27 contra o flagship atual de cada provedor principal:
| Provedor · modelo | Recall (quebrados detectados) | FPR (limpos sinalizados) | Gap de pontuação | Tempo de parede |
|---|---|---|---|---|
| OpenAI · gpt-5.6-sol | 100% (12/12) | 0% (0/12) | +3.3 | 10.9 min |
| OpenAI · gpt-5.5 | 100% (12/12) | 0% (0/12) | +3.3 | 11.5 min |
| Anthropic · claude-opus-5 | 100% (12/12) | 8% (1/12) | +4.1 | 21.0 min |
| Google · gemini-3.6-flash | 100% (12/12) | 17% (2/12) | +5.1 | 4.9 min |
| Anthropic · claude-sonnet-5 | 100% (12/12) | 33% (4/12) | +3.1 | 10.8 min |
Leia isto assim: recall não é mais um diferencial. Todo flagship atual detecta todas as 12 falhas semeadas. Essa é a descoberta — há um ano não era verdade, e isso significa que a escolha do modelo não decide mais se o MotionLint funciona. Escolha com base em custo e latência.
Não classifique esses modelos pela coluna FPR. Uma única execução de 24 fixtures não consegue resolvê-la. Em duas execuções limpas da mesma suíte, sem nada mudado além da amostragem, o FPR variou de 1–2 fixtures por modelo — gpt-5.5 1/12 → 0/12, gpt-5.6-sol 2/12 → 0/12, gemini-3.6-flash 3/12 → 2/12. Uma fixture equivale a 8 pontos percentuais, então toda a variação entre "0%" e "17%" está dentro do piso de ruído. Trate a coluna como "todos eles ocasionalmente sinalizam algo limpo", não como um ranking.
Por que os números antigos no histórico desta tabela estavam errados
A primeira execução de 2026-07-27 desta suíte colocou claude-opus-5 com 83% de recall — último entre todos os cinco modelos — e a edição de 2026-04-29 desta tabela relatou vários modelos com 0% de FPR. Ambos foram artefatos de um bug do MotionLint, não comportamento do modelo.
O max_tokens da Anthropic tinha como padrão 4096. Respostas verbosas atingiam o teto no meio do JSON, e o resultado não analisável era pontuado como 0/10, no issues found — indistinguível de uma revisão limpa. Duas das três truncagens do Opus 5 caíram em fixtures quebradas, o que produziu o número inteiro de 83%.
O mesmo bug reduziu o FPR em todos os lugares: uma revisão truncada não relata nada, então não pode gerar um falso positivo. Qualquer "0% FPR" histórico estava medindo parcialmente parsing quebrado, não precisão do modelo. Corrigido em 2026-07-27, junto com os caminhos irmãos que transformavam respostas truncadas, recusadas e bloqueadas por segurança em resultados com aparência limpa.
Scorecards completos por provedor em .motionlint/stress/ após executar scripts/run-all-benchmarks.mjs. Use --only <provider>:<model> para reexecutar um único modelo.
Provedores em profundidade
| Provedor | Modelo | Configuração | Qualidade (24 fixtures) | Custo por revisão¹ |
|---|---|---|---|---|
google | gemini-3.6-flash | GOOGLE_API_KEY=… | 100% recall · 17% FPR · +5.1 gap | $0.019 |
anthropic | claude-sonnet-5 | ANTHROPIC_API_KEY=… | 100% recall · 33% FPR · +3.1 gap | $0.089 |
openai | gpt-5.5 | OPENAI_API_KEY=… | 100% recall · 0% FPR · +3.3 gap | $0.248 |
openai | gpt-5.6-sol | OPENAI_API_KEY=… | 100% recall · 0% FPR · +3.3 gap | $0.265 |
anthropic | claude-opus-5 | ANTHROPIC_API_KEY=… | 100% recall · 8% FPR · +4.1 gap | $0.293 |
ollama | qualquer modelo de visão | ollama serve + ollama pull <model> | não avaliado nesta execução | $0 |
mock | stub heurístico | (fallback automático) | n/a — stub determinístico para testes de fumaça de CI | $0 |
¹ Medido, não estimado — um motionlint review real por modelo contra o aplicativo demo nos 2 viewports padrão, página inteira, lendo as contagens reais de tokens do campo de uso de cada provedor e multiplicando pelo preço de tabela publicado. Reproduza com formatUsageLine() em qualquer execução. O Sonnet 5 usa sua taxa introdutória (até 2026-08-31); ela aumenta aproximadamente pela metade depois disso. A revisão de fluxo envia uma imagem composta por fluxo, mas a folha de contato é maior. O Animation Tuner e o motionlint audit fazem zero chamadas de LLM e não custam nada.
Tokens de saída dominam. A entrada está dentro de 2× em todos os cinco modelos; a saída varia de 1.390 (Gemini) a 10.219 (Opus 5). Essa variação de 7×, não o tamanho da imagem, é o que torna o modelo mais caro 15× o mais barato.
Como escolher
- Padrão. Google
gemini-3.6-flash— 100% de recall, 13× mais barato que o Opus 5 e o mais rápido dos cinco (4.9 min). Como todos os modelos detectaram todas as falhas, não há argumento de qualidade para pagar mais por padrão. - Casa Anthropic.
claude-sonnet-5a $0.089 — 3× mais barato queclaude-opus-5com recall idêntico. O Opus 5 custa mais e levou 2× o tempo de parede (21.0 min vs 10.8) sem vantagem medida de recall; recorra a ele apenas se valorizar seu gap de pontuação ligeiramente maior (+4.1 vs +3.1). - Casa OpenAI.
gpt-5.5egpt-5.6-solsão indistinguíveis em todos os eixos medidos e dentro de 7% no preço. Escolha o que sua conta já tem. - Gate de CI rigoroso. Qualquer um deles em recall. Não escolha por FPR — veja a ressalva do piso de ruído acima. Se falsos positivos importam para seu gate, execute seus próprios fixtures em vez de confiar em uma única execução de 24 fixtures nossa.
- Local / isolado (air-gapped). Qualquer modelo de visão do Ollama funciona, mas confirme que ele é capaz de visão: alguns aceitam imagens pela API, as ignoram silenciosamente e respondem apenas com base no prompt. Nenhum foi avaliado nesta execução.
Trocando de provedor
Todo comando respeita --provider e --model:
motionlint review http://localhost:3000 --provider openai --model gpt-5.5
motionlint flow --spec flows/signup.json --provider google --model gemini-3.6-flash
motionlint review http://localhost:3000 --provider ollama --model llava:13b
Fazendo benchmark do seu próprio provedor
Para comparar um novo provedor com o mesmo teste de estresse de 24 fixtures:
node -e "
import('./dist/config/env.js').then(async ({ loadEnv }) => {
loadEnv();
const { runStress, renderStressMarkdown } = await import('./dist/flow/stress.js');
const { writeFile, mkdir } = await import('node:fs/promises');
const { resolve } = await import('node:path');
await mkdir('.motionlint/stress', { recursive: true });
const r = await runStress({
stressPath: resolve('eval/animation-stress.json'),
fixturesDir: resolve('eval/animation-fixtures'),
artifactDir: resolve('.motionlint/stress'),
provider: 'YOUR_PROVIDER', // 'openai' | 'google' | 'ollama'
});
await writeFile('.motionlint/stress/SCORECARD.md', renderStressMarkdown(r), 'utf8');
console.error('Recall:', (r.broken_recall*100).toFixed(0)+'%, FPR:', (r.good_false_positive_rate*100).toFixed(0)+'%, gap:', r.avg_score_gap.toFixed(1));
});
"
Abra .motionlint/stress/SCORECARD.md para o detalhamento por padrão.
Como o motionlint flow funciona
Capturas de tela estáticas não podem dizer se as animações e os estados de interação de um fluxo funcionam — apenas se o frame final parece correto. O motionlint flow preenche essa lacuna.
Dada uma jornada de usuário com script, ele:
- Executa a jornada no Chromium headless via Playwright — clicando, digitando, passando o mouse, rolando, pressionando teclas exatamente como um usuário faria.
- Captura uma rajada de 16 quadros ao longo de 750ms (intervalos de 50ms) após cada interação via screencast CDP (
Page.captureScreenshotJPEG, ~8ms por captura). 50ms é metade do limiar de detecção visual humana e abaixo do intervalo mínimo de animação de 100ms típico da indústria — animações curtas como pressionamentos de botão de 100ms são capturadas com 2-3 quadros de estado intermediário. Cada rajada de interação também é submetida a diff de pixels para latência de entrada→feedback — interações sem reconhecimento visível dentro da janela da rajada são sinalizadas deterministicamente. - Grava o vídeo completo do Playwright como um artefato que você pode percorrer depois.
- Compõe cada rajada em uma folha de contatos rotulada — uma linha por etapa, quadros dispostos em sub-linhas.
- Envia a folha para o LLM de visão com uma rubrica ciente do fluxo cobrindo: animações ausentes, animações defeituosas/instáveis, estados de carregamento ausentes, desempenho percebido, affordance e mudanças de estado, coreografia, suavidade, oscilação acidental, continuidade de navegação, respeito ao movimento reduzido.
- Produz um relatório Markdown com rastreamento por etapa, descobertas classificadas e um bloco “Prompt para Claude Code” no final — cole-o no CC e ele age diretamente sobre as descobertas.
Tratamento de múltiplas animações
Uma única gravação pode capturar e analisar várias animações simultâneas. Validado em:
- Revelação de dashboard (3 concorrentes: escalonamento de blocos + rampas de contador + subida de barras de gráfico)
- Pilha de modais (fade de fundo + deslizamento e fade do modal + escalonamento de conteúdo interno)
- Feedback de formulário rico (anel de foco + pressionamento de botão + spinner de carregamento + cartão de sucesso)
- Acionado por rolagem (barra de progresso de rolagem + revelação de seção via IntersectionObserver + hero com parallax)
O LLM identifica corretamente quais animações estão quebradas sem sinalizar erroneamente as que funcionam — veja a tabela de qualidade validada.
Animações acionadas por rolagem
Para sites com animações vinculadas à rolagem, scroll <px> etapas animam a rolagem ao longo da janela da rajada via requestAnimationFrame, para que cada quadro mostre a posição de rolagem progressiva e o LLM veja o timing conforme a página rola.
Exemplos de fluxo
# Inline DSL — semicolon-separated steps
motionlint flow \
--url http://localhost:3000 \
--steps "navigate /signup; click input#email; type input#email=ada@example.com; click button[type=submit]; wait 2000; capture \"post-submit\"" \
--name signup-happy-path
# Or load a structured spec with expected_animations[] hints
motionlint flow --spec flows/signup.json --provider anthropic
# Pass team motion preferences (philosophy + inspirations + accepted defaults)
# Embedded into the prompt AND the report's CC handoff block.
motionlint flow --spec flows/signup.json --preferences flows/preferences.md
# Tighten the interval below 50ms for fine-grained timing review
motionlint flow --spec flows/signup.json --interval 30 --burst-ms 600
# Auto-detect: scan the page's animations, pick an interval that captures
# the shortest one with 4 frames inside it (clamped to [20, 100]ms).
motionlint flow --spec flows/signup.json --auto-interval
Referência DSL inline
| Ação | Formato | Notas |
|---|---|---|
| navigate | navigate /pricing | caminho ou URL completa |
| click | click button#start | seletor CSS |
| hover | hover .feature | seletor CSS |
| type | type input#email=ada@example.com | seletor=valor |
| press | press Enter | tecla do teclado |
| scroll | scroll 800 | pixels; anima ao longo da janela da rajada |
| wait | wait 500 | ms |
| capture | capture "post-submit" | captura uma rajada explícita com rótulo opcional |
Padrões: uma rajada de quadros é capturada após cada interação. Passe --no-implicit-bursts para capturar apenas em etapas capture explícitas. Passe --no-record para pular o vídeo.
Três fluxos de exemplo prontos para execução estão no repositório: flows/signup.json, flows/loading-state.json e flows/preferences.md.
Como o Animation Tuner funciona
A maioria das ferramentas de codificação com IA gera animações do zero. O Tuner permite ajustar as animações que já estão rodando na sua página, em tempo real, e entregar as mudanças ao seu agente de codificação como um prompt estruturado.
motionlint tune http://localhost:3000 --open
Isso:
- Abre seu aplicativo no Chromium headless com um script de instrumentação que conecta as principais bibliotecas TS de animação (Motion One, GSAP, anime.js, @formkit/auto-animate, lottie-web) além de todas as transições CSS e
@keyframesrodando na página. - Captura cada animação detectada: o seletor do elemento, a biblioteca de origem, os parâmetros de timing e a caixa delimitadora.
- Gera uma página HTML interativa autocontida em
.motionlint/tuner/index.html(abre automaticamente com--open):- Superfície de preview ao vivo por animação (Shadow DOM — sem iframes, sem flash, com tema da página de origem).
- Sliders para duração / atraso / escalonamento / velocidade.
- Menu suspenso de easing predefinido — as curvas fortes de Emil Kowalski lideram (ease-out, ease-in-out, gaveta iOS), depois as opções mais suaves/decorativas.
- Verificação de padrões embutida — cada cartão sinaliza onde a animação se desvia dos padrões de movimento (selo de gravidade, correção, valor sugerido), com uma pontuação no cabeçalho.
- Caixa de comentários por animação para fundamentar as decisões de design.
- Exporta um arquivo markdown além de um prompt pronto para Claude Code com um bloco JSON
changes[]estruturado. Cole-o no CC e ele edita seu código para aplicar os novos parâmetros.
$ motionlint tune http://localhost:3000
→ Capturing animations on http://localhost:3000…
detected 15 animation(s)
tuner → /Users/you/proj/.motionlint/tuner/index.html
open with: file:///Users/you/proj/.motionlint/tuner/index.html
Padrões de animação — motionlint audit
MotionLint codifica os padrões de design-engenharia de Emil Kowalski como um linter determinístico — sem modelo de visão, sem chave de API, sem custo. motionlint audit instrumenta a página, lê os valores reais de timing/easing/transform que cada animação está rodando e os avalia:
| Categoria | O que ela detecta | O padrão |
|---|---|---|
| Easing | ease-in em UI; curvas embutidas fracas em entradas deliberadas | Entrando/saindo → ease-out forte cubic-bezier(0.23, 1, 0.32, 1); nunca ease-in |
| Duração | Movimento de UI acima do teto de 300ms (modais/gavetas recebem 200–500ms) | Uma transição de 180ms parece mais ágil que uma de 400ms; saídas ~20% mais rápidas |
| Fisicalidade | Entradas scale(0) | Nada aparece do nada — comece de scale(0.95) + opacity: 0 |
| Desempenho | transition: all, animar propriedades de layout, loops infinitos soltos | Anime apenas transform e opacity — eles pulam layout/paint |
| Coesão | Proliferação de curvas de easing feitas à mão; intervalos de escalonamento fora da faixa de 30–80ms | Curvas e durações devem viver como tokens compartilhados; entradas agrupadas escalonadas com 30–80ms de diferença |
| Duração (pares) | Saídas que não são mais rápidas que a entrada correspondente (fadeIn 300ms / fadeOut 300ms) | Saídas rodam ~20% mais rápidas que a entrada correspondente |
motionlint audit http://localhost:3000 --open # polished HTML report, scored 0–100
motionlint audit http://localhost:3000 --json audit.json --ci # machine-readable; non-zero on critical
Adicione --layout para também verificar layout (alvos de toque, tamanho de texto, contraste, overflow) a partir de medições ao vivo do DOM — ainda determinístico, ainda sem chave de API.
Adicione --watch [dir] para reexecutar a auditoria em mudanças de arquivo sob [dir] (padrão: cwd) e imprimir a pontuação com um delta após cada execução — uma leitura ao vivo enquanto você itera. Observação recursiva requer macOS, Windows ou Linux com Node 20+.
O relatório emparelha cada descoberta com um painel antes → depois; descobertas de easing renderizam uma comparação ao vivo de curva cubic-bezier para que a correção fique visível, não apenas descrita. Os mesmos padrões alimentam o prompt de revisão flow (para que as descobertas de visão citem regras concretas) e aparecem embutidos no Animation Tuner.
Servidor MCP — ferramentas, recursos, implantação
O MotionLint fornece um servidor MCP sobre stdio para que um agente LLM possa dirigi-lo diretamente em um chat. O subcomando motionlint mcp o inicia; o cliente agente inicia o processo quando uma ferramenta é chamada.
Instalação no Claude Code
Versão publicada no npm (recomendada):
claude mcp add motionlint -- npx -y motionlint mcp
Checkout local (útil durante o desenvolvimento):
claude mcp add motionlint -- node /absolute/path/to/motionlint/dist/index.js mcp
Após o registro:
- Confirme que ele aparece:
claude mcp list—motionlintdeve aparecer comorunningouavailable. - Garanta que as chaves de API estejam acessíveis. O servidor MCP herda as variáveis de ambiente do processo em que é iniciado. Caminho mais limpo: coloque um arquivo
.envno diretório do projeto a partir do qual você está trabalhando — o MotionLint o carrega automaticamente na inicialização. - Primeira execução:
npx playwright install chromiumse ainda não o fez.
Depois, no Claude Code:
"Use motionlint para revisar o app local em mobile e desktop e me diga os 3 principais problemas para corrigir."
"Execute motionlint review_flow em
http://localhost:3000/signupcom as etapasclick input#email; type input#email=test@test.com; click button[type=submit]; wait 2000; capturee verifique as animações.""Execute motionlint tune_animations em
http://localhost:3000/pricing— quero ajustar finamente as animações de hover dos cartões."
Ferramentas expostas
| Ferramenta | O que ela faz |
|---|---|
review_url(url, viewports?, provider?, model?, wait_for?, record?, format?, max_findings?, max_pr_annotations?, new_only?) | Revisão estática de UX de uma URL em vários viewports. Retorna um relatório markdown / JSON / SARIF. |
review_routes(base_url, routes, viewports?, ..., max_findings?, max_pr_annotations?, new_only?) | A mesma revisão em múltiplas rotas de um aplicativo. |
review_flow(url, steps?|spec_path?, preferences_path?, provider?, ...) | Revisão de animação/interação de uma jornada de usuário roteirizada. Retorna um relatório de fluxo com o bloco estruturado de transferência para CC. |
tune_animations(url, viewport_*?, settle_ms?, output?) | Detecta cada animação em uma página e escreve um tuner HTML interativo. Retorna o caminho do arquivo. |
get_latest_report(format?) | Retorna o conteúdo do relatório de revisão/fluxo mais recente. |
Recursos: motionlint://reports/latest — o conteúdo do relatório mais recente.
Checklist de implantação
Antes de implantar ou compartilhar o servidor MCP com outros usuários:
- Build está atualizado.
npm run builde verifique sedist/index.jsexiste. Sem isso,motionlint mcpnão iniciará. - Playwright Chromium instalado na máquina de destino:
npx playwright install chromium. O gancho de pós-instalação lembra, mas não é obrigatório (não baixamos automaticamente um binário de 300 MB nonpm install). - Chaves de API acessíveis — via variáveis de ambiente do shell ou via um arquivo
.envno diretório de trabalho a partir do qual o cliente MCP é lançado. - Teste de fumaça da superfície MCP.
npm testinclui um teste de fumaça MCP que inicia o servidor, lista ferramentas e verifica a superfície esperada de ferramentas. - Nenhum segredo commitado.
.envestá no gitignore;.env.exampledeve ser um placeholder. Vale uma verificação finalgit diff --cached | grep -i 'sk-\|api_key'antes de enviar. - Confirme com
claude mcp listque o servidor aparece e não está gerando erros na inicialização.
Integração com CI
# .github/workflows/ux.yml
- run: npm ci
- run: npx playwright install chromium
- run: npx motionlint review $STAGING_URL --ci --threshold critical --format sarif -o ux.sarif
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: ux.sarif }
O MotionLint sai com 1 quando problemas críticos excedem o limite configurado (failOnCritical) — conecte-o como uma verificação de status.
O que captura · o que analisa
Captura:
- Capturas de tela de página inteira em três viewports padrão (mobile 375 / tablet 768 / desktop 1440). Substitua por configuração.
- Capturas de tela acima da dobra com
--no-full-page. - Vídeos da execução de navegação+captura com
--record(Playwright.webm). - Sequências de interação antes da captura:
click,hover,type,scroll,wait. - Estado de autenticação: cookies,
localStoragee um scriptbeforeNavigate— tudo configurável em.motionlintrc.json.
A folha de contatos de fluxo motionlint — rajadas com timestamp após cada interação, exatamente o que o modelo de visão vê.
Analisa: cada captura de tela é enviada a um modelo de visão com um prompt de revisão de UX opinativo cobrindo doze dimensões (hierarchy, spacing, alignment, typography, color, contrast, responsiveness, interaction, content, navigation, consistency, loading_state). Para cada problema, o modelo retorna:
{
"category": "hierarchy",
"severity": "critical | warning | suggestion",
"location": "above-the-fold hero",
"issue": "Primary CTA blends into the background gradient.",
"why_it_matters": "Users miss the conversion path on first scroll.",
"fix": "Increase background contrast or use a solid surface behind the button."
}
Substitua o prompt com --rules path/to/your-design-rules.md para injetar heurísticas específicas do projeto.
Cada captura de revisão também tira um instantâneo do DOM: elementos notáveis (cabeçalhos, CTAs, campos de entrada) recebem refs estáveis (E1, E2, …) com retângulos em pixels medidos, listados no prompt para que o modelo possa fundamentar uma descoberta com "element_ref": "E3". As refs citadas resolvem de volta para seus retângulos e são desenhadas como caixas delimitadoras coloridas por gravidade na captura de tela no relatório HTML (e relatadas como Where: E3 at (x, y) w×h em markdown). Refs que a página nunca listou são descartadas — o modelo não pode anotar o que não foi mostrado.
Com --format html as descobertas são renderizadas como um único relatório compartilhável — anel de pontuação, detalhamento por dimensão e um painel problema → correção por descoberta com a captura de tela anotada:
Referência de configuração
Coloque um .motionlintrc.json na raiz do seu repositório (ou use motionlint.config.js / uma chave "motionlint" em package.json):
{
"provider": "auto",
"fallbackProvider": "anthropic",
"fallbackModel": "claude-sonnet-5",
"viewports": {
"mobile": { "width": 375, "height": 812 },
"tablet": { "width": 768, "height": 1024 },
"desktop": { "width": 1440, "height": 900 }
},
"defaultViewports": ["mobile", "desktop"],
"waitFor": "networkidle",
"waitTimeout": 10000,
"screenshotDir": ".motionlint/screenshots",
"videoDir": ".motionlint/videos",
"reportDir": ".motionlint/reports",
"rules": null,
"record": false,
"maxFindings": null,
"maxPrAnnotations": null,
"memory": {
"enabled": true,
"path": ".motionlint/memory.json",
"baseline": ".motionlintignore",
"newOnly": false
},
"resources": { "maxConcurrentReviews": null, "providerCallsPerMinute": null, "maxTokensPerRun": null },
"ci": { "threshold": "warning", "failOnCritical": true },
"auth": { "cookies": null, "localStorage": null, "beforeNavigate": null }
}
Controle de volume de revisão
Reexecutar a revisão nas mesmas rotas costumava exibir os mesmos achados a cada execução. Dois mecanismos mantêm a saída focada:
- Limite de saída por execução —
--max-findings N(oumaxFindingsna configuração) mantém apenas os N principais achados por execução, ordenados por severidade, para que um agente trabalhe primeiro no que é mais importante. A linhaOmitteddo relatório informa quantos foram limitados. - Limite de superfície de PR —
--max-pr-annotations N(oumaxPrAnnotationsna configuração; somente SARIF) emite no máximo N resultados por relatório, ordenados por severidade, para que um upload de code-scanning não inunde um PR com anotações. A contagem descartada vai para a propriedadeomitted_by_pr_capda execução SARIF. - Limite de recursos —
resources.maxConcurrentReviewslimita quantas revisões são executadas ao mesmo tempo em um processo (um servidor MCP atendendo vários agentes em voo), eresources.providerCallsPerMinuteé um teto de janela deslizante em todo o processo para chamadas de LLM de visão (cota do provedor / controle de gastos; também se aplica a revisõesflow, onde cada amostra--consistencyconta). Ambos têm como padrão ilimitado; ambos são somente configuração. Observe que eles se compõem: uma revisão que ocupa um slot de concorrência também espera o limitador de taxa, então valores apertados em ambos multiplicam a latência. - Teto de custo — o uso de tokens de cada chamada do provedor é capturado e totalizado por execução (uma linha
Tokens:nos relatórios,token_usagenas propriedades da execução SARIF).--max-tokens N(ouresources.maxTokensPerRunna configuração) define um orçamento de tokens por execução: assim que o total acumulado o ultrapassa, os viewports restantes são ignorados e o relatório os lista sobskipped_viewports. Provedores que não relatam uso ainda contam chamadas, mas não consomem orçamento. - Memória entre execuções — cada achado recebe um id estável (hash de categoria + localização do elemento + texto do problema normalizado). A detecção de recorrência vai além do hash exato: compatibilidade de sinônimos de categoria mais sobreposição de tokens canônicos (limiares calibrados em dados reais entre execuções) corresponde à mesma falha mesmo quando o LLM de visão a reformula entre execuções. As ocorrências são registradas por URL em
.motionlint/memory.json; achados recorrentes são anotados com visto em N execuções anteriores em vez de serem descartados silenciosamente. Opte por apenas deltas com--new-only. Para dispensar permanentemente um achado, copie seu id para.motionlintignore(um hash por linha, comentários#e notas finais permitidos). Desative tudo com--no-memory.
A saída SARIF carrega o id do achado como um partialFingerprint, para que o code scanning do GitHub deduplique o mesmo achado entre execuções e PRs nativamente.
Revisões concorrentes do mesmo projeto são seguras: o armazenamento de memória é atualizado sob um bloqueio de arquivo ciente de obsolescência (memory.json.lock), para que execuções paralelas não sobrescrevam as ocorrências registradas umas das outras. Um bloqueio travado nunca falha uma revisão — após uma curta espera, a execução avisa e prossegue sem ele.
Casos de uso
- Guarda-corpo de UX pré-merge. Desenvolvedor solo ou startup de 2 pessoas sem designer. Execute
motionlint review https://pr-123.preview.example.com --ci --threshold criticalno CI; aviso ou pior bloqueia o merge até que você pelo menos veja os problemas. - Colega de design MCP dentro do Claude Code. Adicione MotionLint como um servidor MCP e peça ao CC: "revise o aplicativo local em mobile e desktop e me diga os 3 principais problemas para corrigir." O CC conduz a ferramenta e recebe feedback anotado na mesma conversa.
- Monitoramento contínuo de qualidade. Agende um cron noturno (
motionlint review https://prod.example.com --format sarif -o ux.sarif) e exiba SARIF no seu painel de code-scanning para que regressões de produção sejam detectadas na manhã seguinte. - QA de animação / fluxo em um recurso que você acabou de lançar.
motionlint flowexecuta uma jornada de usuário roteirizada via Playwright como um humano faria, captura rajadas de quadros em cada interação, grava vídeo e pede ao LLM para revisar o comportamento da animação nos quadros capturados. - Ajuste de animação ao vivo + transferência para o Claude Code. Capture cada animação em uma página, ajuste tempo/curva/atraso ao vivo com controles deslizantes, exporte um prompt estruturado que o CC possa usar diretamente.
Estrutura do projeto
src/
capture/ Playwright capture (screenshot, mosaic, DOM snapshot) + interaction sequences
providers/ Vision LLM providers (ollama, anthropic, openai, google, mock) + self-consistency wrapper
analysis/ Rubric-style UX prompt + JSON parser + rule injection
report/ Markdown / JSON / SARIF report generators
eval/ Tiered eval harness (L1/L2/L3 fixtures, scorer, runner, report)
flow/ Flow runner — spec parser, capture orchestrator, animation-aware report
tuner/ Animation Tuner — extractor, instrumentation script, Shadow-DOM render
mcp/ MCP server for Claude Code
cli/ Commander.js commands + terminal output
config/ cosmiconfig loader + .env loader
demo/ TS animation showcase used as a review target
flows/ Sample flow specs (signup, loading-state) for `motionlint flow`
eval/fixtures/ Labelled HTML pages with seeded UX faults at three complexity levels
test/ Node test runner unit + integration tests
Roteiro
v0.1 (esta versão) — lançado:
- Três comandos CLI:
review,flow,tune+ servidor MCP (motionlint mcp). - Cinco provedores de visão: Anthropic, OpenAI, Google, Ollama, mock.
- Revisão estática multi-viewport com captura em mosaico, canal lateral de medição DOM, amostragem de autoconsistência, pontuação por palavras-chave suaves com grafo de sinônimos.
- Harness de avaliação em camadas (L1 / L2 / L3) com 21 fixtures rotuladas e JSON estruturado
next_actions[]para ferramentas de codificação LLM a jusante. - Revisão de fluxo em intervalos de 50ms entre quadros via screencast CDP (16 quadros × rajada de 750ms), com suporte a múltiplas animações e rolagem.
- Animation Tuner com prévias Shadow-DOM, controles deslizantes ao vivo, predefinições de easing, exportação para Claude Code.
- Harness de teste de estresse de animação validado com 100% de recall / 0% de FPR em 24 fixtures em 12 padrões.
- Markdown de preferências de movimento da equipe (
--preferences) embutido na rubrica do LLM e no bloco de transferência do CC. - Varredura de intervalo automático (
--auto-interval) que escolhe um intervalo entre quadros com base na animação mais curta detectada na página. - Saída SARIF para code scanning do GitHub.
v0.2 (em andamento) — lançado até agora:
- Contabilidade de tokens + teto de custo por execução (
--max-tokens/resources.maxTokensPerRun; linhaTokens:em cada relatório). - Descoberta automática de rotas (
--discover-routes: sitemap.xml + diretório de aplicativo Next.js). - Caixas delimitadoras anotadas: referências de elementos DOM no prompt, achados desenhados na captura de tela no relatório HTML.
- Grades de estados de interação (
--state-grid: padrão/hover/foco/ativo por elemento, uma imagem rotulada). - Histórico de scorecard do provedor com detecção de regressão por modelo (
.motionlint/eval-history.json). - Evolução de prompt em malha fechada a partir de avaliação
next_actions(eval --evolve→ heurísticas aprendidas em prompts de revisão). - Duas novas regras de auditoria: faixa de intervalo de escalonamento (30–80ms) e saída ~20% mais rápida que a entrada.
v0.2 (próximo):
- Wrapper de GitHub Action (
motionlint-action).
Agradecimentos
MotionLint se apoia no trabalho de outras pessoas:
- Emil Kowalski — os padrões de animação por trás de
motionlint audit, as predefinições de easing do tuner e a rubrica de revisão de fluxo são destilados de seus escritos de design-engineering e de seu curso animations.dev. Suas bibliotecas de UI de código aberto — sonner (toasts) e vaul (drawers) — são implementações de referência vivas do movimento que essas regras descrevem. MotionLint é um projeto independente, não afiliado ou endossado por Emil. - ctx (ctx.rs) — busca local de histórico de agentes de codificação. Nós o usamos enquanto desenvolvemos a camada de memória entre execuções para estudar como os achados sobrevivem (ou desaparecem) entre execuções de agentes; esses experimentos moldaram diretamente o design do id de achado e da linha de base.
Licença
MIT © Resila Technologies Inc.