WEB ATELIER (UDIT) · Aprender haciendo, con teoría, práctica y reflexión compartida

Tailwind CSS: Accesibilidad y Rendimiento — Diseño Inclusivo y Optimizado

URL: https://ruvebal.github.io/web-atelier-udit/lessons/es/tailwind/accessibility-performance/

📋 Tabla de contenidos

Tailwind CSS: Accesibilidad y Rendimiento — Diseño Inclusivo y Optimizado

Divulgación de asistencia IA: Esta lección se basa en experiencia docente desde septiembre 2024, con iteraciones de IA siguiendo ciclos investigación–práctica–investigación.

🎭 Enfoque de Codificación Crítica

Esta lección sigue la metodología del atelier (exploración → reflexión → conceptualización → producción → exhibición). Construimos no para automatizar, sino para articular — dar forma al pensamiento mediante ritmo, reflexión y resistencia.

  • Exploración: Auditoría de interfaces para inclusión y métricas de rendimiento.
  • Reflexión: Comprensión de accesibilidad como imperativo de diseño y ética.
  • Conceptualización: Conexión de rendimiento con experiencia de usuario y justicia social.
  • Producción: Construcción de interfaces performantes y accesibles que funcionan para todos.
  • Exhibición: Demostración de diseño inclusivo mediante mejoras medibles.

Requisitos

📚 Antes de comenzar

  • S1–S4 completadas: SPA Tailwind completa con componentes, estado e interactividad
  • Bases de accesibilidad: Comprensión de WCAG, HTML semántico, ARIA
  • Conceptos de rendimiento: Familiaridad con Core Web Vitals y optimización
  • Herramientas de desarrollo: Experiencia con dev tools de navegador y Lighthouse

🚀 Diseño Inclusivo: Rendimiento para Todos los Usuarios

La accesibilidad y el rendimiento son aspectos interconectados del diseño inclusivo. El pobre rendimiento afecta desproporcionadamente a usuarios con:

  • Conexiones lentas: Áreas rurales, regiones en desarrollo, redes móviles
  • Dispositivos antiguos: Potencia de procesamiento y memoria limitados
  • Tecnologías asistivas: Lectores de pantalla, control por voz, métodos de entrada alternativos
  • Discapacidades cognitivas: Interfaces simplificadas reducen carga cognitiva

Nuestro enfoque crea experiencias PWA-ready que son rápidas, accesibles y funcionan en todos los dispositivos y capacidades de usuario.

S5 — Accesibilidad y Rendimiento (Auditoría, Optimización, Testing)

Esta sesión se enfoca en auditoría para cumplimiento de accesibilidad y optimización de rendimiento, asegurando que nuestras interfaces funcionen para todos.

Implementación paso a paso

  1. Añade skip link para navegación por teclado:

    <!-- Skip link - debe ser el primer elemento enfocable -->
    <a
    	href="#main"
    	class="sr-only focus:not-sr-only focus:absolute focus:top-4 focus:left-4 bg-primary-500 text-white px-4 py-2 rounded z-50">
    	Saltar al contenido principal
    </a>
    
  2. Audita estructura semántica y landmarks:

    <!-- Asegura jerarquía de encabezados apropiada -->
    <header>
    	<h1>Título Principal de Página</h1>
    </header>
    
    <nav role="navigation" aria-label="Navegación principal">
    	<!-- Contenido de navegación -->
    </nav>
    
    <main id="main">
    	<section>
    		<h2>Título de Sección</h2>
    		<!-- Contenido de sección -->
    	</section>
    </main>
    
    <footer role="contentinfo">
    	<!-- Contenido de footer -->
    </footer>
    
  3. Comprueba y corrige contraste de colores:

    /* Asegura cumplimiento WCAG AA (4.5:1 para texto normal, 3:1 para texto grande) */
    .text-gray-600 {
    	/* Puede necesitar ajuste para mejor contraste */
    }
    .bg-primary-500 {
    	color: #ffffff;
    } /* Asegura contraste suficiente */
    
  4. Implementa gestión de foco apropiada:

    /* Focus-visible para navegación por teclado */
    .focus-visible\:ring-2:focus-visible {
    	outline: 2px solid transparent;
    	outline-offset: 2px;
    }
    
  5. Añade soporte para movimiento reducido:

    /* Respeta preferencias de usuario para movimiento reducido */
    @media (prefers-reduced-motion: reduce) {
    	*,
    	*::before,
    	*::after {
    		animation-duration: 0.01ms !important;
    		animation-iteration-count: 1 !important;
    		transition-duration: 0.01ms !important;
    	}
    }
    
  6. Optimiza imágenes y assets:

    <!-- Imágenes responsive con texto alt apropiado -->
    <picture>
    	<source media="(min-width: 768px)" srcset="hero-large.webp" />
    	<img
    		src="hero-small.webp"
    		alt="Imagen hero mostrando desarrollo web moderno"
    		loading="lazy"
    		class="w-full h-64 object-cover" />
    </picture>
    
  7. Ejecuta auditoría Lighthouse:

    # Instala CLI de Lighthouse
    npm install -g lighthouse
    
    # Ejecuta auditoría de rendimiento
    lighthouse http://localhost:5173 --output html --output-path ./lighthouse-report.html
    
  8. Analiza y corrige problemas de rendimiento:

    // Optimiza tamaño de bundle mediante carga lazy de componentes no críticos
    const loadComponent = async (componentName) => {
    	const module = await import(`./components/${componentName}.js`);
    	return module.default;
    };
    
    // Usa cuando el componente sea necesario
    const ContactForm = await loadComponent('ContactForm');
    
  9. Prueba con tecnologías asistivas:

    • Usa lector de pantalla (NVDA, JAWS, VoiceOver)
    • Prueba navegación solo por teclado
    • Verifica compatibilidad con modo de alto contraste
    • Comprueba con throttling de red lenta
  10. Haz commit de tus mejoras de accesibilidad y rendimiento:

    git add .
    git commit -m "feat: S5 - Auditoría de accesibilidad + optimizaciones de rendimiento (WCAG AA, mejoras Lighthouse)"
    

🎯 Ejercicio Práctico: Ruta de Demostración de Accesibilidad

Crea una ruta completa mostrando características de accesibilidad y demostrando cumplimiento WCAG:

Ejercicio 5.1: Crea Ruta de Muestra de Accesibilidad

// src/views/accesibilidad.js
// Ver versión en inglés (src/views/accessibility.js) para código completo
// Incluye demostraciones de:
// - Skip links y navegación por teclado
// - Estructura semántica HTML5
// - Contraste de color (WCAG AA: 4.5:1)
// - Estados de foco visibles
// - Etiquetas ARIA y regiones live
// - Patrón de navegación por pestañas con flechas
// - Contenido solo para lectores de pantalla (.sr-only)
export default {
	template: `
    <section class="py-16 min-h-screen">
      <div class="container mx-auto px-4 max-w-6xl">
        <h1 class="text-4xl font-bold text-gray-900 mb-8">Muestra de Características de Accesibilidad</h1>
        <p class="text-lg text-gray-600 mb-12">
          Demostrando componentes conformes con WCAG 2.1 AA y patrones de diseño inclusivo.
        </p>

        <!-- Demo de Skip Link -->
        <div class="bg-white rounded-lg shadow-md p-6 mb-8">
          <h2 class="text-2xl font-bold text-gray-900 mb-4">Enlaces de Salto (Skip Links)</h2>
          <p class="text-gray-700 mb-4">
            Prueba presionando <kbd class="px-2 py-1 bg-gray-200 rounded">Tab</kbd> para revelar el enlace de salto en la parte superior.
            Los skip links ayudan a usuarios de teclado a navegar directamente al contenido principal.
          </p>
        </div>

        <!-- Más secciones similares a la versión en inglés... -->
        
        <a href="#/" class="inline-block text-primary-500 hover:text-primary-600 font-medium">← Volver a Inicio</a>
      </div>
    </section>
  `,
	script() {
		// Funcionalidad similar a la versión en inglés
		// Ver src/views/accessibility.js para implementación completa
	},
};

Ejercicio 5.2: Registra la Ruta

// src/views/index.js
import accesibilidad from './accesibilidad.js'; // Añade esto

export const views = {
	// ... otras rutas
	'/accesibilidad': accesibilidad, // Añade esto
	404: notFound,
};

Nota: Para el código completo de src/views/accesibilidad.js, adapta el ejemplo de src/views/accessibility.js traduciendo los textos al español.

<!-- index.html -->
<li><a href="#/accesibilidad" class="hover:text-blue-400 transition-colors">Accesibilidad</a></li>

Commit tu muestra de accesibilidad:

git add .
git commit -m "feat: S5 - Añadir ruta de muestra de características de accesibilidad (demo cumplimiento WCAG AA)"

🎨 Por Qué Importa Esta Ruta

  • Documentación viva: Demuestra características de accesibilidad en acción
  • Campo de pruebas: Prueba características con teclado y lectores de pantalla
  • Educación al cliente: Muestra a stakeholders por qué importa la accesibilidad
  • Pieza de portafolio: Prueba comprensión de diseño inclusivo
  • Guía de referencia: Copia patrones a otras rutas

💡 Lista de Verificación de Testing de Accesibilidad

Usa tu ruta /accesibilidad para verificar:

  1. ✅ Navega con Tab por todos los elementos interactivos
  2. ✅ Prueba con lector de pantalla (VoiceOver, NVDA, JAWS)
  3. ✅ Verifica contraste de color con DevTools
  4. ✅ Navega usando solo teclado
  5. ✅ Prueba con zoom del navegador al 200%
  6. ✅ Verifica que etiquetas ARIA sean significativas
  7. ✅ Comprueba jerarquía de encabezados
  8. ✅ Ejecuta auditoría de accesibilidad en Lighthouse

🎓 Explicaciones Pedagógicas

Metodología de Auditoría de Accesibilidad

La auditoría sistemática de accesibilidad involucra múltiples perspectivas:

Testing automatizado:

  • Lighthouse: Puntuaciones de rendimiento, accesibilidad, SEO, PWA
  • axe-core: Extensión de navegador para chequeos detallados de accesibilidad
  • WAVE: Herramienta web de evaluación de accesibilidad

Testing manual:

  • Navegación por teclado: Tabulación a través de todos los elementos interactivos
  • Testing con lectores de pantalla: Usa NVDA/JAWS/VoiceOver
  • Contraste de colores: Herramientas como WebAIM contrast checker
  • Gestión de foco: Asegura orden de foco lógico e indicadores visibles

Testing de usuario:

  • Cognitive walkthroughs: Recorre tareas desde perspectiva de usuario
  • Usuarios de tecnologías asistivas: Testing real con audiencias objetivo
  • Testing de rendimiento: Prueba en conexiones lentas y dispositivos antiguos

Estrategias de Optimización de Rendimiento

Los Core Web Vitals miden experiencia de usuario real:

  • LCP (Largest Contentful Paint): Rendimiento de carga (< 2.5s)
  • FID (First Input Delay): Interactividad (< 100ms)
  • CLS (Cumulative Layout Shift): Estabilidad visual (< 0.1)

Optimizaciones específicas de Tailwind:

  • Purgado de CSS: Remueve automáticamente clases utility no utilizadas
  • CSS crítico: Inline estilos above-the-fold
  • Imágenes responsive: Usa srcset y atributos alt apropiados
  • Optimización de fuentes: Subset de fuentes y usa font-display: swap

Preguntas Críticas del Atelier

Siguiendo nuestra metodología del atelier, reflexiona sobre estas preguntas:

Exploración

  • ¿Cuál hallazgo de auditoría de accesibilidad te sorprendió más sobre tu interfaz?
  • ¿Cómo cambiaron las métricas de rendimiento tu comprensión de “suficientemente bueno”?

Reflexión

  • ¿A quién excluyen actualmente tus decisiones de diseño y rendimiento, y cómo respondiste?
  • ¿Cuáles optimizaciones de rendimiento tuvieron el mayor impacto versus esfuerzo?

Conceptualización

  • ¿Qué es “suficiente” rendimiento para tu audiencia específica y caso de uso?
  • ¿Cómo se relacionan accesibilidad y rendimiento con conceptos más amplios de inclusión digital?

Producción

  • ¿Son tus mejoras de accesibilidad y rendimiento mínimas pero impactantes?
  • ¿Qué tan mantenibles son tus patrones de optimización para características futuras?

Exhibición

  • ¿Cómo demostrarás mejoras antes/después de accesibilidad y rendimiento?
  • ¿Qué escenarios específicos usarás para mostrar diseño inclusivo?

Preguntas Críticas

Preguntas de Reflexión

  • ¿Cómo cambia el sistema de utilidades de Tailwind tu enfoque hacia implementación de accesibilidad?
  • ¿Cuál es la relación entre diseño visual y accesibilidad cognitiva?
  • ¿Cómo puede la codificación crítica moldear nuestra comprensión de inclusión digital y justicia?
  • ¿Cómo representa tu interfaz optimizada tus valores como diseñador inclusivo?

Preguntas de Ética

  • ¿Cuáles necesidades se priorizan en tus decisiones de accesibilidad y rendimiento?
  • ¿Cómo abordan o perpetúan tus optimizaciones las divisiones digitales?
  • ¿Qué ocurre cuando requisitos de rendimiento chocan con necesidades de accesibilidad?

Preguntas de Filosofía Técnica

  • ¿Es “accesible por defecto” alcanzable en sistemas utility-first, o requiere consideración explícita?
  • ¿Cómo se relaciona la optimización de rendimiento con sostenibilidad e impacto ambiental?
  • ¿Qué significa “diseñar para los márgenes” en rendimiento web y accesibilidad?

🏗️ Scaffold Mínimo de Repositorio

Aquí tienes un punto de partida completo para tu trabajo de accesibilidad y rendimiento:

a11y-rendimiento-tailwind/
├── index.html
├── src/
│   ├── main.js
│   ├── a11y-audit.js
│   ├── performance-optimizer.js
│   └── style.css
├── lighthouse-report.html
└── tailwind.config.js

src/a11y-audit.js:

// Utilidades de auditoría de accesibilidad
class AccessibilityAuditor {
	constructor() {
		this.issues = [];
		this.init();
	}

	init() {
		// Comprueba problemas comunes de accesibilidad
		this.checkSkipLinks();
		this.checkHeadings();
		this.checkAltText();
		this.checkColorContrast();
		this.checkFocusManagement();
	}

	checkSkipLinks() {
		const skipLinks = document.querySelectorAll('a[href^="#"]:first-child');
		if (skipLinks.length === 0) {
			this.logIssue('Falta skip link para navegación por teclado');
		}
	}

	checkHeadings() {
		const headings = document.querySelectorAll('h1, h2, h3, h4, h5, h6');
		let previousLevel = 0;

		headings.forEach((heading, index) => {
			const level = parseInt(heading.tagName.charAt(1));

			if (index === 0 && level !== 1) {
				this.logIssue('El primer encabezado debe ser H1');
			}

			if (level > previousLevel + 1) {
				this.logIssue(`Salto de nivel de encabezado: ${previousLevel} a ${level}`);
			}

			previousLevel = level;
		});
	}

	checkAltText() {
		const images = document.querySelectorAll('img');
		images.forEach((img) => {
			if (!img.alt && !img.hasAttribute('aria-hidden')) {
				this.logIssue(`Imagen sin texto alt: ${img.src}`);
			}
		});
	}

	checkColorContrast() {
		// Esto se integraría con una librería de chequeo de contraste
		console.log('Chequeo de contraste de colores: Usa WebAIM contrast checker o axe-core');
	}

	checkFocusManagement() {
		const focusableElements = document.querySelectorAll(
			'a[href], button, input, textarea, select, [tabindex]:not([tabindex="-1"])'
		);

		if (focusableElements.length === 0) {
			this.logIssue('No se encontraron elementos enfocables');
		}
	}

	logIssue(message) {
		this.issues.push(message);
		console.warn('Problema de A11y:', message);
	}

	generateReport() {
		return {
			issues: this.issues,
			score: Math.max(0, 100 - this.issues.length * 10),
			recommendations: this.getRecommendations(),
		};
	}

	getRecommendations() {
		return [
			'Añade skip links para usuarios de teclado',
			'Asegura jerarquía de encabezados apropiada (H1 → H2 → H3)',
			'Proporciona texto alt para todas las imágenes significativas',
			'Prueba ratios de contraste de colores (WCAG AA: 4.5:1)',
			'Verifica orden de foco e indicadores de foco visibles',
		];
	}
}

export default AccessibilityAuditor;

src/performance-optimizer.js:

// Utilidades de optimización de rendimiento
class PerformanceOptimizer {
	constructor() {
		this.metrics = {};
		this.init();
	}

	init() {
		this.measureCoreWebVitals();
		this.optimizeImages();
		this.optimizeFonts();
		this.setupLazyLoading();
	}

	measureCoreWebVitals() {
		// Largest Contentful Paint
		new PerformanceObserver((list) => {
			const entries = list.getEntries();
			const lastEntry = entries[entries.length - 1];
			this.metrics.lcp = lastEntry.startTime;
			console.log('LCP:', this.metrics.lcp);
		}).observe({ entryTypes: ['largest-contentful-paint'] });

		// First Input Delay
		new PerformanceObserver((list) => {
			const entries = list.getEntries();
			entries.forEach((entry) => {
				this.metrics.fid = entry.processingStart - entry.startTime;
				console.log('FID:', this.metrics.fid);
			});
		}).observe({ entryTypes: ['first-input'] });

		// Cumulative Layout Shift
		new PerformanceObserver((list) => {
			let clsValue = 0;
			const entries = list.getEntries();

			entries.forEach((entry) => {
				if (!entry.hadRecentInput) {
					clsValue += entry.value;
				}
			});

			this.metrics.cls = clsValue;
			console.log('CLS:', this.metrics.cls);
		}).observe({ entryTypes: ['layout-shift'] });
	}

	optimizeImages() {
		const images = document.querySelectorAll('img[loading="lazy"]');

		if ('IntersectionObserver' in window) {
			const imageObserver = new IntersectionObserver((entries) => {
				entries.forEach((entry) => {
					if (entry.isIntersecting) {
						const img = entry.target;
						img.src = img.dataset.src;
						imageObserver.unobserve(img);
					}
				});
			});

			images.forEach((img) => imageObserver.observe(img));
		}
	}

	optimizeFonts() {
		// Usa font-display: swap para mejor rendimiento
		const style = document.createElement('style');
		style.textContent = `
      @font-face {
        font-family: 'CustomFont';
        src: url('./fonts/custom.woff2') format('woff2');
        font-display: swap;
      }
    `;
		document.head.appendChild(style);
	}

	setupLazyLoading() {
		// Carga lazy de componentes
		this.loadComponent = (componentName) => {
			return import(`./components/${componentName}.js`);
		};
	}

	generateReport() {
		return {
			metrics: this.metrics,
			recommendations: this.getRecommendations(),
			score: this.calculatePerformanceScore(),
		};
	}

	calculatePerformanceScore() {
		const { lcp = 0, fid = 0, cls = 0 } = this.metrics;

		// Scoring simple basado en umbrales de Core Web Vitals
		let score = 100;

		if (lcp > 2500) score -= 30;
		else if (lcp > 4000) score -= 50;

		if (fid > 100) score -= 20;
		else if (fid > 300) score -= 40;

		if (cls > 0.1) score -= 20;
		else if (cls > 0.25) score -= 40;

		return Math.max(0, score);
	}

	getRecommendations() {
		return [
			'Implementa carga lazy para imágenes below the fold',
			'Usa font-display: swap para fuentes web',
			'Minimiza y comprime bundles CSS/JS',
			'Implementa service worker para caché',
			'Optimiza imágenes (formato WebP, sizing apropiado)',
		];
	}
}

export default PerformanceOptimizer;

Referencias


Siguiente: S6 - Build y Deploy →