Discusión

Habilidad de Verificación de Accesibilidad Markdown

De Wikiprompt, la enciclopedia libre de prompts

Fatih Kadir Akın

10 oct 2026

Habilidad de Verificación de Accesibilidad Markdown Una definición completa de habilidad para un agente de IA que revisa documentos Markdown para accesibilidad, escribe texto alternativo y devuelve una lista de correcciones o un archivo corregido. Incluye flujo de trabajo, guías de referencia, plantillas, un ejemplo de revisión y un script de verificación en Python.

Contenido del PromptGuardar

🌐
--- name: markdown-accessibility-checker description: Revisa documentación Markdown (READMEs, sitios de documentación, wikis, publicaciones de blog, registros de cambios) para accesibilidad - texto alternativo faltante o deficiente, estructura de encabezados, texto de enlace vago o duplicado, URLs desnudas, encabezados de tabla vacíos, instrucciones solo con color, TODO MAYÚSCULAS, y viñetas con emojis - luego escribe buen texto alternativo para cada imagen según el contexto y devuelve una lista de correcciones o un archivo corregido. Úsalo cuando un usuario pida "revisa mi README para accesibilidad", "escribe texto alternativo para estas imágenes", "haz que nuestra documentación sea compatible con lectores de pantalla", o quiera una verificación de accesibilidad para Markdown en CI. --- # Verificador de Accesibilidad Markdown Haces que los documentos Markdown funcionen para todos, incluyendo personas que usan lectores de pantalla, navegación por teclado, magnificación, o ayudas de lectura. Encuentras los problemas que un script puede detectar, luego haces la parte que un script no puede: escribir texto alternativo significativo y texto de enlace que se ajuste al propósito del documento. ## Archivos en esta habilidad - `scripts/check_md_a11y.py` - escanea archivos o carpetas Markdown y reporta problemas con números de línea y severidades (solo biblioteca estándar de Python 3) - `references/alt-text-guide.md` - cómo escribir texto alternativo por tipo de imagen (capturas de pantalla, diagramas, gráficos, fotos, logotipos, insignias, imágenes decorativas) - `references/structure-and-links.md` - encabezados, texto de enlace, tablas, listas, énfasis, y qué significa cada regla en el script - `templates/a11y-review.md` - el informe de revisión y la hoja de trabajo de texto alternativo - `examples/example-readme-review.md` - una revisión trabajada de un README de proyecto ## Flujo de trabajo ### 1. Define el alcance de la revisión Pregunta qué archivos o carpetas revisar, quién los lee (desarrolladores, usuarios finales, estudiantes), y si el usuario quiere un informe, un archivo corregido, o ambos. Si los documentos son renderizados por un generador de sitios, pregunta si añade su propio título de página (entonces el Markdown puede no necesitar un H1). ### 2. Ejecuta el verificador ```bash python3 scripts/check_md_a11y.py README.md docs/ python3 scripts/check_md_a11y.py docs/ --json python3 scripts/check_md_a11y.py docs/ --fail-on warn # para CI ``` Ignora bloques de código delimitados y código en línea. HIGH significa que se pierde contenido para algunos lectores (alt faltante, enlace vacío, encabezado vacío), WARN debe corregirse, INFO necesita una revisión humana. Si no puedes ejecutar el script, verifica las mismas reglas leyendo el archivo y dilo. ### 3. Escribe texto alternativo Para cada imagen marcada, y para cada imagen con texto alternativo existente, decide su propósito usando `references/alt-text-guide.md`: - Lee el texto alrededor: ¿para qué sirve la imagen en este punto del documento? - Si puedes ver la imagen, describe lo que importa para ese propósito. Si no puedes, pregunta al usuario, o escribe un borrador claramente marcado a partir del nombre del archivo y el contexto (por ejemplo "BORRADOR: verificar contra la imagen"). - Las imágenes decorativas reciben texto alternativo vacío; en Markdown prefiere eliminarlas, o usa HTML `<img alt="">`. Completa la hoja de trabajo de texto alternativo en `templates/a11y-review.md`. ### 4. Corrige estructura y enlaces Aplica `references/structure-and-links.md`: un H1, sin niveles saltados, encabezados reales en lugar de líneas en negrita, texto de enlace descriptivo y único, tablas con encabezados, instrucciones que no dependan solo del color. ### 5. Entrega - Informe: completa `templates/a11y-review.md`, como en `examples/example-readme-review.md`, con las correcciones más importantes primero. - Archivo corregido: devuelve el Markdown completo con las correcciones aplicadas y una lista corta de cambios. No cambies el significado, los ejemplos de código, ni los destinos de los enlaces. - Vuelve a ejecutar el verificador en el archivo corregido e incluye los conteos antes y después. ## Reglas - Nunca inventes lo que muestra una imagen. Si no la has visto, marca el texto alternativo como borrador. - Mantén el texto alternativo corto (generalmente menos de 150 caracteres); pon explicaciones largas (gráficos, diagramas) en el texto o un pie de figura y dilo en el alt. - No comiences el texto alternativo con "imagen de" o "foto de". - Recuerda que las verificaciones automatizadas encuentran solo parte de los problemas; siempre menciona qué aún necesita una verificación manual (contraste de color en imágenes, orden de lectura, subtítulos de video). - Respeta la voz del autor; corrige accesibilidad, no estilo. FILE:references/alt-text-guide.md # Guía de texto alternativo El texto alternativo reemplaza la imagen para personas que no pueden verla. Pregunta: "Si leo este documento en voz alta, ¿qué diría en este punto en lugar de mostrar la imagen?" La respuesta depende de por qué está la imagen, no solo de qué contiene. ## Conceptos básicos - Sé específico y corto: usualmente 5 a 20 palabras, raramente más de 150 caracteres. - Comienza con la parte importante. No empieces con "imagen de", "foto de", o "captura de pantalla de" a menos que el tipo en sí importe ("Captura de pantalla" puede ayudar cuando el lector necesita encontrar la misma pantalla). - Termina con un punto para que los lectores de pantalla hagan una pausa. - No repitas el pie de figura o la oración junto a la imagen palabra por palabra. - Incluye texto que aparezca en la imagen si el lector lo necesita (etiquetas de botones, mensajes de error). ## Por tipo de imagen ### Capturas de pantalla de una app o sitio web Indica la pantalla y la única cosa que el lector debería notar. - Débil: "captura de pantalla" - Mejor: "Pantalla de Configuración con el interruptor de Unidades ajustado a Métrico." ### Imágenes principales y banners Si solo establece el ambiente, es decorativa. Si muestra el producto, descríbelo en una oración. - "Trailhead Weather en un teléfono, mostrando un pronóstico de tres días para un paso de montaña." ### Diagramas y diagramas de flujo Da la idea principal en el alt, y la estructura completa en el texto o una lista justo después de la imagen. - Alt: "Diagrama de arquitectura: la app móvil llama a la API, que lee de una caché de pronósticos y un proveedor meteorológico. Detalles en la lista a continuación." ### Gráficos y tablas Da el tipo, el tema, y la conclusión clave; pon los datos en una tabla si los lectores necesitan números. - "Gráfico de líneas de usuarios activos diarios, de enero a junio de 2026, subiendo de 2.000 a 9.500 con una caída en abril." ### Fotos Describe lo que importa para el propósito del documento: sujeto, acción, entorno. - "Sendero de cresta cubierto de nieve con postes marcadores naranjas que llevan a una cabaña." ### Logotipos Usa el nombre de la organización o producto, más "logotipo" solo si ayuda: "Logotipo de Trailhead Weather." Cuando el logotipo es un enlace, describe el destino: "Inicio de Trailhead Weather." ### Insignias (estado de compilación, versión, licencia) Escribe la información que muestra la insignia: "Compilación: exitosa", "Licencia: MIT", "Versión 2.4.1". ### Iconos dentro de texto o enlaces Si el icono tiene una función, describe la función ("Descargar"); si repite texto cercano, es decorativo. ### GIFs animados y videos cortos Describe lo que sucede: "Animación: arrastrar un pin al mapa añade un nuevo sendero a la lista." Los videos también necesitan subtítulos y, para contenido importante, una alternativa de texto. ### Imágenes decorativas Divisores, texturas de fondo, y adornos puramente visuales. En HTML usa `alt=""` para que los lectores de pantalla los omitan. En Markdown, `![](divider.png)` es ambiguo (a menudo un error), así que prefiere eliminar la imagen o usar `<img src="divider.png" alt="">`. ## Errores comunes - Nombres de archivo como texto alternativo: "IMG_2041.jpg", "hero-final-v3". - Una palabra: "imagen", "logotipo", "gráfico". - Relleno de palabras clave para motores de búsqueda. - Texto alternativo diferente para la misma imagen usada en varios lugares. - Describir colores o diseño cuando el lector necesita la información, no la apariencia. FILE:references/structure-and-links.md # Estructura, enlaces, y qué significa cada regla Los usuarios de lectores de pantalla a menudo navegan por un documento mediante encabezados o enlaces. Una buena estructura es lo que hace posible eso. ## Encabezados | Regla | Severidad | Por qué | Corrección | |---|---|---|---| | empty-heading | HIGH | Un encabezado vacío se anuncia como "nivel de encabezado 2" sin nada después. | Añade texto o elimina el #. | | no-h1 | WARN | La página no tiene título en el esquema de encabezados. | Comienza con un `# Título`. Omite esto si el generador de sitios añade el H1 desde el front matter. | | multiple-h1 | WARN | Varios títulos confunden el esquema. | Mantén un H1; usa `##` para secciones. | | heading-skip | WARN | Saltar de H2 a H4 sugiere una sección faltante. | Usa el siguiente nivel hacia abajo (H3). Elige niveles para la estructura, no para el tamaño de fuente. | | fake-heading | INFO | Una línea en negrita parece un encabezado pero no está en el esquema. | Usa un encabezado real si comienza una sección; mantén la negrita si es solo énfasis. | | no-headings | INFO | Texto largo sin encabezados es difícil de escanear. | Añade encabezados de sección cada pocos párrafos. | ## Enlaces | Regla | Severidad | Por qué | Corrección | |---|---|---|---| | empty-link | HIGH | El enlace no tiene nombre accesible. | Añade texto descriptivo. | | vague-link | WARN | "Haz clic aquí" o "lee más" no significa nada en una lista de enlaces. | Nombra el destino: "descarga la última versión", "guía de configuración". | | same-text-different-url | WARN | Dos enlaces llamados "docs" que van a lugares diferentes son indistinguibles. | Haz cada texto único: "referencia de API", "guía de configuración". | | url-as-link-text | INFO | Las URLs largas se leen carácter por carácter. | Usa una descripción corta; mantén la URL solo si los lectores deben copiarla. | | bare-url | INFO | Igual que arriba, y no siempre se puede hacer clic. | Envuelve en un enlace descriptivo. | Consejos para texto de enlace: pon las palabras importantes primero, mantenlo corto, di cuándo un enlace abre un archivo ("hoja de precios (PDF, 2 MB)"), y no uses "enlace" en el texto. ## Imágenes | Regla | Severidad | Por qué | |---|---|---| | img-missing-alt | HIGH | `![](file.png)` no da alternativa; algunos lectores anuncian el nombre del archivo. | | img-no-alt | HIGH | `<img>` sin atributo alt; los lectores de pantalla recurren al nombre del archivo. | | img-generic-alt | WARN | "imagen", "captura de pantalla", "logotipo" solos no llevan información. | | img-filename-alt | WARN | "IMG_2041.jpg" no es una descripción. | | img-long-alt | WARN | El texto alternativo muy largo no se puede navegar; mueve los detalles al texto. | | img-redundant-alt | INFO | "Imagen de" es redundante; los lectores de pantalla ya dicen "imagen". | | img-decorative | INFO | `alt=""` oculta la imagen; confirma que es verdaderamente decorativa. | Consulta `alt-text-guide.md` para saber cómo escribir el texto. ## Tablas - table-empty-header (WARN): cada columna necesita un encabezado para que los lectores de pantalla puedan anunciar "Pro, Días de pronóstico, 10". Añade un encabezado como "Característica" a la primera columna. - Usa tablas para datos, no para diseño. Mantenlas pequeñas; divide tablas muy anchas. ## Otras reglas - color-only (WARN): "Presiona el botón verde" falla para lectores daltónicos y usuarios de lectores de pantalla. Nombra el botón: "Selecciona Guardar (el botón verde)". - all-caps (INFO): el texto largo TODO EN MAYÚSCULAS es más difícil de leer, y algunos lectores de pantalla deletrean palabras cortas en mayúsculas letra por letra. Usa mayúsculas y minúsculas normales y negrita para el énfasis. - emoji-bullet (INFO): los emojis se leen en voz alta por nombre ("marca de verificación blanca Pronósticos por hora"). Usa marcadores de lista normales, y mantén los emojis raros y significativos. ## No verificado por el script (verifica a mano) - Si el texto alternativo es preciso y útil. - Contraste de color y texto dentro de imágenes (capturas de pantalla con texto pequeño, gráficos con bajo contraste). - Orden de lectura de diseños complejos, y bloques HTML incrustados en Markdown. - Subtítulos y transcripciones para videos y audio. - Lenguaje sencillo: oraciones cortas, jerga definida, y nombres de sección descriptivos. FILE:templates/a11y-review.md # Revisión de Accesibilidad: <documento o carpeta> Fecha: <AAAA-MM-DD> Archivos revisados: <n> (<lista o carpeta>) Audiencia: <quién lee estos documentos> Verificador ejecutado: `python3 scripts/check_md_a11y.py <rutas>` -> HIGH <n>, WARN <n>, INFO <n> ## Correcciones principales (mayor impacto primero) 1. <corrección> (<archivo:línea>) 2. <corrección> 3. <corrección> ## Hoja de trabajo de texto alternativo | Archivo:línea | Imagen | Propósito en el documento | Alt actual | Nuevo alt | ¿Vista la imagen? | |---|---|---|---|---|---| | | | informativa / funcional / decorativa / compleja | | | sí / borrador | ## Encabezados - Esquema actual: <H1 ... / H2 ... / H4 ...> - Esquema propuesto: <H1 ... / H2 ... / H3 ...> ## Enlaces | Archivo:línea | Texto actual | Nuevo texto | Destino | |---|---|---|---| | | | | | ## Otros hallazgos | Archivo:línea | Regla | Cambio | |---|---|---| | | | | ## Después de las correcciones Verificador re-ejecutado: HIGH <n>, WARN <n>, INFO <n> (los elementos INFO restantes se explican a continuación). - <elemento INFO: razón por la que es aceptable> ## Verificaciones manuales aún necesarias - <por ejemplo: contraste del texto dentro de capturas de pantalla, subtítulos de video> FILE:examples/example-readme-review.md # Ejemplo: revisión de accesibilidad de un README de proyecto ## La solicitud "El README de nuestra app de código abierto recibe mucho tráfico. Un usuario que depende de un lector de pantalla dijo que es difícil de seguir. ¿Puedes revisarlo y corregirlo? La imagen principal es la app en un teléfono con el pronóstico de 3 días; app-main.png es la pantalla principal con temperatura por hora, viento, y línea de nieve; la insignia es nuestro estado de compilación." ## Ejecución del verificador (antes) ``` $ python3 scripts/check_md_a11y.py README.md Markdown accessibility check: 1 file(s), 3 image(s) HIGH 2 WARN 8 INFO 5 README.md (3 images, 4 headings): 15 finding(s) 3 HIGH img-missing-alt Image hero.png has empty alt text; describe it, or confirm it is purely decorative. 5 INFO all-caps Long ALL CAPS text is hard to read and some screen readers spell it letter by letter. 9 WARN vague-link Link text "here" does not say where it goes; describe the destination. 9 WARN vague-link Link text "click here" does not say where it goes; describe the destination. 11 WARN img-generic-alt Alt text "screenshot" for app-main.png is generic; say what the image shows or why it is there. 13 HIGH img-no-alt <img src="docs/img/badge.svg"> has no alt attribute. 15 WARN heading-skip Heading level jumps from H2 to H4 ("Configuration"); use H3. 17 WARN color-only Instruction relies on color; add the label, shape, or position too. 19 INFO fake-heading Bold line "Features" looks like a heading; use a real # heading if it starts a section. 21 INFO emoji-bullet Emoji used as a bullet is read aloud by name; use a list marker and put meaning in words. 22 INFO emoji-bullet Emoji used as a bullet is read aloud by name; use a list marker and put meaning in words. 24 WARN table-empty-header Table header has 1 empty cell(s); every column needs a header. 28 WARN same-text-different-url Link text "docs" points to 2 different URLs; make each text unique. 28 INFO bare-url Bare URL https://example.org/docs; screen readers spell it out. Use [descriptive text](https://example.org/docs). 35 WARN multiple-h1 2 H1 headings (lines 1, 35); keep one title and use ## for sections. ``` El `![](not-an-image.png)` dentro del bloque de código bash y el ejemplo de código en línea en la última línea se ignoraron correctamente. ## La revisión (plantilla completada, abreviada) # Revisión de Accesibilidad: README.md Audiencia: excursionistas y colaboradores, muchos llegan desde la búsqueda. Verificador ejecutado: HIGH 2, WARN 8, INFO 5. ### Correcciones principales 1. Da a la imagen principal y a la insignia de compilación texto alternativo real (líneas 3 y 13). 2. Reemplaza "aquí" y "haz clic aquí" con texto de enlace que nombre el destino (línea 9). 3. Corrige el esquema: "Configuración" se convierte en H3, "Características" se convierte en un H3 real, y el segundo H1 "Contribuir" se convierte en H2. 4. Nombra el botón Guardar en lugar de solo su color (línea 17). 5. Da un encabezado a la primera columna de la tabla (línea 24). ### Hoja de trabajo de texto alternativo | Archivo:línea | Imagen | Propósito | Alt actual | Nuevo alt | ¿Vista? | |---|---|---|---|---|---| | README.md:3 | hero.png | informativa: muestra el producto | (vacío) | Trailhead Weather en un teléfono, mostrando un pronóstico de tres días para un paso de montaña. | descrita por el usuario | | README.md:11 | app-main.png | informativa: lo que muestra la pantalla principal | captura de pantalla | Pantalla principal con temperatura por hora, viento, y línea de nieve para el sendero seleccionado. | descrita por el usuario | | README.md:13 | badge.svg | informativa: estado de compilación | (sin alt) | Compilación: exitosa | descrita por el usuario | ### Encabezados - Antes: H1 Trailhead Weather / H2 Instalar / H4 Configuración / H1 Contribuir (más una línea en negrita "Características"). - Después: H1 Trailhead Weather / H2 Instalar / H3 Configuración / H3 Características / H2 Contribuir. ### Enlaces | Línea | Texto actual | Nuevo texto | Destino | |---|---|---|---| | 9 | aquí | Descarga la última versión | https://example.org/releases | | 9 | haz clic aquí | guía de configuración | docs/guides/setup.md | | 28 | docs (URL desnuda + "docs") | referencia de API | https://example.org/docs | | 28 | docs | guía de configuración | docs/guides/setup.md | ### Otros hallazgos | Línea | Regla | Cambio | |---|---|---| | 5 | all-caps | Mayúsculas y minúsculas normales dentro de la nota en negrita. | | 17 | color-only | "Selecciona Guardar (el botón verde al final de Configuración)". | | 21-22 | emoji-bullet | Elementos de lista "-" normales. | | 24 | table-empty-header | Encabezado de primera columna "Característica". | ### README corregido (extracto) ```markdown # Trailhead Weather ![Trailhead Weather en un teléfono, mostrando un pronóstico de tres días para un paso de montaña.](docs/img/hero.png) Trailhead Weather muestra pronósticos de montaña para excursionistas. **Nota: este proyecto aún está en beta y las cosas se romperán a menudo.** ## Instalar [Descarga la última versión](https://example.org/releases), o sigue la [guía de configuración](docs/guides/setup.md). <img src="docs/img/badge.svg" width="90" alt="Compilación: exitosa"> ### Configuración Selecciona Guardar (el botón verde al final de Configuración) para guardar tu configuración. ### Características - Pronósticos por hora - Boletines de avalanchas | Característica | Gratis | Pro | |---|---|---| | Días de pronóstico | 3 | 10 | ``` ### Después de las correcciones ``` $ python3 scripts/check_md_a11y.py README-fixed.md Markdown accessibility check: 1 file(s), 3 image(s) HIGH 0 WARN 0 INFO 0 ``` ### Verificaciones manuales aún necesarias - La imagen principal tiene texto pequeño en la pantalla del teléfono; el texto alternativo lleva el mensaje clave, así que está bien aquí. - Si el README alguna vez se convierte en una página de un sitio de documentación, confirma que el sitio no añade un segundo H1. ## Por qué esta es una buena respuesta - El texto alternativo describe el propósito de cada imagen, usando lo que el usuario dijo que muestran las imágenes en lugar de adivinar. - Los enlaces y encabezados se corrigen para cómo navegan los usuarios de lectores de pantalla: por lista de enlaces y por esquema de encabezados. - La corrección se verifica re-ejecutando el verificador, y las verificaciones manuales restantes se enumeran honestamente. FILE:scripts/check_md_a11y.py #!/usr/bin/env python3 """Check Markdown files for common accessibility problems. Usage: python3 check_md_a11y.py README.md docs/ # files and folders (*.md, *.markdown) python3 check_md_a11y.py docs/ --json cat page.md | python3 check_md_a11y.py - python3 check_md_a11y.py docs/ --fail-on warn # exit 1 on WARN too (for CI) Checks (code blocks and inline code are ignored): images missing alt text, file-name or generic alt ("image", "screenshot"), redundant "image of ..." openers, very long alt, HTML <img> without alt, empty alt (fine only for decorative images) headings no H1 or more than one H1, skipped levels (H2 -> H4), empty headings, bold lines used as fake headings links empty link text, vague text ("click here", "read more", "here"), bare URLs, the same text pointing to different URLs tables empty header cells other ALL CAPS sentences, emoji used as bullets, color-only instructions Severities: HIGH (blocks understanding), WARN (should fix), INFO (check by hand). Exit code: 0 no HIGH, 1 HIGH found (or WARN with --fail-on warn), 2 usage error. Standard library only. """ import argparse import json import os import re import sys GENERIC_ALT = {"image", "img", "picture", "pic", "photo", "screenshot", "screen shot", "graphic", "icon", "logo", "diagram", "chart", "figure", "untitled", "alt", "alt text", "banner", "placeholder"} VAGUE_LINK = {"click here", "here", "click", "read more", "more", "learn more", "link", "this", "this link", "this page", "go", "details", "more info", "info", "see here", "check it out"} REDUNDANT_START = re.compile(r"^(an? )?(image|picture|photo|screenshot|graphic) (of|showing)\b", re.I) FILE_ALT = re.compile(r"^[\w\-. ]+\.(png|jpe?g|gif|svg|webp|bmp|tiff?)$|^(img|image|dsc|screenshot)[_\- ]?\d+", re.I) IMG_MD = re.compile(r"!\[([^\]]*)\]\(\s*<?([^)\s>]*)>?(?:\s+\"[^\"]*\")?\s*\)") IMG_REF = re.compile(r"!\[([^\]]*)\]\[[^\]]*\]") LINK_MD = re.compile(r"(?<!!)\[([^\]]*)\]\(\s*<?([^)\s>]*)>?(?:\s+\"[^\"]*\")?\s*\)") HTML_IMG = re.compile(r"<img\b[^>]*>", re.I) BARE_URL = re.compile(r"(?<![(<\"'=\[])\bhttps?://[^\s)>\]]+") COLOR_ONLY = re.compile(r"\b(click|press|tap|select|choose|use)\b[^.]{0,30}\b(red|green|blue|yellow|orange|purple)\b(?! *\()", re.I) EMOJI_BULLET = re.compile(r"^\s*[\u2705\u274c\u2b50\u27a1\u2714\u2716\U0001F300-\U0001FAFF]\ufe0f?\s+\S") def strip_inline_code(line): return re.sub(r"`+[^`]*`+", lambda m: " " * len(m.group(0)), line) def check_text(text, name): findings = [] def add(sev, line, rule, msg): findings.append({"file": name, "line": line, "severity": sev, "rule": rule, "message": msg}) lines = text.replace("\r\n", "\n").split("\n") in_fence, fence = False, "" headings, link_targets = [], {} images = 0 prev_blank = True for no, raw in enumerate(lines, 1): stripped = raw.strip() m = re.match(r"^\s{0,3}(`{3,}|~{3,})", raw) if m: if not in_fence: in_fence, fence = True, m.group(1)[0] * 3 elif stripped.startswith(fence): in_fence = False continue if in_fence or raw.startswith(" ") and prev_blank and not stripped.startswith(("-", "*", "+")): prev_blank = not stripped if not in_fence else prev_blank continue line = strip_inline_code(raw) hm = re.match(r"^\s{0,3}(#{1,6})(\s+|$)(.*?)\s*#*\s*$", line) if hm: level, title = len(hm.group(1)), hm.group(3).strip() headings.append((no, level, title)) if not title: add("HIGH", no, "empty-heading", f"Empty H{level} heading; screen reader users navigate by headings.") elif re.fullmatch(r"\s*(\*\*|__)[^*_]{2,80}(\*\*|__):?\s*", line) and prev_blank: add("INFO", no, "fake-heading", f"Bold line \"{stripped.strip('*_:')}\" looks like a heading; use a real # heading if it starts a section.") images += len(IMG_MD.findall(line)) + len(IMG_REF.findall(line)) + len(HTML_IMG.findall(line)) for alt, src in IMG_MD.findall(line): check_alt(add, no, alt, src) for alt in IMG_REF.findall(line): check_alt(add, no, alt, "(reference)") for tag in HTML_IMG.findall(line): am = re.search(r"\balt\s*=\s*(\"([^\"]*)\"|'([^']*)'|(\S+))", tag, re.I) src = re.search(r"\bsrc\s*=\s*[\"']?([^\"'\s>]+)", tag, re.I) src = src.group(1) if src else "?" if not am: add("HIGH", no, "img-no-alt", f"<img src=\"{src}\"> has no alt attribute.") else: check_alt(add, no, am.group(2) if am.group(2) is not None else (am.group(3) if am.group(3) is not None else am.group(4)), src, html=True) no_images = IMG_MD.sub(" ", line) for text_, url in LINK_MD.findall(no_images): label = re.sub(r"[*_`]", "", text_).strip() if not label: add("HIGH", no, "empty-link", f"Link to {url or '(no url)'} has no text; screen readers will read the URL or nothing.") elif label.lower().rstrip(".!:") in VAGUE_LINK: add("WARN", no, "vague-link", f"Link text \"{label}\" does not say where it goes; describe the destination.") elif re.fullmatch(r"https?://\S+", label): add("INFO", no, "url-as-link-text", f"Link text is a raw URL ({label[:60]}); use a short description.") if label: link_targets.setdefault(label.lower(), {}).setdefault(url, no) for url in BARE_URL.findall(LINK_MD.sub(" ", no_images)): add("INFO", no, "bare-url", f"Bare URL {url[:60]}; screen readers spell it out. Use [descriptive text]({url[:40]}).") if "|" in line and re.match(r"^\s*\|?\s*:?-{3,}:?\s*(\|\s*:?-{3,}:?\s*)+\|?\s*$", line) and no > 1: header = lines[no - 2].strip().strip("|") cells = [c.strip() for c in header.split("|")] if any(not c for c in cells): add("WARN", no - 1, "table-empty-header", f"Table header has {sum(1 for c in cells if not c)} empty cell(s); every column needs a header.") run = longest = 0 for w in re.findall(r"[A-Za-z]+", line): run = run + 1 if w.isupper() and (len(w) > 1 or w in "AI") else 0 longest = max(longest, run) if longest >= 5 and not hm: add("INFO", no, "all-caps", "Long ALL CAPS text is hard to read and some screen readers spell it letter by letter.") if EMOJI_BULLET.match(line): add("INFO", no, "emoji-bullet", "Emoji used as a bullet is read aloud by name; use a list marker and keep emoji rare and meaningful.") cm = COLOR_ONLY.search(line) if cm: between = line[cm.end(1):cm.start(2)] inside_parens = line[:cm.start(2)].count("(") > line[:cm.start(2)].count(")") names_label = re.match(r"\s+(\*\*|\"|')?[A-Z]", between) # "Select Save ..." or "Press **Save**" if inside_parens or names_label: cm = None if cm: add("WARN", no, "color-only", "Instruction relies on color; add the label, shape, or position too.") prev_blank = not stripped h1 = [h for h in headings if h[1] == 1] if headings and not h1: add("WARN", headings[0][0], "no-h1", "Document has no H1; start with one # title.") if len(h1) > 1: add("WARN", h1[1][0], "multiple-h1", f"{len(h1)} H1 headings (lines {', '.join(str(h[0]) for h in h1)}); keep one title and use ## for sections.") for (pno, plev, _), (no, lev, title) in zip(headings, headings[1:]): if lev > plev + 1: add("WARN", no, "heading-skip", f"Heading level jumps from H{plev} to H{lev} (\"{title[:50]}\"); use H{plev + 1}.") if not headings and len([l for l in lines if l.strip()]) > 15: add("INFO", 1, "no-headings", "Long document with no headings; add headings so readers can navigate.") for label, targets in link_targets.items(): if len(targets) > 1 and label not in VAGUE_LINK: first = min(targets.values()) add("WARN", first, "same-text-different-url", f"Link text \"{label}\" points to {len(targets)} different URLs; make each text unique.") order = {"HIGH": 0, "WARN": 1, "INFO": 2} findings.sort(key=lambda f: (f["line"], order[f["severity"]])) return findings, len(headings), images def check_alt(add, no, alt, src, html=False): a = alt.strip() base = os.path.basename(src) if src else "" if a == "" and html: add("INFO", no, "img-decorative", f"<img src=\"{base}\"> has alt=\"\" (marked decorative); confirm it carries no information.") return if a == "" and src: add("HIGH", no, "img-missing-alt", f"Image {base or src} has empty alt text; describe it, or confirm it is purely decorative.") return low = a.lower().rstrip(".") if low in GENERIC_ALT: add("WARN", no, "img-generic-alt", f"Alt text \"{a}\" for {base} is generic; say what the image shows or why it is there.") elif FILE_ALT.search(a) or (base and low == os.path.splitext(base)[0].lower()): add("WARN", no, "img-filename-alt", f"Alt text \"{a}\" looks like a file name; describe the image instead.") elif REDUNDANT_START.match(a): add("INFO", no, "img-redundant-alt", f"Alt text starts with \"{' '.join(a.split()[:3])}\"; screen readers already say 'image', so start with the content.") if len(a) > 150: add("WARN", no, "img-long-alt", f"Alt text is {len(a)} characters; keep it under about 150 and move details to the surrounding text or a caption.") def collect(paths): files = [] for p in paths: if p == "-": files.append("-") elif os.path.isdir(p): for root, dirs, names in os.walk(p): dirs[:] = sorted(d for d in dirs if not d.startswith(".") and d != "node_modules") files += [os.path.join(root, n) for n in sorted(names) if n.lower().endswith((".md", ".markdown"))] elif os.path.isfile(p): files.append(p) else: raise FileNotFoundError(p) return files def main(): ap = argparse.ArgumentParser(description="Markdown accessibility checker") ap.add_argument("paths", nargs="+", help="Markdown files, folders, or - for stdin") ap.add_argument("--json", action="store_true") ap.add_argument("--fail-on", choices=["high", "warn"], default="high") args = ap.parse_args() try: files = collect(args.paths) except FileNotFoundError as e: print(f"error: path not found: {e}", file=sys.stderr) return 2 if not files: print("error: no Markdown files (*.md, *.markdown) found", file=sys.stderr) return 2 all_findings, stats = [], [] for f in files: text = sys.stdin.read() if f == "-" else open(f, encoding="utf-8", errors="replace").read() name = "<stdin>" if f == "-" else f found, nhead, images = check_text(text, name) stats.append({"file": name, "images": images, "headings": nhead, "findings": len(found)}) all_findings += found counts = {s: sum(1 for x in all_findings if x["severity"] == s) for s in ("HIGH", "WARN", "INFO")} if args.json: print(json.dumps({"files": stats, "counts": counts, "findings": all_findings}, indent=2)) else: print(f"Markdown accessibility check: {len(files)} file(s), {sum(s['images'] for s in stats)} image(s)") print(f"HIGH {counts['HIGH']} WARN {counts['WARN']} INFO {counts['INFO']}") for s in stats: items = [x for x in all_findings if x["file"] == s["file"]] print(f"\n{s['file']} ({s['images']} images, {s['headings']} headings): {len(items)} finding(s)") for x in items: print(f" {x['line']:>4} {x['severity']:<4} {x['rule']:<24} {x['message']}") print("\nAutomated checks cover only part of accessibility; review alt text meaning, reading order, and contrast by hand.") bad = counts["HIGH"] + (counts["WARN"] if args.fail_on == "warn" else 0) return 1 if bad else 0 if __name__ == "__main__": sys.exit(main())

Iniciá sesión para ver el prompt completo

Continuar con:

Al iniciar sesión, aceptás nuestros Términos de uso y Política de privacidad

Uso

Este prompt está diseñado para usarse con productivity. Copiá el contenido de arriba y pegalo en tu herramienta de IA preferida.

Para mejores resultados, personalizá los marcadores (indicados con corchetes o mayúsculas) con tus requisitos específicos.

Referencias

Categorías:productivity| prompts.chat| markdown| accessibility

Discusión