Accesibilidad, impresión y edición posterior
Ver las tres consecuencias del modelo de imagen que no se notan hasta que es tarde: quién puede leer el gráfico, cómo se imprime y si alguien podrá modificarlo.
Las diferencias entre raster y vector que se ven en pantalla son las menos graves. Las que de verdad condicionan un proyecto aparecen semanas después: cuando alguien pide que el gráfico sea legible con un lector de pantalla, cuando el informe hay que imprimirlo en A3, o cuando el diseñador quiere cambiar un color y descubre que ya no existe ningún sitio donde cambiarlo. Las tres tienen la misma raíz.
- Explicar por qué la accesibilidad de un gráfico depende del modelo de imagen y no del esfuerzo.
- Describir qué ocurre al imprimir un raster y un vector, y qué resolución hace falta.
- Valorar el coste de la edición posterior según lo que el proyecto haya guardado.
- Decidir qué guardar en un proyecto gráfico para no cerrar puertas.
Accesibilidad: quién puede leer el gráfico
Un lector de pantalla no ve píxeles: recorre el árbol de accesibilidad, que se construye a partir del DOM. Esto tiene una consecuencia tajante.
Un SVG en línea produce nodos en ese árbol. Puedes darle un rol, un nombre accesible con <title>, una descripción larga con <desc>, y hacer que cada región relevante sea anunciable. Un gráfico de barras en SVG puede leerse serie a serie si lo estructuras bien.
Un raster —una imagen o un canvas— es opaco. Lo único que se puede exponer es un texto alternativo desde fuera. Para una foto eso basta: “retrato de una mujer en un mercado” es toda la información que hay. Para un gráfico de datos no basta ni de lejos, porque la información es la que está dentro.
El canvas tiene un mecanismo específico: el contenido de reserva, es decir, los nodos que pones dentro del elemento <canvas>. Los navegadores no los pintan, pero los exponen al árbol de accesibilidad. Es lo mínimo exigible y es mucho mejor que nada.
<canvas id="ventas" width="600" height="300" role="img"
aria-label="Ventas trimestrales de 2026">
<table>
<caption>Ventas trimestrales de 2026 en miles de euros</caption>
<tr><th>Trimestre</th><th>Ventas</th></tr>
<tr><td>T1</td><td>412</td></tr>
<tr><td>T2</td><td>488</td></tr>
<tr><td>T3</td><td>355</td></tr>
<tr><td>T4</td><td>601</td></tr>
</table>
</canvas>
Esa tabla no se ve nunca y contiene toda la información del gráfico. Es un patrón barato, correcto y sorprendentemente poco usado.
Conviene ser honesto sobre sus límites: el contenido de reserva funciona bien para un gráfico estático. Para un canvas interactivo, donde el usuario navega, selecciona y modifica, mantener sincronizado un árbol paralelo de elementos accesibles es un trabajo real y frágil, y el resultado nunca iguala al de una interfaz construida con elementos nativos.
El error de proceso más caro de todo el gráfico web es tratar la accesibilidad como una capa que se pone encima cuando el producto ya funciona. En gráficos no funciona así, porque el modelo de imagen que elegiste el primer día ya decidió cuánta accesibilidad es alcanzable. Si el gráfico está en SVG, hacerlo accesible es añadir atributos a nodos que ya existen: horas de trabajo. Si está en canvas, hacerlo accesible es construir y mantener un modelo paralelo en el DOM que refleje el estado de una escena que solo existe en tus variables: semanas de trabajo, y una fuente permanente de desincronización. Y hay una asimetría más cruel todavía: si eliges SVG y resulta que necesitas rendimiento, migrar a canvas es mecánico, porque la geometría ya está descrita y solo cambia quién la pinta. Si eliges canvas y resulta que necesitas accesibilidad, migrar a SVG te obliga a reconstruir el modelo de escena que nunca escribiste. La regla que sale de ahí es incómoda pero correcta: cuando dudes entre canvas y SVG y el gráfico transmita información, empieza por SVG. Solo baja a canvas cuando hayas medido que SVG no llega, y entonces la migración será fácil porque ya tendrás el modelo.
Impresión: la resolución que hace falta
Una pantalla ronda los 96 píxeles CSS por pulgada; una impresora de oficina trabaja a 300 o 600 puntos por pulgada, y una imprenta a 300 líneas efectivas con más. La proporción es de tres a seis veces en cada eje, lo que significa entre nueve y treinta y seis veces más píxeles.
Un raster preparado para pantalla impreso a tamaño real se ve mal, y no hay remedio. Un vector se rasteriza a la resolución de la impresora en el momento de imprimir y sale perfecto.
Para el canvas hay una consecuencia concreta: al imprimir, el navegador manda a la impresora el búfer tal cual está, con su resolución de pantalla. No hay ningún mecanismo por el que el canvas se regenere más grande. Si el gráfico tiene que imprimirse bien, hay dos caminos.
El primero es generar una versión de alta resolución bajo demanda, redibujando la misma escena en un canvas auxiliar mucho más grande y sustituyendo la imagen en la vista de impresión.
// Regenerar la escena a 4x para imprimir
function exportarParaImprimir(dibujarEscena, ancho, alto, factor = 4) {
const c = document.createElement('canvas');
c.width = ancho * factor;
c.height = alto * factor;
const ctx = c.getContext('2d');
ctx.scale(factor, factor);
dibujarEscena(ctx); // la misma funcion, en unidades logicas
return c.toDataURL('image/png');
}
const img = new Image();
img.src = exportarParaImprimir(dibujarEscena, 600, 300, 4);
img.className = 'solo-impresion';
document.body.appendChild(img);
Ese patrón solo es posible si tu función de dibujo trabaja en unidades lógicas y recibe el contexto como parámetro. Es un argumento de diseño más para separar la escena del dispositivo.
El segundo camino es generar un SVG a partir del mismo modelo de escena. Cuesta más, pero da un resultado independiente de la resolución y además abre la puerta a la exportación a PDF.
Edición posterior: qué queda cuando el proyecto envejece
La tercera consecuencia es la menos visible y la que más dinero cuesta a lo largo del tiempo: ¿qué se puede modificar dentro de un año?
De un vector se puede modificar todo: mover un punto, cambiar un color, escalar una parte, sustituir una fuente, exportar a otro formato. La descripción sigue ahí.
De un raster no se puede modificar casi nada sin degradarlo. Cambiar un color plano exige seleccionar la región y rellenar, lo que deja bordes sucios por el antialiasing. Cambiar el tamaño degrada. Cambiar un texto es imposible sin rehacer la zona.
En el canvas esa pregunta se traduce en una decisión de arquitectura muy concreta: ¿guardas el modelo de la escena o solo el resultado?
// Malo: el estado esta en los pixeles y no hay vuelta atras
ctx.fillStyle = '#89b4fa';
ctx.fillRect(x, y, w, h);
// Bien: el estado esta en datos, el canvas es solo la proyeccion
const escena = [{ tipo: 'rect', x, y, w, h, color: '#89b4fa' }];
function pintar(ctx, escena) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
for (const f of escena) {
ctx.fillStyle = f.color;
if (f.tipo === 'rect') ctx.fillRect(f.x, f.y, f.w, f.h);
}
}
La versión de abajo cuesta diez líneas más y te da, de golpe: deshacer y rehacer, serialización a JSON, exportación a SVG, hit testing exacto, redibujado a cualquier resolución, tests unitarios sobre la escena sin necesidad de un navegador, y la posibilidad de cambiar el motor de dibujo sin tocar la lógica.
La versión de arriba te da un fichero PNG.
Qué guardar
De las tres consecuencias sale una única regla de proyecto: guarda siempre la representación de nivel más alto que tengas, y genera las demás desde ella.
Si el gráfico nace de datos, guarda los datos. Si nace de una descripción geométrica, guarda la descripción. Si nace de un programa de dibujo, guarda el fichero del programa además del exportado. El raster es siempre el último eslabón, el que se puede regenerar y el que nunca hay que tratar como fuente.
Aplicado al canvas, eso significa que el búfer de píxeles es un artefacto de salida, no el estado de tu aplicación. Cuando el búfer es el estado, has creado un producto que solo se puede mirar.