Una diseñadora de mi equipo se pasó una tarde entera exportando manualmente 48 iconos como SVG individuales porque nuestro «sistema de iconos» no era más que un archivo Figma llamado «Icons - FINAL v3». Cada icono tenía un tamaño de mesa de trabajo distinto, algunos usaban trazos y otros rellenos, y los nombres de archivos eran del estilo de «icon-arrow-right-UPDATED». Cuando la persona desarrolladora frontend intentó usarlos, la mitad de los iconos se cortaban en el límite del viewBox. Yo he pasado por lo mismo. Aquí está el sistema que uso ahora para que esto no vuelva a pasar.
Todos los iconos se almacenan en un único archivo Figma, en una cuadrícula de mesas de trabajo de 24×24. Todos los iconos usan la misma cuadrícula base, el mismo ancho de trazo (1,5 px) y la misma convención de nomenclatura: icon-name--variant. Sin sufijos como «FINAL» o «UPDATED». El archivo está enlazado a la documentación del sistema de diseño en Notion o Storybook, no enterrado en la carpeta de borradores de alguien.
A partir de ese archivo fuente, los iconos se exportan mediante un plugin de Figma (SVG Export) con estas opciones activadas: Inline SVG, Remove unused IDs, Minify output.
Para sitios que no usan un framework de componentes, yo creo una hoja de sprite SVG: un único archivo que contiene todos los iconos como elementos <symbol>, cada uno con un ID único. Lo incluyes una sola vez en la página (oculto) y haces referencia a los iconos individuales con <use>:
<!-- Include once, usually in the header -->
<svg style="display:none" aria-hidden="true">
<symbol id="icon-search" viewBox="0 0 24 24">
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16...">
</symbol>
<symbol id="icon-cart" viewBox="0 0 24 24">
<path d="M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7...">
</symbol>
</svg>
<!-- Use anywhere on the page -->
<button aria-label="Search">
<svg aria-hidden="true"><use href="#icon-search"/></svg>
</button>
La ventaja: una sola solicitud HTTP para todos los iconos. El navegador almacena en caché la hoja de sprite, y todas las referencias a los iconos son instantáneas. El atributo viewBox de cada elemento <symbol> garantiza que el escalado funcione correctamente sin importar el tamaño del contenedor <svg>.
En proyectos basados en componentes, cada icono se convierte en su propio componente. Yo uso SVGR (para React) o el equivalente para Vue/Svelte para generar componentes automáticamente desde los archivos SVG exportados. El script de construcción se ejecuta en cada push, así que los iconos en producción siempre coinciden con la fuente de Figma.
// IconSearch.tsx — auto-generated from icon-search.svg
import { SVGProps } from 'react';
export const IconSearch = (props: SVGProps<SVGSVGElement>) => (
<svg width={24} height={24} fill="none" {...props}>
<path d="M15.5 14h-.79l-.28-.27..." />
</svg>
);
El componente acepta los props estándar de SVG — className, color, size — así que la persona desarrolladora que lo usa nunca tiene que abrir el archivo SVG. Solo lo importa y lo usa como cualquier otro componente.
Todos los sistemas de iconos defectuosos que he heredado tenían los mismos problemas:
stroke para dibujarse se pueden recolorear con CSS. Los que usan fill también, pero mezclar ambos en la misma biblioteca significa que algunos iconos cambian de color en el hover y otros no.Arreglar esto a posteriori es tedioso, pero vale la pena. Yo suelo dedicar un día a estandarizar todo a 24×24, solo con trazos (con fill="none") y a renombrar los archivos antes de generar los componentes. El coste de una sola vez se amortiza cada vez que alguien añade un icono sin tener que preguntarme.
Toda la configuración tarda alrededor de 2 horas para un proyecto nuevo y ahorra decenas de horas a lo largo de su ciclo de vida. Lo más importante es que significa que nadie nunca tiene que preguntar "¿dónde está el archivo del ícono?" — la respuesta siempre es el mismo lugar.