Envías documentación o un portal interno. Se ve bien en tu portátil. Entonces alguien lo abre en un navegador corporativo muy restringido con CSP estricto, fuentes personalizadas bloqueadas y modo oscuro activado. De pronto tus callouts “útiles” se convierten en cuadrados vacíos, iconos desalineados y violaciones de contraste que hacen que Legal se incomode.
Los bloques de llamada son operativamente engañosos: son UI, marca, accesibilidad y políticas de seguridad chocando en el tiempo de renderizado. La forma limpia de resolverlo es SVG en línea + variables CSS. Sin fuentes de iconos. Sin glifos misteriosos. Sin confiar a que la fuente cargue antes de que tus usuarios pierdan la paciencia.
Tabla de contenidos
- Por qué deberías dejar de usar fuentes de iconos
- Hechos y un poco de historia (útil, no nostálgico)
- Objetivos de diseño: qué significan realmente los callouts “buenos”
- Componente de referencia: HTML + SVG en línea + variables CSS
- Accesibilidad: lo que se rompe silenciosamente
- Temas con variables CSS: modo oscuro, colores de marca y sobrescrituras por página
- Patrones de entrega: SVG por callout vs sprite vs symbols
- CSP y seguridad: mantener iconos sin romper la política
- Rendimiento y fiabilidad: qué ralentiza y por qué
- Guía rápida de diagnóstico
- Tareas prácticas (comandos, salidas, decisiones)
- Tres mini-historias de la vida corporativa
- Errores comunes: síntomas → causa raíz → solución
- Listas de verificación / plan paso a paso
- Preguntas frecuentes
- Conclusión: próximos pasos que no te avergonzarán después
Por qué deberías dejar de usar fuentes de iconos
Las fuentes de iconos fueron un truco ingenioso cuando todos lidiábamos con navegadores antiguos y no había buenas herramientas SVG. Siguen “funcionando” hasta que añades restricciones del mundo real: políticas CSP, protecciones de privacidad, integridad de subrecursos, peculiaridades en la carga de fuentes y requisitos de accesibilidad que no se limitan a “se ve en mi máquina”.
Verdad operacional
Si tu interfaz depende de un archivo de fuente para transmitir significado, construiste una dependencia de fiabilidad disfrazada de tipografía.
Las fuentes de iconos fallan de formas que parecen “bugs aleatorios de UI”
FOIT/FOUT no es solo un problema de marketing. Cuando la fuente no carga, los iconos pueden renderizarse como tofu (cuadrados de glifo faltante), caracteres del área de uso privado o el glifo equivocado por la selección de fuente de reserva. Eso no es solo feo; es engañoso. Un callout “peligro” sin icono puede ser aceptable. Un callout con el icono equivocado es cómo haces que alguien borre el conjunto de datos incorrecto.
Subconjunto de fuentes es un campo minado. Alguien intenta optimizar el tamaño recortando la fuente de iconos a “solo lo que usamos”. Luego una página nueva usa un icono que no está en el subconjunto. En staging se ve bien (fuente completa en caché), en producción no. Bienvenido al tipo de bug que consume una tarde y algunas relaciones personales.
Accesibilidad con fuentes de iconos es mayormente deseo. Los lectores de pantalla no interpretan “glifo en código E018” como “advertencia”. Terminas esparciendo texto oculto y eso se desincroniza.
CSP y navegadores empresariales: muchas organizaciones bloquean fuentes desde orígenes terceros, y algunas deshabilitan fuentes remotas por completo. La fuente de iconos suele ser lo primero que desaparece. El SVG en línea, cuando se hace correctamente, puede ser compatible con CSP y autocontenido.
Además: las fuentes de iconos fomentan estilos perezosos (solo ajustar font-size y listo). SVG te obliga a enfrentar tamaño, viewBox y alineación. Es molesto diez minutos y luego te salva por años.
Broma #1: Las fuentes de iconos son como un RAID 0 hecho de sentimientos: ultrarrápidas hasta que necesitas que sean correctas.
Hechos y un poco de historia (útil, no nostálgico)
- Hecho 1: Las fuentes de iconos se hicieron populares porque las herramientas y el soporte SVG eran incómodos; las fuentes ya estaban cacheadas y eran comprimibles.
- Hecho 2: Muchas fuentes de iconos usan el Área de Uso Privado de Unicode, que explícitamente deja el significado y la compatibilidad como “tu problema”.
- Hecho 3: SVG 1.1 fue Recomendación W3C en 2011; la adopción masiva tardó porque el ecosistema de herramientas necesitó madurar.
- Hecho 4: El cambio de “imágenes por doquier” a “SVG en línea” se aceleró cuando el diseño responsivo hizo que los iconos raster de tamaño fijo fueran dolorosos.
- Hecho 5: Las propiedades personalizadas de CSS (variables) llegaron ampliamente a navegadores modernos alrededor de 2017–2018, lo que facilitó componentes tematizables sin preprocesadores.
- Hecho 6: Las políticas CSP del mundo real se endurecieron tras incidentes XSS de alto perfil que llevaron a las compañías a restringir scripts en línea, fuentes remotas y orígenes no seguros.
- Hecho 7: El empuje por prefers-color-scheme hizo las fuentes de iconos menos convenientes porque a menudo quieres que trazos/rellenos coincidan con currentColor y tokens de tema.
- Hecho 8: Los “sprites SVG” (symbols) respondieron al marcado repetitivo, pero introdujeron sus propias consideraciones de cross-origin y caché.
Objetivos de diseño: qué significan realmente los callouts “buenos”
Los callouts no son decoración. Son un recurso de UI para la gestión de la atención. En sistemas de producción y documentación interna, los callouts suelen ser la última barrera antes de que alguien haga algo costoso. Eso significa que el componente debe ser:
| Objetivo | Por qué importa operativamente | Cómo ayuda SVG en línea + variables CSS |
|---|---|---|
| Consistente entre entornos | Navegadores empresariales, CSP estricto, fuentes bloqueadas, docs sin conexión, exportación a PDF. | SVG es autocontenido; el tema vía variables no depende del renderizado de fuentes. |
| Accesible por defecto | Riesgo en auditorías, cumplimiento interno, personas reales que usan lectores de pantalla. | Control de ARIA, iconos decorativos vs informativos, tokens de contraste. |
| Tematizable sin reescrituras | Modo oscuro, actualización de marca, integraciones en portales de socios. | Las propiedades personalizadas se propagan; sobrescribes tokens, no selectores. |
| Performante | Los sitios de documentación viven o mueren por la percepción de rendimiento. | Sin fetch de fuentes; los iconos pintan con currentColor; evita cambios de diseño. |
| Seguro bajo CSP | Los equipos de seguridad aplican políticas; pedir excepciones es capital político. | SVG en línea sin scripts; evita fuentes remotas y JS en línea arriesgado. |
| Mantenible | Te olvidarás por qué lo hiciste. El futuro tú estará cansado. | El marcado es explícito; los iconos son paths; los tokens están centralizados. |
Guía opinada: trata los callouts como tratas el logging. Un poco de estructura al principio previene mucho caos después. Quieres un conjunto pequeño de tipos (info/warn/danger/success), un set de iconos estable y tokens para accent/background/border. Todo lo demás es perder el tiempo en detalles.
Componente de referencia: HTML + SVG en línea + variables CSS
Esta es una base apta para producción. No requiere paso de build. No depende de bibliotecas de iconos externas. Degrada razonablemente. Y es trivial de tematizar.
Patrón de marcado (icono decorativo)
La mayoría de los iconos de callout son decorativos. El tipo de callout se comunica con el texto del encabezado (“Warning”, “Note”, “Danger”), no solo con el icono. En ese caso, oculta el SVG para tecnologías asistivas con aria-hidden="true".
cr0x@server:~$ cat callout-example.html
<aside class="callout callout--warning" role="note">
<div class="callout__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" focusable="false">
<path d="M12 9v4"></path>
<path d="M12 17h.01"></path>
<path d="M10.3 4.3 2.6 18a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0z"></path>
</svg>
</div>
<div>
<p class="callout__title">Warning</p>
<p class="callout__body">Do not run this migration twice. The second run will delete data you meant to keep.</p>
</div>
</aside>
Patrón CSS (tokens primero, selectores después)
Fíjate cómo el componente usa currentColor en el SVG y define colores vía propiedades personalizadas. Eso mantiene el tema limpio: defines --accent y el icono + borde lo siguen.
cr0x@server:~$ cat callout.css
.callout{
--accent: #60a5fa;
--c-bg: color-mix(in srgb, var(--accent) 16%, transparent);
--c-border: color-mix(in srgb, var(--accent) 35%, #223047);
display: grid;
grid-template-columns: 22px 1fr;
gap: 12px;
padding: 14px;
border-radius: 12px;
background: var(--c-bg);
border: 1px solid var(--c-border);
align-items: start;
}
.callout__icon svg{
width: 100%;
height: 100%;
display: block;
stroke: currentColor;
fill: none;
stroke-width: 1.9;
stroke-linecap: round;
stroke-linejoin: round;
}
.callout--warning{ --accent: #fbbf24; }
.callout--danger{ --accent: #fb7185; }
.callout--success{ --accent: #34d399; }
Info
Este estilo usa currentColor para que el icono siga automáticamente el token de acento.
Éxito
Mismo marcado. Tokens diferentes. Sin clases extra para iconos, sin pesos de fuente, sin drama.
Cuándo no deberías insertar SVG en línea
Insertar SVG en línea por todas partes puede inflar el HTML si tienes cientos de iconos en una página. Eso es un trade-off real. Si tus páginas de docs tienen docenas de callouts, quizá quieras un sprite de símbolos o un paso de build para deduplicar. Pero no optes por un sprite solo porque te parezca “más correcto”. Hazlo cuando hayas medido repetición y afecte tu ruta crítica.
Accesibilidad: lo que se rompe silenciosamente
La mayoría de fallos de accesibilidad no parecen fallos. Parecen “funciona para mí”. Luego llega una auditoría, o un empleado presenta una queja, o tu equipo de producto se bloquea para lanzar. Los callouts son componentes simples, lo que hace especialmente vergonzoso equivocarse.
Iconos decorativos vs informativos
Decide si el icono transmite información que no está en el texto. Si el título ya dice “Warning”, el icono es decorativo. Usa aria-hidden="true" en el contenedor del icono o en el SVG y evita anuncios redundantes.
Si el icono es el único indicador (no recomendado), entonces debe tener un nombre accesible. Mejor: siempre incluye un título visible.
Roles y semántica
Usa contenedores semánticos. <aside> funciona bien para callouts; señala “contenido de apoyo”. Para tech asistiva, role="note" suele ser razonable. Evita el spam de roles. No conviertas cada callout en una alerta; los usuarios terminarán ignorándolas y los lectores de pantalla las anunciarán agresivamente.
Contraste de color y tematización
Tu color de acento no es tu color de borde. Tu borde no es tu fondo. Únelos con variables, pero no asumas que un único valor funciona en todos los temas. Si debes calcular tonos, usa métodos seguros como color-mix() con porcentajes razonables y verifica el contraste en esquemas claro y oscuro.
Modo de fallo de accesibilidad
Si tu callout “warning” depende de amarillo sobre blanco, no es una advertencia; es una sugerencia tenue.
Comportamiento de foco y contenido interactivo dentro de callouts
Los callouts suelen contener enlaces (“Ver el runbook”). Asegura que los enlaces tengan estilos de foco visibles y evita colocar controles interactivos cerca del icono si afecta objetivos táctiles en móvil. El contenedor del callout no debe ser focusable salvo que sea clicable. Los callouts clicables suelen ser una trampa UX: la gente intenta seleccionar texto y es redirigida.
Tematización con variables CSS: modo oscuro, colores de marca y sobrescrituras por contenedor
Las variables CSS son el tipo de aburrido que funciona. Son configurables en tiempo de ejecución, hacen cascade naturalmente y no requieren gimnasia de preprocesadores. Para callouts, quieres un pequeño conjunto de tokens que puedas sobrescribir en distintos ámbitos:
- Valores globales (
:root) - Sobrescrituras de tema (claro/oscuro o skins de producto)
- Sobrescrituras a nivel de contenedor (un sitio de docs integrado en un portal de socios)
- Sobrescrituras a nivel de componente (un callout específico necesita un acento personalizado)
Estrategia de tokens que escala
No pongas colores crudos en cada tipo de callout en diez archivos distintos. Define tokens semánticos para tipos y calcula valores derivados (fondo, borde) con reglas previsibles. Tu yo futuro te lo agradecerá cuando tengas que hacer un refresh de marca sin buscar el archivo CSS que nadie recuerda.
cr0x@server:~$ cat tokens.css
:root{
--callout-radius: 12px;
--callout-pad: 14px;
--callout-gap: 12px;
--callout-info: #60a5fa;
--callout-warning: #fbbf24;
--callout-danger: #fb7185;
--callout-success: #34d399;
--callout-mix-bg: 16%;
--callout-mix-border: 35%;
}
.callout{
border-radius: var(--callout-radius);
padding: var(--callout-pad);
gap: var(--callout-gap);
--accent: var(--callout-info);
--c-bg: color-mix(in srgb, var(--accent) var(--callout-mix-bg), transparent);
--c-border: color-mix(in srgb, var(--accent) var(--callout-mix-border), #223047);
}
.callout--info{ --accent: var(--callout-info); }
.callout--warning{ --accent: var(--callout-warning); }
.callout--danger{ --accent: var(--callout-danger); }
.callout--success{ --accent: var(--callout-success); }
Theming por contenedor
Si tus docs están embebidos en otra app, no luches contra ello. Deja que el host defina acentos ajustando variables en el contenedor.
cr0x@server:~$ cat embed-example.html
<div class="partner-skin" style="--callout-info:#22c55e; --callout-warning:#a78bfa;">
<aside class="callout callout--info" role="note">...</aside>
<aside class="callout callout--warning" role="note">...</aside>
</div>
Ese style inline a veces es polémico. Si no puedes usarlo por CSP, aplica una clase y define las sobrescrituras en una hoja de estilos. Lo importante es el principio: sobrescribe tokens en un límite, no reglas CSS individuales.
Patrones de entrega: SVG en línea por callout vs sprite vs symbols
Tienes tres enfoques realistas:
- SVG en línea por instancia (simple, verboso)
- Sprite de <symbol> en línea (deduplicado, sigue siendo local)
- Sprite externo referenciado por <use> (cacheable, dolores cross-origin)
Patrón A: SVG en línea por callout
Mejor cuando tienes pocas llamadas por página y quieres máxima fiabilidad. También es lo más simple de enviar en HTML estático generado desde Markdown.
Patrón B: sprite <symbol> en línea en el documento
Inserta un SVG oculto al principio de la página, define los symbols una vez y réferéncialos con <use>. Eso deduplica marcado manteniéndolo en el mismo origen del documento y evitando fetchs externos.
cr0x@server:~$ cat sprite-inline.html
<svg aria-hidden="true" style="position:absolute;width:0;height:0;overflow:hidden">
<symbol id="icon-warning" viewBox="0 0 24 24">
<path d="M12 9v4"></path>
<path d="M12 17h.01"></path>
<path d="M10.3 4.3 2.6 18a2 2 0 0 0 1.7 3h15.4a2 2 0 0 0 1.7-3L13.7 4.3a2 2 0 0 0-3.4 0z"></path>
</symbol>
</svg>
<aside class="callout callout--warning">
<div class="callout__icon" aria-hidden="true">
<svg viewBox="0 0 24 24" focusable="false">
<use href="#icon-warning"></use>
</svg>
</div>
<div>...</div>
</aside>
Atento a rarezas de navegador con referencias externas y ciertos setups de CSP; los symbols en línea suelen ser lo menos problemático.
Patrón C: sprite externo
Bueno para sitios grandes donde la caché entre páginas importa. Arriesgado si tu entorno bloquea SVG cross-origin, si tu CSP lo impide o si tu pipeline añade una capa CDN que reescribe cabeceras inesperadamente. Los sprites externos también complican flujos de “exportar un HTML único” (generación de PDF, bundles offline).
Mi consejo: empieza con SVG en línea por callout o symbols en línea. Pasa a sprites externos solo después de medir un problema real de payload y verificar compatibilidad CSP en tu entorno más restrictivo.
CSP y seguridad: mantener iconos sin romper la política
Los equipos de seguridad no ponen barreras por deporte. Han aprendido por incidentes. El SVG en línea es generalmente seguro cuando es sólo paths, sin scripts, sin manejadores de eventos, sin referencias externas y sin inyección de contenido no confiable.
Reglas seguras para SVG (edición producción)
- Usa elementos simples:
<path>,<circle>,<rect>. Evita<foreignObject>. - No incluyas
<script>dentro del SVG. No te rías; la gente lo hace. - Sin manejadores inline como
onloadoonclick. - Si los SVG los suministra el usuario (contenido CMS), sanitiza agresivamente en servidor.
- Evita referencias externas en
<use>a menos que controles cabeceras y CSP end-to-end.
“La esperanza no es una estrategia.”
—General H. Norman Schwarzkopf
Esa cita aparece en ingeniería porque aplica dolorosamente: si tu sistema de iconos depende de “debería cargar”, has construido esperanza en tu UI.
Dónde CSP afecta específicamente a los callouts
Rara vez es el SVG en sí. Son las decisiones alrededor:
- Estilos inline bloqueados: si tematizas vía atributos
styleinline, puede que necesitesstyle-src 'unsafe-inline'o nonce/hash en CSP. Evita necesitarlo. Prefiere clases y CSS estático. - Sprites externos bloqueados: el navegador puede negarse a cargar
<use href="...sprite.svg#id">dependiendo de la política, cross-origin y cabeceras. - Sanitizadores que quitan SVG: algunos sanitizadores de HTML eliminan
<svg>completamente o quitan atributos comoviewBox. Tu pipeline podría “proteger” rompiendo el render.
Rendimiento y fiabilidad: qué ralentiza y por qué
Rendimiento aquí no es “SVG es rápido”. Rendimiento es: la página es usable pronto, los callouts no desplazan el diseño y los iconos no provocan reflows extraños. SVG en línea ayuda porque evita fetch de fuentes y reduce la posibilidad de swaps tardíos. Pero aún puedes autolesionarte.
Costes comunes de rendimiento
- Inflado de HTML: repetir un path de 600 bytes 60 veces suma. A veces gzip te salva; a veces no.
- CSS sin control: selectores excesivamente complejos en sitios grandes pueden hacer el recálculo de estilos más caro que el propio SVG.
- Cambio de diseño: si no reservas el espacio del icono, el texto del callout puede saltar cuando el SVG pinta o cuando cargan fuentes.
- Coste de render: SVGs extremadamente detallados (muchos puntos, filtros) pueden ralentizar el pintado. Mantén los iconos de callout simples.
Postura práctica de fiabilidad
Para callouts, prefiere iconos basados en trazos con viewBox consistente (24×24 es común). Limítate a un conjunto pequeño. Evita filtros. No animes. Tu icono de callout no es una imagen principal de marketing. Es una señal de tráfico.
Broma #2: La única animación aceptable en un callout de advertencia es tu ritmo cardíaco cuando te das cuenta de que ejecutaste el comando en producción.
Guía rápida de diagnóstico
Cuando los iconos de callout “rompen”, quieres identificar el cuello de botella rápido: ¿es marcado, CSS, CSP, sanitizador o entrega? Aquí el orden que ahorra tiempo.
Primero: confirma que el icono existe en el DOM
- Abre DevTools, inspecciona el callout.
- ¿Hay un elemento
<svg>conviewBox? - ¿Están presentes los elementos
<path>?
Si el SVG falta: probablemente tu sanitizador, renderizador Markdown o paso de templates lo eliminó.
Segundo: comprueba estilos computados para tamaño y color
- Confirma que el contenedor del icono tiene ancho/alto explícitos.
- Confirma que el SVG usa
stroke: currentColor(o fill) y quecolorestá establecido en el icono/contenedor. - Verifica si un reset global está poniendo
svg { display: inline; }o sobrescribiendo dimensiones.
Si el SVG está presente pero invisible: probablemente tienes desajustes en fill/stroke, falta de viewBox o un token de color que se resuelve a transparente.
Tercero: descarta CSP y referencias externas
- Mira la consola por errores de recursos bloqueados.
- Si usas sprites externos, prueba con un sprite de symbols en línea para aislar problemas cross-origin/CSP.
- Si la tematización usa atributos inline style, verifica si CSP bloquea estilos inline.
Cuarto: mide payload y repetición
- Revisa tamaño transferido de HTML y CSS.
- Cuenta callouts por página y repetición de datos de path idénticos.
- Si es grande: considera un sprite de symbols o dedupe en build-time.
Tareas prácticas (comandos, salidas, decisiones)
Estas son tareas reales que puedes ejecutar en una máquina Linux en CI o en un servidor para diagnosticar problemas. Cada tarea incluye qué buscar y la decisión que debes tomar.
Tarea 1: Verifica que tu HTML construido aún contiene elementos SVG
cr0x@server:~$ rg -n "
Qué significa la salida: Estás viendo etiquetas SVG y atributos viewBox en el output renderizado, no solo en plantillas fuente.
Decisión: Si esto está vacío, tu pipeline Markdown o sanitizador eliminó SVG. Arregla la configuración del renderer antes de tocar CSS.
Tarea 2: Detecta SVG sin viewBox (causa clásica de “icono invisible”)
cr0x@server:~$ rg -n "
Qué significa la salida: Al menos una etiqueta SVG carece de viewBox. Sin él, el escalado es impredecible y puede colapsar a nada.
Decisión: Añade viewBox a cada icono (estandariza en 0 0 24 24). No lo “arregles” con transformaciones CSS.
Tarea 3: Revisa uso de sprites externos que puedan activar problemas CSP
cr0x@server:~$ rg -n "
Qué significa la salida: Estás referenciando un sprite externo por HTTP(S). Esto puede ser bloqueado por CSP, restricciones cross-origin o funciones de privacidad.
Decisión: Prefiere symbols en línea o sprites del mismo origen a menos que hayas verificado cabeceras y CSP en el entorno más estricto.
Tarea 4: Identifica theming vía style inline que podría bloquear CSP
cr0x@server:~$ rg -n "style=\"[^\"]*--callout" dist/**/*.html | head
dist/guide/embed.html:12:<div class="partner-skin" style="--callout-info:#22c55e">
Qué significa la salida: Las variables CSS se establecen mediante estilos inline. Si CSP bloquea estilos inline, el theming fallará silenciosamente.
Decisión: Mueve las sobrescrituras de tokens a hojas de estilo basadas en clases, o configura CSP con nonces/hashes si realmente necesitas style inline.
Tarea 5: Confirma que tu CSS establece stroke: currentColor o fill: currentColor
cr0x@server:~$ rg -n "stroke:\s*currentColor|fill:\s*currentColor" dist/**/*.css
dist/assets/site.css:418:.callout__icon svg{stroke:currentColor;fill:none;stroke-width:1.9}
Qué significa la salida: Los iconos heredan color correctamente, por lo que el theming vía color o variables de acento funcionará.
Decisión: Si falta, decide un enfoque consistente: iconos con trazos (stroke=currentColor, fill=none) o iconos rellenos (fill=currentColor). Mezclar ambos es cómo algunos iconos desaparecen en modo oscuro.
Tarea 6: Detecta resets CSS que rompen el tamaño de SVG
cr0x@server:~$ rg -n "svg\s*\{|svg\s*,|svg\s+\w" dist/**/*.css | head
dist/assets/reset.css:33:svg{display:inline}
dist/assets/reset.css:34:svg{vertical-align:middle}
Qué significa la salida: Resets globales afectan SVG. No siempre es incorrecto, pero suele ser código no revisado.
Decisión: Asegura que tu componente establezca explícitamente width/height/display en SVG. No confíes en que resets globales actúen como esperas.
Tarea 7: Mide el inflado del HTML (repetición de SVG en línea)
cr0x@server:~$ wc -c dist/guide/backup.html
248312 dist/guide/backup.html
Qué significa la salida: Esta página pesa ~248 KB sin comprimir. Puede estar bien o ser excesivo según objetivos del sitio.
Decisión: Si las páginas son consistentemente grandes y contienen iconos repetidos, considera cambiar a symbols en línea para deduplicar.
Tarea 8: Cuenta callouts por página (predecir repetición y riesgo de layout)
cr0x@server:~$ rg -c "class=\"callout\b" dist/guide/backup.html
27
Qué significa la salida: 27 callouts en una página es mucho. La repetición importa ahora.
Decisión: Si estás en el rango 20–50, SVG en línea por instancia probablemente sea desperdicio. Pasa a symbols o includes en build-time.
Tarea 9: Verifica gzip/brotli en activos estáticos (porque los bytes importan)
cr0x@server:~$ gzip -c dist/guide/backup.html | wc -c
42186
Qué significa la salida: Gzip reduce 248 KB a ~42 KB. Los paths SVG repetitivos comprimen bien.
Decisión: Si el tamaño comprimido ya es pequeño, no sobreoptimices. Si sigue siendo grande, mueve a symbols o reduce complejidad de iconos.
Tarea 10: Revisa cabeceras del servidor por CSP que puedan bloquear estilos o referencias SVG
cr0x@server:~$ curl -sI https://docs.example.internal/guide/backup | rg -i "content-security-policy|x-content-type-options|x-frame-options"
content-security-policy: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
Qué significa la salida: Los estilos inline están bloqueados (style-src 'self' sin 'unsafe-inline' o nonces). Se permiten data URLs para imágenes.
Decisión: No uses atributos style inline para theming; mantén tokens en CSS. Para sprites externos, asegúrate de que sean same-origin ('self').
Tarea 11: Valida que tu sanitizador SVG no quite atributos necesarios
cr0x@server:~$ node -p "require('fs').readFileSync('dist/guide/backup.html','utf8').includes('viewBox=')"
true
Qué significa la salida: Al menos un viewBox sobrevivió al pipeline.
Decisión: Si es falso, arregla la allowlist del sanitizador (viewBox, aria-*, role) o deja de sanitizar artefactos build confiables dos veces.
Tarea 12: Encuentra icons con fill=none accidentales que debían ser rellenos
cr0x@server:~$ rg -n "fill=\"none\"" dist/**/*.html | head
dist/guide/alerts.html:77:<path fill="none" d="M12 2a10 10 0 1 0 0 20"></path>
Qué significa la salida: Algunos paths fuerzan explícitamente el fill. Si tu set incluye iconos rellenos, eso puede hacerlos invisibles.
Decisión: Normaliza iconos: elimina atributos fill y controla vía CSS, o asegura que el componente soporte ambos con clases explícitas.
Tarea 13: Detecta datos de path duplicados que deberían ser un símbolo
cr0x@server:~$ rg -n "M10\.3 4\.3 2\.6 18" dist/**/*.html | wc -l
54
Qué significa la salida: El mismo path de triángulo de advertencia aparece 54 veces en tu output. Es duplicación.
Decisión: Si esto crece, mueve a un sprite de symbols en línea o includes en build-time para deduplicar.
Tarea 14: Confirma que las sobrescrituras de variables CSS se aplican (búsqueda rápida de tokens)
cr0x@server:~$ rg -n "--callout-(info|warning|danger|success)" dist/**/*.css
dist/assets/site.css:12:--callout-info:#60a5fa;
dist/assets/site.css:13:--callout-warning:#fbbf24;
dist/assets/site.css:14:--callout-danger:#fb7185;
dist/assets/site.css:15:--callout-success:#34d399;
Qué significa la salida: Los tokens existen en el CSS desplegado, no solo en tu repo fuente.
Decisión: Si faltan tokens, tu paso de build está eliminando un archivo o tu bundler tree-shakeó variables por mala configuración.
Tres mini-historias de la vida corporativa
Mini-historia 1: El incidente causado por una suposición equivocada
Un equipo mantenía un manual de operaciones interno usado durante incidentes. El manual tenía callouts para “Danger”, “Warning” y “Note”. Los iconos se rendereaban vía una fuente de iconos alojada en un dominio compartido. La suposición: “La fuente está cacheada en todos lados, así que es básicamente gratis”.
Luego se desplegó un cambio de seguridad. El proxy corporativo comenzó a eliminar respuestas de fuentes remotas a menos que vinieran de un origen allowlisteado. Nadie coordinó con el equipo de docs porque, en una empresa grande, el sitio de docs es “solo contenido estático”.
Durante un incidente real, los respondedores abrieron el manual desde un perfil de navegador endurecido. Todos los iconos de advertencia se convirtieron en cuadrados vacíos. Peor, los títulos de los callouts eran sutiles y el icono hacía la mayor parte del trabajo visual. Un ingeniero no vio una nota de “no ejecutar dos veces” y volvió a ejecutar un script de remediación. No destruyó el mundo, pero extendió la caída y generó inconsistencias de datos que luego hubo que limpiar.
La discusión post-incidente no fue sobre fuentes. Fue sobre suposiciones: el sitio de docs tenía una dependencia no registrada en una fuente remota, y esa dependencia importó justo cuando los docs eran más necesarios.
La solución fue directa y correcta: SVG en línea para los cuatro tipos de callout, incluidos en la página. Los iconos pasaron a ser parte del artefacto, no una apuesta en la red. El equipo también aumentó el peso visual del texto del título para que el icono dejara de ser la señal primaria.
Mini-historia 2: La optimización que salió mal
Otro grupo construyó un system de diseño pulido para herramientas internas. Reemplazaron fuentes de iconos por un único sprite SVG externo para reducir duplicación de HTML. Se sirvió desde un CDN y se referenciaba con <use href="...icons.svg#id">. Se veía elegante en diagramas.
Luego empezaron a recibir reportes: “Iconos faltan en Safari”, “Iconos faltan en PDFs”, “Iconos faltan en vistas embebidas”. Los iconos faltantes no eran aleatorios; se correlacionaban con entornos que renderizan páginas en contextos restringidos. Algunas herramientas embebían otras en iframes con headers CSP distintos. Algunos usuarios exportaban páginas a PDF vía un servicio que no recuperaba subrecursos cross-origin. Algunos navegadores se comportaban distinto con referencias externas en <use> según cabeceras y caché.
El equipo intentó arreglar con más reglas de caché y ajuste de cabeceras. Eso ayudó en un sitio y rompió otro. Clásico. Habían cambiado unos pocos kilobytes de marcado duplicado por un problema de sistemas distribuidos involucrando orígenes, cabeceras y renderizadores.
El compromiso eventual fue sensato: sprites de symbols en línea para apps que necesitaban fiabilidad (especialmente herramientas de incidentes), sprites externos solo para páginas públicas de marketing donde la caché importaba y la CSP podía moldearse. El sistema de diseño documentó ambos patrones y puso por defecto el más fiable.
Mini-historia 3: La práctica aburrida que salvó el día
Un equipo de plataforma de docs ejecutaba un generador de sitio estático con un sanitizador HTML estricto porque el contenido lo escribían muchos equipos. Al principio se deshabilitó el soporte SVG “por seguridad”. En lugar de eludir el sanitizador, hicieron lo aburrido: crearon una pequeña allowlist para un subconjunto SVG restringido usado solo para iconos.
La allowlist permitía <svg> con viewBox, focusable y atributos ARIA; permitía <path> con d; y eliminaba todo lo demás. Sin scripts. Sin foreignObject. Sin referencias externas. También forzaron un tamaño estándar de icono y quitaron estilos inline.
Meses después, otro equipo pegó por accidente un SVG complejo exportado desde una herramienta de diseño dentro de un callout. El sanitizador lo redujo a nada útil, y el build falló por una regla de lint que detectó “icono vacío”. El autor recibió un mensaje claro con instrucciones de remediación.
No fue glamoroso. Pero evitó que una clase de problemas de seguridad y rendimiento entrara al sitio, y convirtió “iconos SVG en callouts” en un defecto seguro en vez de una excepción que requeriría pedir permiso a Seguridad.
Errores comunes: síntomas → causa raíz → solución
1) Síntoma: iconos se muestran como cuadros vacíos o letras aleatorias
Causa raíz: Sigues usando fuentes de iconos en algún lugar y la fuente no cargó o fue sustituida.
Solución: Elimina fuentes de iconos para callouts. Reemplaza por SVG en línea. También busca CSS como .icon:before { content: "\e018"; font-family: ... } que pueda quedar en la base de código.
2) Síntoma: SVG está en el DOM pero no se ve nada
Causa raíz: Falta viewBox, o tu SVG usa paths de relleno mientras tu CSS fuerza fill: none, o el color se resuelve a transparente.
Solución: Estandariza todos los iconos con viewBox; decide stroke vs fill; establece stroke: currentColor o fill: currentColor según corresponda; verifica el color computado en el contenedor del icono.
3) Síntoma: iconos visibles en algunas páginas pero faltan en otras
Causa raíz: Referencias a sprites externos bloqueadas por CSP o condiciones cross-origin, o las rutas de output del build difieren entre secciones.
Solución: Usa symbols en línea para docs y herramientas internas. Si debes usar sprites externos, mantenlos same-origin y verifica CSP y cabeceras en todos los contextos (iframes y servicios de generación de PDF incluidos).
4) Síntoma: el layout del callout salta cuando la página carga
Causa raíz: No se reservó espacio para el icono (no hay width/height en el wrapper) o las fuentes cargan tarde y cambian la altura de línea.
Solución: Da al contenedor del icono un tamaño fijo y pon el SVG en display: block. Mantén la tipografía del callout estable y evita swaps de fuente para UI críticas.
5) Síntoma: el modo oscuro hace que los bordes se vean sucios o de bajo contraste
Causa raíz: Los colores derivados se ajustaron solo para modo claro; la mezcla produce bordes embarrados en fondos oscuros.
Solución: Define porcentajes de mezcla separados para temas claro/oscuro o define tokens de borde explícitos por tema. Prueba contraste; no lo juzgues a ojo.
6) Síntoma: lectores de pantalla anuncian “graphic” repetidamente o leen el título del icono
Causa raíz: SVG no marcado como decorativo, o usaste <title> dentro del SVG sin intención.
Solución: Para iconos decorativos: aria-hidden="true". Para iconos con significado: pon un label apropiado y no dupliques el título del callout.
7) Síntoma: algunos iconos están desalineados verticalmente respecto al texto
Causa raíz: SVG es inline-level y se alinea a la línea base; las relaciones de aspecto y centros visuales difieren entre iconos.
Solución: Usa display: block en SVG, wrapper con tamaño fijo y estandariza iconos al mismo viewBox y centro visual.
8) Síntoma: iconos desaparecen después de un release de “endurecimiento” de seguridad
Causa raíz: El sanitizador quita elementos/atributos SVG, o CSP bloquea estilos inline usados para theming.
Solución: Crea una allowlist SVG explícita para iconos; elimina theming via estilos inline; asegura que todos los atributos necesarios (viewBox, aria-hidden) estén permitidos.
Listas de verificación / plan paso a paso
Paso a paso: migrar de fuentes de iconos a callouts con SVG en línea
- Haz inventario de los callouts actuales. Lista tipos y dónde aparecen (docs, UI de producto, emails, exportaciones).
- Define el set mínimo de iconos. Info, warning, danger, success suele ser suficiente. Menos iconos significan menos inconsistencias.
- Normaliza la geometría de iconos. Elige un viewBox estándar (comúnmente 0 0 24 24) y grosor de trazo consistente.
- Elige estilo: trazo o relleno. Los sets mixtos son posibles, pero necesitarás CSS extra. Manténlo simple salvo que el diseño requiera lo contrario.
- Construye el componente con tokens. Usa
--accent, calcula--c-bgy--c-border. - Haz los iconos decorativos por defecto. Requiere títulos visibles. Oculta SVGs con
aria-hiddena menos que tengas una razón específica. - Prueba bajo CSP estricto. Prueba específicamente con estilos inline bloqueados y fuentes remotas bloqueadas.
- Prueba modo oscuro y temas de alto contraste. Verifica bordes y contraste de texto.
- Ejecuta lint de HTML/SVG en CI. Detecta viewBox faltantes, paths vacíos o atributos eliminados.
- Mide payload y repetición. Si el inflado es real, cambia de SVG por instancia a symbols en línea.
- Documenta el patrón. No una novela: un ejemplo, lista de hacer/no hacer y tokens para sobrescribir.
- Elimina dependencias antiguas de fuentes de iconos. No dejes CSS fantasma. El CSS fantasma siempre vuelve para perseguirte.
Checklist: preparación para release de iconos de callout
- Todos los SVG de callout incluyen
viewBoxy se renderizan a 1x y 2x sin recortar. - Los iconos heredan color vía
currentColor(stroke o fill) y son visibles en modo oscuro. - Los títulos de callout son visibles y transmiten el tipo sin depender del color del icono.
- Iconos decorativos tienen
aria-hidden="true". - No quedan dependencias de fuentes externas para iconos.
- Las páginas se renderizan correctamente con CSP: no se requieren estilos inline, no hay recursos bloqueados.
- Rendimiento: los iconos no causan cambios de diseño; las dimensiones del wrapper están fijas.
Preguntas frecuentes
1) ¿Por qué SVG en línea en lugar de una librería de iconos?
Las librerías están bien si controlas la cadena de build y puedes garantizar salida consistente. Para callouts específicamente, quieres la superficie de dependencia más pequeña y predecible. SVG en línea es explícito y portable, especialmente para docs estáticos y runbooks de incidentes.
2) ¿Debería usar iconos rellenos o con trazo?
Elige uno para todo el sistema de callouts. Los iconos con trazo y stroke: currentColor son fáciles de tematizar y suelen verse bien a tamaños pequeños. Los iconos rellenos también están bien, pero asegúrate de controlar fill consistentemente y evita mezclar semánticas.
3) ¿Es seguro usar color-mix()?
Está ampliamente soportado en navegadores modernos, pero si tienes requisitos legacy puede que necesites tokens explícitos (colores de fondo/borde precompletados). Operativamente: si no puedes controlar la base de navegadores, no calcules colores en tiempo de ejecución.
4) ¿Cómo evito que el HTML crezca mucho si inlineo SVG por todas partes?
Usa un sprite de symbols en línea (<symbol>) al principio del documento y referencia con <use>. Deduplica sin fetchs cross-origin. Si tu pipeline lo permite, también puedes incluir iconos como partials.
5) ¿Los iconos SVG en línea violan CSP?
SVG en línea no es script en línea. Los problemas vienen de scripts dentro de SVG, manejadores de eventos o referencias externas. Mantén SVGs en formas simples y sanitiza si el contenido no es confiable. También evita atributos style inline para theming si CSP los bloquea.
6) ¿Cuál es el mejor elemento HTML semántico para un callout?
<aside> es un buen valor por defecto. Puedes añadir role="note" si ayuda a las tecnologías asistivas. Evita usar role="alert" a menos que el callout represente información urgente y dinámica que deba anunciarse inmediatamente.
7) Mi sanitizador quita viewBox. ¿Puedo sobrevivir sin él?
Puedes, pero no deberías. Sin viewBox, el escalado es frágil y puede producir renderizados vacíos. Arregla la allowlist del sanitizador. Si tu plataforma no permite viewBox de forma segura, necesitas otra estrategia (por ejemplo renderizar en servidor a marcado seguro), no hacks CSS.
8) ¿Cómo aseguro que el icono se alinee perfectamente con el título con distintas fuentes?
Usa un wrapper de icono de tamaño fijo y pon el SVG en display: block. Ajusta con margin-top solo si es necesario, pero prefiere alinear con line-height consistente y el tamaño del box del icono. No confíes en la alineación baseline para SVG.
9) ¿Puedo embedir el SVG como data URL en su lugar?
Puedes, pero suele ser peor para mantenibilidad y puede bloquearse según CSP (img-src). SVG en línea es más claro, más fácil de tematizar y más fácil de auditar.
10) Mis docs están en Markdown y el renderer quita HTML. ¿Qué hago?
Entonces necesitas una extensión Markdown para callouts que renderice HTML confiable en build-time, o un sistema de shortcodes/componentes. No pidas a los autores pelear con el renderer; haz que la “forma correcta” sea fácil y consistente.
Conclusión: próximos pasos que no te avergonzarán después
Los callouts son pequeños, pero viven en la intersección de fiabilidad y atención humana. SVG en línea + variables CSS es la elección práctica porque reduce dependencias, sobrevive en entornos estrictos y mantiene la tematización coherente.
Siguientes pasos:
- Estandariza cuatro tipos de callout y un set mínimo de iconos con viewBox y grosor de trazo compartidos.
- Implementa el componente basado en tokens (acento/fondo/borde) y oculta iconos decorativos de tecnologías asistivas.
- Ejecuta las tareas de diagnóstico anteriores en CI: busca viewBox faltantes, referencias a sprites externos accidentales y CSS de fuentes de iconos remanente.
- Prueba bajo tu perfil CSP más estricto y en modo oscuro antes de lanzar.
- Si tienes inflado de payload, cambia a symbols en línea—no pases directamente a sprites externos a menos que controles cabeceras en todos lados.
Si solo recuerdas una cosa
Haz visible el significado en texto; deja que el icono lo refuerce. Luego haz que el icono sea autocontenido, tematizable y aburrido.