Ir al contenido
Evaluar mi proyecto
Componentes · FondosDisponible

Fondo de metabolas líquidas

Círculos de color que se funden como líquido gracias a un filtro SVG gooey; solo CSS y SVG.

Todo se mezcla

Código completo

Es exactamente el archivo que se descarga. Cópialo a tu proyecto y úsalo.

fondo-metabolas.tsx59 líneas
Descargar
"use client";

import { useId, useState, type CSSProperties, type ReactNode } from "react";

/** Fondo de metabolas: círculos de color que se funden como líquido gracias a un filtro SVG "gooey". Solo CSS y SVG, con botón de pausa. React + TypeScript, sin dependencias. */
export type FondoMetabolasProps = {
  colors?: string[];
  children?: ReactNode;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gcfmb-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: #fff3e6; color: #2b1a3a; font: 500 16px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; }
.gcfmb-root *, .gcfmb-root *::before, .gcfmb-root *::after { box-sizing: border-box; }
.gcfmb-root :focus-visible { outline: 2px solid #2b1a3a; outline-offset: 3px; }
.gcfmb-root[data-paused="true"] .gcfmb-b { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) { .gcfmb-b { animation: none !important; } }
.gcfmb-field { position: absolute; inset: 0; z-index: -1; filter: var(--gcfmb-filter); }
.gcfmb-b { position: absolute; width: var(--z); height: var(--z); border-radius: 50%; background: var(--c); left: var(--x); top: var(--y); will-change: transform; animation: gcfmb-move var(--t) ease-in-out var(--d) infinite alternate; }
.gcfmb-svg { position: absolute; width: 0; height: 0; }
.gcfmb-pause { position: absolute; right: 14px; bottom: 14px; z-index: 3; display: inline-flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 12px; border: 0; border-radius: 999px; background: #2b1a3a; color: #fff3e6; font: 600 12px/1 ui-sans-serif, system-ui, sans-serif; cursor: pointer; }
@keyframes gcfmb-move { 0% { transform: translate(0, 0); } 50% { transform: translate(var(--dx), var(--dy)); } 100% { transform: translate(calc(var(--dx) * -.6), calc(var(--dy) * .8)); } }`;

const SPEC = [
  { x: "8%", y: "12%", z: "30%", dx: "40cqi", dy: "20%", t: "11s" },
  { x: "62%", y: "6%", z: "26%", dx: "-30cqi", dy: "40%", t: "13s" },
  { x: "30%", y: "55%", z: "34%", dx: "30cqi", dy: "-30%", t: "15s" },
  { x: "75%", y: "58%", z: "24%", dx: "-40cqi", dy: "-20%", t: "10s" },
  { x: "46%", y: "28%", z: "18%", dx: "20cqi", dy: "30%", t: "9s" },
];

export function FondoMetabolas({ colors = ["#ff6f61", "#ffb347", "#8a6dff", "#2ec4b6", "#ff4f9a"], children, className = "", style }: FondoMetabolasProps) {
  const uid = useId().replace(/[^a-zA-Z0-9]/g, "");
  const fid = `gcfmb-f-${uid}`;
  const [paused, setPaused] = useState(false);
  return (
    <section className={`gcfmb-root ${className}`} style={{ "--gcfmb-filter": `url(#${fid})`, ...style } as CSSProperties} data-paused={paused}>
      <style>{CSS}</style>
      <svg className="gcfmb-svg" aria-hidden="true" focusable="false">
        <filter id={fid}>
          <feGaussianBlur in="SourceGraphic" stdDeviation="22" result="b" />
          <feColorMatrix in="b" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 24 -10" />
        </filter>
      </svg>
      <div className="gcfmb-field" aria-hidden="true">
        {SPEC.slice(0, Math.max(3, colors.length)).map((s, i) => (
          <span key={i} className="gcfmb-b" style={{ "--c": colors[i % colors.length], "--x": s.x, "--y": s.y, "--z": `min(${s.z}, 260px)`, "--dx": s.dx, "--dy": s.dy, "--t": s.t, "--d": `${-i * 2}s` } as CSSProperties} />
        ))}
      </div>
      {children}
      <button className="gcfmb-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.

terminal.sh1 líneas
npx shadcn@latest add https://www.gassierconsulting.com/r/fondo-metabolas

Ejemplo de uso de Fondo de metabolas líquidas

Después de copiar fondo-metabolas.tsx a tu proyecto, impórtalo y colócalo donde lo necesites.

ejemplo.tsx5 líneas
import { FondoMetabolas } from "./fondo-metabolas";

export default function Ejemplo() {
  return <FondoMetabolas>Contenido</FondoMetabolas>;
}

Propiedades de FondoMetabolas

FondoMetabolas acepta 4 propiedades: todas son opcionales y tienen un valor por defecto, así que funciona sin configurar nada.

PropiedadTipoDescripción
colors?string[]Opcional.
children?ReactNodeOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Accesibilidad y rendimiento

El archivo tiene 59 líneas (4 KB) y define 1 animación con @keyframes. Esto es lo que hace Fondo de metabolas líquidas 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".
Licencia
MIT
Dependencias
React >=18
Compatibilidad
React 18 y 19 · Next.js (componente de cliente)