defs y el contenido que no se pinta
Qué elementos de SVG nunca se renderizan por sí solos, por qué defs no es obligatorio y sí recomendable, y el orden de declaración en un documento con referencias.
La lista de los que no se pintan
Estos elementos no producen ningún píxel por el hecho de estar en el documento, estén donde estén:
defs, symbol, clipPath, mask, pattern, marker, filter, linearGradient, radialGradient, stop, metadata, title, desc, script, style y foreignObject cuando no tiene un contexto que lo soporte.
Cada uno lo declara por sí mismo. Un linearGradient colocado directamente como hijo del svg raíz, sin defs alrededor, no dibuja una franja de color en medio del lienzo: no dibuja nada, y se puede referenciar exactamente igual.
<!-- Esto funciona perfectamente, sin defs --><svg viewBox="0 0 100 60" width="200"> <linearGradient id="g"> <stop offset="0" stop-color="#89b4fa" /> <stop offset="1" stop-color="#cba6f7" /> </linearGradient> <rect width="100" height="60" fill="url(#g)" /></svg>Entonces, ¿para qué sirve defs?
Qué aporta defs de verdad
Tres cosas, y ninguna es ocultar.
Convención. Un lector que abre el fichero sabe que todo lo que hay dentro de defs es material de referencia y que el dibujo real empieza después. En un SVG de trescientas líneas eso vale mucho.
Un sitio para lo que sí se pintaría. Aquí está el uso funcional. path, g, circle y compañía sí se renderizan. Si quieres definir una forma para instanciarla varias veces con use sin que aparezca también la original, tienes que meterla en defs, que es el único contenedor que suprime el renderizado de sus hijos.
<svg viewBox="0 0 200 60" width="400"> <defs> <g id="pieza"> <circle cx="0" cy="0" r="12" fill="#a6e3a1" /> <circle cx="0" cy="0" r="5" fill="#11111b" /> </g> </defs> <use href="#pieza" x="30" y="30" /> <use href="#pieza" x="100" y="30" /> <use href="#pieza" x="170" y="30" /></svg>Sin el defs, el grupo original se pintaría en el origen del lienzo además de las tres instancias.
Un punto de anclaje para las herramientas. Los optimizadores tratan defs de forma especial: buscan ahí las definiciones sin usar para eliminarlas, y saben que un id dentro de defs es probablemente un objetivo de referencia y no un simple nombre.
defs no es display none
La distinción importa porque las dos cosas se parecen y se comportan distinto.
Un elemento con display: none no se renderiza y no se puede referenciar como contenido pintado. Un clipPath con display: none sigue funcionando como recorte, porque su papel no es pintarse. Pero un g con display: none referenciado por un use no aparece, porque lo que el use instancia es contenido renderizable y el display: none se hereda al clon.
Ese es un bug clásico y merece la regla explícita: el contenido que va a instanciar un use se esconde metiéndolo en defs, nunca con display: none. visibility: hidden tiene el mismo problema, y con un matiz peor: se puede revertir en un descendiente, así que produce resultados a medias.
La contrapartida es que un elemento dentro de defs sí participa en la cascada. Una regla circle { fill: red } alcanza a los círculos que hay dentro de defs, y su valor computado viaja al clon. Eso es útil (te permite tematizar el original) y sorprendente la primera vez que un selector genérico afecta a algo que creías fuera del documento.
El orden de declaración no importa
Las referencias por id en SVG se resuelven contra el documento completo, no contra lo que ya se ha parseado. Un fill="url(#g)" funciona aunque el gradiente esté declarado más abajo.
<svg viewBox="0 0 100 60" width="200"> <rect width="100" height="60" fill="url(#g)" /> <defs> <linearGradient id="g"> <stop offset="0" stop-color="#f38ba8" /> <stop offset="1" stop-color="#fab387" /> </linearGradient> </defs></svg>Esto es distinto de HTML, donde el orden sí manda para muchas cosas, y es lo que permite que las herramientas de exportación coloquen el bloque defs al final sin romper nada. Dicho esto, la convención de ponerlo al principio existe por una razón práctica: cuando lees un fichero de arriba abajo, ver primero las definiciones te ahorra saltar.
Un caso donde el orden sí importa: si dos elementos comparten el mismo id, gana el primero en el documento. Es la puerta de entrada al problema de colisión de identificadores, que se trata a fondo en las referencias por id y el ámbito global.