full upgrade to dev

This commit is contained in:
2026-01-07 14:30:00 +01:00
parent 4dc727fcd6
commit 26a8610aa7
34 changed files with 6890 additions and 920 deletions

View File

@@ -1,42 +1,47 @@
'use client';
"use client";
import { motion, useMotionValue, useTransform, useSpring } from 'framer-motion';
import { useEffect, useState } from 'react';
import { motion, useMotionValue, useTransform, useSpring } from "framer-motion";
import { useEffect, useState } from "react";
export const BackgroundBlobs = () => {
const mouseX = useMotionValue(0);
const mouseY = useMotionValue(0);
const springConfig = { damping: 50, stiffness: 200 };
const springConfig = { damping: 50, stiffness: 50, mass: 2 };
const springX = useSpring(mouseX, springConfig);
const springY = useSpring(mouseY, springConfig);
// Parallax offsets
const x1 = useTransform(springX, (value) => value / 20);
const y1 = useTransform(springY, (value) => value / 20);
const x2 = useTransform(springX, (value) => value / -15);
const y2 = useTransform(springY, (value) => value / -15);
const x3 = useTransform(springX, (value) => value / 10);
const y3 = useTransform(springY, (value) => value / 10);
// Very subtle parallax offsets
const x1 = useTransform(springX, (value) => value / 30);
const y1 = useTransform(springY, (value) => value / 30);
const x2 = useTransform(springX, (value) => value / -25);
const y2 = useTransform(springY, (value) => value / -25);
const x3 = useTransform(springX, (value) => value / 20);
const y3 = useTransform(springY, (value) => value / 20);
const x4 = useTransform(springX, (value) => value / -35);
const y4 = useTransform(springY, (value) => value / -35);
const x5 = useTransform(springX, (value) => value / 15);
const y5 = useTransform(springY, (value) => value / 15);
useEffect(() => {
const handleMouseMove = (e: MouseEvent) => {
// Center the coordinate system
const { innerWidth, innerHeight } = window;
const x = e.clientX - innerWidth / 2;
const y = e.clientY - innerHeight / 2;
const x = e.clientX - window.innerWidth / 2;
const y = e.clientY - window.innerHeight / 2;
mouseX.set(x);
mouseY.set(y);
};
window.addEventListener('mousemove', handleMouseMove);
return () => window.removeEventListener('mousemove', handleMouseMove);
window.addEventListener("mousemove", handleMouseMove);
return () => window.removeEventListener("mousemove", handleMouseMove);
}, [mouseX, mouseY]);
// Prevent hydration mismatch
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
@@ -44,34 +49,120 @@ export const BackgroundBlobs = () => {
if (!mounted) return null;
return (
<div className="fixed inset-0 overflow-hidden pointer-events-none z-0 liquid-container">
<motion.div
className="absolute top-[-10%] left-[-10%] w-[40vw] h-[40vw] bg-liquid-mint/40 rounded-full blur-[80px] mix-blend-multiply opacity-70"
style={{ x: x1, y: y1 }}
animate={{
scale: [1, 1.2, 1],
rotate: [0, 90, 0],
}}
transition={{ duration: 25, repeat: Infinity, ease: "linear" }}
/>
<motion.div
className="absolute top-[20%] right-[-10%] w-[35vw] h-[35vw] bg-liquid-lavender/40 rounded-full blur-[80px] mix-blend-multiply opacity-70"
style={{ x: x2, y: y2 }}
animate={{
scale: [1, 1.1, 1],
rotate: [0, -60, 0],
}}
transition={{ duration: 30, repeat: Infinity, ease: "linear" }}
/>
<motion.div
className="absolute bottom-[-10%] left-[20%] w-[45vw] h-[45vw] bg-liquid-rose/30 rounded-full blur-[80px] mix-blend-multiply opacity-70"
style={{ x: x3, y: y3 }}
animate={{
scale: [1, 1.3, 1],
rotate: [0, 45, 0]
}}
transition={{ duration: 35, repeat: Infinity, ease: "linear" }}
/>
<div className="fixed inset-0 overflow-hidden pointer-events-none z-0">
{/* Mint blob - top left */}
<motion.div
className="absolute top-[-10%] left-[-10%] w-[40vw] h-[40vw] bg-liquid-mint/40 rounded-full blur-[100px] mix-blend-multiply"
style={{ x: x1, y: y1 }}
animate={{
scale: [1, 1.15, 1],
rotate: [0, 45, 0],
}}
transition={{
duration: 40,
repeat: Infinity,
ease: "easeInOut",
repeatType: "reverse",
}}
/>
{/* Lavender blob - top right */}
<motion.div
className="absolute top-[10%] right-[-5%] w-[35vw] h-[35vw] bg-liquid-lavender/35 rounded-full blur-[100px] mix-blend-multiply"
style={{ x: x2, y: y2 }}
animate={{
scale: [1, 1.1, 1],
rotate: [0, -30, 0],
}}
transition={{
duration: 45,
repeat: Infinity,
ease: "easeInOut",
repeatType: "reverse",
}}
/>
{/* Rose blob - bottom left */}
<motion.div
className="absolute bottom-[-5%] left-[15%] w-[45vw] h-[45vw] bg-liquid-rose/35 rounded-full blur-[100px] mix-blend-multiply"
style={{ x: x3, y: y3 }}
animate={{
scale: [1, 1.2, 1],
rotate: [0, 60, 0],
}}
transition={{
duration: 50,
repeat: Infinity,
ease: "easeInOut",
repeatType: "reverse",
}}
/>
{/* Peach blob - middle right */}
<motion.div
className="absolute top-[40%] right-[10%] w-[30vw] h-[30vw] bg-orange-200/30 rounded-full blur-[120px] mix-blend-multiply"
style={{ x: x4, y: y4 }}
animate={{
scale: [1, 1.25, 1],
rotate: [0, -45, 0],
}}
transition={{
duration: 55,
repeat: Infinity,
ease: "easeInOut",
repeatType: "reverse",
}}
/>
{/* Blue blob - center */}
<motion.div
className="absolute top-[50%] left-[40%] w-[38vw] h-[38vw] bg-blue-200/30 rounded-full blur-[110px] mix-blend-multiply"
style={{ x: x5, y: y5 }}
animate={{
scale: [1, 1.18, 1],
rotate: [0, 90, 0],
}}
transition={{
duration: 48,
repeat: Infinity,
ease: "easeInOut",
repeatType: "reverse",
}}
/>
{/* Pink blob - bottom right */}
<motion.div
className="absolute bottom-[10%] right-[-8%] w-[32vw] h-[32vw] bg-pink-200/35 rounded-full blur-[100px] mix-blend-multiply"
animate={{
scale: [1, 1.12, 1],
rotate: [0, -60, 0],
x: [0, -20, 0],
y: [0, 20, 0],
}}
transition={{
duration: 43,
repeat: Infinity,
ease: "easeInOut",
repeatType: "reverse",
}}
/>
{/* Yellow-green blob - top center */}
<motion.div
className="absolute top-[5%] left-[45%] w-[28vw] h-[28vw] bg-lime-200/30 rounded-full blur-[115px] mix-blend-multiply"
animate={{
scale: [1, 1.22, 1],
rotate: [0, 75, 0],
x: [0, 15, 0],
y: [0, -15, 0],
}}
transition={{
duration: 52,
repeat: Infinity,
ease: "easeInOut",
repeatType: "reverse",
}}
/>
</div>
);
};

View File

@@ -1,33 +0,0 @@
export const GooFilter = () => (
<svg
style={{ position: 'fixed', top: 0, left: 0, width: 0, height: 0, pointerEvents: 'none', zIndex: -1 }}
xmlns="http://www.w3.org/2000/svg"
version="1.1"
>
<defs>
{/* Global subtle filter */}
<filter id="goo">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" result="blur" />
<feColorMatrix
in="blur"
mode="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 19 -9"
result="goo"
/>
<feComposite in="SourceGraphic" in2="goo" operator="atop" />
</filter>
{/* Stronger filter specifically for LiquidHeading interaction */}
<filter id="goo-text">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" result="blur" />
<feColorMatrix
in="blur"
mode="matrix"
values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 25 -10"
result="goo"
/>
<feComposite in="SourceGraphic" in2="goo" operator="atop" />
</filter>
</defs>
</svg>
);

View File

@@ -1,19 +0,0 @@
'use client';
import clsx from 'clsx';
interface LiquidHeadingProps {
text: string;
className?: string;
level?: 1 | 2 | 3 | 4;
}
export const LiquidHeading = ({ text, className, level = 1 }: LiquidHeadingProps) => {
const Tag = `h${level}` as keyof JSX.IntrinsicElements;
return (
<Tag className={clsx("font-bold tracking-tight text-stone-800", className)}>
{text}
</Tag>
);
};