Laravel

Lens for Laravel: auditar accesibilidad en local con axe-core y arreglarla con IA

Autorangel cruz
Publicado
Lectura9 min de lectura
Lens for Laravel: auditar accesibilidad en local con axe-core y arreglarla con IA

Lens for Laravel es un auditor de accesibilidad que corre dentro de tu aplicación: escanea con axe-core a través de Browsershot, te dice en qué línea de qué fichero Blade, React o Vue está el problema, y puede proponerte el arreglo con un modelo de IA. Se instala como dependencia de desarrollo, no manda tu código a ningún SaaS por defecto, y el AI Fix es opcional y puede correr contra un modelo local con Ollama.

La accesibilidad suele quedarse en dos extremos igual de inútiles: o una auditoría externa cara que llega una vez al año en PDF, o una extensión de navegador que te dice "este botón no tiene nombre accesible" y te deja buscando a mano en qué componente vive ese botón. Lens ataca justo el medio, el bucle del día a día.

Qué hace

  • Escanea con axe-core, el motor de Deque, que es el estándar de facto para comprobaciones automáticas de WCAG.
  • Renderiza JavaScript a través de Browsershot y Chromium, así que analiza el DOM hidratado y no el HTML que salió del servidor. Si tu página monta la mitad de la interfaz en el cliente, esto no es opcional.
  • Mapea la violación a su origen: resources/views/**/*.blade.php o ficheros bajo resources/js, con fichero, línea y una etiqueta sourceType que vale blade, react o vue.
  • Guarda histórico de escaneos, con recuentos, URLs afectadas y tendencia, y permite comparar dos escaneos para ver qué es nuevo, qué se arregló y qué sigue igual.
  • Corre en CI con un baseline, para fallar solo cuando aparece una regresión nueva.
  • Propone arreglos con IA, si lo activas.

Requisitos, que no son uno solo

Son dos matrices distintas, y conviene mirarlas antes de instalar.

Para el escáner, que es el grueso del paquete:

Requisito Versión
PHP ^8.2
Laravel ^10.0, ^11.0, ^12.0 o ^13.0
Node.js cualquier LTS reciente
Puppeteer ^21 recomendado
Chromium el de Puppeteer o el de tu entorno

Para el AI Fix, que es más estrecha:

Requisito Versión
PHP ^8.3
Laravel ^12.0 o ^13.0
SDK opcional laravel/ai 0.3.2 o superior

Una aplicación en Laravel 10 con PHP 8.2 conserva escaneo, crawling, histórico, PDF, previsualización, localización de origen, estados interactivos y CLI. Lo único que pierde es el AI Fix, y el panel lo dice en vez de esconder el botón sin explicación.

El paquete depende de spatie/browsershot (^4 o ^5), así que necesitas Puppeteer instalado en la aplicación anfitriona:

npm install puppeteer --save-dev

Si ya has hecho imágenes Open Graph con Browsershot, esta parte ya la tienes montada.

Instalación

composer require webcrafts-studio/lens-for-laravel --dev

El service provider se auto-descubre. Las migraciones solo hacen falta si quieres histórico:

php artisan migrate

Y el AI Fix se instala aparte, a propósito. El paquete no requiere un SDK de IA en su composer.json, solo lo sugiere:

composer require laravel/ai --dev

Después, el panel vive en http://tu-app.test/lens-for-laravel/dashboard.

El escaneo desde la terminal

El comando de Artisan usa el mismo escáner, el mismo crawler y el mismo localizador de origen que el panel, así que no hay dos comportamientos distintos que reconciliar:

# La URL raíz de la aplicación
php artisan lens:audit
 
# URLs concretas
php artisan lens:audit http://tu-app.test/about http://tu-app.test/contacto
 
# Rastrear y auditar las páginas internas que descubra
php artisan lens:audit --crawl
 
# Solo violaciones de nivel A, o de A y AA
php artisan lens:audit --a
php artisan lens:audit --aa
 
# El conjunto de reglas acumulativo de WCAG 2.2
php artisan lens:audit --wcag=2.2
 
# Escanear una página tras login, como un usuario existente
php artisan lens:audit http://tu-app.test/dashboard --as-user=1

Dos ejes que se confunden con facilidad y que son independientes: --wcag=2.0|2.1|2.2 elige la versión del estándar, y --a, --aa, --all eligen el nivel de conformidad que se muestra. El estándar por defecto es WCAG 2.0, por compatibilidad hacia atrás con las instalaciones que ya existían.

El baseline en CI

Cualquiera que haya intentado meter una comprobación de accesibilidad en un proyecto con años encima conoce el problema: la primera ejecución devuelve cientos de violaciones, el pipeline se pone rojo, y a la semana alguien añade el continue-on-error y la comprobación deja de existir.

Lens lo resuelve así:

# Tras revisar el estado actual, se congela como baseline
php artisan lens:audit --crawl --baseline
 
# En CI, se compara contra ese baseline
php artisan lens:audit --crawl --fail-on-new

El baseline vive por defecto en storage/app/lens-for-laravel/baseline.json, y admite otra ruta con --baseline-file. La comparación usa huellas estables basadas en la regla, la ruta normalizada de la URL, el selector y el fichero de origen cuando lo hay, de modo que cambiar de máquina local a la de CI no invalida el baseline.

También hay un umbral duro, --threshold=10, que devuelve código de salida 1 cuando se superan esas violaciones, para quien prefiera un techo absoluto en vez de un baseline.

AI Fix

El AI Fix se activa cuando se cumplen cuatro cosas a la vez: PHP 8.3 o superior, Laravel 12 o superior, el paquete laravel/ai instalado, y LENS_FOR_LARAVEL_AI_ENABLED sin poner a false.

El flujo, resumido: Lens localiza el fichero y la línea, extrae el elemento o componente más pequeño que sea relevante en vez de una ventana arbitraria de líneas, envía la incidencia, el fragmento de DOM que falla, las etiquetas WCAG y ese trozo de código al proveedor, y recibe un reemplazo mínimo con su explicación. El panel muestra un diff, que puedes aceptar tal cual o editar en un editor con números de línea antes de aplicar.

Hay un detalle en ese flujo que me parece la mejor decisión del paquete: una corrección aplicada se marca como pendiente y sigue contando como violación hasta que un nuevo escaneo de axe-core la verifique. El modelo dice que lo arregló y el escáner decide si es verdad.

Los proveedores en la nube que soporta son Gemini, OpenAI, Anthropic, OpenRouter, xAI, DeepSeek y Mistral, configurados por variables de entorno:

LENS_FOR_LARAVEL_AI_PROVIDER=anthropic
ANTHROPIC_API_KEY=tu-clave

Y en local, con Ollama:

ollama pull qwen2.5-coder:7b
LENS_FOR_LARAVEL_AI_PROVIDER=ollama
LENS_FOR_LARAVEL_AI_OLLAMA_MODEL=qwen2.5-coder:7b
LENS_FOR_LARAVEL_AI_OLLAMA_TIMEOUT=120
OLLAMA_URL=http://127.0.0.1:11434

Ollama sirve en http://127.0.0.1:11434 por defecto, así que la URL se puede omitir. Ojo con un cambio de nombre que ya documenta el propio README: laravel/ai 0.3.x llamaba a esa variable OLLAMA_BASE_URL, y las versiones actuales usan OLLAMA_URL.

Sobre privacidad, el README es explícito: el AI Fix envía el fragmento de DOM que falla, los detalles de la incidencia, las etiquetas WCAG y un fragmento acotado del código fuente. Con el endpoint local de Ollama, eso no sale de la máquina. Con un endpoint remoto o un proveedor en la nube, sí. No manda el repositorio entero, pero revisar ese contexto en busca de secretos sigue siendo tu trabajo.

Los límites que el propio paquete admite

Es la sección que suelo leer primero en un README, y aquí está escrita sin adornos.

Un escaneo limpio no es conformidad WCAG. axe-core automatiza muchas comprobaciones de alta confianza, y ni axe ni Lens pueden determinar conformidad completa: lo que obtienes es evidencia de las comprobaciones automáticas.

La localización del origen es heurística. Puede fallar o confundirse con componentes muy abstraídos, componentes propios que renderizan HTML por dentro (el ejemplo del README es <LogoImage />), constructores dinámicos de clases sin ninguna clase literal, claves de CSS modules que no se parecen a la clase generada, o elementos que solo aparecen tras una interacción.

Y la lista de lo que hay que seguir haciendo a mano: navegación por teclado, lectores de pantalla (NVDA, JAWS, VoiceOver), validación manual de formularios, y comprobar modales, menús, desplegables, acordeones y pestañas.

Seguridad: pensado para local

Lens está pensado para entornos locales y controlados, y trae varias defensas de serie: el acceso al panel se restringe por enabled_environments, las URLs a escanear tienen que coincidir con el host de APP_URL (el escaneo de dominios externos está bloqueado), y el AI Fix rechaza el path traversal, escribe solo en rutas de Blade, React o Vue, y bloquea código generado que contenga shell_exec, system, exec, passthru, proc_open, popen o eval, además de etiquetas PHP de apertura nuevas que no estuvieran en el bloque original.

Los escaneos autenticados aceptan solo un id numérico de usuario, el login ocurre en el servidor, las cookies de sesión son de vida corta y no se registran ni se almacenan, y el estado de autenticación previo se restaura al terminar. El paquete rechaza a propósito las contraseñas y las cookies crudas.

La postura recomendada en producción es directa:

'enabled_environments' => ['local'],

Y si lo habilitas en staging, protege la ruta:

'middleware' => ['web', 'auth'],

Estado del proyecto

El paquete es MIT y el repositorio se creó el 25 de febrero de 2026. Lleva un ritmo alto: la v3.0.0 se publicó el 4 de julio de 2026 y la v3.5.0 el 6 de septiembre de 2026, con la 3.3 (Ollama), la 3.4 (escaneos autenticados y cuatro proveedores más) y la 3.5 (modelo de IA configurable) en la misma semana larga. Al escribir esto acumula 51 estrellas en GitHub, o sea que es joven y con poca gente mirándolo todavía; para una dependencia de desarrollo el riesgo es asumible, pero conviene saberlo.

Preguntas frecuentes

¿Qué es Lens for Laravel?

Un auditor de accesibilidad local para aplicaciones Laravel. Escanea con axe-core renderizando a través de Browsershot, mapea cada violación al fichero Blade, React o Vue de origen, guarda histórico y puede generar arreglos asistidos por IA. Se instala con composer require webcrafts-studio/lens-for-laravel --dev.

¿Necesito una API de pago para usarlo?

No. El escáner completo, el crawler, el histórico, los informes en PDF y el gate de CI funcionan sin ningún SDK de IA. El AI Fix es opcional y, si lo quieres, puede correr contra un modelo local con Ollama sin pagar a ningún proveedor.

¿Funciona con React, Vue e Inertia?

Sí. Localiza el origen en ficheros bajo resources/js y etiqueta cada resultado con sourceType (blade, react o vue). Las páginas de Inertia bajo resources/js/Pages/** se incluyen automáticamente, y hay un modo de crawler que renderiza JavaScript para aplicaciones SPA.

¿Puedo usarlo en CI sin que el pipeline se ponga rojo desde el primer día?

Sí, para eso está el baseline. Congelas el estado actual con php artisan lens:audit --crawl --baseline y en CI ejecutas --fail-on-new, que solo falla cuando aparecen violaciones nuevas respecto a ese baseline.

¿Qué versión de PHP y Laravel hace falta?

Para escanear, PHP 8.2 o superior y Laravel 10, 11, 12 o 13. Para el AI Fix, PHP 8.3 o superior, Laravel 12 o 13 y laravel/ai 0.3.2 o superior. En un proyecto que no cumpla la segunda matriz, todo lo demás sigue funcionando.

¿Se puede usar en producción?

Está pensado para local y entornos controlados, y trae restricciones para que sea así: el panel se limita por entorno y solo escanea el host de APP_URL. Si lo habilitas en staging, protege la ruta con middleware de autenticación.

¿Un escaneo sin violaciones significa que mi sitio es accesible?

No, y el propio paquete lo dice. axe-core cubre comprobaciones automáticas de alta confianza, que son una parte del WCAG. Sigue haciendo falta prueba con teclado, con lector de pantalla y revisión manual de formularios y componentes interactivos.

Fuentes

¿Tienes un proyecto en mente?

Trabajo con Laravel, WordPress, SEO técnico y servidores MCP. El primer paso es una llamada de descubrimiento, sin costo ni compromiso, donde me cuentas qué necesitas y te digo con honestidad si puedo ayudarte.

Hablemos