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

npm version license: MIT CI

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

motionlint audit running in a terminal: the demo app's /loading route scores 64/100 with findings across duration, easing and accessibility

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

MotionLintFerramentas de regressão visual (Percy, Chromatic, Playwright snapshots)Geradores de design com IA (v0, Galileo, Claude Design, Stitch)
Auditoria de movimento determinística13 verificações, medidas na página ao vivo — sem chave de API, $0
Revisão de UX em múltiplos viewportsdescobertas classificadas em 12 dimensõesapenas diffs de pixelsgera novos layouts a partir de prompts
Revisão de animaçãoRajadas de frames de 50ms via screencast CDP → folha de contato → LLM
Ajuste de animação ao vivoPré-visualizações Shadow-DOM + sliders + exportação para Claude Codegera novo movimento, não ajusta o que já existe
Servidor MCP nativo✓ MCP stdio para Claude Code / Cursorvaria
Gate de CI✓ SARIF + códigos de saída para varredura de código✓ limites de diff de imagem
Qualidade validada100% de recall em um teste de estresse com 24 fixtures, em 5 modelos de fronteiran/an/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 · modeloRecall (quebrados detectados)FPR (limpos sinalizados)Gap de pontuaçãoTempo de parede
OpenAI · gpt-5.6-sol100% (12/12)0% (0/12)+3.310.9 min
OpenAI · gpt-5.5100% (12/12)0% (0/12)+3.311.5 min
Anthropic · claude-opus-5100% (12/12)8% (1/12)+4.121.0 min
Google · gemini-3.6-flash100% (12/12)17% (2/12)+5.14.9 min
Anthropic · claude-sonnet-5100% (12/12)33% (4/12)+3.110.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

ProvedorModeloConfiguraçãoQualidade (24 fixtures)Custo por revisão¹
googlegemini-3.6-flashGOOGLE_API_KEY=…100% recall · 17% FPR · +5.1 gap$0.019
anthropicclaude-sonnet-5ANTHROPIC_API_KEY=…100% recall · 33% FPR · +3.1 gap$0.089
openaigpt-5.5OPENAI_API_KEY=…100% recall · 0% FPR · +3.3 gap$0.248
openaigpt-5.6-solOPENAI_API_KEY=…100% recall · 0% FPR · +3.3 gap$0.265
anthropicclaude-opus-5ANTHROPIC_API_KEY=…100% recall · 8% FPR · +4.1 gap$0.293
ollamaqualquer modelo de visãoollama serve + ollama pull <model>não avaliado nesta execução$0
mockstub 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-5 a $0.089 — 3× mais barato que claude-opus-5 com 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.5 e gpt-5.6-sol sã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:

  1. Executa a jornada no Chromium headless via Playwright — clicando, digitando, passando o mouse, rolando, pressionando teclas exatamente como um usuário faria.
  2. Captura uma rajada de 16 quadros ao longo de 750ms (intervalos de 50ms) após cada interação via screencast CDP (Page.captureScreenshot JPEG, ~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.
  3. Grava o vídeo completo do Playwright como um artefato que você pode percorrer depois.
  4. Compõe cada rajada em uma folha de contatos rotulada — uma linha por etapa, quadros dispostos em sub-linhas.
  5. 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.
  6. 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çãoFormatoNotas
navigatenavigate /pricingcaminho ou URL completa
clickclick button#startseletor CSS
hoverhover .featureseletor CSS
typetype input#email=ada@example.comseletor=valor
presspress Entertecla do teclado
scrollscroll 800pixels; anima ao longo da janela da rajada
waitwait 500ms
capturecapture "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.

The Animation Tuner: replaying a detected animation, dragging its duration slider from 300ms to 150ms, then applying the ease-out (Emil) preset

motionlint tune http://localhost:3000 --open

Isso:

  1. 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 @keyframes rodando na página.
  2. Captura cada animação detectada: o seletor do elemento, a biblioteca de origem, os parâmetros de timing e a caixa delimitadora.
  3. 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.
  4. 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:

The audit HTML report: score ring, then scrolling through findings — each shows what's happening, why it matters, the fix, and current vs suggested easing curves drawn as graphs

CategoriaO que ela detectaO padrão
Easingease-in em UI; curvas embutidas fracas em entradas deliberadasEntrando/saindo → ease-out forte cubic-bezier(0.23, 1, 0.32, 1); nunca ease-in
DuraçãoMovimento 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
FisicalidadeEntradas scale(0)Nada aparece do nada — comece de scale(0.95) + opacity: 0
Desempenhotransition: all, animar propriedades de layout, loops infinitos soltosAnime apenas transform e opacity — eles pulam layout/paint
CoesãoProliferação de curvas de easing feitas à mão; intervalos de escalonamento fora da faixa de 30–80msCurvas 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:

  1. Confirme que ele aparece: claude mcp listmotionlint deve aparecer como running ou available.
  2. 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 .env no diretório do projeto a partir do qual você está trabalhando — o MotionLint o carrega automaticamente na inicialização.
  3. Primeira execução: npx playwright install chromium se 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/signup com as etapas click input#email; type input#email=test@test.com; click button[type=submit]; wait 2000; capture e 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

FerramentaO 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 build e verifique se dist/index.js existe. Sem isso, motionlint mcp nã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 no npm install).
  • Chaves de API acessíveis — via variáveis de ambiente do shell ou via um arquivo .env no diretório de trabalho a partir do qual o cliente MCP é lançado.
  • Teste de fumaça da superfície MCP. npm test inclui um teste de fumaça MCP que inicia o servidor, lista ferramentas e verifica a superfície esperada de ferramentas.
  • Nenhum segredo commitado. .env está no gitignore; .env.example deve ser um placeholder. Vale uma verificação final git diff --cached | grep -i 'sk-\|api_key' antes de enviar.
  • Confirme com claude mcp list que 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, localStorage e um script beforeNavigate — tudo configurável em .motionlintrc.json.

A flow burst-capture contact sheet: timestamped frames of the signup form animating, laid out in a grid — this is what the vision model reviews

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:

The HTML review report: score ring animates in, then issue-to-fix panels with embedded screenshots scroll past

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 (ou maxFindings na 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 linha Omitted do relatório informa quantos foram limitados.
  • Limite de superfície de PR--max-pr-annotations N (ou maxPrAnnotations na 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 propriedade omitted_by_pr_cap da execução SARIF.
  • Limite de recursosresources.maxConcurrentReviews limita quantas revisões são executadas ao mesmo tempo em um processo (um servidor MCP atendendo vários agentes em voo), e resources.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ões flow, onde cada amostra --consistency conta). 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_usage nas propriedades da execução SARIF). --max-tokens N (ou resources.maxTokensPerRun na 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 sob skipped_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 critical no 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 flow executa 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; linha Tokens: 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.