Cómo crear plantillas Django compatibles con las WCAG

El proyecto WebAIM Million analiza cada año las páginas de inicio de los un millón de sitios más populares. En 2021, la página promedio tenía 51,4 errores de accesibilidad. Los desarrolladores de Django tienen ventajas estructurales porque las plantillas están centralizadas y el framework fomenta una separación limpia, pero Django no escribe código accesible por ti.

Las plantillas accesibles de Django comienzan con HTML semántico. Usa <nav> para la navegación, <main> para el contenido principal, <header> y <footer> para las regiones de la página. Los lectores de pantalla dependen de esta estructura. Los formularios necesitan etiquetas adecuadas, no marcadores de posición. La navegación por teclado debe funcionar sin necesidad de ratón.

La comunidad de Django está trabajando en la accesibilidad. El proyecto django-admin-keyshortcuts añadió atajos de teclado al administrador en 2025. Hay un tique abierto y en discusión sobre las opciones vacías en los menús desplegables de selección. El sitio de documentación ahora utiliza etiquetas ARIA para distinguir las regiones de navegación.

Las pruebas requieren herramientas como WAVE, Axe y lectores de pantalla. Las pruebas automatizadas en integración continua con Curlylint o djhtml detectan problemas básicos. Las pruebas manuales con NVDA o VoiceOver detectan el resto.

Cómo crear plantillas Django compatibles con las WCAG

Cómo crear plantillas Django conformes con WCAG

El proyecto WebAIM Million analiza cada año las páginas de inicio de los un millón de sitios más populares. En 2021, encontraron un promedio de 51,4 errores de accesibilidad por página. Eso es por página de inicio, no por sitio.

Los desarrolladores de Django tienen una ventaja. El framework fomenta la estructura. Las plantillas viven en un solo lugar. Los formularios manejan la validación. La interfaz de administración se genera a partir de los modelos. Esa estructura facilita la implementación de la accesibilidad y hace que sea más difícil romperla.

Pero Django no escribe plantillas accesibles por ti. Tienes que hacerlo tú.

Empieza con HTML semántico, no con divs

Los lectores de pantalla dependen de un HTML que les indique lo que significan las cosas. Un <button> es un botón. Un elemento <nav> contiene la navegación. Los encabezados desde <h1> hasta <h6> crean un esquema de tu contenido.

Las guías de HTML de Wagtail, que se aplican a cualquier proyecto Django (utilice Wagtail o no), establecen dos principios básicos: escribir HTML válido y escribir HTML semántico. Apuntan al doctype HTML5.

Esto es lo que significa el HTML semántico en la práctica. En lugar de esto:

html

<div class="header">  <div class="logo">...</div>  <div class="nav">    <div class="nav-item">Inicio</div>    <div class="nav-item">Acerca de</div>  </div> </div>

Escribes esto:

html

<header>  <div class="logo">...</div>  <nav aria-label="Principal">    <ul>      <li><a href="/">Inicio</a></li>      <li><a href="/about/">Acerca de</a></li>    </ul>  </nav> </header>

La diferencia no es visual. Es estructural. Una persona usuaria de lector de pantalla puede saltar directamente a la navegación. Sabe cuántos elementos hay en la lista. Escucha "lista, dos elementos" antes de que leas los enlaces.

El propio sitio de documentación de Django hizo estas mejoras. Envolvieron su tabla de contenidos en <nav aria-label="Tabla de contenidos de la documentación">. Hicieron lo mismo con los enlaces anterior/siguiente al pie de las páginas. Pequeños cambios, gran impacto para las personas que usan tecnología de asistencia.

Los campos de formulario necesitan etiquetas, siempre

El sistema de formularios de Django facilita la renderización de campos. {{ form.as_p }} y {{ form.as_table }} generan etiquetas automáticamente. Eso es bueno.

Pero si renderizas los formularios manualmente, tienes que incluir etiquetas. Cada input necesita una. No marcadores de posición. Los marcadores de posición desaparecen cuando el usuario escribe. No son un sustituto.

El reciente proyecto de atajos de teclado del administrador de Django, django-admin-keyshortcuts, tuvo que considerar cuidadosamente las interacciones con los formularios. El equipo añadió atajos como / para enfocar la barra de búsqueda y Ctrl+s para guardar objetos. Esos atajos solo funcionan si los campos del formulario están correctamente etiquetados y pueden recibir el foco.

Aquí tienes un patrón accesible para un campo de formulario en Django:

html

<div class="field-wrapper">  {{ form.email.label_tag }}  {{ form.email.errors }}  {{ form.email }}  {% if form.email.help_text %}    <p class="help" id="{{ form.email.auto_id }}_helptext">{{ form.email.help_text }}</p>  {% endif %} </div>

El método label_tag genera el elemento <label> correcto con el atributo for apuntando al ID del input. Eso es la base. Sin él, los usuarios de lectores de pantalla no saben qué escribir.

Los desplegables necesitan opciones vacías claras

Un tique de Django de octubre de 2024 (#35870) abordó algo específico: las opciones vacías en los menús desplegables (select). Marijke Luttekes registró el tique, y James Scholes, que es ciego y experto en accesibilidad, proporcionó comentarios.

El problema era qué debería decir la opción vacía. Cuando un desplegable no tiene un valor inicial, Django muestra una opción vacía. Pero, ¿qué texto debería tener esa opción?

James Scholes recomendó "(seleccione una opción)". Los paréntesis son importantes. Interrumpen lo menos posible la navegación por teclado con la primera letra. Si alguien está escribiendo para saltar entre opciones, los paréntesis no interfieren como podría hacerlo otra puntuación.

El tique sigue abierto, pero la lección es clara. Los pequeños detalles importan. El texto dentro de una opción afecta a cómo navegan los usuarios de lectores de pantalla. El comportamiento predeterminado de Django puede no ser óptimo, y es posible que tengas que personalizarlo.

Si estás construyendo formularios con menús desplegables, comprueba qué dice tu opción vacía. Considera si es clara para alguien que aún no puede ver las otras opciones.

La navegación por teclado no es opcional

Algunos usuarios no pueden usar un ratón. Navegan por las interfaces con el tabulador. Usan atajos de teclado. Tus plantillas deben soportar eso.

El equipo de Wagtail CMS ha estado trabajando en comprobaciones de accesibilidad integradas en el propio CMS. Están usando Axe, el mismo motor que potencia muchas herramientas de prueba de accesibilidad, para señalar problemas mientras los editores crean contenido. Una de las cosas que comprueban es el foco del teclado: ¿puedes alcanzar todos los elementos interactivos?

A. Rafey Khan trabajó durante el verano de 2025 en atajos de teclado para el administrador de Django a través de Google Summer of Code. Su paquete, django-admin-keyshortcuts, añade:

  • / para enfocar la búsqueda
  • j/k para enfocar el objeto siguiente o anterior
  • Ctrl+s para guardar
  • Alt+d para confirmar eliminación

Los atajos vienen con un diálogo para que los usuarios puedan descubrirlos. Eso es crucial. Añadir atajos que nadie conoce no sirve de ayuda.

Pero Khan también encontró limitaciones. Algunos atajos no funcionan en Safari. Las teclas modificadoras se comportan de manera diferente según el navegador. Es posible que el equipo tenga que esperar correcciones en la librería subyacente de «hotkeys». La compensación es que el soporte de teclado multinavegador es más difícil de lo que parece.

Si estás construyendo plantillas Django personalizadas, pruébalas solo con teclado. Desconecta el ratón. Navega con el tabulador por cada enlace, botón y campo de formulario. ¿Puedes llegar a todo? ¿Sabes dónde estás? Si no es así, tus plantillas necesitan trabajo.

ARIA: úsalo con moderación o no lo uses

Los atributos WAI-ARIA pueden resolver problemas de accesibilidad, pero también pueden crearlos. Las guías HTML de Wagtail hacen referencia a las «ARIA Authoring Practices» y señalan específicamente: "No ARIA es mejor que una ARIA defectuosa".

Esto viene del propio W3C. Añadir ARIA incorrectamente puede sobrescribir la semántica nativa y empeorar las cosas para los usuarios de lectores de pantalla.

Si tu botón es un <button>, no necesitas role="button". Eso ya está ahí. Si tu navegación es un <nav>, no necesitas role="navigation". El HTML nativo gana.

Usa ARIA cuando estés construyendo algo que HTML no puede expresar. Un panel de pestañas. Un diálogo modal. Una región viva que se actualiza sin recargar la página. Esos casos necesitan ARIA. Para todo lo demás, usa el elemento HTML correcto.

El sitio de documentación de Django añadió etiquetas ARIA a sus regiones de navegación, como aria-label="Tabla de contenidos de la documentación". Eso es apropiado porque desambigua múltiples elementos nav. Sin ello, los usuarios de lectores de pantalla escucharían "navegación, navegación" y no sabrían cuál es cuál.

Prueba con herramientas reales, no solo con esperanza

No puedes adivinar si tus plantillas son accesibles. Tienes que probar.

El equipo de Wagtail está integrando comprobaciones de accesibilidad directamente en el CMS. Están usando Axe y almacenando resultados a lo largo del tiempo para mostrar tendencias. El objetivo es dar a los editores una puntuación y ayudarles a mejorar el contenido a medida que lo crean.

Para los desarrolladores de Django, existen varias herramientas:

  • WAVE de WebAIM. Extensiones de navegador gratuitas que muestran los errores visualmente.
  • Axe de Deque. Se integra con las herramientas de desarrollo del navegador y puede ejecutarse en integración continua (CI).
  • Curlylint y djhtml. Estas herramientas analizan las plantillas Django específicamente para problemas de HTML. Wagtail las usa.
  • NVDA en Windows o VoiceOver en Mac. Lectores de pantalla gratuitos. Úsalos.

El proyecto AccChecky, construido en una hackathon, usaba la API de WAVE para comprobar sitios y mostrar resultados con gráficos. Ese es un enfoque. El enfoque más simple es ejecutar WAVE en tus páginas y corregir lo que encuentre.

Un ejemplo: construyendo una navegación accesible

Recorramos una plantilla Django real para un menú de navegación principal.

Primero, la plantilla base podría incluir un bloque para la navegación:

html

<!DOCTYPE html> <html lang="es"> <head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1.0">  <title>{% block title %}Mi sitio{% endblock %}</title> </head> <body>  <a href="#main" class="skip-link">Saltar al contenido principal</a>  <header>    <nav aria-label="Principal">      <ul>        <li><a href="/" {% if request.path == '/' %}aria-current="page"{% endif %}>Inicio</a></li>        <li><a href="/about/" {% if request.path == '/about/' %}aria-current="page"{% endif %}>Acerca de</a></li>        <li><a href="/services/" {% if request.path == '/services/' %}aria-current="page"{% endif %}>Servicios</a></li>        <li><a href="/contact/" {% if request.path == '/contact/' %}aria-current="page"{% endif %}>Contacto</a></li>      </ul>    </nav>  </header>  <main id="main">    {% block content %}{% endblock %}  </main> </body> </html>

Esto hace varias cosas:

  • lang="es" indica al lector de pantalla en qué idioma pronunciar.
  • El enlace de salto («skip link») permite a los usuarios de teclado omitir la navegación.
  • <nav aria-label="Principal"> identifica la región de navegación.
  • aria-current="page" indica al usuario del lector de pantalla qué página es la actual.
  • <main id="main"> proporciona el destino del enlace de salto.

Todo es HTML. Sin magia de ARIA. Simplemente usando los elementos correctamente.

El atajo de la superposición (overlay) y por qué evitarlo

Hay un paquete de PyPI llamado django-all-in-one-accessibility. Instala un widget que añade una barra de herramientas de accesibilidad a tu sitio. Los usuarios pueden cambiar el contraste, aumentar el tamaño del texto y usar un lector de pantalla.

Se instala en dos minutos. No cuesta nada. Parece una solución rápida.

No es una solución.

Las superposiciones tienen problemas fundamentales. Añaden JavaScript que modifica la página después de que se carga. Pueden entrar en conflicto con la propia tecnología de asistencia del usuario. Dan la ilusión de accesibilidad sin corregir el código subyacente. Las guías de Wagtail sobre "No ARIA es mejor que una ARIA defectuosa" se aplican doblemente a las superposiciones.

Si usas una superposición y llamas accesible a tu sitio, te estás engañando a ti mismo y a tus usuarios. La superposición no corrige la falta de texto alternativo en las imágenes. No corrige la estructura de encabezados. No hace que tus formularios tengan etiquetas adecuadas. Solo añade una barra de herramientas.

Construye plantillas accesibles en su lugar. Es más trabajo. También es el único enfoque que realmente funciona.

Lo que Django está haciendo internamente

El propio proyecto Django ha estado trabajando en la accesibilidad. El paquete de atajos de teclado surgió del GSoC 2025 y podría terminar integrándose en el núcleo («core»). El tique sobre las opciones vacías en los menús desplegables está abierto y en discusión. El sitio de documentación se ha actualizado con un mejor HTML semántico.

La propia interfaz de administración de Django, que miles de desarrolladores usan a diario, está recibiendo atención. Los atajos de teclado la hacen utilizable para personas que no pueden usar un ratón. Un mejor etiquetado de formularios ayuda a todo el mundo.

Si estás construyendo plantillas Django, puedes seguir la misma trayectoria. Empieza con HTML semántico. Etiqueta tus formularios. Prueba con teclado y lector de pantalla. Corrige lo que esté roto.

Las herramientas que realmente necesitas

No necesitas herramientas empresariales costosas para crear plantillas Django accesibles. Necesitas:

  • Un editor de código
  • Un navegador
  • La extensión WAVE
  • Un lector de pantalla para pruebas
  • Curlylint o djhtml para el análisis estático («linting»)

Las guías de interfaz de usuario de Wagtail recomiendan djhtml para formatear plantillas Django y Curlylint para analizarlas. También recomiendan ejecutar make lint para comprobar tu código.

Curlylint comprueba cosas como la falta de texto alternativo en imágenes, el orden incorrecto de los encabezados y la ausencia de etiquetas en los formularios. Se ejecuta sobre tus plantillas, no sobre el HTML renderizado, por lo que detectas problemas temprano.

Añádelo a tu flujo de integración continua (CI). Ejecútalo en cada solicitud de cambios («pull request»). Deja de fusionar código que no supere las comprobaciones básicas de accesibilidad.

La disyuntiva: tiempo frente a demandas judiciales

Construir plantillas accesibles lleva tiempo. Tienes que aprender las reglas. Tienes que probar. Tienes que corregir cosas que se ven bien visualmente pero fallan para los usuarios de lectores de pantalla.

La alternativa es ser demandado.

La ADA (Ley de Estadounidenses con Discapacidades) no tiene un estándar técnico para sitios web, pero los tribunales recurren a las WCAG. WCAG 2.1 Nivel AA es el punto de referencia de facto. Si tu sitio no lo cumple, estás expuesto.

Para los desarrolladores de Django, la elección es clara. Invierte tiempo ahora para construirlo bien. O gasta dinero más tarde en abogados y acuerdos extrajudiciales.

Conclusión

Las plantillas Django son solo HTML con algunas etiquetas de plantilla. HTML tiene la accesibilidad incorporada. Úsala.

Escribe HTML semántico. Usa <nav>, <main>, <header>, <footer>. Etiqueta tus formularios. Asegúrate de que los usuarios de teclado puedan alcanzar todo. Añade enlaces de salto («skip links»). Prueba con tecnología de asistencia real.

La comunidad de Django está trabajando en esto. El administrador está recibiendo atajos de teclado. La documentación está mejorando. Se están discutiendo tiques sobre menús desplegables y opciones vacías.

Puedes esperar a que Django resuelva todos estos problemas por ti. O puedes resolverlos tú mismo en tus plantillas ahora mismo. Un enfoque te deja vulnerable. El otro hace que tu sitio funcione para más personas.

Frequently Asked Questions

Use semantic HTML. Don't wrap everything in
tags. Use
Every form field needs a proper
There's an open Django ticket (#35870) about this. When a dropdown has no initial value, Django shows a blank option. The question is what text that option should display. James Scholes, an accessibility expert who is blind, recommended "(select an option)". The parentheses matter because they disrupt first-letter keyboard navigation as little as possible. The ticket is still open, so you may need to customize this yourself if the default doesn't work for your users.
Use ARIA sparingly. The W3C's guidance is "No ARIA is better than Bad ARIA." Native HTML elements already have semantics built in. A
Avoid them. Overlays add JavaScript that modifies the page after it loads. They conflict with users' own assistive technology. They give the illusion of accessibility without fixing underlying code like missing alt text or improper heading structure. The Wagtail guidelines reference "No ARIA is better than Bad ARIA," and the same applies to overlays. Build accessible templates instead. It's more work and it actually works.
Use multiple tools. WAVE from WebAIM is a free browser extension that shows errors visually. Axe integrates with browser dev tools and can run in CI. Curlylint and djhtml lint Django templates specifically for HTML issues. Then test with real screen readers. NVDA is free for Windows. VoiceOver is built into every Mac. Turn them on and try to use your own site without looking at the screen. It's revealing.
At minimum, users need to tab through every interactive element and see where they are. That's the baseline. The django-admin-keyshortcuts project added / to focus search, j/k to navigate between objects, Ctrl+s to save, and Alt+d to delete. Those are good patterns. The package also includes a dialog so users can discover the shortcuts. Adding shortcuts no one knows about doesn't help.
Automated tools find about 30% of accessibility issues. They can't tell if a screen reader user can actually navigate your site. They can't evaluate whether your link text makes sense out of context. They can't tell if your heading order is logical. WAVE and Axe are essential starting points, but you must test manually with assistive technology. The Wagtail team is building automated checks into their CMS, but they still recommend manual testing.
Django is working on it. The admin keyboard shortcuts project came out of Google Summer of Code 2025 and may eventually merge into core. The ticket about select dropdown blank options is open and being discussed. The documentation site has been updated with better semantic HTML and ARIA labels. Django doesn't write accessible templates for you, but the framework gives you the tools to do it.
Add Curlylint or djhtml to check your templates on every pull request. They catch missing alt text, improper heading order, and missing form labels. Run automated tests with Axe or similar tools against your rendered pages. Stop merging code that fails basic checks. The Wagtail guidelines recommend running make lint and using specific linters to enforce their UI standards. You can do the same.
A skip link is a link at the very top of the page that jumps to the main content. It's usually hidden visually but appears when keyboard users tab to it. Without it, keyboard users have to tab through every navigation link, every header item, every piece of chrome before reaching your content. The pattern is with corresponding id="main" on your main content container. It's simple and required.
Building accessible templates takes more time upfront. You have to learn the rules, test, and fix issues. But it's permanent. Overlays take two minutes to install and then you're still not accessible. The trade-off is time now versus lawsuits later. ADA website lawsuits settle for $5,000 to $15,000 typically, and you can get sued multiple times. A few days of development work costs less than one settlement.
The W3C's WCAG Quick Reference at w3.org/WAI/WCAG21/quickref. WAVE browser extension from WebAIM. Axe DevTools. NVDA screen reader. Curlylint on GitHub. The Django documentation site itself for examples of accessible navigation and structure. The django-admin-keyshortcuts repository for keyboard shortcut patterns.

Ready to Find Your Perfect Vehicle?

Browse our extensive inventory or schedule a test drive today!

Janeth

About Janeth

Comments (0)

No comments yet.

Get More Info