Fondo Frutiger Aero con burbujas
Cielo azul brillante, rayos de luz, colina y burbujas de cristal que suben con reflejos, al estilo de 2007.
Aire fresco
Código completo
Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.
"use client";
import { useState, type CSSProperties, type ReactNode } from "react";
/** Fondo Frutiger Aero: cielo azul brillante, rayos de luz y burbujas de cristal que suben con reflejos. Estilo skeuomórfico de 2007. Con botón de pausa. React + TypeScript, sin dependencias. */
export type FondoBurbujasProps = {
count?: number;
children?: ReactNode;
className?: string;
style?: CSSProperties;
};
const CSS = `
.gcfbu-root { box-sizing: border-box; position: relative; isolation: isolate; container-type: inline-size; width: 100%; min-height: 480px; overflow: hidden; display: grid; place-items: center; background: linear-gradient(180deg, #3aa0ff 0%, #7cd0ff 45%, #c8f1ff 80%, #e8fff0 100%); color: #05345f; font: 500 16px/1.5 "Segoe UI", "Lucida Grande", ui-sans-serif, system-ui, sans-serif; }
.gcfbu-root *, .gcfbu-root *::before, .gcfbu-root *::after { box-sizing: border-box; }
.gcfbu-root :focus-visible { outline: 3px solid #05345f; outline-offset: 3px; }
.gcfbu-root[data-paused="true"] .gcfbu-b, .gcfbu-root[data-paused="true"] .gcfbu-rays { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) { .gcfbu-b, .gcfbu-rays { animation: none !important; } }
.gcfbu-rays { position: absolute; z-index: -3; inset: -30% -10% 20% -10%; background: conic-gradient(from 200deg at 30% 0%, transparent 0 8%, rgba(255, 255, 255, .5) 12%, transparent 17% 26%, rgba(255, 255, 255, .35) 30%, transparent 36% 48%, rgba(255, 255, 255, .45) 52%, transparent 58%); filter: blur(6px); animation: gcfbu-sway 14s ease-in-out infinite alternate; transform-origin: 30% 0; }
.gcfbu-hill { position: absolute; z-index: -2; left: -10%; right: -10%; bottom: -14%; height: 40%; border-radius: 50% 50% 0 0; background: radial-gradient(120% 100% at 50% 0%, #8ee06a, #3f9f2f); box-shadow: inset 0 14px 30px rgba(255, 255, 255, .35); }
.gcfbu-b { position: absolute; z-index: -1; bottom: -90px; left: var(--x); width: var(--s); height: var(--s); border-radius: 50%; background: radial-gradient(circle at 32% 28%, rgba(255, 255, 255, .95) 0 8%, rgba(255, 255, 255, .0) 22%), radial-gradient(circle at 50% 60%, rgba(255, 255, 255, .0) 55%, rgba(255, 255, 255, .55) 100%), radial-gradient(circle at 50% 50%, rgba(160, 230, 255, .25), rgba(60, 150, 255, .18)); box-shadow: inset 0 0 0 1.5px rgba(255, 255, 255, .8), inset 0 -8px 14px rgba(255, 255, 255, .35), 0 6px 14px rgba(0, 80, 160, .15); will-change: transform; animation: gcfbu-rise var(--t) linear var(--d) infinite; }
.gcfbu-pause { position: absolute; right: 14px; top: 14px; z-index: 3; display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 14px; border: 1px solid #1f78c8; border-radius: 999px; background: linear-gradient(180deg, #fff, #cfe9ff); color: #05345f; font: 600 12px/1 "Segoe UI", ui-sans-serif, system-ui, sans-serif; cursor: pointer; box-shadow: inset 0 1px 0 #fff, 0 2px 6px rgba(0, 80, 160, .3); }
@keyframes gcfbu-rise { 0% { transform: translate(0, 0); opacity: 0; } 10% { opacity: 1; } 50% { transform: translate(var(--w), -300px); } 100% { transform: translate(calc(var(--w) * -1), -640px); opacity: .9; } }
@keyframes gcfbu-sway { to { transform: rotate(8deg); } }`;
export function FondoBurbujas({ count = 18, children, className = "", style }: FondoBurbujasProps) {
const [paused, setPaused] = useState(false);
return (
<section className={`gcfbu-root ${className}`} style={style} data-paused={paused}>
<style>{CSS}</style>
<div className="gcfbu-rays" aria-hidden="true" />
<div className="gcfbu-hill" aria-hidden="true" />
{Array.from({ length: count }, (_, i) => {
const s = 22 + ((i * 37) % 70);
return <span key={i} className="gcfbu-b" aria-hidden="true" style={{ "--x": `${(i * 53) % 96}%`, "--s": `${s}px`, "--t": `${12 + ((i * 7) % 9)}s`, "--d": `${-((i * 2.3) % 14)}s`, "--w": `${((i * 11) % 30) - 15}px` } as CSSProperties} />;
})}
{children}
<button className="gcfbu-pause" type="button" aria-pressed={paused} onClick={() => setPaused((value) => !value)}>
<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">{paused ? <path d="M7 4v16l13-8z" /> : <path d="M6 4h4v16H6zM14 4h4v16h-4z" />}</svg>
{paused ? "Reanudar" : "Pausar"} fondo
</button>
</section>
);
}
Úsalo en tu proyecto
Instálalo con la CLI de shadcn.
npx shadcn@latest add https://www.gassierconsulting.com/r/fondo-burbujasEjemplo de uso de Fondo Frutiger Aero con burbujas
Después de copiar fondo-burbujas.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.
import { FondoBurbujas } from "./fondo-burbujas";
export default function Ejemplo() {
return <FondoBurbujas>Contenido</FondoBurbujas>;
}Propiedades de FondoBurbujas
FondoBurbujas acepta 4 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.
| Propiedad | Tipo | Descripción |
|---|---|---|
count? | number | Opcional. |
children? | ReactNode | Opcional. |
className? | string | Opcional. |
style? | CSSProperties | Opcional. |
Accesibilidad y rendimiento
El archivo tiene 45 líneas (4 KB) y define 2 animaciones con @keyframes. Esto es lo que hace Fondo Frutiger Aero con burbujas por quien lo usa:
- Detecta prefers-reduced-motion y baja o quita el movimiento cuando la persona lo pide.
- Muestra un anillo de foco visible al navegar con teclado (:focus-visible).
- Expone su estado a tecnologías de apoyo con atributos aria-*.
- Es un componente de cliente de Next.js: incluye la directiva "use client".