# yukyu-component-doctor > Doctor de Componentes para YuKyuDATA - Diagnóstico y reparación de componentes rotos, detección de bugs visuales, memory leaks, problemas de accesibilidad, inconsistencias CSS, y refactoring de código legacy. Incluye herramientas de auditoría automatizada. - Author: jokken79 - Repository: jokken79/YuKyuDATA-app1.0v - Version: 20260122213326 - Stars: 1 - Forks: 0 - Last Updated: 2026-02-06 - Source: https://github.com/jokken79/YuKyuDATA-app1.0v - Web: https://mule.run/skillshub/@@jokken79/YuKyuDATA-app1.0v~yukyu-component-doctor:20260122213326 --- --- name: yukyu-component-doctor description: "Doctor de Componentes para YuKyuDATA - Diagnóstico y reparación de componentes rotos, detección de bugs visuales, memory leaks, problemas de accesibilidad, inconsistencias CSS, y refactoring de código legacy. Incluye herramientas de auditoría automatizada." --- # YuKyu Component Doctor Sistema experto para diagnosticar, reparar y mejorar componentes del frontend. Especializado en detectar y corregir problemas comunes en aplicaciones web empresariales. ## Cuándo Usar Este Skill - Componentes que no se renderizan correctamente - Estilos rotos o inconsistentes - Memory leaks en JavaScript - Problemas de accesibilidad - Bugs visuales en dark/light mode - Refactoring de código legacy - Auditorías de calidad de código --- ## Diagnóstico Rápido ### Script de Auditoría Automática Ejecuta este script en la consola del navegador para detectar problemas comunes: ```javascript (function auditComponents() { const issues = []; // 1. Detectar emojis usados como iconos document.querySelectorAll('button, a, span').forEach(el => { if (/[\u{1F300}-\u{1F9FF}]/u.test(el.textContent)) { issues.push({ type: 'emoji-icon', severity: 'medium', element: el, message: 'Emoji usado como icono - usar SVG en su lugar' }); } }); // 2. Detectar elementos clickeables sin cursor-pointer document.querySelectorAll('button, a, [onclick], [data-action]').forEach(el => { const cursor = getComputedStyle(el).cursor; if (cursor !== 'pointer') { issues.push({ type: 'missing-cursor', severity: 'low', element: el, message: 'Elemento clickeable sin cursor-pointer' }); } }); // 3. Detectar imágenes sin alt document.querySelectorAll('img:not([alt])').forEach(el => { issues.push({ type: 'missing-alt', severity: 'high', element: el, message: 'Imagen sin atributo alt' }); }); // 4. Detectar inputs sin label document.querySelectorAll('input, select, textarea').forEach(el => { const id = el.id; const label = id ? document.querySelector(`label[for="${id}"]`) : null; const ariaLabel = el.getAttribute('aria-label'); if (!label && !ariaLabel) { issues.push({ type: 'missing-label', severity: 'high', element: el, message: 'Input sin label asociado' }); } }); // 5. Detectar problemas de contraste (aproximado) document.querySelectorAll('*').forEach(el => { const style = getComputedStyle(el); const color = style.color; const bg = style.backgroundColor; // Simplificado - en producción usar librería de contraste }); // 6. Detectar z-index wars const zIndexes = []; document.querySelectorAll('*').forEach(el => { const z = getComputedStyle(el).zIndex; if (z !== 'auto' && parseInt(z) > 100) { zIndexes.push({ element: el, zIndex: parseInt(z) }); } }); if (zIndexes.length > 5) { issues.push({ type: 'z-index-war', severity: 'medium', elements: zIndexes, message: `${zIndexes.length} elementos con z-index > 100` }); } // Reporte console.group('🏥 Component Doctor - Audit Report'); console.log(`Total issues: ${issues.length}`); const grouped = issues.reduce((acc, issue) => { acc[issue.severity] = acc[issue.severity] || []; acc[issue.severity].push(issue); return acc; }, {}); if (grouped.high?.length) { console.group('🔴 High Severity'); grouped.high.forEach(i => console.log(i.message, i.element)); console.groupEnd(); } if (grouped.medium?.length) { console.group('🟡 Medium Severity'); grouped.medium.forEach(i => console.log(i.message, i.element)); console.groupEnd(); } if (grouped.low?.length) { console.group('🟢 Low Severity'); grouped.low.forEach(i => console.log(i.message, i.element)); console.groupEnd(); } console.groupEnd(); return issues; })(); ``` --- ## Problemas Comunes y Soluciones ### 1. Modal No Se Muestra **Síntomas:** - Modal existe en DOM pero no es visible - Click en botón no hace nada **Diagnóstico:** ```javascript const modal = document.getElementById('my-modal'); console.log('Display:', getComputedStyle(modal).display); console.log('Visibility:', getComputedStyle(modal).visibility); console.log('Opacity:', getComputedStyle(modal).opacity); console.log('Z-Index:', getComputedStyle(modal).zIndex); ``` **Soluciones:** ```css /* Problema: display: none que no cambia */ .modal { display: flex; /* Siempre flex */ visibility: hidden; opacity: 0; pointer-events: none; transition: opacity 0.3s, visibility 0.3s; } .modal.active { visibility: visible; opacity: 1; pointer-events: auto; } ``` ```javascript // Problema: clase no se agrega function openModal(id) { const modal = document.getElementById(id); if (!modal) { console.error(`Modal ${id} not found`); return; } modal.classList.add('active'); document.body.style.overflow = 'hidden'; // Prevent scroll } ``` --- ### 2. Dropdown No Funciona **Síntomas:** - Menú no aparece al hacer click - Menú aparece pero no se cierra **Diagnóstico:** ```javascript const dropdown = document.querySelector('.dropdown'); const menu = dropdown.querySelector('.dropdown-menu'); console.log('Menu visibility:', getComputedStyle(menu).visibility); console.log('Event listeners:', getEventListeners(dropdown)); // Solo en Chrome DevTools ``` **Solución Completa:** ```html ``` ```css .dropdown { position: relative; } .dropdown-menu { position: absolute; top: 100%; left: 0; min-width: 200px; background: var(--glass-bg); backdrop-filter: blur(12px); border: 1px solid var(--glass-border); border-radius: var(--radius-md); opacity: 0; visibility: hidden; transform: translateY(-10px); transition: all 0.2s ease; z-index: 100; } .dropdown.open .dropdown-menu { opacity: 1; visibility: visible; transform: translateY(0); } ``` ```javascript // Event delegation para todos los dropdowns document.addEventListener('click', (e) => { const trigger = e.target.closest('.dropdown-trigger'); // Cerrar todos los dropdowns abiertos document.querySelectorAll('.dropdown.open').forEach(d => { if (!d.contains(e.target)) { d.classList.remove('open'); d.querySelector('.dropdown-trigger') ?.setAttribute('aria-expanded', 'false'); } }); // Toggle el dropdown clickeado if (trigger) { const dropdown = trigger.closest('.dropdown'); const isOpen = dropdown.classList.toggle('open'); trigger.setAttribute('aria-expanded', isOpen); } }); // Cerrar con ESC document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { document.querySelectorAll('.dropdown.open').forEach(d => { d.classList.remove('open'); }); } }); ``` --- ### 3. Tabla No Se Actualiza **Síntomas:** - Datos cambian pero tabla sigue igual - Filas duplicadas o faltantes **Diagnóstico:** ```javascript console.log('Table rows:', document.querySelectorAll('tbody tr').length); console.log('Data length:', App.state.employees.length); ``` **Solución:** ```javascript function renderTable(data) { const tbody = document.querySelector('#employees-table tbody'); // IMPORTANTE: Limpiar antes de renderizar tbody.innerHTML = ''; if (data.length === 0) { tbody.innerHTML = `
📋

データがありません

`; return; } // Usar DocumentFragment para mejor performance const fragment = document.createDocumentFragment(); data.forEach(emp => { const tr = document.createElement('tr'); tr.dataset.id = emp.id; tr.innerHTML = ` ${escapeHtml(emp.name)} ${emp.remaining_days} ${emp.used_days} `; fragment.appendChild(tr); }); tbody.appendChild(fragment); } ``` --- ### 4. Memory Leak en Componentes **Síntomas:** - App se vuelve lenta con el tiempo - Uso de memoria crece constantemente **Diagnóstico:** ```javascript // En Chrome DevTools > Memory > Take heap snapshot // Buscar detached DOM nodes // O usar Performance Monitor // DevTools > More tools > Performance monitor ``` **Causas Comunes y Soluciones:** ```javascript // ❌ PROBLEMA: Event listeners no removidos class Modal { constructor() { this.handleEsc = (e) => { if (e.key === 'Escape') this.close(); }; document.addEventListener('keydown', this.handleEsc); } close() { this.element.remove(); // ❌ Event listener sigue activo! } } // ✅ SOLUCIÓN: Remover listeners en cleanup class Modal { constructor() { this.handleEsc = (e) => { if (e.key === 'Escape') this.close(); }; document.addEventListener('keydown', this.handleEsc); } close() { document.removeEventListener('keydown', this.handleEsc); this.element.remove(); } } // ❌ PROBLEMA: setInterval sin limpiar function startPolling() { setInterval(() => { fetchData(); }, 5000); } // ✅ SOLUCIÓN: Guardar referencia y limpiar let pollInterval = null; function startPolling() { pollInterval = setInterval(() => { fetchData(); }, 5000); } function stopPolling() { if (pollInterval) { clearInterval(pollInterval); pollInterval = null; } } // ❌ PROBLEMA: Closures que retienen referencias function createHandlers(elements) { elements.forEach(el => { el.addEventListener('click', () => { // Esta closure retiene 'elements' completo console.log(elements.length); }); }); } // ✅ SOLUCIÓN: Evitar retener referencias innecesarias function createHandlers(elements) { const count = elements.length; elements.forEach(el => { el.addEventListener('click', () => { console.log(count); // Solo retiene el número }); }); } ``` --- ### 5. Estilos Dark/Light Mode Inconsistentes **Síntomas:** - Elementos blancos en dark mode - Texto ilegible al cambiar tema - Bordes invisibles **Diagnóstico:** ```javascript // Verificar qué tema está activo console.log('Theme:', document.documentElement.getAttribute('data-theme')); // Verificar CSS variables const styles = getComputedStyle(document.documentElement); console.log('--glass-bg:', styles.getPropertyValue('--glass-bg')); console.log('--text-primary:', styles.getPropertyValue('--text-primary')); ``` **Solución - Checklist de Variables:** ```css /* Asegurar que TODAS las variables tengan override en light mode */ [data-theme="light"] { /* Backgrounds */ --glass-bg: rgba(255, 255, 255, 0.85); --bg-base: #f8fafc; --bg-card: #ffffff; /* Text */ --text-primary: #1e293b; --text-secondary: #64748b; --text-muted: #94a3b8; /* Borders */ --border-color: rgba(0, 0, 0, 0.1); --glass-border: rgba(0, 0, 0, 0.08); /* Shadows */ --shadow-md: 0 4px 6px rgba(0, 0, 0, 0.07); } /* Verificar elementos específicos */ [data-theme="light"] .glass-panel { background: var(--glass-bg); color: var(--text-primary); border-color: var(--border-color); } [data-theme="light"] .input-glass { background: rgba(255, 255, 255, 0.9); color: var(--text-primary); border: 1px solid rgba(0, 0, 0, 0.15); } ``` --- ### 6. Componente No Responde a Eventos **Síntomas:** - Click no hace nada - Hover no funciona - Eventos keyboard ignorados **Diagnóstico:** ```javascript // Verificar si hay elementos superpuestos const el = document.querySelector('.my-button'); const rect = el.getBoundingClientRect(); const topEl = document.elementFromPoint( rect.left + rect.width/2, rect.top + rect.height/2 ); console.log('Top element:', topEl); console.log('Is same?', topEl === el); // Verificar pointer-events console.log('pointer-events:', getComputedStyle(el).pointerEvents); ``` **Soluciones:** ```css /* Problema: Elemento invisible sobre el botón */ .overlay { pointer-events: none; /* Permite click-through */ } /* Problema: Parent con overflow */ .container { overflow: visible; /* O ajustar z-index del hijo */ } /* Problema: Elemento disabled */ .btn:disabled { pointer-events: none; opacity: 0.5; } ``` ```javascript // Verificar que el evento se está registrando const btn = document.querySelector('.my-button'); btn.addEventListener('click', (e) => { console.log('Click event fired!', e); }); // Si usa event delegation, verificar el selector document.addEventListener('click', (e) => { console.log('Click target:', e.target); console.log('Closest button:', e.target.closest('.my-button')); }); ``` --- ## Refactoring Patterns ### 1. De onclick Inline a Event Delegation ```html ``` ```javascript // ❌ ANTES function handleClick(id) { ... } // ✅ DESPUÉS document.addEventListener('click', (e) => { const btn = e.target.closest('[data-action="click"]'); if (btn) { const id = btn.dataset.id; handleClick(id); } }); ``` ### 2. De innerHTML Inseguro a Template Seguro ```javascript // ❌ ANTES - Vulnerable a XSS element.innerHTML = `
${userData.name}
`; // ✅ DESPUÉS - Seguro function createUserElement(userData) { const div = document.createElement('div'); div.textContent = userData.name; // Auto-escaped return div; } // O con template seguro function escapeHtml(str) { const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } element.innerHTML = `
${escapeHtml(userData.name)}
`; ``` ### 3. De Callbacks a Async/Await ```javascript // ❌ ANTES - Callback hell function loadData(callback) { fetch('/api/data') .then(res => res.json()) .then(data => { fetch(`/api/detail/${data.id}`) .then(res => res.json()) .then(detail => { callback(detail); }); }); } // ✅ DESPUÉS - Async/await limpio async function loadData() { try { const dataRes = await fetch('/api/data'); const data = await dataRes.json(); const detailRes = await fetch(`/api/detail/${data.id}`); const detail = await detailRes.json(); return detail; } catch (error) { console.error('Error loading data:', error); throw error; } } ``` --- ## Herramientas de Debugging ### 1. CSS Debug Mode ```css /* Agregar temporalmente para ver layout */ * { outline: 1px solid rgba(255, 0, 0, 0.2) !important; } /* Ver solo ciertos elementos */ .debug * { outline: 1px solid red !important; background: rgba(255, 0, 0, 0.1) !important; } ``` ### 2. JavaScript Debug Helpers ```javascript // Logging mejorado const debug = { log: (...args) => console.log('[DEBUG]', ...args), table: (data) => console.table(data), time: (label) => console.time(label), timeEnd: (label) => console.timeEnd(label), trace: () => console.trace(), // DOM inspector inspect: (selector) => { const el = document.querySelector(selector); console.log({ element: el, computed: getComputedStyle(el), rect: el.getBoundingClientRect(), dataset: { ...el.dataset } }); } }; // Performance measurement function measurePerformance(fn, label = 'Operation') { const start = performance.now(); const result = fn(); const end = performance.now(); console.log(`${label}: ${(end - start).toFixed(2)}ms`); return result; } ``` ### 3. Network Debug ```javascript // Interceptar fetch para debugging const originalFetch = window.fetch; window.fetch = async (...args) => { console.log('[FETCH]', args[0], args[1]); const start = performance.now(); try { const response = await originalFetch(...args); console.log('[FETCH DONE]', args[0], `${(performance.now() - start).toFixed(0)}ms`); return response; } catch (error) { console.error('[FETCH ERROR]', args[0], error); throw error; } }; ``` --- ## Checklist de Reparación ### Antes de Empezar - [ ] Reproducir el bug consistentemente - [ ] Identificar el componente afectado - [ ] Revisar console errors - [ ] Revisar network errors ### Durante la Reparación - [ ] Hacer backup del código original - [ ] Cambios pequeños e incrementales - [ ] Probar después de cada cambio - [ ] Documentar la solución ### Después de Reparar - [ ] Probar en dark y light mode - [ ] Probar en diferentes viewports - [ ] Verificar accesibilidad - [ ] Verificar que no hay memory leaks - [ ] Probar edge cases --- ## Recursos ### DevTools - [Chrome DevTools](https://developer.chrome.com/docs/devtools/) - [Firefox Developer Tools](https://firefox-source-docs.mozilla.org/devtools-user/) ### Validadores - [W3C Markup Validator](https://validator.w3.org/) - [WAVE Accessibility](https://wave.webaim.org/) - [Lighthouse](https://developers.google.com/web/tools/lighthouse) ### Performance - [WebPageTest](https://www.webpagetest.org/) - [PageSpeed Insights](https://pagespeed.web.dev/)