Ir al contenido
Evaluar mi proyecto
Componentes · FondosDisponible

Fondo de malla de gradientes

Cinco manchas de color desenfocadas que se mezclan lentamente sobre una capa de grano; con botón de pausa.

Un fondo que respira

Código completo

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

fondo-malla.tsx53 líneas
Descargar
"use client";

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

/** Fondo de malla de gradientes: cinco manchas de color desenfocadas que se mezclan lentamente sobre una capa de grano. Con botón de pausa. React + TypeScript, sin dependencias. */
export type FondoMallaProps = {
  colors?: [string, string, string, string, string];
  base?: string;
  children?: ReactNode;
  className?: string;
  style?: CSSProperties;
};

const GRAIN = "url(\"data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1  0 0 0 0 1  0 0 0 0 1  0 0 0 .55 0'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>\")";

const CSS = `
.gcfma-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; color: #fff; background: var(--gcfma-base, #1b1240); font: 500 16px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; }
.gcfma-root *, .gcfma-root *::before, .gcfma-root *::after { box-sizing: border-box; }
.gcfma-root :focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.gcfma-root[data-paused="true"] .gcfma-b { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) { .gcfma-b { animation: none !important; } }
.gcfma-b { position: absolute; z-index: -2; width: 62%; aspect-ratio: 1; border-radius: 50%; background: var(--c); filter: blur(60px); opacity: .9; will-change: transform; animation: gcfma-move var(--t) ease-in-out var(--d) infinite alternate; }
.gcfma-grain { position: absolute; inset: 0; z-index: -1; background-image: ${GRAIN}; opacity: .22; mix-blend-mode: soft-light; pointer-events: none; }
.gcfma-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: rgba(0, 0, 0, .35); color: #fff; font: 600 12px/1 ui-sans-serif, system-ui, sans-serif; cursor: pointer; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px); }
.gcfma-pause:hover { background: rgba(0, 0, 0, .5); }
@keyframes gcfma-move { 0% { transform: translate(var(--x0), var(--y0)) scale(1); } 100% { transform: translate(var(--x1), var(--y1)) scale(1.25); } }`;

const POS = [
  { x0: "-30%", y0: "-30%", x1: "20%", y1: "10%", t: "14s", d: "0s" },
  { x0: "40%", y0: "-20%", x1: "-10%", y1: "30%", t: "17s", d: "-4s" },
  { x0: "-20%", y0: "30%", x1: "30%", y1: "-10%", t: "19s", d: "-8s" },
  { x0: "30%", y0: "40%", x1: "-30%", y1: "10%", t: "16s", d: "-2s" },
  { x0: "10%", y0: "0%", x1: "-20%", y1: "-30%", t: "21s", d: "-6s" },
];

export function FondoMalla({ colors = ["#ff4f9a", "#4f7bff", "#27d3c3", "#ffb347", "#8a4fff"], base = "#1b1240", children, className = "", style }: FondoMallaProps) {
  const [paused, setPaused] = useState(false);
  return (
    <section className={`gcfma-root ${className}`} style={{ "--gcfma-base": base, ...style } as CSSProperties} data-paused={paused}>
      <style>{CSS}</style>
      {colors.map((color, index) => (
        <div key={index} className="gcfma-b" aria-hidden="true" style={{ "--c": color, "--x0": POS[index].x0, "--y0": POS[index].y0, "--x1": POS[index].x1, "--y1": POS[index].y1, "--t": POS[index].t, "--d": POS[index].d, left: `${(index % 3) * 22}%`, top: `${Math.floor(index / 3) * 36 - 10}%` } as CSSProperties} />
      ))}
      <div className="gcfma-grain" aria-hidden="true" />
      {children}
      <button className="gcfma-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-malla

Ejemplo de uso de Fondo de malla de gradientes

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

ejemplo.tsx5 líneas
import { FondoMalla } from "./fondo-malla";

export default function Ejemplo() {
  return <FondoMalla base="Ejemplo">Contenido</FondoMalla>;
}

Propiedades de FondoMalla

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

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

Personaliza Fondo de malla de gradientes con variables CSS

Define estas variables en el contenedor o en tu hoja de estilos para cambiar los colores sin tocar el componente. Si no las defines, se usa el valor por defecto.

VariableValor por defecto
--gcfma-base#1b1240

Accesibilidad y rendimiento

El archivo tiene 53 líneas (4 KB) y define 1 animación con @keyframes. Esto es lo que hace Fondo de malla de gradientes 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-*.
  • Usa backdrop-filter para el efecto de vidrio; en navegadores sin soporte el panel se ve sin desenfoque.
  • 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)