Ir al contenido
Evaluar mi proyecto
Componentes · FondosDisponible

Fondo de olas en capas

Cuatro curvas SVG con paralaje que se desplazan a distinta velocidad bajo un sol de atardecer.

Marea de verano

Código completo

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

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

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

/** Fondo de olas en capas: cuatro curvas SVG con paralaje que se desplazan a distinta velocidad sobre un cielo degradado. Con botón de pausa. React + TypeScript, sin dependencias. */
export type FondoOlasProps = {
  colors?: [string, string, string, string];
  sky?: string;
  children?: ReactNode;
  className?: string;
  style?: CSSProperties;
};

const CSS = `
.gcfol-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: var(--gcfol-sky, linear-gradient(180deg, #ffe3c7, #ffb7a0 55%, #ff8fa3)); color: #fff; font: 500 16px/1.5 ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif; }
.gcfol-root *, .gcfol-root *::before, .gcfol-root *::after { box-sizing: border-box; }
.gcfol-root :focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.gcfol-root[data-paused="true"] .gcfol-w { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) { .gcfol-w { animation: none !important; } }
.gcfol-sun { position: absolute; z-index: -3; left: 50%; bottom: 34%; width: min(36cqi, 260px); aspect-ratio: 1; margin-left: calc(min(36cqi, 260px) / -2); border-radius: 50%; background: radial-gradient(circle, #fff7cf 0 55%, rgba(255, 247, 207, .0) 72%); box-shadow: 0 0 80px 30px rgba(255, 238, 190, .6); }
.gcfol-waves { position: absolute; left: 0; right: 0; bottom: 0; height: 56%; z-index: -1; }
.gcfol-w { position: absolute; left: 0; bottom: 0; width: 200%; height: var(--h); animation: gcfol-slide var(--t) linear infinite var(--dir, normal); }
.gcfol-w svg { display: block; width: 100%; height: 100%; }
.gcfol-pause { position: absolute; right: 14px; top: 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(40, 20, 60, .45); color: #fff; font: 600 12px/1 ui-sans-serif, system-ui, sans-serif; cursor: pointer; }
.gcfol-pause:hover { background: rgba(40, 20, 60, .6); }
@keyframes gcfol-slide { to { transform: translateX(-50%); } }`;

const PATH = "M0 60 C 120 20 240 100 360 60 S 600 20 720 60 S 960 100 1080 60 S 1320 20 1440 60 S 1680 100 1800 60 S 2040 20 2160 60 S 2400 100 2520 60 S 2760 20 2880 60 V 200 H 0 Z";

export function FondoOlas({ colors = ["#ff7a8a", "#e0568f", "#9b3f9a", "#5a2a7a"], sky, children, className = "", style }: FondoOlasProps) {
  const [paused, setPaused] = useState(false);
  const layers = [
    { h: "88%", t: "34s", dir: "normal" },
    { h: "70%", t: "26s", dir: "reverse" },
    { h: "52%", t: "19s", dir: "normal" },
    { h: "34%", t: "13s", dir: "reverse" },
  ];
  return (
    <section className={`gcfol-root ${className}`} style={{ ...(sky ? ({ "--gcfol-sky": sky } as CSSProperties) : {}), ...style }} data-paused={paused}>
      <style>{CSS}</style>
      <div className="gcfol-sun" aria-hidden="true" />
      <div className="gcfol-waves" aria-hidden="true">
        {layers.map((layer, i) => (
          <div key={i} className="gcfol-w" style={{ "--h": layer.h, "--t": layer.t, "--dir": layer.dir } as CSSProperties}>
            <svg viewBox="0 0 2880 200" preserveAspectRatio="none">
              <path d={PATH} fill={colors[i]} transform={`translate(${i * -240} 0)`} />
            </svg>
          </div>
        ))}
      </div>
      {children}
      <button className="gcfol-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-olas

Ejemplo de uso de Fondo de olas en capas

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

ejemplo.tsx5 líneas
import { FondoOlas } from "./fondo-olas";

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

Propiedades de FondoOlas

FondoOlas 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]Opcional.
sky?stringOpcional.
children?ReactNodeOpcional.
className?stringOpcional.
style?CSSPropertiesOpcional.

Personaliza Fondo de olas en capas 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
--dirnormal

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 olas en capas 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)