"use client";
import * as React from "react";
import { MorphingShapeBackground, type MorphingShapeBlob } from "@/components/ui/morphing-shape-background";
const PREVIEW_MATTE_NOISE = `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='240' height='240'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.65 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>")`;
/* ───────────────────────── Morphing Shape Background, playground ──────────
* Lava-lamp blobs are synthesised on the fly from a palette + count + scale
* + speed, so users can sweep across totally different moods (chili → violet
* → emerald) without every prop needing its own slider.
* ────────────────────────────────────────────────────────────────────────── */
type PaletteId =
| "chili"
| "violet"
| "sky"
| "emerald"
| "amber"
| "mono";
interface MorphingState {
palette: PaletteId;
blobCount: number;
scale: number; // 0..2 multiplier on blob size
speedFactor: number; // 0.4..2 inverse multiplier on duration
blur: number; // 30..140
grain: boolean;
}
const settings = {
palette: "chili",
blobCount: 4,
scale: 1,
speedFactor: 1,
blur: 80,
grain: true,
};
/* Palette gradients, each entry is paired (start, end) so blobs read as
* "atmospheric depth" rather than flat slabs. Ordered loosely warm → cool. */
const PALETTES: Record<
PaletteId,
{ name: string; gradients: string[]; swatch: string }
> = {
chili: {
name: "Wensity Red",
swatch: "linear-gradient(135deg, #cd1c18, #ff8a73)",
gradients: [
"linear-gradient(135deg, #cd1c18 0%, #ff8a73 100%)",
"linear-gradient(135deg, #9b1313 0%, #38000a 100%)",
"linear-gradient(135deg, #ff8a73 0%, #ffe2db 100%)",
"linear-gradient(135deg, #ffa896 0%, #cd1c18 100%)",
],
},
violet: {
name: "Violet",
swatch: "linear-gradient(135deg, #4c1d95, #a78bfa)",
gradients: [
"linear-gradient(135deg, #6d28d9 0%, #c4b5fd 100%)",
"linear-gradient(135deg, #4c1d95 0%, #1e1b4b 100%)",
"linear-gradient(135deg, #a78bfa 0%, #ede9fe 100%)",
"linear-gradient(135deg, #7c3aed 0%, #4c1d95 100%)",
],
},
sky: {
name: "Sky",
swatch: "linear-gradient(135deg, #0369a1, #7dd3fc)",
gradients: [
"linear-gradient(135deg, #0284c7 0%, #7dd3fc 100%)",
"linear-gradient(135deg, #0c4a6e 0%, #082f49 100%)",
"linear-gradient(135deg, #38bdf8 0%, #e0f2fe 100%)",
"linear-gradient(135deg, #0ea5e9 0%, #0369a1 100%)",
],
},
emerald: {
name: "Emerald",
swatch: "linear-gradient(135deg, #047857, #6ee7b7)",
gradients: [
"linear-gradient(135deg, #059669 0%, #6ee7b7 100%)",
"linear-gradient(135deg, #064e3b 0%, #022c22 100%)",
"linear-gradient(135deg, #34d399 0%, #d1fae5 100%)",
"linear-gradient(135deg, #10b981 0%, #047857 100%)",
],
},
amber: {
name: "Amber",
swatch: "linear-gradient(135deg, #b45309, #fcd34d)",
gradients: [
"linear-gradient(135deg, #d97706 0%, #fcd34d 100%)",
"linear-gradient(135deg, #92400e 0%, #451a03 100%)",
"linear-gradient(135deg, #f59e0b 0%, #fef3c7 100%)",
"linear-gradient(135deg, #fbbf24 0%, #b45309 100%)",
],
},
mono: {
name: "Mono",
swatch: "linear-gradient(135deg, #1f2937, #6b7280)",
gradients: [
"linear-gradient(135deg, #374151 0%, #9ca3af 100%)",
"linear-gradient(135deg, #111827 0%, #030712 100%)",
"linear-gradient(135deg, #6b7280 0%, #f3f4f6 100%)",
"linear-gradient(135deg, #4b5563 0%, #1f2937 100%)",
],
},
};
/* Layout templates, anchor positions, opacity, durations. These never
* change with the palette so the user gets a consistent "rhythm" across
* palette swaps. */
const BLOB_TEMPLATES: ReadonlyArray<
Omit<MorphingShapeBlob, "color"> & { sizeBase: number }
> = [
{ sizeBase: 0.85, top: 10, left: 8, blur: 80, opacity: 0.55, duration: 28 },
{ sizeBase: 0.7, top: 35, left: 55, blur: 90, opacity: 0.6, duration: 36, reverse: true, delay: -8 },
{ sizeBase: 0.55, top: 60, left: 20, blur: 70, opacity: 0.4, duration: 32, delay: -14 },
{ sizeBase: 0.45, top: 5, left: 65, blur: 60, opacity: 0.45, duration: 24, reverse: true, delay: -4 },
{ sizeBase: 0.6, top: 45, left: 78, blur: 75, opacity: 0.42, duration: 30, delay: -10 },
{ sizeBase: 0.5, top: 78, left: 70, blur: 65, opacity: 0.38, duration: 26, reverse: true, delay: -2 },
];
function buildBlobs(state: MorphingState): MorphingShapeBlob[] {
const palette = PALETTES[state.palette];
const count = Math.min(Math.max(state.blobCount, 1), BLOB_TEMPLATES.length);
return BLOB_TEMPLATES.slice(0, count).map((tpl, i) => ({
color: palette.gradients[i % palette.gradients.length],
size: (tpl.sizeBase ?? 0.7) * state.scale,
top: tpl.top,
left: tpl.left,
blur: state.blur,
opacity: tpl.opacity,
duration: (tpl.duration ?? 30) / state.speedFactor,
delay: tpl.delay,
reverse: tpl.reverse,
}));
}
/* ───────────────────────── Preview ───────────────────────── */
export function MorphingShapeBackgroundPreview(props: Partial<typeof settings> = {}) {
const state = { ...settings, ...props };
const blobs = React.useMemo(() => buildBlobs(state), [state]);
return (
<div className="relative isolate w-full max-w-6xl overflow-hidden rounded-[1.35rem] border border-black/[0.08] bg-[radial-gradient(ellipse_90%_70%_at_50%_-10%,#ffffff_0%,#f4f4f5_72%)] shadow-[0_1px_2px_rgba(0,0,0,.06),0_18px_46px_-28px_rgba(0,0,0,.26)] dark:border-white/[0.06] dark:bg-[radial-gradient(ellipse_90%_70%_at_50%_-10%,#15151b_0%,#0a0a0b_72%)] dark:shadow-[0_1px_2px_rgba(0,0,0,.4),0_18px_46px_-28px_rgba(0,0,0,.72)]">
<div
aria-hidden
className="pointer-events-none absolute inset-0 opacity-[0.04] mix-blend-overlay dark:opacity-[0.055]"
style={{ backgroundImage: PREVIEW_MATTE_NOISE, backgroundSize: "240px 240px" }}
/>
<div
aria-hidden
className="pointer-events-none absolute inset-0 bg-[linear-gradient(to_bottom,rgba(0,0,0,0.035)_0%,transparent_28%,transparent_74%,rgba(0,0,0,0.06)_100%)] dark:bg-[linear-gradient(to_bottom,rgba(0,0,0,0.18)_0%,transparent_26%,transparent_74%,rgba(0,0,0,0.32)_100%)]"
/>
<MorphingShapeBackground
blobs={blobs}
grain={state.grain}
className="relative h-[600px]"
>
<div className="flex h-[600px] flex-col items-center justify-center gap-4 px-8 text-center">
<h2 className="font-display text-5xl font-extrabold leading-[1.02] tracking-tight text-white sm:text-6xl">
Wensity<span className="ml-1 inline-block h-2.5 w-2.5 self-end bg-red-600 align-baseline" />
</h2>
<p className="max-w-md text-[14px] leading-relaxed text-white/75">
Blobs morph their border-radius and rotate slowly. Grain on top kills banding and adds matte texture.
</p>
</div>
</MorphingShapeBackground>
</div>
);
}
/* ───────────────────────── Controls ───────────────────────── */
/* ───────────────────────── Hero variant ───────────────────────── */
export default function Demo(props: Partial<typeof settings>) {
return <MorphingShapeBackgroundPreview {...props} />;
}