motionlint
Detecta animaciones defectuosas antes de su lanzamiento. Auditoría de movimiento determinista + revisión de diseño con visión-LLM.
Documentación
MotionLint
Evalúa la calidad de las animaciones de cualquier página con un solo comando. Sin clave de API, sin configuración.
npx motionlint audit http://localhost:3000 --open
Determinista: se mide desde la página en vivo, sin intervención de LLM. Requisito único: npx playwright install chromium.
MotionLint mide el movimiento que tu aplicación realmente envía — duraciones, curvas de easing, intervalos de escalonamiento, tiempos de salida, soporte de reduced-motion — y lo puntúa contra un conjunto publicado de estándares de animación. Ease-in en un desplegable, un modal de 600ms, una tarjeta que escala desde 0, hover que se dispara en táctil: todo detectado, con el valor medido y una corrección concreta.
La auditoría es gratuita y offline. Añade una clave de API y MotionLint también hace revisión de diseño con LLM de visión — capturas de pantalla multi-viewport y ráfagas de frames de 50ms de recorridos reales de usuario, evaluadas por un modelo y entregadas a tu agente de código como hallazgos clasificados. Se ejecuta como servidor MCP dentro de Claude Code y Cursor.
Por qué existe esto
Los agentes de IA de código leen JSX, HTML y CSS — son ciegos a lo que el usuario realmente ve, hace clic y observa animar. Las reglas en un prompt le dicen al agente lo que debería ocurrir; nada comprueba lo que ocurrió. Los modales que deberían deslizarse simplemente aparecen; los estados de carga se omiten; los anillos de foco desaparecen. La revisión de código no puede detectar nada de esto antes del merge, porque nada de ello es visible en el diff.
MotionLint cierra ese ciclo: mide la aplicación en ejecución y devuelve el veredicto.
En qué se diferencia
| MotionLint | Herramientas de regresión visual (Percy, Chromatic, Playwright snapshots) | Generadores de diseño con IA (v0, Galileo, Claude Design, Stitch) | |
|---|---|---|---|
| Auditoría de movimiento determinista | 13 comprobaciones, medidas desde la página en vivo — sin clave de API, $0 | ✗ | ✗ |
| Revisión UX multi-viewport | hallazgos clasificados en 12 dimensiones | solo diffs de píxeles | genera nuevos diseños desde prompts |
| Revisión de animación | ráfagas de frames de 50ms vía screencast CDP → hoja de contactos → LLM | ✗ | ✗ |
| Ajuste de animación en vivo | vistas previas Shadow-DOM + sliders + exportación Claude Code | ✗ | genera movimiento nuevo, no ajusta lo existente |
| Servidor MCP nativo | ✓ MCP stdio para Claude Code / Cursor | ✗ | varía |
| Puerta de CI | ✓ SARIF + códigos de salida para escaneo de código | ✓ umbrales de diff de imágenes | ✗ |
| Calidad validada | 100% de recall en una prueba de estrés de 24 fixtures, en 5 modelos frontera | n/a | n/a |
La brecha conceptual que MotionLint cierra: las herramientas de regresión visual detectan lo que cambió pero no si los nuevos píxeles son buenos; las herramientas de diseño con IA generan desde cero pero no revisan lo que ya está en ejecución. MotionLint revisa el comportamiento en vivo con un LLM de visión y devuelve el veredicto al ciclo de codificación.
Empieza aquí — sin clave de API
npx playwright install chromium # one-time per machine (~300MB)
npx motionlint audit http://localhost:3000 --open
Esa es toda la configuración para la auditoría. Es determinista, funciona offline, no cuesta nada y funciona en cualquier URL que puedas cargar — tu servidor de desarrollo, un deploy de staging o el sitio de otra persona. Requiere Node 18+.
Las reglas que comprueba están publicadas en docs/STANDARDS.md — léelas antes de instalar nada.
Después: revisión de diseño con LLM
Configura una clave de API (ANTHROPIC_API_KEY, OPENAI_API_KEY o GOOGLE_API_KEY — o ejecuta Ollama localmente gratis) y tres comandos más se desbloquean:
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 de Claude Code / Cursor
claude mcp add motionlint -- npx -y motionlint mcp
Superficie completa de flags — puertas de CI, descubrimiento de rutas, Storybook, modo oscuro, líneas base
# 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
Paquete en npm: motionlint.
Ejemplo de salida de terminal para una revisión de flujo:
$ 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
Prueba la demo
Un showcase de animaciones TS multi-ruta se incluye en demo/ — que cubre Motion One, GSAP, anime.js, @formkit/auto-animate y lottie-web — incluyendo una página de una sola URL con temática de gato que ejercita todas las capacidades de MotionLint:
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
Rutas disponibles: /, /pricing, /signup, /dashboard, /loading, /cat. Los informes van a .motionlint/reports/, las capturas a .motionlint/screenshots/, los videos a .motionlint/videos/.
Configuración
Claves de API
MotionLint carga automáticamente un archivo .env desde el directorio de trabajo al iniciar:
# .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
Las variables de entorno reales tienen prioridad sobre .env. Sin clave configurada y sin Ollama en ejecución, MotionLint recurre a un proveedor mock determinista para que el pipeline completo (captura → análisis → informe) siga ejecutándose de extremo a extremo para pruebas de humo.
Auto-detección de proveedor
MotionLint auto-detecta en este orden: Ollama (local) → Anthropic → OpenAI → Google. El primero con una clave de API funcional (o servicio en ejecución) gana. Anula con --provider <name> y --model <id>. Consulta Proveedores en profundidad para la tarjeta de puntuación por proveedor y cómo elegir.
Todo lo siguiente es para lectores que quieran entender cómo funciona MotionLint internamente, elegir el proveedor adecuado para su flujo de trabajo o conectarlo a CI.
Calidad validada entre proveedores
El pipeline de revisión de flujo fue sometido a pruebas de estrés con 12 patrones comunes de animación de aplicaciones web × 2 variantes (24 fixtures en total) — entradas escalonadas, hover/press/focus, entradas de modales, skeletons de carga, errores de formulario, toasts, contadores incrementales, dashboards multi-animación, escalonamiento modal con contenido, feedback de formulario rico (focus + press + spinner + éxito) y animaciones dirigidas por scroll (barra de progreso + revelación con IntersectionObserver + parallax).
Ejecutado el 2026-07-27 contra el buque insignia actual de cada proveedor principal:
| Proveedor · modelo | Recall (rotos detectados) | FPR (limpios marcados) | Brecha de puntuación | Tiempo de pared |
|---|---|---|---|---|
| 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 |
Léelo así: el recall ya no es un diferenciador. Cada buque insignia actual detecta los 12 fallos sembrados. Ese es el hallazgo — hace un año no era cierto, y significa que la elección del modelo ya no decide si MotionLint funciona. Elige por costo y latencia.
No clasifiques estos modelos por la columna FPR. Una sola ejecución de 24 fixtures no puede resolverla. En dos ejecuciones limpias de la suite idéntica, sin cambiar nada más que el muestreo, el FPR se movió 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. Un fixture son 8 puntos porcentuales, así que todo el rango entre "0%" y "17%" está dentro del ruido. Trata la columna como "todos estos ocasionalmente marcan algo limpio", no como una clasificación.
Por qué los números antiguos en el historial de esta tabla eran incorrectos
La primera ejecución del 2026-07-27 de esta suite puso a claude-opus-5 en 83% de recall — el último entre los cinco modelos — y la edición del 2026-04-29 de esta tabla reportó varios modelos con 0% de FPR. Ambos fueron artefactos de un bug de MotionLint, no del comportamiento del modelo.
El max_tokens de Anthropic tenía un valor predeterminado de 4096. Las respuestas verbosas alcanzaban el límite a mitad del JSON, y el resultado no analizable se puntuaba como 0/10, no issues found — indistinguible de una revisión limpia. Dos de las tres truncaciones de Opus 5 aterrizaron en fixtures rotos, lo que produjo la cifra completa del 83%.
El mismo bug deflactó el FPR en todas partes: una revisión truncada no reporta nada, por lo que no puede generar un falso positivo. Cualquier "0% FPR" histórico medía en parte un análisis roto, no la precisión del modelo. Corregido el 2026-07-27, junto con las rutas hermanas que convertían respuestas truncadas, rechazadas y bloqueadas por seguridad en resultados de apariencia limpia.
Tarjetas de puntuación completas por proveedor en .motionlint/stress/ después de ejecutar scripts/run-all-benchmarks.mjs. Usa --only <provider>:<model> para re-ejecutar un solo modelo.
Proveedores en profundidad
| Proveedor | Modelo | Configuración | Calidad (24 fixtures) | Costo por revisión¹ |
|---|---|---|---|---|
google | gemini-3.6-flash | GOOGLE_API_KEY=… | 100% recall · 17% FPR · +5.1 brecha | $0.019 |
anthropic | claude-sonnet-5 | ANTHROPIC_API_KEY=… | 100% recall · 33% FPR · +3.1 brecha | $0.089 |
openai | gpt-5.5 | OPENAI_API_KEY=… | 100% recall · 0% FPR · +3.3 brecha | $0.248 |
openai | gpt-5.6-sol | OPENAI_API_KEY=… | 100% recall · 0% FPR · +3.3 brecha | $0.265 |
anthropic | claude-opus-5 | ANTHROPIC_API_KEY=… | 100% recall · 8% FPR · +4.1 brecha | $0.293 |
ollama | cualquier modelo de visión | ollama serve + ollama pull <model> | no evaluado en esta ejecución | $0 |
mock | stub heurístico | (respaldo automático) | n/a — stub determinista para pruebas de humo en CI | $0 |
¹ Medido, no estimado — un motionlint review real por modelo contra la app demo en los 2 viewports predeterminados, página completa, leyendo los conteos reales de tokens del campo de uso de cada proveedor y multiplicando por el precio de lista publicado. Reproduce con formatUsageLine() en cualquier ejecución. Sonnet 5 usa su tarifa introductoria (hasta 2026-08-31); aproximadamente sube a la mitad después. La revisión de flujo envía una imagen compuesta por flujo, pero la hoja de contactos es más grande. El Ajustador de Animación y motionlint audit hacen cero llamadas al LLM y no cuestan nada.
Los tokens de salida dominan. La entrada está dentro de 2× en los cinco modelos; la salida abarca de 1,390 (Gemini) a 10,219 (Opus 5). Ese rango de 7×, no el tamaño de imagen, es lo que hace que el modelo más caro sea 15× el más barato.
Cómo elegir
- Predeterminado. Google
gemini-3.6-flash— 100% recall, 13× más barato que Opus 5 y el más rápido de los cinco (4.9 min). Dado que todos los modelos detectaron todas las fallas, no hay argumento de calidad para pagar más por defecto. - Casa Anthropic.
claude-sonnet-5a $0.089 — 3× más barato queclaude-opus-5con recall idéntico. Opus 5 cuesta más y tomó 2× el tiempo de pared (21.0 min vs 10.8) sin ventaja medida de recall; úsalo solo si valoras su brecha de puntuación ligeramente mayor (+4.1 vs +3.1). - Casa OpenAI.
gpt-5.5ygpt-5.6-solson indistinguibles en cada eje medido y dentro del 7% en precio. Toma el que tu cuenta ya tenga. - Puerta de CI estricta. Cualquiera de ellos en recall. No elijas por FPR — ver la advertencia de ruido arriba. Si los falsos positivos importan para tu puerta, ejecuta tus propios fixtures en lugar de confiar en una sola ejecución de 24 fixtures nuestra.
- Local / aislado. Cualquier modelo de visión de Ollama funciona, pero confirma que sí es capaz de visión: algunos aceptan imágenes por la API, las ignoran silenciosamente y responden solo desde el prompt. Ninguno fue evaluado en esta ejecución.
Cambiar de proveedor
Cada comando respeta --provider y --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
Evaluar tu propio proveedor
Para comparar un nuevo proveedor contra la misma prueba de estrés 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));
});
"
Abre .motionlint/stress/SCORECARD.md para el desglose por patrón.
Cómo funciona motionlint flow
Las capturas estáticas no pueden decirte si las animaciones y los estados de interacción de un flujo funcionan — solo si el frame final se ve bien. motionlint flow llena ese vacío.
Dado un recorrido de usuario guionizado, este:
- Ejecuta el recorrido en Chromium headless mediante Playwright: hace clic, escribe, pasa el cursor, desplaza y pulsa teclas exactamente como lo haría un usuario.
- Captura una ráfaga de 16 fotogramas durante 750 ms (intervalos de 50 ms) después de cada interacción mediante la transmisión de pantalla CDP (
Page.captureScreenshotJPEG, ~8 ms por toma). 50 ms es la mitad del umbral humano de detección visual y está por debajo del intervalo mínimo de animación típico de la industria de 100 ms; las animaciones cortas, como pulsaciones de botón de 100 ms, se capturan con 2-3 fotogramas de estado intermedio. Cada ráfaga de interacción también se compara por diferencias de píxeles para medir la latencia de entrada→retroalimentación; las interacciones sin reconocimiento visible dentro de la ventana de ráfaga se marcan de forma determinista. - Graba el video completo de Playwright como un artefacto que puedes revisar más tarde.
- Compone cada ráfaga en una hoja de contactos etiquetada: una fila por paso, con los fotogramas dispuestos en subfilas.
- Envía la hoja al LLM de visión con una rúbrica consciente del flujo que cubre: animaciones faltantes, animaciones con errores/entre cortes, estados de carga faltantes, rendimiento percibido, affordance y cambios de estado, coreografía, suavidad, parpadeo accidental, continuidad de navegación, respeto por movimiento reducido.
- Produce un informe en Markdown con un seguimiento paso a paso, hallazgos clasificados y un bloque "Prompt para Claude Code" al final: pégalo en CC y actuará directamente sobre los hallazgos.
Manejo de múltiples animaciones
Una sola grabación puede capturar y analizar múltiples animaciones concurrentes. Validado en:
- Revelación de panel (3 concurrentes: escalonamiento de mosaicos + rampas de contadores + elevación de barras de gráfico)
- Pila de modales (desvanecimiento de fondo + deslizamiento y desvanecimiento del modal + escalonamiento del contenido interno)
- Retroalimentación de formularios enriquecidos (anillo de enfoque + pulsación de botón + indicador de carga + tarjeta de éxito)
- Impulsado por desplazamiento (barra de progreso de desplazamiento + revelación de secciones con IntersectionObserver + héroe con parallax)
El LLM identifica correctamente cuáles animaciones están rotas sin marcar falsamente las que funcionan; consulta la tabla de calidad validada.
Animaciones impulsadas por desplazamiento
Para sitios con animaciones vinculadas al desplazamiento, scroll <px> pasos animan el desplazamiento durante la ventana de ráfaga mediante requestAnimationFrame para que cada fotograma muestre la posición de desplazamiento progresiva y el LLM vea la sincronización mientras la página se desplaza.
Ejemplos de flujo
# 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
Referencia DSL en línea
| Acción | Forma | Notas |
|---|---|---|
| navigate | navigate /pricing | ruta o URL completa |
| click | click button#start | selector CSS |
| hover | hover .feature | selector CSS |
| type | type input#email=ada@example.com | selector=valor |
| press | press Enter | tecla de teclado |
| scroll | scroll 800 | píxeles; anima durante la ventana de ráfaga |
| wait | wait 500 | ms |
| capture | capture "post-submit" | toma una ráfaga explícita con etiqueta opcional |
Valores predeterminados: se toma una ráfaga de fotogramas después de cada interacción. Pasa --no-implicit-bursts para ráfagas solo en pasos capture explícitos. Pasa --no-record para omitir el video.
Tres flujos de muestra listos para ejecutar se incluyen en el repositorio: flows/signup.json, flows/loading-state.json y flows/preferences.md.
Cómo funciona el Animation Tuner
La mayoría de las herramientas de codificación con IA generan animaciones desde cero. El Tuner te permite ajustar las animaciones que ya se están ejecutando en tu página, en tiempo real, y devolver los cambios a tu agente de codificación como un prompt estructurado.
motionlint tune http://localhost:3000 --open
Esto:
- Abre tu aplicación en Chromium headless con un script de instrumentación que engancha las principales bibliotecas de animación TS (Motion One, GSAP, anime.js, @formkit/auto-animate, lottie-web) además de todas las transiciones CSS y
@keyframesque se ejecutan en la página. - Captura cada animación detectada: el selector del elemento, la biblioteca de origen, los parámetros de sincronización y el cuadro delimitador.
- Genera una página HTML interactiva autocontenida en
.motionlint/tuner/index.html(se abre automáticamente con--open):- Superficie de vista previa en vivo por animación (Shadow DOM: sin iframes, sin parpadeos, con tema de la página de origen).
- Controles deslizantes para duración / retraso / escalonamiento / velocidad.
- Menú desplegable de ajustes de easing: las curvas fuertes de Emil Kowalski lideran (ease-out, ease-in-out, cajón de iOS), luego las opciones más suaves/decorativas.
- Verificación de estándares en línea: cada tarjeta señala dónde la animación se desvía de los estándares de movimiento (insignia de severidad, corrección, valor sugerido), con una puntuación de encabezado.
- Cuadro de comentarios por animación para la justificación de diseño.
- Exporta un archivo markdown más un prompt listo para Claude Code con un bloque JSON estructurado
changes[]. Pégalo en CC y editará tu código para aplicar los nuevos 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
Estándares de animación — motionlint audit
MotionLint codifica los estándares de ingeniería de diseño de Emil Kowalski como un linter determinista: sin modelo de visión, sin clave API, sin costo. motionlint audit instrumenta la página, lee los valores reales de sincronización/easing/transformación que ejecuta cada animación y los califica:
| Categoría | Qué detecta | El estándar |
|---|---|---|
| Easing | ease-in en UI; curvas integradas débiles en entradas deliberadas | Entrada/salida → ease-out fuerte cubic-bezier(0.23, 1, 0.32, 1); nunca ease-in |
| Duración | Movimiento de UI por encima del límite de 300 ms (modales/cajones reciben 200–500 ms) | Una transición de 180 ms se siente más ágil que una de 400 ms; las salidas ~20% más rápidas |
| Fisicalidad | Entradas scale(0) | Nada aparece de la nada: comienza desde scale(0.95) + opacity: 0 |
| Rendimiento | transition: all, animar propiedades de layout, bucles infinitos sueltos | Anima solo transform y opacity: omiten layout/pintura |
| Cohesión | Dispersión de curvas de easing hechas a mano; intervalos de escalonamiento fuera de la banda de 30–80 ms | Las curvas y duraciones deben vivir como tokens compartidos; las entradas agrupadas se escalonan con 30–80 ms de separación |
| Duración (pares) | Salidas que no son más rápidas que su entrada (fadeIn 300 ms / fadeOut 300 ms) | Las salidas se ejecutan ~20% más rápido que la entrada correspondiente |
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
Agrega --layout para también verificar el layout (objetivos táctiles, tamaño de texto, contraste, desbordamiento) a partir de mediciones DOM en vivo: sigue siendo determinista, sigue sin clave API.
Agrega --watch [dir] para volver a ejecutar la auditoría en cambios de archivos bajo [dir] (predeterminado: cwd) e imprimir la puntuación con un delta después de cada ejecución: una lectura en vivo mientras iteras. La vigilancia recursiva requiere macOS, Windows o Linux con Node 20+.
El informe empareja cada hallazgo con un panel antes → después; los hallazgos de easing muestran una comparación en vivo de la curva cubic-bezier para que la corrección sea visible, no solo descrita. Los mismos estándares alimentan el prompt de revisión flow (para que los hallazgos de visión citen reglas concretas) y aparecen en línea en el Animation Tuner.
Servidor MCP: herramientas, recursos, implementación
MotionLint incluye un servidor MCP sobre stdio para que un agente LLM pueda manejarlo directamente dentro de un chat. El subcomando motionlint mcp lo inicia; el cliente agente genera el proceso cuando se llama a una herramienta.
Instalación en Claude Code
Versión publicada en npm (recomendada):
claude mcp add motionlint -- npx -y motionlint mcp
Checkout local (útil durante el desarrollo):
claude mcp add motionlint -- node /absolute/path/to/motionlint/dist/index.js mcp
Después del registro:
- Confirma que aparece:
claude mcp list—motionlintdebería mostrarse comorunningoavailable. - Asegúrate de que las claves API sean accesibles. El servidor MCP hereda el entorno en el que se genera. La vía más limpia: coloca un archivo
.enven el directorio del proyecto desde el que trabajas; MotionLint lo carga automáticamente al iniciar. - Primera ejecución:
npx playwright install chromiumsi aún no lo has hecho.
Luego en Claude Code:
"Usa motionlint para revisar la aplicación local en móvil y escritorio y dime los 3 problemas principales a corregir."
"Ejecuta motionlint review_flow en
http://localhost:3000/signupcon los pasosclick input#email; type input#email=test@test.com; click button[type=submit]; wait 2000; capturey revisa las animaciones.""Ejecuta motionlint tune_animations en
http://localhost:3000/pricing: quiero ajustar finamente las animaciones de hover de las tarjetas."
Herramientas expuestas
| Herramienta | Qué hace |
|---|---|
review_url(url, viewports?, provider?, model?, wait_for?, record?, format?, max_findings?, max_pr_annotations?, new_only?) | Revisión UX estática de una URL en múltiples viewports. Devuelve un informe markdown / JSON / SARIF. |
review_routes(base_url, routes, viewports?, ..., max_findings?, max_pr_annotations?, new_only?) | La misma revisión en múltiples rutas de una aplicación. |
review_flow(url, steps?|spec_path?, preferences_path?, provider?, ...) | Revisión de animación/interacción de un recorrido de usuario guionizado. Devuelve un informe de flujo con el bloque estructurado de traspaso a CC. |
tune_animations(url, viewport_*?, settle_ms?, output?) | Detecta cada animación en una página y escribe un tuner HTML interactivo. Devuelve la ruta del archivo. |
get_latest_report(format?) | Devuelve el contenido del informe de revisión/flujo más reciente. |
Recursos: motionlint://reports/latest — el contenido del informe más reciente.
Lista de verificación de implementación
Antes de implementar o compartir el servidor MCP con otros usuarios:
- La compilación está actualizada.
npm run buildy luego verifica quedist/index.jsexista. Sin esto,motionlint mcpno se iniciará. - Playwright Chromium instalado en la máquina de destino:
npx playwright install chromium. El hook de postinstall lo recuerda, pero no se aplica (no descargamos automáticamente un binario de 300 MB ennpm install). - Claves API accesibles: ya sea mediante variables de entorno del shell o mediante un archivo
.enven el directorio de trabajo desde el que el cliente MCP se lanza. - Prueba de humo de la superficie MCP.
npm testincluye una prueba de humo MCP que inicia el servidor, lista las herramientas y verifica la superficie esperada de herramientas. - Sin secretos confirmados.
.envestá en gitignore;.env.exampledebería ser un marcador de posición. Vale la pena ungit diff --cached | grep -i 'sk-\|api_key'final antes de hacer push. - Confirma con
claude mcp listque el servidor aparece y no da errores al iniciar.
Integración con 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 }
MotionLint sale con 1 cuando los problemas críticos superan el umbral configurado (failOnCritical): conéctalo como verificación de estado.
Qué captura · qué analiza
Capturas:
- Capturas de pantalla de página completa en tres viewports predeterminados (móvil 375 / tableta 768 / escritorio 1440). Anula mediante configuración.
- Capturas de pantalla above-the-fold con
--no-full-page. - Videos de la ejecución de navegación+captura con
--record(Playwright.webm). - Secuencias de interacción antes de la captura:
click,hover,type,scroll,wait. - Estado de autenticación: cookies,
localStoragey un scriptbeforeNavigate: todo configurable en.motionlintrc.json.
A motionlint flow contact sheet — timestamped bursts after each interaction, exactly what the vision model sees.
Analiza: cada captura de pantalla se envía a un modelo de visión con un prompt de sistema de revisión UX con opiniones que cubre doce dimensiones (hierarchy, spacing, alignment, typography, color, contrast, responsiveness, interaction, content, navigation, consistency, loading_state). Para cada problema, el modelo devuelve:
{
"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."
}
Anula el prompt con --rules path/to/your-design-rules.md para inyectar heurísticas específicas del proyecto.
Cada captura de revisión también toma una instantánea DOM: los elementos notables (encabezados, CTAs, entradas) reciben referencias estables (E1, E2, …) con rectángulos de píxeles medidos, listados en el prompt para que el modelo pueda fundamentar un hallazgo con "element_ref": "E3". Las referencias citadas se resuelven de vuelta a sus rectángulos y se dibujan como cuadros delimitadores de color según severidad en la captura de pantalla en el informe HTML (y se informan como Where: E3 at (x, y) w×h en markdown). Las referencias que la página nunca listó se descartan: el modelo no puede anotar lo que no se le mostró.
Con --format html los hallazgos se muestran como un informe único compartible: anillo de puntuación, desglose por dimensión y un panel problema → corrección por hallazgo con la captura de pantalla anotada:
Referencia de configuración
Coloca un .motionlintrc.json en la raíz de tu repositorio (o usa motionlint.config.js / una clave "motionlint" en 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 }
}
Control de volumen de revisión
Volver a ejecutar la revisión sobre las mismas rutas solía mostrar los mismos hallazgos en cada ejecución. Dos mecanismos mantienen la salida enfocada:
- Límite de salida por ejecución —
--max-findings N(omaxFindingsen configuración) conserva solo los N hallazgos principales por ejecución, ordenados por severidad, para que un agente trabaje primero en lo más importante. La líneaOmitteddel informe indica cuántos fueron limitados. - Límite de superficie de PR —
--max-pr-annotations N(omaxPrAnnotationsen configuración; solo SARIF) emite como máximo N resultados por informe, ordenados por severidad, para que una carga de code-scanning no inunde un PR con anotaciones. El recuento de descartados aparece en la propiedadomitted_by_pr_capde la ejecución SARIF. - Límite de recursos —
resources.maxConcurrentReviewslimita cuántas revisiones se ejecutan a la vez en un proceso (un servidor MCP que atiende a varios agentes en vuelo), yresources.providerCallsPerMinutees un límite de ventana deslizante a nivel de proceso para llamadas de LLM de visión (control de cuota/gasto del proveedor; también se aplica a revisionesflow, donde cada muestra--consistencycuenta). Ambos tienen un valor predeterminado ilimitado; ambos son solo de configuración. Ten en cuenta que se combinan: una revisión que ocupa un espacio de concurrencia también espera al limitador de tasa, por lo que valores ajustados en ambos multiplican la latencia. - Techo de costo — el uso de tokens de cada llamada al proveedor se captura y se totaliza por ejecución (una línea
Tokens:en los informes,token_usageen las propiedades de ejecución SARIF).--max-tokens N(oresources.maxTokensPerRunen configuración) establece un presupuesto de tokens por ejecución: una vez que el total acumulado lo supera, se omiten las viewports restantes y el informe las enumera bajoskipped_viewports. Los proveedores que no informan uso aún cuentan llamadas pero no consumen presupuesto. - Memoria entre ejecuciones — cada hallazgo recibe un id estable (hash de categoría + ubicación del elemento + texto de problema normalizado). La detección de recurrencia va más allá del hash exacto: compatibilidad de sinónimos de categoría más superposición de tokens canónicos (umbrales calibrados con datos reales entre ejecuciones) coincide con la misma falla incluso cuando el LLM de visión la reformula entre ejecuciones. Los avistamientos se registran por URL en
.motionlint/memory.json; los hallazgos recurrentes se anotan con visto en N ejecuciones anteriores en lugar de descartarse silenciosamente. Opta por solo deltas con--new-only. Para descartar permanentemente un hallazgo, copia su id en.motionlintignore(un hash por línea, comentarios#y notas finales permitidas). Desactiva todo con--no-memory.
La salida SARIF lleva el id del hallazgo como partialFingerprint, por lo que el code scanning de GitHub deduplica el mismo hallazgo entre ejecuciones y PRs de forma nativa.
Las revisiones concurrentes del mismo proyecto son seguras: el almacén de memoria se actualiza bajo un bloqueo de archivo consciente de obsolescencia (memory.json.lock), por lo que las ejecuciones paralelas no se sobrescriben los avistamientos registrados entre sí. Un bloqueo atascado nunca falla una revisión: después de una breve espera, la ejecución advierte y continúa sin él.
Casos de uso
- Guardarraíl de UX previo a la fusión. Desarrollador en solitario o startup de 2 personas sin diseñador. Ejecuta
motionlint review https://pr-123.preview.example.com --ci --threshold criticalen CI; una advertencia o peor bloquea la fusión hasta que al menos hayas visto los problemas. - Colega de diseño MCP dentro de Claude Code. Agrega MotionLint como servidor MCP, luego pide a CC: "revisa la app local en móvil y escritorio y dime los 3 problemas principales a corregir". CC maneja la herramienta y recibe comentarios anotados en la misma conversación.
- Monitoreo continuo de calidad. Programa un cron nocturno (
motionlint review https://prod.example.com --format sarif -o ux.sarif) y muestra SARIF en tu panel de code-scanning para que las regresiones de producción se detecten a la mañana siguiente. - QA de animación / flujo en una función que acabas de lanzar.
motionlint flowejecuta un recorrido de usuario con script a través de Playwright como lo haría un humano, captura ráfagas de fotogramas en cada interacción, graba video y pide al LLM revisar el comportamiento de animación en los fotogramas capturados. - Ajuste de animación en vivo + traspaso a Claude Code. Captura cada animación en una página, ajusta tiempo/curva/retraso en vivo con controles deslizantes, exporta un prompt estructurado que CC puede usar directamente.
Estructura del proyecto
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
Hoja de ruta
v0.1 (esta versión) — enviado:
- Tres comandos CLI:
review,flow,tune+ servidor MCP (motionlint mcp). - Cinco proveedores de visión: Anthropic, OpenAI, Google, Ollama, mock.
- Revisión estática multi-viewport con captura de mosaico, canal lateral de medición DOM, muestreo de autoconsistencia, puntuación de palabras clave suaves con grafo de sinónimos.
- Harness de evaluación por niveles (L1 / L2 / L3) con 21 fixtures etiquetados y JSON estructurado
next_actions[]para herramientas de codificación LLM posteriores. - Revisión de flujo a intervalos de 50ms entre fotogramas mediante screencast CDP (16 fotogramas × ráfaga de 750ms), con soporte de múltiples animaciones y desplazamiento.
- Ajustador de animación con vistas previas de Shadow-DOM, controles deslizantes en vivo, presets de curvas, exportación a Claude-Code.
- Harness de prueba de estrés de animación validado con 100% de recall / 0% de FPR en 24 fixtures en 12 patrones.
- Markdown de preferencias de movimiento del equipo (
--preferences) integrado en la rúbrica del LLM y el bloque de traspaso a CC. - Escaneo de intervalo automático (
--auto-interval) que elige un intervalo entre fotogramas basado en la animación más corta detectada en la página. - Salida SARIF para code scanning de GitHub.
v0.2 (en progreso) — enviado hasta ahora:
- Contabilidad de tokens + techo de costo por ejecución (
--max-tokens/resources.maxTokensPerRun; líneaTokens:en cada informe). - Descubrimiento automático de rutas (
--discover-routes: sitemap.xml + directorio de app de Next.js). - Cajas delimitadoras anotadas: referencias de elementos DOM en el prompt, hallazgos dibujados en la captura de pantalla en el informe HTML.
- Cuadrículas de estados de interacción (
--state-grid: default/hover/focus/active por elemento, una imagen etiquetada). - Historial de tarjetas de puntuación del proveedor con detección de regresión por modelo (
.motionlint/eval-history.json). - Evolución de prompt de bucle cerrado desde eval
next_actions(eval --evolve→ heurísticas aprendidas en prompts de revisión). - Dos nuevas reglas de auditoría: banda de intervalo de escalonamiento (30–80ms) y salida ~20% más rápida que entrada.
v0.2 (siguiente):
- Wrapper de GitHub Action (
motionlint-action).
Agradecimientos
MotionLint se apoya en el trabajo de otras personas:
- Emil Kowalski — los estándares de animación detrás de
motionlint audit, los presets de curvas del ajustador y la rúbrica de revisión de flujo se destilan de su escritura de ingeniería de diseño y su curso animations.dev. Sus bibliotecas de UI de código abierto — sonner (toasts) y vaul (drawers) — son implementaciones de referencia vivas del movimiento que describen estas reglas. MotionLint es un proyecto independiente, no afiliado ni respaldado por Emil. - ctx (ctx.rs) — búsqueda de historial local para agentes de codificación. Lo usamos mientras desarrollábamos la capa de memoria entre ejecuciones para estudiar cómo los hallazgos sobreviven (o desaparecen) entre ejecuciones de agentes; esos experimentos dieron forma directamente al diseño de id de hallazgo y línea base.
Licencia
MIT © Resila Technologies Inc.