Sprites SVG
Un sprite es un único archivo SVG con un símbolo por icono. Las páginas hacen referencia a los símbolos con <use>, el navegador descarga el archivo y lo guarda en caché una sola vez, y no se ejecuta nada de JavaScript.
Obtén el sprite
El paquete incluye un sprite con todos los iconos en glyphfox/sprite.svg. Cada icono es un <symbol> cuyo id es gf- más el nombre del icono, con su propio viewBox y la configuración de trazo del conjunto.
npm install glyphfox cp node_modules/glyphfox/dist/sprite.svg public/icons/sprite.svg
<svg xmlns="http://www.w3.org/2000/svg"> <symbol id="gf-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">...</symbol> <symbol id="gf-search" viewBox="0 0 24 24" ...>...</symbol> ... </svg>
Si usas un bundler, puedes dejar que sea él quien copie el archivo y le añada un hash de contenido al nombre. En Vite, el sufijo ?url devuelve la URL final del recurso:
import spriteUrl from 'glyphfox/sprite.svg?url'; export const icon = (name) => `<svg class="icon" width="24" height="24" aria-hidden="true"><use href="${spriteUrl}#gf-${name}"></use></svg>`;
El sprite completo reúne los 419 iconos en 117.9 KB, o 15.7 KB con gzip. Un sprite con los 30 iconos que usa la estructura base de una app típica ocupa 7.5 KB, 1.3 KB con gzip; la CLI lo genera por ti, como se muestra más abajo.
Haz referencia a un símbolo
Escribe un svg exterior con el tamaño que quieras y un elemento use que apunte al símbolo. El símbolo aporta su propio viewBox, así que el elemento exterior solo necesita ancho y alto.
<button type="button" class="btn"> <svg class="icon" width="20" height="20" aria-hidden="true"> <use href="/icons/sprite.svg#gf-download"></use> </svg> Download </button>
.icon { width: 1.25em; height: 1.25em; flex-shrink: 0; vertical-align: -0.2em; } .btn:hover .icon { color: #ff7a1a; }
Los símbolos dibujan con stroke="currentColor", y color se hereda en el contenido de use, así que el icono sigue el color de texto de su padre y cualquier color que definas en el svg exterior. El ancho y el alto definidos en CSS prevalecen sobre los atributos, lo que permite que el tamaño siga al tamaño de fuente.
Solo desde el mismo origen
Los navegadores solo siguen una referencia use a un archivo que esté en el mismo origen que la página. Un sprite alojado en una CDN como jsDelivr, o en otro subdominio de tu sitio, no muestra nada, y las cabeceras CORS no cambian eso. Sirve el sprite desde tu propio origen, o descárgalo una vez e insértalo en la página:
// jsDelivr sends CORS headers, so fetch works where <use> across origins does not. const res = await fetch('https://cdn.jsdelivr.net/npm/glyphfox@1/dist/sprite.svg'); const holder = document.createElement('div'); holder.setAttribute('aria-hidden', 'true'); holder.style.cssText = 'position:absolute;width:0;height:0;overflow:hidden'; holder.innerHTML = await res.text(); document.body.prepend(holder); // now <use href="#gf-home"></use> works anywhere on the page
Incrusta el sprite en la página
Un sprite en línea son los mismos símbolos colocados una sola vez en el HTML, normalmente justo después de <body>. Las referencias usan entonces solo el fragmento, #gf-home, no hay ninguna solicitud extra y los iconos se renderizan con el primer pintado. La contrapartida es que cada página vuelve a llevar los símbolos, así que incrusta un subconjunto pequeño y no el conjunto completo.
<body> <svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" style="position:absolute;width:0;height:0;overflow:hidden"> <symbol id="gf-home" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">...</symbol> <symbol id="gf-search" viewBox="0 0 24 24" ...>...</symbol> </svg> <a href="/"><svg class="icon" aria-hidden="true"><use href="#gf-home"></use></svg> Home</a> </body>
Oculta el sprite con tamaño cero y posicionamiento absoluto, no con display:none. Algunos navegadores dejan de renderizar las máscaras que están dentro de un SVG oculto, y el estilo sólido usa máscaras para recortar sus líneas interiores.
Genera un subconjunto con la CLI
glyphfox build genera un sprite solo con los iconos que indiques, renderizados con las props que pases. Los nombres pueden venir de la línea de comandos o de un archivo con un nombre por línea; en el archivo se permiten espacios, comas y comentarios con #.
npx glyphfox build home search settings bell user --type sprite --out public/icons npx glyphfox build --from icons.txt --type sprite --weight 1.5 --out public/icons npx glyphfox build --from icons.txt --type sprite --variant solid --out public/icons/solid
# app shell home, search, settings, bell, user menu x chevron-down # badges and fusions work too file+check cloud&lock:overlap
Los nombres simples conservan su id: home pasa a ser gf-home. Las especificaciones con insignia o fusión reciben un id con los signos reemplazados: file+check pasa a ser gf-file_check, y cloud&lock:overlap, gf-cloud__lock-overlap. Abre el archivo generado una vez para copiar los ids que necesites.
Generar el sprite al tamaño en que lo muestras también te da la compensación óptica de los componentes: --size 16 escribe el trazo ligeramente más grueso que glyphfox usa para los iconos de 16 píxeles, así que un sprite para tablas densas tiene el mismo peso visual que el resto de la interfaz.
El mismo comando con --type css genera en su lugar una hoja de estilos con clases de máscara (<span class="gf gf-home">), para sitios que quieren clases de icono sin Tailwind CSS. La guía de Tailwind CSS explica cómo se comportan los iconos de máscara.
Opciones de compilación
Un sprite es estático, así que las props compartidas se convierten en flags de compilación. Estas son las que importan en un sprite; cada flag significa lo mismo que la prop del mismo nombre en la tabla de props.
| Flag | Efecto en los símbolos |
|---|---|
--type sprite | genera sprite.svg (el tipo predeterminado); css y package son los otros tipos |
--out DIR | directorio de salida; por defecto, el directorio actual |
--from FILE | lee los nombres de un archivo |
--weight N | peso del trazo de 1 a 3, escrito en cada símbolo |
--size N | el tamaño en píxeles al que lo muestras, para la compensación óptica del trazo por debajo de 24 |
--corners sharp | uniones y extremos rectos |
--variant duotone, --variant solid | el estilo de todos los símbolos |
--color C, --secondary-color C | colores fijos en lugar de currentColor |
--secondary-opacity N | opacidad de la capa duotone o del relleno sólido |
--rotate DEG, --flip-x, --flip-y | fija en los símbolos una rotación o un reflejo |
Qué puede cambiar CSS y qué no
Lo que dibuja use es una copia del símbolo. El CSS aplicado al svg exterior o al elemento use solo llega a esa copia por herencia, y por eso funciona color. Con las propiedades que define el propio símbolo no ocurre lo mismo: cada símbolo lleva stroke-width, stroke-linecap y stroke-linejoin como atributos, y estos prevalecen sobre cualquier valor heredado desde fuera. Definir stroke-width en .icon no tiene ningún efecto.
Decide el peso, las esquinas y el estilo al generar el sprite, y genera dos sprites si necesitas dos pesos. Cuando necesites controlar por instancia el peso, un segundo color o el movimiento, renderiza en su lugar SVG en línea con un componente o con toSvg.
Las animaciones del icono completo siguen funcionando, porque actúan sobre el elemento exterior: .icon.spin { animation: spin 1s linear infinite } hace girar un icono del sprite como a cualquier otro elemento.
Accesibilidad
Un icono de sprite es un svg exterior que escribes tú, así que te corresponde a ti definir su semántica. Oculta los iconos decorativos con aria-hidden="true". Para un icono que transmite significado por sí solo, convierte el elemento exterior en una imagen con nombre:
<svg class="icon" role="img" aria-label="Payment failed"> <use href="/icons/sprite.svg#gf-circle-alert"></use> </svg>
Los botones que solo contienen un icono necesitan la etiqueta en el propio botón, como se explica en la guía de accesibilidad.
Caché
Un sprite externo es una única solicitud que comparten todas las páginas. Sírvelo con una caché de larga duración y un hash de contenido en el nombre del archivo, como sprite.3f9a1c.svg, para que cada build nuevo cambie la URL en lugar de tener que esperar a que caduquen las cachés. Los bundlers lo hacen por ti cuando importas el archivo. Cada URL de sprite distinta se descarga una vez por página, sin importar cuántos iconos hagan referencia a ella.
Errores comunes
- Un sprite en otro origen
- Un
useexterno que apunta a una CDN o a otro subdominio muestra una caja vacía; el navegador solo informa de la solicitud bloqueada en la consola. Aloja el archivo en el origen de la página o incrústalo en ella. - Capas duotone en el sprite completo
- Los 42 iconos que tienen una capa duotone dibujada a mano la conservan en
glyphfox/sprite.svg, como un relleno translúcido del color actual. Si quieres contornos puros, genera un subconjunto: el estilo predeterminadooutlineomite la capa, y--variant duotonela conserva. - Máscaras en sprites ocultos o externos
- Los símbolos de estilo sólido usan una
maskpara recortar sus líneas interiores. Las máscaras pueden no renderizarse cuando el sprite está oculto condisplay:none, y la compatibilidad con máscaras dentro de un archivo de sprite externo varía entre navegadores. Prueba el estilo sólido en los navegadores que necesitas cubrir, o incrusta ese sprite. - Estados vivos en un sprite
- Los presets como
spinodrawestán acotados a una clase delsvgraíz, que un símbolo no conserva, así que--animateno tiene ningún efecto visible en un sprite. Anima el elemento exterior con tu propio CSS o usa SVG en línea. - Ids en conflicto
- Un sprite en línea añade a la página ids como
gf-home. Dos sprites en línea con el mismo icono, o un elemento propio con el mismo id, vuelven ambiguas las referencias. Incrusta un solo sprite por página.
Preguntas frecuentes
¿Cuándo es mejor un sprite que los componentes?
En sitios estáticos o renderizados en el servidor sin un framework de JavaScript, y en páginas que repiten muchos iconos: los símbolos se descargan una vez y cada uso ocupa unos pocos bytes de marcado. Los componentes ganan cuando los iconos cambian con el estado, se transforman, se animan o necesitan un segundo color, porque un sprite queda fijado en tiempo de compilación.
¿Puedo hacer referencia directamente al sprite de jsDelivr?
No con use, porque los navegadores bloquean las referencias externas a otros orígenes. Descarga el archivo e insértalo en la página, como se muestra más arriba, o cópialo a tu propio servidor.