Limitaciones conocidas
Estas limitaciones se exponen con claridad desde el principio para que no tengas que descubrirlas por tu cuenta. Cada elemento aquí es una decisión deliberada y razonada, no un descuido, pero "deliberada" no significa "sin importancia antes de confiar en esta herramienta".
Estructurales: esta arquitectura no puede hacer esto en absoluto
surea11y es un análisis estático del DOM: lee el árbol DOM y los estilos calculados en un instante determinado, sin capacidad de simular la interacción del usuario, esperar cambios de estado asíncronos ni medir el diseño real en tamaños de viewport arbitrarios. No se trata de reglas faltantes: ninguna implementación de regla, por ingeniosa que sea, puede resolver esto sin una arquitectura fundamentalmente distinta (automatización de navegador real que genere eventos de teclado/puntero reales a lo largo del tiempo):
- Detección de trampas de teclado (WCAG 2.1.2): requiere mover el foco por la página y observar dónde termina, algo que ninguna lectura única del DOM puede hacer. Se ha definido el alcance de una técnica que funcionaría (consulta la sección sobre trampas de teclado de
ACT_RULE_MAPPING.md), pero sería la primera comprobación de este motor que modifica la página que está inspeccionando, así que no está implementada y, aunque lo estuviera, no formaría parte de un análisis normal. Mientras tanto, nada en el marcado estático la sustituye. - Reflujo / recorte al aplicar zoom (WCAG 1.4.10): requiere una medición real del diseño (
clientWidth/scrollWidth) en un viewport simulado equivalente a 320px. A diferencia de algunas heurísticas basadas en declaraciones CSS presentes en otras partes de este motor, no existe ningún sustituto en marcado estático para determinar si "el contenido se recorta al 400% de zoom". - Estado dinámico o posterior a la interacción: todo lo que solo existe después de un clic, un hover o una carga de datos asíncrona (el contenido de un modal, las opciones de un desplegable, los mensajes de error de validación de un formulario) es invisible para un análisis del DOM actual de la página. Si tu framework lo renderiza de forma anticipada (incluso fuera de pantalla u
hidden), es analizable; si solo existe tras la interacción, no lo es, a menos que produzcas esa interacción tú mismo antes de analizar (por ejemplo, haz clic en el botón y luego analiza).
Dependientes del entorno: depende de cómo lo ejecutes
- jsdom (Node, sin navegador real) no tiene motor de diseño CSS. Las reglas que necesitan geometría real (sobre todo
target-size-minimum, WCAG 2.5.8, necesita ungetBoundingClientRect()real) informannotApplicablebajo jsdom puro en lugar de adivinar. Ejecuta bajo un navegador real (Puppeteer/Playwright, consultaINTEGRATION.mdPatrón 2) para obtener resultados reales de estas reglas. - Saber si el texto hace salto de línea requiere diseño, así que los hallazgos de text-spacing en texto que no hace salto se informan para revisión. WCAG 1.4.12 y las reglas ACT que lo sustentan se aplican solo a texto que contiene un salto de línea suave, algo que un análisis estático no puede determinar.
avoid-inline-spacingtrata el texto como si hiciera salto de línea por defecto, así que un valor forzado ordinario por debajo de la métrica sigue fallando; cuando el salto de línea no es posible por una razón que sí es visible sin diseño (texto al que no se le permite hacer salto o un elemento de ancho fijo dentro de un ancestro con desplazamiento horizontal), informacantTellen su lugar. El texto que nunca hace salto de línea por otro motivo sigue informándose como fallo. <dialog>y otros elementos ocultos por la hoja de estilos predeterminada del agente de usuario (sin atributoopen,display: nonesegún la especificación), junto con cualquier otro subárbol oculto mediantedisplay:none,visibility:hidden,[hidden]o un<details>cerrado, quedan excluidos de la evaluación de reglas por defecto: coincidiendo con el comportamiento sensible a la visibilidad de otros motores consolidados. Este es un valor predeterminado deliberado (engineOptions.includeHiddenElements: false), no un descuido: el contenido oculto no es accesible mediante tecnología de asistencia ni teclado hasta que se muestra, así que marcar por defecto un defecto de marcado dentro de él a menudo sería ruido. DefineengineOptions.includeHiddenElements: truepara evaluar igualmente subárboles ocultos o colapsados: por ejemplo, para detectar un defecto de marcado (como una referencia de ID ARIA rota) antes de que un diálogo llegue a abrirse. ConsultaENGINE_OPTIONS.mdpara conocer la opción y exactamente qué mecanismos de ocultación cubre.- El
text-shadowcalculado por jsdom no es fiable en una segunda lectura del mismo elemento. Confirmado en jsdom 29.1.1: leer un valor calculado detext-shadowpor segunda vez en el mismo elemento (mediante cualquier propiedad de acceso, desde cualquierCSSStyleDeclarationrecién solicitado para ese elemento, sin importar el caché) devuelve de forma silenciosa un valor distinto e incorrecto de "sin sombra" en lugar del valor realmente declarado. La primera lectura siempre es correcta. Este motor lo evita internamente leyendo eltext-shadowde cada elemento exactamente una vez por ejecución y guardando el resultado en caché (consulta__textShadowInfoElensrc/core/contrast-helpers.js), así que un único análisis no se ve afectado. Solo aparece si tú mismo leesgetComputedStyle(el).textShadowmás de una vez sobre el mismo elemento analizado por jsdom. Un navegador real no tiene este error. - Bajo jsdom, el tiempo de análisis crece con el cuadrado de la profundidad del DOM, no con el número de elementos. jsdom resuelve el CSS heredado recorriendo la cadena de ancestros de un elemento en cada llamada a
getComputedStyle, así que una llamada por elemento cuesta O(número de elementos × profundidad). Medido en jsdom 29.1.1 sin código del motor involucrado: 4000 elementos en una sola cadena tardan 8,3s solo engetComputedStyle, frente a 0,25s para esos mismos 4000 como hermanos. Los propios recorridos de ancestros del motor están acotados y se mantienen lineales, así que este es un coste de jsdom y no de las reglas. Afecta arunDomRulesInPagey a todo lo construido sobre él, incluido@surea11y/test-matchers; un navegador real calcula el estilo heredado de forma nativa y no tiene esta forma de coste. Los frameworks de componentes anidan habitualmente entre 100 y 300 niveles, lo cual sigue siendo cómodamente rápido; se vuelve perceptible a partir de aproximadamente 1000. - Marcado estático frente a estado del DOM en vivo o posterior a la hidratación. La lógica de las reglas en sí es agnóstica respecto al origen del DOM: evalúa cualquier DOM que reciba, ya sea HTML estático analizado por jsdom (Patrón 1) o una pestaña de navegador real ya cargada e hidratada (Patrón 2, consulta
INTEGRATION.md). Pero la CLI (npx @surea11y/cli scan <url>) obtiene específicamente solo HTML estático, sin ejecución de JS. Consulta la documentación de la CLI. Para un widget hidratado por un framework de JS cuyo marcado renderizado en el servidor entrega intencionadamente un estado antes de que el JS del cliente lo sincronice (por ejemplo,<input type="checkbox" aria-checked="true">entregado antes de que el JS del cliente establezca la propiedad nativacheckedpara que coincida durante la hidratación, un patrón extremadamente común y totalmente legítimo), un análisis de la CLI solo ve el marcado previo a la hidratación. Un análisis que se ejecuta dentro de una pestaña de navegador real ya cargada ve en cambio el estado posterior a la hidratación, así que ambos pueden discrepar exactamente en esta clase de elemento por razones que no tienen nada que ver con la corrección de la regla. Por esoaria-checked-state-mismatchestá limitado amanual/cantTellen lugar de unfailestricto. Si necesitas precisión de DOM en vivo para comprobaciones sensibles a la hidratación, ejecuta la librería directamente contra una página ya cargada mediante el Patrón 2, no la CLI de obtención estática.
Comprobaciones no implementadas: decisiones que no pueden automatizarse de forma segura
Estos no cuentan con una heurística comparablemente segura para el nivel de exigencia de este motor (fail debe seguir reservado exclusivamente para violaciones deterministas). Construirlas de todos modos capturaría casi nada (demasiado estrictas para ser útiles) o arriesgaría falsos positivos reales (demasiado amplias para confiar en ellas):
- "¿Es significativo este texto de encabezado o etiqueta?": los encabezados y etiquetas reales son enormemente variados y legítimamente cortos ("FAQ", "Nombre", "Resumen" son todos válidos), así que nada permite decidir a partir del marcado si un encabezado describe la sección que tiene debajo o si una etiqueta describe el campo que tiene al lado. Lo que sí se puede decidir es que algunas cadenas no pueden describir nada:
heading-qualityyform-control-label-qualitymarcan marcadores de posición olvidados, campos de plantilla numerados, nombres de archivo y URLs contra listas curadas de coincidencia exacta, el mismo compromiso de precisión sobre exhaustividad que hacelink-name-quality. Ambas son reglasmanuallimitadas acantTell: señalan un candidato para revisión, nunca afirman que el texto sea incorrecto. - "¿Describe este mensaje de error el problema?": qué dispara un error de validación y su contenido casi siempre dependen de JS o de una librería de validación, invisibles para un análisis estático de entrada; no es solo un problema de diseño de heurísticas.
- Subcomprobaciones detalladas de medios basados en tiempo (WCAG 1.2.x tiene unos 8 casos distintos a nivel de regla ACT más allá de lo que está implementado): el propio contenido de audio/vídeo es fundamentalmente imposible de verificar a partir de marcado estático; los dos casos más amplios y seguros están cubiertos (
media-alternative-transcript-evidence,video-caption), los más específicos no lo están, de forma deliberada. - Análisis del contenido de imágenes de texto (WCAG 1.4.5/1.4.9): requeriría una comprensión de imágenes equivalente a OCR; fuera del alcance de un motor de marcado estático.
- Controles activados por movimiento (WCAG 2.5.4): caso de nicho, baja incidencia real; no priorizado, no estructuralmente imposible.
- Los dos casos límite de ACT en
img-alt-decorative: un<img>cuyo estado actual de solicitud de red no está "completamente disponible" (aún cargando o roto), y un<canvas>totalmente transparente (sin nada realmente dibujado en él), están ambos exentos de la propia aplicabilidad de ACT. Ninguno de los dos se puede decidir a partir de un análisis estático del DOM (sin decodificación de imágenes, sin lectura de píxeles del canvas), así que ambos se dejan dentro del alcance en lugar de eximirse: un falso positivo poco frecuente que un revisor humano descarta de un vistazo, más seguro que subinformar en silencio un elemento realmente excluido o no decorativo.
Qué significa esto en la práctica
Nada de lo anterior es exclusivo de surea11y: toda herramienta de accesibilidad basada en análisis estático (incluidos otros motores consolidados) comparte las limitaciones estructurales, y la mayoría comparte también las de criterio. La razón para exponerlo explícitamente aquí: un pass de este motor (o de cualquier herramienta automatizada) nunca sustituye la revisión manual que el propio WCAG exige para los criterios anteriores. Consulta WCAG_CONFORMANCE.md para saber exactamente qué afirma y qué no afirma un pass/pass compuesto.