Anotación, etiquetado directo y título
Lo que convierte un gráfico en un argumento: el título que dice el hallazgo, las etiquetas junto a los datos en lugar de una leyenda, las líneas de referencia, y el algoritmo de colocar etiquetas sin que choquen.
Un gráfico sin anotar obliga al lector a descubrir por su cuenta lo que tú ya sabes, y la mayoría no lo hace: mira tres segundos y pasa. El título, las etiquetas junto a los datos y las líneas de referencia son la diferencia entre mostrar unos números y comunicar un hallazgo, y cuestan menos código que el propio gráfico. La parte difícil no es decidir qué anotar: es colocar las etiquetas sin que se pisen.
- Escribir el título de un gráfico como una afirmación con verbo.
- Sustituir una leyenda por etiquetado directo y justificar por qué.
- Añadir líneas y bandas de referencia que den sentido a los valores.
- Resolver el solapamiento de etiquetas con un algoritmo de separación en un eje.
El título dice el hallazgo
Compara dos títulos del mismo gráfico:
- «Ventas por trimestre, 2025-2026»
- «Las ventas cayeron un 12 % en el tercer trimestre y no se han recuperado»
El primero describe los ejes, que ya están etiquetados. El segundo dice lo que hay que ver. Y no es una cuestión de tono periodístico: es que el lector medio dedica unos segundos al gráfico, y en esos segundos o lee el título o no lee nada.
Las tres reglas del título útil:
Tiene verbo. Si no puedes escribirlo con verbo, no sabes qué muestra el gráfico, y eso es un problema anterior al título.
Es verificable en la imagen. Un título que afirma algo que no se ve en el gráfico es una mentira con soporte gráfico. Si el título dice que la caída es del 12 por ciento, esa cifra tiene que poder leerse.
El subtítulo lleva el contexto. Unidades, periodo, fuente, filtros aplicados. Lo que el lector necesita para saber de qué está hablando el título, en un segundo nivel tipográfico más pequeño.
Y el corolario incómodo: si el gráfico no tiene hallazgo, quizá no debería estar en el panel. Un panel con veinte gráficos donde ninguno afirma nada es un panel que nadie mira.
Etiquetado directo en lugar de leyenda
Una leyenda obliga al lector a un bucle: mirar la línea, buscar su color en la leyenda, leer el nombre, volver a la línea, recordar. Con cuatro series, ese bucle se repite constantemente y consume la atención que debería ir a los datos. Es lo que en teoría de la carga cognitiva se llama atención dividida: dos fuentes de información que hay que integrar mentalmente porque están separadas en el espacio.
La solución es poner el nombre donde ya está mirando el ojo:
// Al final de cada linea, fuera del area de dibujo
for (const serie of series) {
const ultimo = serie.puntos[serie.puntos.length - 1];
const t = el('text', {
x: x(ultimo.fecha) + 6,
y: y(ultimo.valor),
dy: '0.32em',
fill: color(serie.nombre),
'font-size': 12,
}, [serie.nombre]);
capa.appendChild(t);
}
Y el margen derecho se reserva para ellas:
const margen = { arriba: 24, dcha: 96, abajo: 32, izda: 48 };
Ese margen derecho generoso es la marca de un gráfico de líneas bien hecho. El color deja de ser el único canal que identifica la serie, lo que resuelve de paso el problema de accesibilidad de el color en visualización.
Cuándo la leyenda sigue siendo la respuesta: cuando las marcas son demasiado pequeñas para llevar texto al lado (los puntos de una dispersión), cuando hay tantas series que etiquetarlas todas es peor que la leyenda, y cuando el mismo color significa lo mismo en varios gráficos del panel y una leyenda compartida sirve para todos.
En el caso de muchas series, la salida no es la leyenda completa: es etiquetar solo las que importan. Tres líneas con nombre y veinte en gris sin nombre comunican mejor que veintitrés entradas de leyenda.
Las referencias dan sentido a los valores
Un número aislado no es información. «Cuatrocientas incidencias» no significa nada; «cuatrocientas, cuando el objetivo son doscientas» sí. Las referencias son lo que convierte una serie en un juicio, y son cuatro:
La línea de objetivo. Horizontal, discontinua, etiquetada con su valor y su nombre. Con ella, cada punto de la serie se lee como por encima o por debajo, sin cálculo.
La banda de normalidad. Un rectángulo tenue entre dos valores, o entre percentiles históricos. Convierte «está alto» en «está fuera de lo normal», que es una afirmación distinta y mucho más útil.
El periodo anterior. La misma serie desplazada un año, en gris claro y detrás. El lector ve a la vez el valor y si es mejor o peor que lo habitual en esas fechas. Resuelve la estacionalidad sin explicarla.
Los eventos. Una línea vertical con una etiqueta corta donde ocurrió algo que explica un cambio: un despliegue, una campaña, una caída del servicio. Es la anotación que más aprecian los lectores y la que menos se pone, porque requiere tener los eventos en algún sitio. Merece la pena montarlo: convierte un gráfico de métricas en una narración.
function lineaReferencia(g, y, valor, texto) {
const py = y(valor);
g.appendChild(el('line', {
x1: 0, y1: py, x2: ancho, y2: py,
stroke: '#f9e2af', 'stroke-width': 1.5,
'stroke-dasharray': '4 4', 'shape-rendering': 'crispEdges',
}));
g.appendChild(el('text', {
x: ancho, y: py, dy: '-0.4em', 'text-anchor': 'end',
'font-size': 11, fill: '#f9e2af',
}, [texto]));
}
Una regla sobre la jerarquía visual: las referencias van detrás y más tenues que los datos. Una línea de objetivo más gruesa que la serie compite con ella y el ojo va al sitio equivocado.
Qué quitar
La anotación añade tinta, así que hay que compensar quitándola de otro sitio. Lo que sobra casi siempre:
- La leyenda, si has etiquetado directamente. Tener las dos es redundancia pura.
- El título del eje vertical, si el título del gráfico ya dice la magnitud y las etiquetas llevan la unidad.
- Las etiquetas de valor sobre cada barra, si hay eje. Elige: o eje o etiquetas. Con las dos, cada valor está escrito dos veces.
- Los decimales que nadie necesita. «1.234.567 €» en un eje se lee peor que «1,2 M €», y la precisión que se pierde no la usaba nadie.
- La rejilla, según la tarea, con el criterio que ya se estableció en construir un eje a mano.
El etiquetado directo funciona perfectamente hasta que dos series acaban a valores parecidos. Entonces las etiquetas se pisan, y el resultado es peor que la leyenda que querías evitar.
Colocar etiquetas sin solapamiento es un problema conocido y NP-difícil en su forma general, la de colocar cada etiqueta en cualquiera de varias posiciones candidatas alrededor de su punto maximizando el número de etiquetas visibles. Nadie lo resuelve exactamente en un gráfico. Lo que se usa son heurísticas, y para el caso concreto del etiquetado al final de las líneas hay una que funciona muy bien porque el problema se reduce a una sola dimensión: todas las etiquetas están en la misma columna y solo pueden moverse verticalmente.
El algoritmo es una relajación iterativa: mientras dos etiquetas consecutivas se solapen, sepáralas empujando cada una la mitad de lo que se solapan.
function separar(etiquetas, altoEtiqueta = 14, alto, iteraciones = 24) {
const orden = [...etiquetas].sort((a, b) => a.y - b.y);
for (let k = 0; k < iteraciones; k++) {
let movido = false;
for (let i = 0; i < orden.length - 1; i++) {
const a = orden[i], b = orden[i + 1];
const solape = altoEtiqueta - (b.y - a.y);
if (solape > 0) {
a.y -= solape / 2;
b.y += solape / 2;
movido = true;
}
}
// Devolver al area util lo que se haya salido
orden[0].y = Math.max(altoEtiqueta / 2, orden[0].y);
orden[orden.length - 1].y = Math.min(alto - altoEtiqueta / 2, orden[orden.length - 1].y);
if (!movido) break;
}
return orden;
}Es literalmente una simulación de colisiones en un eje, con la misma idea que la fuerza de colisión de las disposiciones de fuerzas, reducida a una dimensión y por tanto barata y determinista. Converge en pocas iteraciones y no necesita física de verdad.
Tres detalles que hacen la diferencia entre que funcione y que parezca que funciona:
Guarda la posición original y dibuja un conector. Si una etiqueta se ha desplazado más de dos o tres píxeles respecto al punto que nombra, dibuja una línea corta desde el punto hasta ella. Sin el conector, una etiqueta desplazada apunta a la serie equivocada, que es peor que no ponerla.
Fija las prioridades. Si no caben todas, la que se sacrifica no debe ser la primera que toque: ordena por importancia (la serie destacada, el valor mayor, la que el usuario ha seleccionado) y elimina desde el final. Un alto disponible dividido entre el alto de etiqueta te dice cuántas caben antes de empezar.
Mide el texto de verdad. El alto es constante, pero el ancho no, y si las etiquetas van dentro del área de dibujo también hay que resolver el eje horizontal. Ahí el problema deja de ser unidimensional y la heurística correcta cambia: prueba cuatro posiciones candidatas alrededor del punto, quédate con la primera libre, y si ninguna lo está, no pongas la etiqueta.
Y la observación general que justifica todo el esfuerzo: el etiquetado es la parte del gráfico donde más código hace falta y menos se escribe. Dibujar la serie son diez líneas; colocar las etiquetas sin que se pisen son cincuenta. Esa asimetría es la razón por la que tantos gráficos hechos a medida acaban con una leyenda: no porque la leyenda sea mejor, sino porque es la salida barata.
Coge un gráfico de líneas con cuatro o más series, quítale la leyenda y etiqueta cada línea en su extremo derecho. Cuando dos etiquetas se pisen, implementa el algoritmo de separación del callout y añade los conectores. Después ponlo al lado de la versión con leyenda y cronometra a alguien respondiendo a «cuál de las series terminó más alto» en cada una.