Un widget de accesibilidad completo, en un solo archivo, sin dependencias. 11 controles reales de accesibilidad que se integran en cualquier web en menos de 5 minutos.
Creado por Connexo (Ecuador) en el marco del convenio con la Fundación Arupo por la inclusión y la accesibilidad. Es de código abierto (MIT): úsalo, modifícalo y compártelo. Cuanta más gente lo integre, más personas con discapacidad podrán usar la web con comodidad. Ese es el objetivo. 🧡
- ¿Qué hace?
- Demo
- Instalación en 5 minutos
- Guías de integración por plataforma
- Actualizar el widget
- Configuración
- API de JavaScript
- Accesibilidad y estándares
- Compatibilidad
- Personalización e idioma
- Preguntas frecuentes
- Contribuir
- Licencia y créditos
Añade un botón flotante ♿ que abre un panel con 11 controles. Todo pensado
para funcionar también en teléfono (táctil), y las preferencias del usuario
se guardan solas entre visitas (localStorage).
| # | Control | Qué hace |
|---|---|---|
| 1 | Tamaño de texto | Escala la tipografía (100% → 150%). |
| 2 | Espaciado de línea | Aumenta el interlineado. |
| 3 | Fuente para dislexia | Tipografía legible + más espaciado entre letras/palabras (enfoque BDA/WebAIM/W3C). |
| 4 | Alto contraste | Fondo negro, texto blanco, enlaces amarillos. |
| 5 | Blanco y negro | Escala de grises total. |
| 6 | Resaltar enlaces | Borde + subrayado en todo lo interactivo. |
| 7 | Guía de lectura | Una franja clara sigue el cursor o el dedo y atenúa el resto. |
| 8 | Cursor grande | Puntero XL de alto contraste (PNG; funciona hasta en Safari). |
| 9 | Lector de voz (TTS) | Selecciona texto (o deslízalo en el móvil) y se lee en voz alta. |
| 10 | Desactivar animaciones | Elimina el movimiento (respeta también prefers-reduced-motion). |
| 11 | Modo Visual Total | Máximo contraste: fondo negro, texto amarillo. |
Abre demo.html con doble clic (no necesita servidor) y prueba los 11
controles. El botón ♿ aparece en la esquina.
Añade una sola línea antes de </body>:
<!-- jsDelivr (desde npm) -->
<script src="https://cdn.jsdelivr.net/npm/connexo-accessibility-widget@1/a11y-widget.min.js" defer></script>
<!-- o unpkg -->
<script src="https://unpkg.com/connexo-accessibility-widget@1/a11y-widget.min.js" defer></script>El
@1fija la versión mayor: recibes las correcciones de la 1.x sin que una futura 2.0 cambie tu sitio sin avisar. Para fijar una versión exacta usa, por ejemplo,@1.0.0.
- Copia
a11y-widget.min.js(oa11y-widget.jssin minificar) a tu sitio. - Añade antes de
</body>:
<script src="/ruta/a11y-widget.min.js" defer></script>Eso es todo. No hay que inicializar nada: el widget arranca solo. ✅
Una copia alojada por ti no se actualiza sola. Mira Actualizar el widget para saber cómo enterarte de las versiones nuevas y cómo ponerlas.
HTML plano
<!doctype html>
<html lang="es">
<body>
<!-- ...tu contenido... -->
<script src="a11y-widget.min.js" defer></script>
</body>
</html>React (Vite / Create React App)
La forma más simple es cargar el script una sola vez al montar la app:
// App.jsx
import { useEffect } from 'react';
export default function App() {
useEffect(() => {
const s = document.createElement('script');
s.src = 'https://cdn.jsdelivr.net/npm/connexo-accessibility-widget@1/a11y-widget.min.js';
s.defer = true;
document.body.appendChild(s);
return () => { s.remove(); };
}, []);
return <>{/* ...tu app... */}</>;
}O, más fácil aún, pega la etiqueta <script> directamente en index.html.
Next.js (App Router o Pages Router)
Usa el componente next/script:
// app/layout.js (o pages/_app.js)
import Script from 'next/script';
export default function RootLayout({ children }) {
return (
<html lang="es">
<body>
{children}
<Script
src="https://cdn.jsdelivr.net/npm/connexo-accessibility-widget@1/a11y-widget.min.js"
strategy="afterInteractive"
/>
</body>
</html>
);
}Vue 3
// main.js
const s = document.createElement('script');
s.src = '/a11y-widget.min.js';
s.defer = true;
document.body.appendChild(s);O simplemente añade la etiqueta <script> en public/index.html.
Angular
En angular.json, agrégalo al array scripts:
"scripts": [
"src/assets/a11y-widget.min.js"
](copia el archivo a src/assets/).
WordPress
Sin plugins: Apariencia → Editor de temas → footer.php, y antes de
</body> pega:
<script src="https://cdn.jsdelivr.net/npm/connexo-accessibility-widget@1/a11y-widget.min.js" defer></script>Con plugin (recomendado para no perderlo al actualizar el tema): usa "Insert Headers and Footers" / "WPCode" y pega la misma línea en el footer.
Shopify
Admin → Temas → Editar código → layout/theme.liquid, antes de </body>:
<script src="https://cdn.jsdelivr.net/npm/connexo-accessibility-widget@1/a11y-widget.min.js" defer></script>Webflow / Squarespace / Wix
Ve a los ajustes de código personalizado del sitio (Custom Code → Footer) y
pega la etiqueta <script> del CDN. En Webflow: Project Settings → Custom Code
→ Footer Code.
Google Tag Manager
Crea una etiqueta de tipo HTML personalizado, pega la etiqueta <script>
del CDN y dispárala en All Pages.
Cómo se actualiza depende de cómo lo instalaste:
| Cómo lo cargas | ¿Se actualiza solo? | Qué hacer |
|---|---|---|
Por CDN, sin versión o con @1 (lo que recomienda esta guía) |
Sí | Nada. Recibes cada versión 1.x nueva; por la caché del CDN puede tardar hasta unas 12 horas. |
Por CDN con una versión exacta (@1.2.0) |
No | Cambia el número en la URL. |
| Copia en tu servidor (Opción B) | No | Descarga el archivo nuevo y reemplázalo (ver abajo). |
Descargar la última versión. Abre esta dirección, guarda el archivo y súbelo en lugar del anterior:
https://cdn.jsdelivr.net/npm/connexo-accessibility-widget@latest/a11y-widget.min.js
Si tu sitio guarda archivos en caché, añade la versión a la dirección del
script para que los navegadores bajen el nuevo:
<script src="/js/a11y-widget.min.js?v=1.2.0" defer></script>.
¿Qué versión tengo? Abre tu página, abre la consola del navegador (F12) y
escribe A11yWidget.version. Si responde undefined, tienes una versión
anterior a la 1.2.0: actualízala.
Aviso de versión nueva (desde la 1.2.0). Las copias que no vienen de un CDN con versión móvil consultan, como máximo una vez por semana y por navegador, cuál es la última versión publicada. Si hay una más nueva:
- aparece un aviso en la consola del navegador (
F12); - si abres tu página añadiendo
?a11y-updatea la dirección (https://tusitio.com/?a11y-update), el pie del panel muestra la versión que usas y si hay una nueva, con un enlace a esta sección.
Los visitantes normales no ven nada. También puedes preguntarlo por código:
A11yWidget.checkForUpdates().then(console.log);
// → { current: '1.2.0', latest: '1.3.0', updateAvailable: true }Se apaga con data-update-check="false" (ver Configuración).
Qué datos implica: ver Preguntas frecuentes.
Opcional. Por atributos en el <script>:
<script src="a11y-widget.min.js"
data-accent="#ff6600"
data-position="bottom-right"
defer></script>O por objeto global antes del script:
<script>
window.A11yWidgetConfig = { accent: '#0066ff', position: 'bottom-left' };
</script>
<script src="a11y-widget.min.js" defer></script>| Opción | Valores | Por defecto |
|---|---|---|
accent |
cualquier color CSS (hex, rgb…) | #ff6600 |
position |
bottom-right · bottom-left · top-right · top-left |
bottom-right |
storageKey |
nombre de la clave de localStorage |
a11y-settings |
updateCheck |
false apaga el aviso de versión nueva (ver Actualizar) |
activo en copias alojadas por ti |
Revisa la esquina antes de publicar. El botón ♿ queda por encima de todo (
z-indexmáximo), así que si tu sitio ya tiene algo flotante en esa esquina —botón de "volver arriba", WhatsApp, chat, aviso de cookies— lo tapará. Cámbialo de esquina conposition, o ponlo en tu menú (ver API de JavaScript).
Contraste del icono: el icono y los textos que van sobre el color
accentse ponen en blanco o en casi negro automáticamente, según cuál se lea mejor (mínimo 3:1, WCAG 1.4.11). Con colores claros, como dorados o amarillos, el icono saldrá oscuro.
A11yWidget.open(); // abre el panel
A11yWidget.close(); // lo cierra
A11yWidget.get(); // → objeto con las preferencias actuales
A11yWidget.set({ highContrast: true, fontSize: 1.25 }); // aplica preferencias
A11yWidget.version; // → '1.2.0' (la versión que estás usando)
A11yWidget.checkForUpdates(); // → Promise { current, latest, updateAvailable }Útil, por ejemplo, para poner tu propio botón "Accesibilidad" en el menú:
<button onclick="A11yWidget.open()" aria-haspopup="dialog">Accesibilidad</button>Si lo haces, oculta el botón flotante y elige el lado por el que se abre el
panel (position con -right lo abre por la derecha, con -left por la
izquierda):
<style>.a11y-launch { display: none !important; }</style>
<script src="…/a11y-widget.min.js" data-position="top-right" defer></script>Al cerrar el panel, el foco vuelve a tu botón. Si tu botón no es un <button>
de verdad (por ejemplo, un <div> con un clic), dale role="button",
tabindex="0" y que responda a Enter y Espacio: si no, quien navega con
teclado no podrá abrir el panel.
Este widget está construido siguiendo buenas prácticas de la WCAG 2.1 y de la guía de la British Dyslexia Association:
- Navegable por teclado: el panel es un
role="dialog"con trampa de foco, se cierra conEscy devuelve el foco al botón que lo abrió. - Semántica ARIA:
aria-presseden los interruptores,aria-labelen los controles, foco visible en todo el sitio (:focus-visible). - Táctil primero: la guía de lectura y el lector de voz funcionan con el dedo en el móvil, no solo con ratón.
- Respeta al sistema: honra
prefers-reduced-motiondel sistema operativo. - No intrusivo: todo es opt-in; al apagar cada modo, la página vuelve a su
estado original. Las preferencias se guardan por usuario en
localStorage.
Nota honesta: un widget ayuda muchísimo, pero no sustituye un sitio bien hecho (HTML semántico, textos alternativos en imágenes, contraste suficiente de base). Es un complemento potente, no una excusa. 🙂
- Navegadores modernos: Chrome, Edge, Firefox y Safari (escritorio y móvil).
- Lector de voz: usa la Web Speech API nativa. La calidad de la voz depende del dispositivo — Android e iOS traen voces muy naturales; en Windows con solo voces del sistema (SAPI) suena más básico. El widget elige automáticamente la mejor voz en español disponible.
- Cursor grande: solo aplica en escritorio (en móvil no hay puntero). Se usan PNG en vez de cursores SVG justamente para que funcione también en Safari.
- Color de marca: cambia
accent(ver Configuración). Internamente es la variable CSS--a11y-accent. - Textos / idioma: los textos están en español dentro del array
CONTROLSena11y-widget.js. Tradúcelos o edítalos ahí y vuelve a minificar connpm run build. - Estilos de los modos: son las reglas
.a11y-*dentro del mismo archivo, fáciles de ajustar.
¿Rompe el diseño de mi sitio? No. Los modos son opt-in y su UI está aislada; al apagarlos, tu sitio queda igual que antes. Mientras están activos, ten en cuenta dos cosas:
- Alto contraste y Modo Visual Total pintan de negro el fondo de todo lo que
tiene texto. Las capas decorativas que van encima del contenido (ruido,
scanlines, viñetas, degradados sobre fotos) se detectan solas —porque no
reciben clics (
pointer-events: none) o porque están posicionadas y no tienen texto ni controles— y se dejan transparentes, para que no tapen la página. - Tamaño de texto y Modo Visual Total agrandan la fuente base (
<html>hasta 24 px). Si tus títulos usanremdentro de contenedores conoverflow: hidden, las palabras largas pueden recortarse. Comprueba tus títulos con el texto al 150 % (WCAG pide que el contenido aguante hasta 200 %).
¿Envía datos a algún servidor?
No envía datos de tus visitantes ni de tu sitio. Todo ocurre en el navegador del
usuario; las preferencias se guardan solo en su dispositivo (localStorage).
Cero rastreo.
Hay una única conexión, y solo en copias alojadas por ti: para el
aviso de versión nueva, cada navegador pide como máximo
una vez por semana el package.json del widget a jsDelivr. Va sin cookies y sin
Referer, así que jsDelivr solo ve la IP, como en cualquier descarga desde un
CDN. Si prefieres cero conexiones, añade data-update-check="false". Si lo
cargas desde el CDN sin versión o con @1, no se hace esa consulta.
¿Necesito build o npm?
No. Es un archivo. npm/esbuild solo hacen falta si quieres editar y
volver a minificar.
¿Puedo poner mi propio botón en vez del flotante?
Sí: usa A11yWidget.open() desde cualquier botón tuyo y oculta el flotante
(ver API de JavaScript).
Los aportes son bienvenidos: abre un issue o un pull request. Ideas útiles: más idiomas, más voces, nuevos controles. Al ser MIT, cualquier agencia puede adaptarlo a su marca.
Editar y reconstruir:
npm install # instala esbuild (solo para build)
# edita a11y-widget.js
npm run build # regenera a11y-widget.min.jsMIT — libre para usar, modificar y redistribuir. Ver LICENSE.
Hecho con 🧡 por Connexo (Ecuador) en convenio con la Fundación Arupo (fundacionarupo.org) por una web más inclusiva para las personas con discapacidad.