K
frame-svg-adaptativos-readme-documentacion

frame-svg: SVG adaptativo para README y documentación

Introducción

Hace unos meses, mientras trabajaba en el README de Pillbox, quería añadir un roadmap que tuviese buena pinta visualmente. El problema es que GitHub permite HTML en los README, pero con bastantes limitaciones: nada de style inline complejo, nada de clases personalizadas, nada de CSS real. Puedes hacer algunas cosas, pero el margen de maniobra es bastante estrecho.

Entonces caí en la cuenta de algo: las imágenes sí se pueden embeber sin esas restricciones. Y un SVG es una imagen. Y un SVG puede contener CSS interno. Y ese CSS puede incluir prefers-color-scheme.

¿Ves por dónde va esto? 😏

De ahí surgió frame-svg: un framework que te permite escribir layouts en JSX y genera SVG estáticos con temas adaptativos integrados. Una sola imagen, modo oscuro y modo claro, sin JavaScript, sin duplicar archivos. En este artículo quiero contarte qué descubrí, cómo funciona y qué puedes construir con él.

La portada de este artículo fue generada con frame-svg. Prueba a cambiar el tema de la web con el botón del menú — verás cómo se adapta automáticamente al modo oscuro o claro sin recargar nada.

El problema: HTML limitado, diseño limitado

Cuando llevas tiempo en GitHub, te das cuenta de que hay una brecha enorme entre lo que puedes hacer en una web normal y lo que puedes hacer en un README. Los mejores READMEs que hayas visto tienen capturas de pantalla, logos, badges… pero rara vez maquetación real. Y los que tienen algo de diseño suelen hacer malabarismos con trucos indirectos.

GitHub sí permite HTML en los README, pero lo sanitiza: elimina estilos, bloquea atributos peligrosos, ignora CSS externo. El resultado es que puedes centrar una imagen con <div align="center">, pero poco más. Cualquier intento serio de diseño choca rápido contra esas restricciones.

Ahí es donde entra el SVG. Un README puede embeber imágenes sin restricciones, y un SVG no es una imagen cualquiera: es código. Puede contener estilos, fuentes, formas, texto. Y ese CSS interno puede usar @media (prefers-color-scheme: dark), la misma query que usa cualquier web para adaptarse al tema del sistema del usuario.

El resultado: una imagen que cambia automáticamente según si el usuario tiene el modo oscuro activado. Sin que GitHub tenga que hacer nada especial. Sin que el usuario haga nada. El SVG sencillamente lo soporta.

Esto no es ningún secreto, pero tampoco es algo que se use mucho, principalmente porque crear un SVG complejo a mano es un suplicio. Coordenadas, grupos, transformaciones… no está pensado para escribirlo a mano. De ahí nació la idea de frame-svg.

La solución: frame-svg como motor de layout en SVG

La pregunta era: ¿puedo tener algo parecido a escribir componentes React, pero que genere SVG en lugar de HTML? Spoiler: sí, y resulta bastante natural.

frame-svg usa una sintaxis JSX que te resultará familiar si has trabajado con cualquier framework moderno. Escribes archivos .frame en TypeScript, defines tus layouts con primitivos, y el framework genera el .svg correspondiente.

Un ejemplo sencillo:

export default (
  <Page width={800} height={200}>
    <Stack direction="row" gap={16} padding={24}>
      <Avatar src="./avatar.png" size={64} />
      <Stack gap={4}>
        <Text size={20} weight="bold">Kevin Illanas</Text>
        <Text size={14} color="$muted">Frontend Developer</Text>
      </Stack>
    </Stack>
  </Page>
)

Eso genera un SVG con todos los estilos embebidos, incluyendo las variantes de tema. El $muted que ves ahí no es un color fijo — es una variable semántica del sistema de temas que se resuelve en un color diferente dependiendo de si el usuario tiene modo oscuro o claro.

El sistema de layout está inspirado en flexbox: puedes anidar Stacks, darles dirección, gap, padding y alineación, y el motor calcula las posiciones finales de cada elemento para el SVG. No es un browser completo — no implementa el 100% del spec de CSS — pero cubre los casos de uso reales de sobra.

Y mientras lo construyes, tienes un servidor de preview en el navegador con hot reload: editas el archivo .frame, guardas, y el SVG se actualiza al instante. Igual que cualquier otro framework moderno, pero generando SVG en lugar de HTML.

Las variables de tema: el corazón adaptativo

El sistema de temas se define en un archivo theme.ts que puedes modificar libremente. Ahí configuras los colores para modo oscuro y modo claro, y el framework los embebe en el SVG generado como CSS con prefers-color-scheme. Por defecto viene con un tema base con 19 variables semánticas — $bg, $surface, $border, $text, $muted, $accent… — pero son un punto de partida, no una imposición.

Cuando el SVG se genera, el resultado es algo así:

.light { --bg: #ffffff; --text: #1a1a1a; --accent: #3b82f6; }
.dark  { --bg: #0f0f0f; --text: #f0f0f0; --accent: #60a5fa; }

@media (prefers-color-scheme: dark) {
  :root { --bg: var(--dark-bg); }
}

Simplificado, pero la idea es esa. Cada elemento del SVG usa var(--text) o var(--accent) en lugar de colores fijos. El navegador (o la interfaz de GitHub) aplica el valor correcto según el tema del sistema. Una sola imagen, dos temas, cero JavaScript.

Lo que me gusta de este enfoque es que el tema no está hardcodeado en el framework — vive en tu proyecto, y tú decides los colores. Puedes adaptar la paleta a tu marca, a los colores de tu proyecto o a lo que te apetezca.

Los componentes disponibles

El framework viene con dos niveles de abstracción. Los primitivos son los bloques básicos de construcción:

  • Page: el lienzo raíz del SVG
  • Stack: contenedor flex (horizontal o vertical)
  • Box: contenedor simple con estilos
  • Text: texto con control de fuente, tamaño, peso y color
  • Circle: forma circular
  • Image: imagen embebida (base64 o URL)
  • Line: línea decorativa
  • Grid: layout de cuadrícula
  • Spacer: espacio flexible

Y luego están los componentes compuestos, que combinan primitivos para casos de uso habituales: Card, Avatar, Callout, FeatureList, FileTree, KeyCombo, Stat e Icon. Estos últimos son los que realmente aceleran la creación de gráficos para README o documentación.

Un FileTree para mostrar la estructura de tu proyecto, un Stat para un badge con métricas, un Callout para destacar una nota importante… todo ello adaptativo, todo generado como SVG limpio.

El entorno de desarrollo

frame-svg incluye una extensión para Visual Studio Code que añade resaltado de sintaxis y autocompletado en los archivos .frame. Sin ella, el editor no sabe qué tipo de archivo es ni qué props acepta cada componente — con ella, tienes la misma experiencia que en cualquier proyecto TypeScript: sugerencias, validación de tipos y navegación al código fuente.

La extensión está en el repositorio y se puede instalar directamente desde él. Una vez instalada, el flujo se vuelve bastante cómodo: escribes JSX, el editor te sugiere las props, y el servidor de preview refleja el resultado en tiempo real. Si quieres ver qué más extensiones marcan la diferencia en el día a día, tengo un artículo dedicado a eso.

El SVG generado es un archivo estático con zero dependencias de runtime. No necesita nada para funcionar. Lo subes donde quieras — GitHub, una documentación en Astro, una wiki — y funciona.

Más allá de los README

Empecé pensando en los README de GitHub porque fue el detonante del proyecto, pero el caso de uso real es más amplio.

Cualquier plataforma que permita embeber imágenes pero limite el HTML tiene el mismo problema. Las wikis de GitHub. Notion (en ciertos contextos). Plataformas de documentación. Incluso un correo electrónico HTML donde no controlas todos los estilos.

El SVG adaptativo funciona en todos esos contextos. Y con un framework que lo genera desde JSX con un sistema de componentes real, el overhead de crear esos gráficos se reduce bastante.

También es útil para Open Graph. Las imágenes de preview de enlaces (og:image) suelen ser estáticas, generadas una vez. Pero un SVG puede ser dinámico si lo sirves desde un endpoint — ahí frame-svg encaja bien como motor de rendering: recibes parámetros, renderizas el SVG, lo devuelves. Todo esto sin un navegador headless.

Conclusión

frame-svg surgió de un hack curioso: los README limitan el HTML, pero las imágenes SVG tienen CSS interno, y ese CSS puede incluir prefers-color-scheme. A partir de ahí, construir un motor de layout que generara esos SVG desde JSX fue la parte divertida del problema 😎

El resultado es una herramienta que, a mi parecer, cubre un hueco real: poder llevar gráficos con tema adaptativo — lo que en inglés se llama SVG responsive — a cualquier plataforma que acepte imágenes, sin depender de JavaScript, sin duplicar archivos, con un solo .svg.

Si te mueves mucho por GitHub y le das importancia a cómo se presentan tus proyectos — como ya hablamos aquí, la primera impresión de un repo importa — puede que frame-svg te resulte útil.

El proyecto está en desarrollo. Seguiré publicando actualizaciones aquí en el blog cuando haya novedades relevantes. Si tienes curiosidad o preguntas, encuéntrame en LinkedIn o en GitHub.