Animaciones SVG para hero con CSS puro — cero JavaScript
11 min read

Animaciones SVG para Hero sin JavaScript: Identidades visuales por página

#SVG #CSS Animation #Performance

La mayoría de las secciones hero en la web siguen un patrón predecible: una imagen de fondo estática, quizás con un gradiente CSS encima, o — si el presupuesto lo permite — una librería de animación JavaScript como GSAP, Lottie o Three.js. Ambos enfoques tienen compromisos. Las imágenes estáticas son aburridas e intercambiables. Las librerías JavaScript inflan el bundle, bloquean el hilo principal y frecuentemente introducen problemas de accesibilidad.

Existe un tercer camino: SVG puro + CSS @keyframes. Este portfolio utiliza exactamente este enfoque para crear seis fondos hero únicos y específicos por página — cada uno con su propio carácter visual, cero sobrecarga de JavaScript y soporte completo de prefers-reduced-motion.

Lo que hace especial este enfoque: cada página no solo tiene un esquema de colores o disposición diferente, sino una metáfora visual fundamentalmente distinta. La página principal muestra un editor de código. La página “Sobre mí” despliega una red de constelaciones. La página de contacto pulsa con ondas de señal. La identidad visual de cada página se define por su animación — no por fotos de stock intercambiables.

Las animaciones SVG con CSS son la técnica más subestimada del diseño web moderno. Combinan un tamaño de archivo cercano a cero con flexibilidad creativa ilimitada.

Sarah Drasner VP of Developer Experience, Netlify

Por qué SVG + CSS en lugar de librerías JavaScript

6
Visuales Hero
Este Portfolio
0 KB
JavaScript
Cero Runtime
< 5 KB
Por SVG (gzipped)
Payload de Red

Elegir SVG + CSS no es una preferencia estilística — es una decisión técnica con ventajas medibles. Las librerías de animación como GSAP (43 KB minificado), Lottie (250 KB+ con el player) o Anime.js (17 KB) añaden sobrecarga de runtime que bloquea el hilo principal. Las animaciones SVG con CSS, en cambio, se ejecutan en el compositor del navegador sobre la GPU — ni siquiera tocan el hilo principal.

Además, hay un aspecto frecuentemente ignorado: la gestión de dependencias. Una librería JavaScript necesita ser actualizada, verificada contra cambios incompatibles y parcheada contra vulnerabilidades de seguridad. Los @keyframes de CSS no tienen versiones, ni dependencias, ni CVEs. Son parte nativa de la plataforma web y funcionan en todos los navegadores — hoy y dentro de diez años.

Criterio SVG + CSS Recommended Lottie GSAP Canvas/WebGL
Tamaño del Bundle 0 KB JS ~250 KB ~43 KB ~50-200 KB
Renderizado GPU Compositing Canvas/SVG DOM/WAAPI GPU (WebGL)
Accesibilidad Nativo (aria-hidden) Limitado Manual Ninguna
prefers-reduced-motion CSS Media Query Requiere JS Requiere JS Requiere JS
Interactividad Hover/Focus CSS Control Total Control Total Control Total
Soporte Navegadores 98%+ (todos modernos) 95%+ 97%+ 93%+
Impacto SEO Ninguno (sin JS) Mínimo Mínimo Ninguno
Curva de Aprendizaje Conocimiento CSS After Effects + JSON API JS WebGL/Shaders
Dependencias Ninguna 1+ paquetes npm 1+ paquetes npm 1-3 paquetes npm
Mantenimiento Cero Actualizaciones necesarias Actualizaciones necesarias Actualizaciones necesarias

El punto clave: para fondos hero decorativos que no requieren interactividad compleja, SVG + CSS es la solución superior. Obtienes animaciones fluidas a 60fps, cero payload de JavaScript y funcionalidades nativas de accesibilidad — gratis.

Los 6 Visuales Hero en detalle

Cada página de este portfolio tiene su propio fondo hero temáticamente apropiado. Los visuales utilizan elementos SVG, animaciones CSS y posicionamiento absoluto para crear un fondo sutil y vivo que complementa el contenido sin distraer. Ninguno de los visuales es puramente decorativo en el sentido de “bonito” — cada uno transmite un mensaje a nivel de contenido sobre la página respectiva.

Visual Hero del Editor de Codigo
HeroVisual — Página Principal

Un editor de código animado con resaltado de sintaxis, un cursor parpadeante y una ventana de terminal superpuesta. Los puntos rojo, amarillo y verde en la barra de título imitan una ventana de macOS. Debajo, líneas de código Astro se muestran con etiquetas, atributos y cadenas resaltados en colores. Formas geométricas flotantes — un círculo luminoso, un anillo y puntos pulsantes — rodean el editor y crean profundidad espacial. El terminal muestra un output de build exitoso con símbolos de verificación.

Visual Hero de Constelacion
AboutHeroVisual — Sobre mí

Una red de constelaciones con un núcleo central luminoso, tres anillos concéntricos que pulsan y se expanden, y ocho nodos conectados mediante líneas SVG discontinuas. Etiquetas tecnológicas flotantes como Astro, React, WordPress, SEO, A11y y TypeScript flotan sobre la escena. Dos blobs de gradiente ambiental crean efectos de nebulosa coloreada. La posición del núcleo se aleatoriza en cada carga de página mediante un snippet de JavaScript que selecciona entre seis zonas predefinidas.

Visual Hero del Portfolio
PortfolioHeroVisual — Portfolio

Una cuadrícula escalonada de nueve baldosas rectangulares que actúan como miniaturas abstractas de proyectos. Cada baldosa pulsa con un efecto sutil de fade-in/out y crece ligeramente hasta el 103% de su tamaño original. Cinco líneas diagonales SVG de cuadrícula cruzan la escena con trazos discontinuos. Etiquetas de categorías flotantes — Web, Branding, UI/UX, SEO, E-Commerce — y corchetes decorativos en las esquinas superior izquierda e inferior derecha enmarcan la composición.

Visual Hero del Blog con Maquina de Escribir
BlogHeroVisual — Blog

Líneas de texto horizontales abstractas con anchos variados (45-90%) imitan párrafos escritos. Dos grupos de estas líneas de texto flotan en diferentes posiciones. Un cursor parpadeante (2x18px) sugiere escritura activa. Grandes comillas flotantes en Georgia serif de 80px y etiquetas de temas como Design, Code, A11y, Performance y CSS completan la atmósfera de escritura.

Visual Hero de Contacto con Sobre
ContactHeroVisual — Contacto

Arcos de señal radiantes se expanden concéntricamente desde un punto focal luminoso — como una señal WiFi expandiéndose. Cuatro anillos circulares (60px, 120px, 200px, 300px de diámetro) con animaciones de onda escalonadas (0s, 0.6s, 1.2s, 1.8s de delay) crean una oleada continua de energía visual. Dos contornos de sobres SVG flotan con ligeras inclinaciones sobre la superficie. Seis puntos de conexión dispersos y cuatro etiquetas flotantes refuerzan el carácter comunicativo.

Visual Hero Legal con Escudo
LegalHeroVisual — Legal

Dos contornos abstractos de documentos con una barra de encabezado más gruesa y cuatro a cinco líneas de texto más finas cada uno imitan documentos legales. Dos símbolos de escudo SVG — uno con marca de verificación para cumplimiento, uno sin ella para requisitos abiertos — flotan sobre la escena. Tres grandes signos de párrafo (§) en tamaños de 35-60px y cinco etiquetas flotantes como DSGVO, WCAG, TMG, BFSG y DDG anclan el visual temáticamente en el ámbito legal.

Anatomía de un fondo Hero SVG

La estructura de cada visual hero sigue un patrón consistente. Los pasos individuales se repiten en cada variante — solo cambian los elementos SVG específicos y las animaciones. Esta consistencia no es casual — permite crear nuevos visuales en menos de una hora, porque la arquitectura ya está establecida.

5 pasos hacia un fondo Hero animado

  1. Contenedor con posicionamiento absoluto

    El contenedor exterior usa position: absolute e inset: 0 para extenderse sobre toda el área hero. pointer-events: none asegura que el fondo no capture clics. aria-hidden="true" lo marca como puramente decorativo — los lectores de pantalla lo ignoran completamente. overflow: hidden previene que los elementos se extiendan más allá del área hero.

  2. SVG viewBox y preserveAspectRatio

    Los elementos SVG como líneas de conexión o cuadrículas usan viewBox="0 0 800 500" con preserveAspectRatio="xMidYMid slice". El viewBox define un sistema de coordenadas virtual que funciona independientemente del tamaño real del contenedor. slice llena el contenedor completamente y recorta cualquier desbordamiento — similar a object-fit: cover para imágenes.

  3. CSS @keyframes para animaciones

    Cada visual define sus propios @keyframes — float, pulse, orbit, blink, shimmer, ripple. Las animaciones usan exclusivamente transform y opacity, ya que solo estas dos propiedades producen animaciones de composición aceleradas por GPU. Nada de width, height, top o left en los keyframes — eso activaría recálculos de layout y destruiría el rendimiento.

  4. animation-delay para sensación orgánica

    Elementos pulsando simultáneamente se ven mecánicos y artificiales. Valores escalonados de animation-delay (0s, 0.3s, 0.6s, 0.9s...) hacen que las animaciones comiencen en tiempos desfasados. Esto crea un ritmo orgánico y vivo, como si los elementos fueran independientes entre sí. Los valores de delay están elegidos deliberadamente de forma irregular para evitar patrones.

  5. Media Query prefers-reduced-motion

    Al final de cada bloque de estilos hay un bloque @media (prefers-reduced-motion: reduce) que desactiva todas las animaciones con animation: none. Los usuarios que han activado movimiento reducido en su sistema operativo ven un fondo estático — los elementos visuales permanecen visibles pero no se mueven.

El patrón fundamental del contenedor

Cada visual hero sigue el mismo andamiaje HTML. Aquí un ejemplo usando el visual más simple — una representación simplificada del fondo hero del blog:

<!-- Contenedor exterior: cubre toda el área hero -->
<div class="blog-bg" aria-hidden="true">
  <!-- Capa 1: Fondo ambiental (blobs, gradientes) -->
  <div class="blog-bg__blob blog-bg__blob--1"></div>
  <div class="blog-bg__blob blog-bg__blob--2"></div>

  <!-- Capa 2: Elementos SVG (líneas, formas, paths) -->
  <svg class="blog-bg__lines" viewBox="0 0 800 500"
       preserveAspectRatio="xMidYMid slice">
    <!-- Contenidos SVG aquí -->
  </svg>

  <!-- Capa 3: Elementos HTML (puntos, etiquetas, cursor) -->
  <div class="blog-bg__cursor"></div>
  <span class="blog-bg__label blog-bg__label--1">Design</span>
</div>
/* El contenedor se extiende sobre todo el hero */
.blog-bg {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}

El z-index: 0 asegura que el contenido del hero (encabezado, botones) con un z-index más alto se sitúe encima. pointer-events: none evita que el fondo decorativo intercepte eventos de ratón o táctiles destinados a los elementos interactivos superiores.

Técnicas de animación CSS

Los seis visuales hero utilizan cuatro tipos fundamentales de animación que se pueden combinar de diversas formas. Todos usan exclusivamente transform y opacity — las únicas propiedades que el navegador puede animar en la GPU sin recálculo de layout.

/* Float — flotación suave arriba y abajo */
/* Crea la impresión de ingravidez */
@keyframes hero-float {
  0%, 100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-12px);
  }
}

/* Aplicado con duraciones y direcciones variadas */
.shape--circle {
  animation: hero-float 6s ease-in-out infinite;
}

.shape--ring {
  /* reverse invierte la dirección — los elementos
     se mueven en sentidos opuestos para más
     tensión visual */
  animation: hero-float 8s ease-in-out infinite reverse;
}

/* Consejo: Duraciones diferentes (6s, 7s, 8s, 9s)
   crean un patrón que nunca se repite porque
   las fases nunca se sincronizan exactamente */
/* Pulse — escalado respirante con opacidad */
/* Simula un brillo/pulsación suave */
@keyframes hero-pulse {
  0%, 100% {
    opacity: 0.3;
    transform: scale(1);
  }
  50% {
    opacity: 0.6;
    transform: scale(1.5);
  }
}

/* Aplicado con delays escalonados */
.dot--1 {
  animation: hero-pulse 3s ease-in-out infinite;
  animation-delay: 0s;
}
.dot--2 {
  animation: hero-pulse 3s ease-in-out infinite;
  animation-delay: 1s;
}
.dot--3 {
  animation: hero-pulse 3s ease-in-out infinite;
  animation-delay: 2s;
}

/* Los delays escalonados (0s, 1s, 2s) crean
   un movimiento de ola a través de los puntos */
/* Ripple — anillos circulares expandiéndose (Contacto) */
/* Simula ondas propagándose desde un punto */
@keyframes contact-ripple {
  0% {
    transform: translate(-50%, -50%) scale(0.8);
    opacity: 0.2;
  }
  100% {
    transform: translate(-50%, -50%) scale(1.15);
    opacity: 0;
  }
}

/* Cuatro anillos con delay escalonado = onda continua */
.arc--1 {
  width: 60px; height: 60px;
  animation: contact-ripple 3s ease-out infinite;
}
.arc--2 {
  width: 120px; height: 120px;
  animation: contact-ripple 3s ease-out infinite 0.6s;
}
.arc--3 {
  width: 200px; height: 200px;
  animation: contact-ripple 3s ease-out infinite 1.2s;
}
.arc--4 {
  width: 300px; height: 300px;
  animation: contact-ripple 3s ease-out infinite 1.8s;
}
/* Cursor Blink — steps() para parpadeo discreto */
/* step-end crea transiciones abruptas en vez de suaves */
@keyframes hero-blink {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
}

.cursor {
  display: inline-block;
  width: 7px;
  height: 14px;
  background: var(--color-primary-500);
  /* step-end = cambio instantáneo, sin desvanecimiento.
     Esto es más realista que un fundido suave
     porque los cursores reales también parpadean
     abruptamente */
  animation: hero-blink 1.2s step-end infinite;
  vertical-align: text-bottom;
}

/* Variante terminal: ligeramente más pequeña */
.cursor--term {
  width: 6px;
  height: 12px;
  margin-left: 4px;
}

Por qué solo transform y opacity?

Los navegadores renderizan animaciones a través de una pipeline: Style > Layout > Paint > Composite. La mayoría de las propiedades CSS (width, height, top, left, margin, padding, border) activan la pipeline completa desde el paso Layout. Cada frame necesita ser recalculado, lo que ocurre en el hilo principal y bloquea JavaScript.

transform y opacity son especiales: solo activan el paso de Composite. El navegador crea una capa GPU separada para el elemento y lo mueve, escala o desvanece allí — sin tocar el hilo principal. Esta es la razón por la que las animaciones CSS con estas propiedades logran 60fps fluidos incluso cuando el hilo principal está ocupado con JavaScript.

Deep Dive: El visual de constelaciones

El AboutHeroVisual es el más complejo de los seis visuales. Combina múltiples técnicas en un efecto de red cohesivo que representa visualmente las habilidades interconectadas de Arnold. Examinemos las capas individuales en detalle.

Estructura de las cinco capas

El visual consiste en cinco capas apiladas una sobre otra. El orden en el DOM determina la profundidad visual:

  1. Blobs ambientales (z-index: auto) — Grandes áreas de color difuminadas (filter: blur(80px)) crean un efecto sutil de nebulosa en el fondo. Dos blobs usando --color-primary-600 y --color-secondary-600 con 5-6% de opacidad.
  2. Líneas de conexión SVG (z-index: auto) — Líneas discontinuas (stroke-dasharray: 4 6) conectan los nodos con el centro. Ocho líneas al núcleo, más cuatro conexiones cruzadas entre nodos exteriores.
  3. Nodos (z-index: auto) — Ocho círculos de 6x6px en posiciones fijas, pulsando entre 25% y 50% de opacidad con la animación about-node-glow. Cada nodo tiene un animation-delay individual.
  4. Núcleo central (z-index: 2) — Un punto de 12px con un resplandor box-shadow y tres anillos concéntricos (80px, 160px, 280px de diámetro) que pulsan con about-ring-pulse.
  5. Etiquetas tech (z-index: auto) — Seis etiquetas de texto flotantes (Astro, React, WordPress, SEO, A11y, TypeScript) en fuente monoespaciada al 18% de opacidad.

Los anillos pulsantes en detalle

/* Tres anillos concéntricos alrededor del núcleo */
.core-ring--1 {
  width: 80px;
  height: 80px;
  opacity: 0.2;
  animation: about-ring-pulse 4s ease-in-out infinite;
}

.core-ring--2 {
  width: 160px;
  height: 160px;
  opacity: 0.1;
  animation: about-ring-pulse 4s ease-in-out infinite 1s;
}

.core-ring--3 {
  width: 280px;
  height: 280px;
  opacity: 0.06;
  animation: about-ring-pulse 4s ease-in-out infinite 2s;
}

/* Custom property privada controla la opacidad base de cada anillo */
.core-ring--1 { --_ring-opacity: 0.2; }
.core-ring--2 { --_ring-opacity: 0.1; }
.core-ring--3 { --_ring-opacity: 0.06; }

@keyframes about-ring-pulse {
  0%, 100% {
    transform: translate(-50%, -50%) scale(1);
    opacity: var(--_ring-opacity, 0.15);
  }
  50% {
    transform: translate(-50%, -50%) scale(1.08);
    opacity: calc(var(--_ring-opacity, 0.15) * 0.6);
  }
}

Observa el truco con --_ring-opacity: cada anillo define su propio valor de opacidad como una propiedad personalizada privada (--_ring-opacity), que la animación luego lee mediante var() y calc(). De esta forma, una única definición de @keyframes puede animar diferentes opacidades base — DRY aplicado a las animaciones CSS. La multiplicación por 0.6 significa: en el pico de la pulsación, la opacidad cae al 60% de su valor base, creando un efecto sutil de “respiración”.

Las líneas SVG viajeras

.about-bg__line {
  stroke: var(--color-primary-500);
  stroke-width: 0.5;
  opacity: 0.08;
  stroke-dasharray: 4 6;
  animation: about-line-dash 20s linear infinite;
}

/* Las conexiones cruzadas son aún más sutiles */
.about-bg__line--faint {
  opacity: 0.04;
  stroke-dasharray: 2 8;
}

@keyframes about-line-dash {
  0% { stroke-dashoffset: 0; }
  100% { stroke-dashoffset: 100; }
}

stroke-dasharray: 4 6 crea un patrón de 4px de trazo y 6px de espacio. La animación luego desplaza el stroke-dashoffset linealmente, lo que hace que los trazos parezcan viajar a lo largo de la línea. Con una duración de 20 segundos, el movimiento es tan sutil que se siente más que se percibe conscientemente — exactamente el nivel correcto para un fondo decorativo.

Las conexiones cruzadas (--faint) usan stroke-dasharray: 2 8 — trazos más cortos con espacios más grandes — y solo 4% de opacidad. Son casi invisibles pero subconscientemente crean la impresión de una red más densa.

Aleatorización de la posición del núcleo

Un detalle sutil: la posición del núcleo central se aleatoriza en cada carga de página. Esto sucede a través de un pequeño snippet de JavaScript que establece propiedades personalizadas CSS:

/* Seis zonas predefinidas — ninguna posición en el área de texto */
const zones = [
  { top: [15, 35], left: [5, 25] },    /* arriba izquierda */
  { top: [15, 35], left: [75, 92] },   /* arriba derecha */
  { top: [60, 85], left: [5, 25] },    /* abajo izquierda */
  { top: [60, 85], left: [75, 92] },   /* abajo derecha */
  { top: [15, 35], left: [40, 60] },   /* arriba centro */
  { top: [70, 85], left: [40, 60] },   /* abajo centro */
];

/* Elegir zona aleatoria, calcular posición dentro */
const zone = zones[Math.floor(Math.random() * zones.length)];
bg.style.setProperty('--core-top', `${coreTop}%`);
bg.style.setProperty('--core-left', `${coreLeft}%`);

Las zonas están elegidas de forma que el núcleo nunca caiga sobre el texto principal del área hero (30-70% en ambos ejes). Así el texto permanece siempre legible mientras el visual luce diferente en cada visita. Las líneas de conexión SVG se actualizan posteriormente para apuntar hacia el nuevo centro.

Consideraciones de rendimiento

El rendimiento de las animaciones no es casualidad — es el resultado de decisiones técnicas deliberadas aplicadas consistentemente en cada visual. Para una visión completa de cómo optimizar los Core Web Vitals, consulta nuestra guía de rendimiento.

Solo Composición

Todas las animaciones usan exclusivamente transform y opacity — las únicas propiedades que el navegador puede animar en la GPU sin recalcular el layout.

Sin Layout Thrash

Nada de width, height, top, left o margin en @keyframes. Estas propiedades activan recálculos de layout que bloquean el hilo principal y causan jank.

Aceleración GPU

transform y opacity se renderizan en una capa de composición separada. El hilo principal queda libre para JavaScript, manejo de eventos y otras tareas.

Solo Above-the-fold

Los visuales hero solo están activos en el viewport visible. Sin animaciones ocultas debajo del fold desperdiciando batería y ciclos de CPU.

< 5 KB gzipped

Cada visual ocupa menos de 5 KB de HTML+CSS comprimido. Para comparar: una imagen JPEG de fondo a 1920px de ancho pesaría 50-200 KB. JSON de Lottie típicamente 30-100 KB.

Sin Runtime JS

Cero JavaScript se necesita para las animaciones. Sin bucle requestAnimationFrame, sin motor de tweens, sin event listeners. El compositor del navegador maneja todo nativamente.

will-change: Lo necesitas?

Una pregunta frecuente: deberías establecer will-change: transform en los elementos animados? La respuesta es matizada:

  • No necesario si ya usas animation o transition con transform — el navegador promueve automáticamente el elemento a una capa GPU.
  • Potencialmente dañino si estableces will-change en demasiados elementos — cada capa GPU consume memoria de video. Con 20+ elementos animados, esto puede convertirse en un problema en dispositivos móviles con VRAM limitada.
  • Útil solo para transiciones de hover que causan un frame drop en el primer disparo porque la capa necesita ser creada.

En este portfolio, will-change no se usa — la propiedad animation por sí sola es suficiente para activar la promoción de capa.

Accesibilidad

Las animaciones decorativas jamás deben comprometer la accesibilidad. Los visuales hero implementan tres capas de soporte de accesibilidad.

1. aria-hidden=“true”

Cada visual hero tiene aria-hidden="true" en su contenedor más externo. Esto significa: los usuarios de lectores de pantalla no son expuestos a ninguno de estos elementos. Como los visuales son puramente decorativos y no aportan valor de contenido, esto es correcto — un lector de pantalla anunciando “div, div, div, svg, line, line, span, Astro, span, React” no ayuda a nadie.

Importante: aria-hidden="true" en el contenedor se hereda automáticamente a todos los hijos. No necesitas establecerlo en cada elemento individual. Un solo atributo en el wrapper es suficiente.

2. prefers-reduced-motion

@media (prefers-reduced-motion: reduce) {
  .about-bg__core-ring,
  .about-bg__node,
  .about-bg__line,
  .about-bg__label,
  .about-bg__blob {
    animation: none;
  }
}

Esta media query CSS responde a la configuración del sistema operativo del usuario. En macOS: Preferencias del Sistema > Accesibilidad > Pantalla > “Reducir movimiento”. En Windows: Configuración > Accesibilidad > Pantalla > “Mostrar animaciones en Windows”. En iOS: Configuración > Accesibilidad > Movimiento > “Reducir Movimiento”. Los elementos SVG permanecen visibles pero estáticos.

3. El contenido permanece accesible

Todo el contenido textual en el hero (encabezado, subtítulo, botones CTA) se sitúa por encima del fondo decorativo en el DOM. Es completamente navegable por teclado, tiene jerarquía correcta de encabezados y funciona idénticamente con y sin los visuales. El pointer-events: none en el contenedor del fondo asegura que ningún elemento decorativo reciba accidentalmente el foco o intercepte eventos táctiles.

Compatibilidad con modo oscuro

Los visuales hero utilizan design tokens (propiedades personalizadas CSS) para todos los valores de color. Esto significa: se adaptan automáticamente al modo de tema activo sin código adicional.

/* Los estilos base usan tokens — se adapta automáticamente */
.about-bg__node {
  background: var(--color-primary-400);
}

.about-bg__line {
  stroke: var(--color-primary-500);
  opacity: 0.08;
}

/* Resplandor del núcleo en modo oscuro */
.about-bg__core-dot {
  box-shadow:
    0 0 20px var(--color-primary-500),
    0 0 60px rgba(1, 95, 252, 0.3);
}

/* Overrides de modo claro para control más fino */
[data-theme="light"] .about-bg__node {
  background: var(--color-primary-500);
}

[data-theme="light"] .about-bg__line {
  stroke: var(--color-primary-600);
  opacity: 0.06;
}

/* Resplandor reducido en modo claro */
[data-theme="light"] .about-bg__core-dot {
  box-shadow:
    0 0 20px var(--color-primary-500),
    0 0 40px rgba(1, 95, 252, 0.15);
}

El principio es directo: los estilos base usan valores de tokens ya definidos para el modo oscuro (--color-primary-400, --color-primary-500). Para el modo claro, los selectores [data-theme="light"] aplican valores ligeramente ajustados — típicamente tonos de color más oscuros y opacidad reducida, ya que los fondos claros toleran menos contraste.

Ajustes responsive

Los visuales hero no simplemente se escalan hacia abajo — se simplifican deliberadamente en pantallas pequeñas para reducir el ruido visual y conservar el rendimiento en dispositivos móviles.

Breakpoints móviles

Todos los visuales incluyen un bloque @media (max-width: 639px) que reduce o simplifica ciertos elementos:

  • Fuentes se reducen de 10px a 8px (etiquetas flotantes)
  • Blobs se encogen de 250-300px a 130-180px
  • Anillos concéntricos reducen su radio (ej. 280px a 180px)
  • Corchetes decorativos se escalan de 40px a 24px
  • Comillas se reducen de 80px a 50px
  • Grupos de líneas de texto se estrechan de 140px a 100px de ancho

El objetivo: en dispositivos móviles, el fondo debe ser perceptible pero no intrusivo. Ningún elemento se oculta completamente — eso destruiría la identidad visual de la página.

Por qué funciona viewBox

Los elementos SVG con viewBox="0 0 800 500" y preserveAspectRatio="xMidYMid slice" se escalan automáticamente con su contenedor. Coordenadas como x1="640" y1="200" permanecen relativas al sistema de coordenadas de 800x500 — sin importar si el contenedor tiene 1920px o 320px de ancho.

Esto significa: no se necesitan media queries para el posicionamiento SVG. Las líneas, cuadrículas y formas se adaptan proporcionalmente. El valor slice es crucial: llena el contenedor completamente (como object-fit: cover), en lugar de crear letterboxing (meet sería el equivalente de contain).

En una pantalla móvil estrecha, los contenidos SVG se recortan en los lados — exactamente como una imagen de fondo con background-size: cover.

Por qué las animaciones CSS son mobile-friendly

Las animaciones CSS de transform y opacity se renderizan en GPUs móviles con la misma eficiencia que en GPUs de escritorio. La clave es el compositor: los navegadores móviles tienen capas de composición dedicadas para animaciones de transform.

En contraste, una animación basada en JavaScript necesitaría despertar el hilo principal, realizar cálculos y manipular el DOM — en un smartphone de 2-3 años, esa es una diferencia de rendimiento perceptible.

Un beneficio adicional: las animaciones CSS se pausan automáticamente cuando la pestaña del navegador no está visible (Page Visibility API). Esto ahorra batería en dispositivos móviles — un bonus que muchas animaciones JavaScript no ofrecen porque necesitan pausar manualmente el bucle requestAnimationFrame.

Flujo de desarrollo en Astro

Los visuales hero están implementados como componentes Astro en la capa de moléculas de la jerarquía de Diseño Atómico. Cada componente es un archivo .astro autónomo con estilos encapsulados incrustados. La clasificación como moléculas (y no como átomos u organismos) sigue una lógica clara: combinan múltiples elementos primitivos (formas SVG, divs, spans) en una unidad funcional, pero no son lo suficientemente complejos para ser organismos. Si quieres ver cómo se organiza una biblioteca completa de componentes con esta metodología, consulta el artículo sobre Atomic Design en la práctica con más de 45 componentes.

Crear el componente

Crear un nuevo archivo .astro bajo src/components/molecules/. Definir interface Props (si es configurable). Construir el markup SVG y la estructura HTML con aria-hidden="true". Definir clases BEM para todos los elementos.

Escribir estilos encapsulados

Todas las reglas CSS van en el bloque <style> del componente. Nomenclatura BEM (hero-visual__element--modifier). Tokens de diseño para todos los valores de color. Definiciones de @keyframes dentro del bloque encapsulado. Sin reglas !important.

Definir @keyframes

Animaciones usando transform y opacity. Duraciones variadas (3-20s) y delays (0-2.5s) para sensación orgánica. ease-in-out para movimientos flotantes, ease-out para ripples expansivos, step-end para parpadeo de cursor, linear para cintas transportadoras infinitas.

Añadir prefers-reduced-motion

Bloque @media (prefers-reduced-motion: reduce) al final de la sección de estilos. Listar todos los selectores animados. Establecer animation: none. Los elementos permanecen visibles, solo el movimiento se detiene. Probar con configuración del SO.

Overrides de modo oscuro

Selectores [data-theme="light"] para opacidad y tonos de color ajustados. Típicamente: pasos de token más oscuros (primary-600 en vez de primary-400) y menor opacidad sobre fondos claros. Reducir resplandor box-shadow.

Breakpoints responsive

@media (max-width: 639px) para simplificaciones móviles. Reducir tamaños de elementos, ajustar tamaños de fuente. Nunca ocultar completamente un elemento — solo reducirlo. Los SVGs basados en viewBox se escalan automáticamente.

Preguntas frecuentes

FAQ: Animaciones SVG para Hero

Conclusión

Las animaciones SVG hero con CSS no son un compromiso — son la elección técnicamente superior para fondos decorativos. Cero payload de JavaScript, animaciones a 60fps aceleradas por GPU, soporte nativo de prefers-reduced-motion y menos de 5 KB por visual. Los seis visuales específicos por página de este portfolio demuestran que con CSS puro se puede crear una identidad visual fuerte que es simultáneamente performante y accesible.

La clave está en la disciplina: solo transform y opacity en @keyframes, valores escalonados de animation-delay para timing orgánico y un bloque consistente de prefers-reduced-motion en cada componente. A eso se suman las decisiones arquitectónicas — aria-hidden="true" en el contenedor, pointer-events: none para transparencia de clics, y SVGs basados en viewBox para responsividad automática.

El resultado son páginas vivas que se sienten como si respiraran — sin que un solo byte de JavaScript sea necesario para lograrlo. Y lo mejor: cuando un usuario prefiere movimiento reducido, ve un fondo bello y estático en lugar de un vacío. Los visuales funcionan con y sin animación — eso es diseño inclusivo.