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.
Por qué SVG + CSS en lugar de librerías JavaScript
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.
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.
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.
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.
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.
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.
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
- 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.
- 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.
- 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.
- 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.
- 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:
- 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-600y--color-secondary-600con 5-6% de opacidad. - 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. - 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 unanimation-delayindividual. - Núcleo central (z-index: 2) — Un punto de 12px con un resplandor
box-shadowy tres anillos concéntricos (80px, 160px, 280px de diámetro) que pulsan conabout-ring-pulse. - 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.
Todas las animaciones usan exclusivamente transform y opacity — las únicas propiedades que el navegador puede animar en la GPU sin recalcular el layout.
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.
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.
Los visuales hero solo están activos en el viewport visible. Sin animaciones ocultas debajo del fold desperdiciando batería y ciclos de CPU.
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.
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
animationotransitioncontransform— el navegador promueve automáticamente el elemento a una capa GPU. - Potencialmente dañino si estableces
will-changeen 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 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.
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.
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.
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.
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.
@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
Chrome DevTools: pestaña Performance > Iniciar grabación > Desplazar la página > Detener grabación. Revisa la fila Frames: barras verdes a 60fps = bien. Barras rojas = jank. Además: pestaña Rendering > Activar paint flashing — destellos verdes sobre elementos animados indican pintados en el hilo principal que debes evitar. Para un chequeo rápido: abre el medidor FPS en Rendering > Frame rendering stats.
Sí, absolutamente. SVG + CSS funciona en cualquier framework. En React, creas un componente funcional que retorna el markup SVG y escribes los estilos en un archivo CSS o herramienta CSS-in-JS. En Vue, usas un Single File Component con <template> para el SVG y <style scoped> para las animaciones. La única diferencia con Astro: los estilos encapsulados de Astro son en build-time (sin sobrecarga de runtime), los de React/Vue son en runtime.
Este portfolio nunca oculta completamente ningún visual. En su lugar, los tamaños de elementos se reducen con un breakpoint @media (max-width: 639px): blobs más pequeños, fuentes más estrechas, anillos más compactos. Esto ahorra recursos GPU sin destruir el concepto visual. Para dispositivos extremadamente antiguos, prefers-reduced-motion adicionalmente desactiva todas las animaciones.
Safari históricamente ha tenido problemas con animaciones SVG SMIL, pero las animaciones CSS sobre elementos SVG funcionan de manera confiable desde Safari 12+. Una peculiaridad conocida: Safari calcula transform-origin en elementos SVG de forma diferente a Chrome/Firefox. Solución: usar translate(-50%, -50%) en lugar de transform-origin: center center en elementos SVG. Este portfolio usa exactamente este patrón para todos los elementos centrados (anillos, arcos).
Una imagen de fondo hero típica a 1920x1080px pesa 80-200 KB (JPEG/WebP). Los visuales SVG+CSS de este portfolio pesan 2-5 KB gzipped por página — eso es un ahorro del 95-98%. En seis páginas, esto ahorra un total de 400-1100 KB de payload de red. Además: sin peticiones HTTP adicionales para archivos de imagen, sin necesidad de preloading.
Sí, mediante la propiedad CSS animation-play-state. Establece animation-play-state: paused en el contenedor o elementos individuales para congelar la animación. Esto puede controlarse via una clase CSS o un atributo data. Ejemplo: [data-animations="paused"] .hero-visual * { animation-play-state: paused; }. Las animaciones se detienen inmediatamente en su posición actual.
En hojas de estilo de impresión, las animaciones son automáticamente ignoradas por el navegador. Como los visuales tienen aria-hidden="true", típicamente se ocultan con display: none en la mayoría de las hojas de estilo de impresión. Si necesitas un bloque de impresión explícito: @media print { .hero-visual { display: none; } }.
Copia un componente visual existente como punto de partida. Cambia el prefijo BEM (ej. pricing-bg en vez de about-bg). Reemplaza los elementos SVG y la estructura HTML con tu nuevo concepto. Adapta las animaciones @keyframes. Asegúrate de que el bloque prefers-reduced-motion incluya todos los nuevos selectores animados. Prueba en modo oscuro y en móvil. El andamiaje (contenedor, posicionamiento, accesibilidad) permanece idéntico.
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.