Los errores que ya no deberías cometer
El catálogo de los fallos del track agrupados por familia, con el síntoma que producen, la causa real y el sitio donde se resolvió cada uno.
Geometría, nitidez y coordenadas
El gráfico se ve borroso. El búfer del lienzo tiene el tamaño CSS en lugar del tamaño en píxeles de dispositivo. Se corrige multiplicando por el ratio y compensando con una transformación, y hay que rehacerlo cuando el ratio cambia, no solo al montar.
Una línea de un píxel se ve gris y de dos píxeles de grosor. Cae entre dos filas de píxeles. En lienzo, desplaza medio píxel; en SVG, shape-rendering="crispEdges". Y si las dos capas tienen que alinearse, usa el mismo criterio en las dos, según el enfoque híbrido.
El tooltip aparece desplazado, y más cuanto más a la derecha. El sistema de coordenadas del evento y el del dibujo no coinciden. Las tres causas: no restar la caja del elemento, no dividir por el ratio de píxeles al leer del lienzo de aciertos, y redondear el tamaño del búfer sin ajustar el viewBox de la capa de encima.
El gráfico entero es NaN y no se dibuja nada. Un dominio degenerado: todos los valores iguales, o un array vacío. La escala divide por cero. Se trata devolviendo el punto medio del rango, y hay que hacerlo en la escala, no en cada punto de uso.
Al reducir el contenedor, todo se descoloca. Coordenadas negativas porque el ancho menos los márgenes ha dado un número menor que cero. Un Math.max(0, ...) en el cálculo de dimensiones lo evita.
El panel se arrastra al redimensionar. El redibujado no se difiere a un fotograma, o se está leyendo el tamaño con getBoundingClientRect dentro del propio observador. Los dos se resuelven en el patrón de componente de gráfico.
SVG generado por código
Los elementos no aparecen aunque estén en el DOM. createElement en lugar de createElementNS. El elemento existe, tiene el nombre correcto y pertenece al namespace de HTML, así que no es un elemento SVG. El inspector no lo distingue a simple vista, y por eso este fallo se busca durante horas. Está en createElementNS y el namespace.
El gradiente no se ve, o se ve como un color plano. Casi siempre es una caja envolvente con una dimensión nula, típicamente una línea horizontal o vertical, con el gradiente declarado en unidades de caja. La explicación completa está en gradientUnits.
Al meter dos iconos en la misma página, uno pierde su gradiente. Colisión de identificadores: los id de un SVG en línea son globales del documento. Está en las referencias por id y el ámbito global.
La sombra o el desenfoque salen cortados. La región del filtro tiene un tamaño por defecto que no cubre lo que el efecto expande. Se amplía con x, y, width y height en el filter.
El SVG exportado no usa la fuente correcta. Un SVG cargado como imagen no puede traer recursos externos, incluidas las fuentes. Está en las fuentes en un SVG exportado.
El fichero descargado no abre en otras aplicaciones. Falta el xmlns en la raíz. Dentro del HTML no hace falta; fuera, sí.
Optimizar el SVG rompió los estilos. Un plugin de optimización ha convertido formas en path, o ha quitado atributos que un selector usaba, o ha renombrado identificadores. Está en SVGO y los plugins que rompen.
El gráfico se reconstruye entero en cada actualización. innerHTML vacío y volver a crear. Se pierden foco, transiciones e identidad de los elementos. La alternativa está en actualizar en vez de recrear.
Representación: los fallos que no dan ningún error
Estos son los peores, porque el código funciona perfectamente.
Las barras no empiezan en cero. La barra codifica con longitud y una longitud sin origen no significa nada. En un gráfico de líneas, en cambio, no truncar puede ser el error.
Los círculos escalan con el radio. El área crece con el cuadrado, así que la diferencia se exagera cuadráticamente. La corrección es la raíz cuadrada.
Un icono el doble de grande para el doble de valor. El mismo error, con imágenes, y por tanto sin ninguna línea de código sospechosa.
El eje dual. El punto de cruce y la correlación aparente los eliges tú al elegir los dos rangos. Las tres alternativas están en los errores clásicos.
La paleta divergente centrada en la mitad de los datos. El neutro cae en un valor arbitrario y el gráfico afirma que crecer un poco es malo. Se comprueba mirando de qué color sale el cero.
Una paleta cualitativa para un dato ordenado. El lector no puede deducir el orden y tiene que memorizar la leyenda.
El arcoíris en un mapa de calor. Luminosidad no monótona: crea fronteras que los datos no tienen y esconde cambios donde sí los hay.
Rojo y verde como único par. Ilegible para una parte apreciable de los lectores. Naranja y azul hacen el mismo trabajo.
Las categorías en orden alfabético. Ordenar por valor es la mejora más barata que existe, salvo en los tres casos donde el orden significa algo.
Un apilado donde la pregunta es sobre la serie del medio. Solo la serie inferior tiene línea base común; las demás flotan y no se pueden comparar.
Suavizar una serie discreta. Una curva suave afirma que hubo valores intermedios. Con curveBasis, además, la curva ni siquiera pasa por los datos.
El título describe los ejes. Un título sin verbo no dice nada que las etiquetas no dijeran ya.
Una leyenda con cuatro entradas. El etiquetado directo elimina el bucle de consulta y libera el color como canal.
Pequeños múltiplos con escala por panel y sin avisar. El lector asume escala compartida y compara alturas que no son comparables.
Rendimiento, accesibilidad y arquitectura
Diez mil nodos SVG que se mueven. El coste por nodo domina. La respuesta es un lienzo para los datos, no trucos para reducir nodos, que acaban en el mismo sitio renunciando a lo que hacía valioso el SVG.
Un filtro por elemento. Cien elementos con sombra pueden costar más que diez mil sin ella. El filtro va en el grupo.
getImageData en el bucle de dibujado sin willReadFrequently. Fuerza una sincronización con la GPU en cada llamada. La opción es obligatoria en el lienzo de aciertos y contraproducente en el visible.
Aciertos fantasma en la selección por color. Alfa distinto de 255, antialiasing en los bordes, o lectura sin multiplicar por el ratio. Los tres están en la interacción.
Un ResizeObserver que nadie desconecta. Mantiene vivo el contenedor entero. Toda función de montaje debe devolver una de destrucción.
Elementos duplicados tras actualizaciones rápidas. Nodos con una transición de salida pendiente que el siguiente join vuelve a capturar. Está en el join de datos.
Un lienzo sin contenido alternativo. No hay nada que leer, nada que buscar, nada que traducir. La tabla dentro del elemento es el único canal.
El color como único canal para distinguir series. Falla con deficiencia de visión del color, en escala de grises y en una fotocopia.
import * as d3 con acceso dinámico a propiedades. Impide la eliminación de código muerto y arrastra el paquete entero.
Dos rutas de renderizado que calculan cada una por su lado. Divergen, y nadie se entera hasta que alguien pone las dos imágenes juntas.
Medición de texto dentro de la capa de cálculo. Rompe la pureza y con ella el render en servidor y las pruebas sin navegador.