Accesibilidad con CSS
Ya envías layouts, motion y trucos del toolkit moderno. La accesibilidad en CSS es la capa que la gente siente: ¿ven dónde está el foco del teclado? ¿pueden leer el texto? ¿una animación giratoria les marean? Esta sesión no es un volcado de HTML (landmarks, alt, ARIA). Son los hábitos de estilo que mantienen usables el foco, el contraste y el movimiento.
El foco debe seguir visible
Quien navega con teclado recorre enlaces y controles con Tab. Si el anillo de foco desaparece, se pierde. Prefiere :focus-visible para que los clics de ratón se queden tranquilos y el foco de teclado se note alto:
:root {
--focus-ring: #2563eb;
}
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline: 3px solid var(--focus-ring);
outline-offset: 3px;
}
.text-link:focus:not(:focus-visible) {
outline: none;
}
:focus-visible apunta a los casos en que el anillo ayuda (casi siempre teclado). Combínalo con un anillo de alto contraste y un poco de outline-offset para que no choque con los bordes.
Nunca outline: none sin reemplazo
Quitar el outline por defecto está bien solo cuando ofreces un estilo de foco personalizado más claro. Un *:focus { outline: none; } a pelo es la trampa clásica:
/* Bad: keyboard users lose the only cue */
.bad-button:focus {
outline: none;
}
/* Good: custom ring that still shows focus */
.good-button:focus-visible {
outline: none;
box-shadow: 0 0 0 3px #fff, 0 0 0 6px #0f766e;
}
Si apagas el outline del navegador, otra cosa debe marcar el control enfocado: box-shadow, color de borde o un cambio de fondo que siga siendo obvio en cualquier tema.
El contraste es una decisión CSS
Los colores de marca se ven nítidos en el mockup y se apagan en el móvil al sol. Trata los pares primer plano / fondo como decisiones de accesibilidad, no solo decoración:
:root {
--surface: #0f172a;
--text: #f8fafc;
--muted: #cbd5e1;
--accent: #38bdf8;
--accent-text: #0c4a6e;
}
.hero {
background: var(--surface);
color: var(--text);
}
.hero .lede {
color: var(--muted);
}
.hero .cta {
background: var(--accent);
color: var(--accent-text);
font-weight: 700;
}
Busca contraste fuerte en el texto de cuerpo y los botones. Los grises decorativos pueden ir más suaves en etiquetas secundarias, pero el copy y los controles principales deben seguir legibles. Comprueba los pares con un medidor de contraste antes de publicar la paleta.
Respeta el movimiento reducido
Las animaciones y los transforms grandes pueden incomodar. Envuelve el movimiento no esencial en prefers-reduced-motion:
.banner {
transition: transform 0.4s ease, opacity 0.4s ease;
}
.banner.is-shown {
transform: translateY(0);
opacity: 1;
}
@media (prefers-reduced-motion: reduce) {
.banner,
.banner.is-shown {
transition: none;
transform: none;
}
.spinner {
animation: none;
}
}
Quien pide al sistema operativo menos movimiento debe llegar al estado final de la UI — solo sin el viaje. Mantén cambios esenciales de opacity o layout; quita giros infinitos y deslizamientos grandes.
Alto contraste y forced-colors
Algunos entornos sustituyen tu paleta (Alto contraste de Windows / forced-colors). Suaviza decoraciones frágiles y conserva bordes reales:
.card {
border: 1px solid #e2e8f0;
background: #fff;
box-shadow: 0 8px 24px rgb(15 23 42 / 12%);
}
@media (forced-colors: active) {
.card {
border: 1px solid CanvasText;
background: Canvas;
box-shadow: none;
color: CanvasText;
}
.card a {
color: LinkText;
}
}
Palabras de color del sistema como Canvas, CanvasText y LinkText ayudan a que los controles sigan visibles cuando el usuario — no tu guía de marca — manda en los colores.
Lista de comprobación del lado CSS
📊 CSS accessibility habits
| Practice | CSS habit | Avoid |
|---|---|---|
| Keep focus findable | :focus-visible + strong ring / shadow |
outline: none with no replacement |
| Readable type | High-contrast text/background tokens | Light grey on white for body copy |
| Kind motion | @media (prefers-reduced-motion: reduce) |
Endless spins for everyone |
| Survive high contrast | Real borders; forced-colors tweaks |
Shadow-only “edges” |
| Reachable controls | Comfortable padding / hit area | Tiny icon-only targets with no focus style |
HTML sigue siendo dueño de landmarks, etiquetas y texto alt. CSS decide si esos controles siguen siendo perceptibles una vez estilizados.
Enlaces de salto y focus-within
Un enlace de salto (skip link) salta la barra repetida. Estilízalo para que aparezca al enfocarse. :focus-within también puede resaltar todo un grupo cuando un hijo tiene el foco:
.skip-link {
position: absolute;
left: 1rem;
top: -3rem;
padding: 0.5rem 0.75rem;
background: #111827;
color: #fff;
border-radius: 0.375rem;
z-index: 20;
}
.skip-link:focus-visible {
top: 1rem;
outline: 3px solid #fbbf24;
outline-offset: 2px;
}
.nav:focus-within {
box-shadow: inset 0 0 0 2px #2563eb;
}
El enlace de salto permanece fuera de pantalla hasta el foco y entonces se hace obvio. El anillo del nav ayuda cuando Tab cae en un enlace hijo dentro de la barra.
Pruébalo tú
- Añade un anillo
:focus-visiblea enlaces y botones; recorre la página con Tab y confirma que siempre ves el foco. - Elige un par de bajo contraste y corrígelo con texto más oscuro o una superficie más profunda.
- Envuelve una transition o animation en
prefers-reduced-motion: reducey cambia el ajuste del SO (o la emulación de DevTools). - Bajo emulación de forced-colors, asegúrate de que las tarjetas sigan mostrando un borde real.
- Estiliza un skip link que solo aparezca con foco de teclado.
Errores frecuentes
outline: none(ooutline: 0) global sin estilo de foco personalizado- Colores de marca bonitos que fallan el contraste en texto de cuerpo y botones primarios
- Animaciones grandes para todos sin puerta
prefers-reduced-motion - Ocultar un control enfocable solo con
opacity: 0mientras sigue recibiendo Tab - Tratar esta lección de CSS como sustituto completo del HTML semántico y ARIA
Resumen
- Mantén el foco de teclado visible — prefiere
:focus-visibley no borres el outline sin una señal más fuerte - Trata el contraste de color como requisito para texto y controles
- Honra
prefers-reduced-motiony endurece estilos paraforced-colors - Estiliza skip links y usa
:focus-withindonde el grupo necesite una señal compartida - Siguiente: Resumen CSS y siguientes pasos (
summary) — cierra el curso antes del mini proyecto
🧠 Pon a Prueba tu Conocimiento
Pon a Prueba tu Conocimiento
Ponte a prueba con este cuestionario interactivo y descubre qué tan bien entiendes el tema
📝 Instrucciones
- Lee cada pregunta cuidadosamente
- Selecciona la mejor respuesta para cada pregunta
- Puedes repetir el quiz tantas veces como quieras
- Tu progreso se mostrará en la parte superior