feat: enhance homescreen with scroll motion

This commit is contained in:
ramvignesh-b
2026-04-28 23:22:57 +05:30
parent 35e8d6761e
commit 574baa6860
4 changed files with 308 additions and 41 deletions
+38 -38
View File
@@ -2,57 +2,57 @@
@plugin "daisyui";
@plugin "daisyui/theme" {
name: "piku";
default: true;
prefersdark: true;
color-scheme: dark;
name: "piku";
default: true;
prefersdark: true;
color-scheme: dark;
--color-base-100: oklch(14% 0.012 35);
--color-base-200: oklch(18% 0.014 33);
--color-base-300: oklch(22% 0.016 32);
--color-base-content: oklch(82% 0.02 70);
--color-base-100: oklch(14% 0.012 35);
--color-base-200: oklch(18% 0.014 33);
--color-base-300: oklch(22% 0.016 32);
--color-base-content: oklch(82% 0.02 70);
--color-primary: oklch(67% 0.11 78);
--color-primary-content: oklch(15% 0.03 70);
--color-primary: oklch(67% 0.11 78);
--color-primary-content: oklch(15% 0.03 70);
--color-secondary: oklch(48% 0.08 305);
--color-secondary-content: oklch(92% 0.01 305);
--color-secondary: oklch(48% 0.08 305);
--color-secondary-content: oklch(92% 0.01 305);
--color-accent: oklch(55% 0.06 325);
--color-accent-content: oklch(18% 0.03 295);
--color-accent: oklch(55% 0.06 325);
--color-accent-content: oklch(18% 0.03 295);
--color-neutral: oklch(28% 0.02 45);
--color-neutral-content: oklch(80% 0.015 60);
--color-neutral: oklch(28% 0.02 45);
--color-neutral-content: oklch(80% 0.015 60);
--color-info: oklch(60% 0.07 240);
--color-info-content: oklch(95% 0.01 240);
--color-success: oklch(60% 0.08 150);
--color-success-content: oklch(16% 0.03 150);
--color-warning: oklch(68% 0.08 72);
--color-warning-content: oklch(18% 0.03 60);
--color-error: oklch(55% 0.1 22);
--color-error-content: oklch(92% 0.01 22);
--color-info: oklch(60% 0.07 240);
--color-info-content: oklch(95% 0.01 240);
--color-success: oklch(60% 0.08 150);
--color-success-content: oklch(16% 0.03 150);
--color-warning: oklch(68% 0.08 72);
--color-warning-content: oklch(18% 0.03 60);
--color-error: oklch(55% 0.1 22);
--color-error-content: oklch(92% 0.01 22);
--radius-selector: 0.5rem;
--radius-field: 0.375rem;
--radius-box: 0.5rem;
--radius-selector: 0.5rem;
--radius-field: 0.375rem;
--radius-box: 0.5rem;
--depth: 1;
--noise: 0.03;
--depth: 1;
--noise: 0.03;
--border: 1px;
--border: 1px;
}
@theme {
--font-display: "Playwrite HR Lijeva Variable", cursive;
--font-sans: "Jost Variable", sans-serif;
--font-serif: "Playfair Display Variable", serif;
--color-glass-bg: rgba(28, 22, 16, 0.45);
--shadow-warm: 0 20px 50px -12px rgba(30, 20, 12, 0.6);
--radius-xl: 1.5rem;
--color-paper: oklch(97% 0.008 80);
--font-display: "Playwrite HR Lijeva Variable", cursive;
--font-sans: "Jost Variable", sans-serif;
--font-serif: "Playfair Display Variable", serif;
--color-glass-bg: rgba(28, 22, 16, 0.45);
--shadow-warm: 0 20px 50px -12px rgba(30, 20, 12, 0.6);
--radius-xl: 1.5rem;
--color-paper: oklch(97% 0.008 80);
}
.glass-card {
@apply bg-glass-bg backdrop-blur-xl border border-white/5 shadow-warm rounded-xl;
@apply bg-glass-bg backdrop-blur-xl border border-white/5 shadow-warm rounded-xl;
}
+260 -3
View File
@@ -1,9 +1,266 @@
import {
motion,
useMotionValueEvent,
useScroll,
useSpring,
useTransform,
} from "motion/react";
import { useRef, useState } from "react";
import Logo from "../components/Logo";
import { EnvelopeReveal } from "../components/reader/EnvelopeReveal";
import { formatDate } from "../utils/dateFormat.ts";
export default function Home() {
const sectionContainer1 = useRef<HTMLDivElement>(null);
const { scrollYProgress: section1ScrollProgress } = useScroll({
target: sectionContainer1,
});
const smoothProgress = useSpring(section1ScrollProgress, {
stiffness: 100,
damping: 30,
restDelta: 0.001,
});
const [isEnvelopeFlipped, setIsEnvelopeFlipped] = useState(true);
const [recipient, setRecipient] = useState("someone dear");
const [ignite, setIgnite] = useState(false);
useMotionValueEvent(section1ScrollProgress, "change", (latestScrollValue) => {
if (latestScrollValue <= 0.6) {
setIsEnvelopeFlipped(true);
} else {
setIsEnvelopeFlipped(false);
}
if (latestScrollValue > 0.68) {
setRecipient("future me");
} else {
setRecipient("someone dear");
}
if (latestScrollValue > 0.77) {
setIgnite(true);
} else {
setIgnite(false);
}
});
return (
<div>
<Logo />
</div>
<section
ref={sectionContainer1}
className="relative w-full h-[850vh] bg-base-100 font-serif"
>
<div className="sticky top-0 h-screen w-full flex flex-col items-center justify-center overflow-hidden">
<motion.div
className="absolute flex flex-col items-center justify-center pointer-events-none"
style={{
opacity: useTransform(smoothProgress, [0, 0.12, 1], [1, 0, 0]),
scale: useTransform(smoothProgress, [0, 0.12], [1, 10]),
}}
>
<h1 className="text-neutral-content/40 text-4xl md:text-6xl text-center px-6">
You've been carrying something
</h1>
<h2 className="text-primary text-5xl md:text-7xl font-extralight mt-4 italic font-display animate-pulse">
unsaid
</h2>
</motion.div>
<motion.div
className="absolute text-center"
style={{
opacity: useTransform(smoothProgress, [0, 0.15, 0.2], [0, 1, 0]),
y: useTransform(smoothProgress, [0, 0.15, 0.2], [40, 0, -40]),
scale: useTransform(smoothProgress, [0, 0.15, 0.2], [0.8, 1, 3]),
}}
>
<div className="mt-6 text-4xl md:text-6xl text-base-content/60 italic">
and that's okay...
</div>
</motion.div>
<motion.div
className="absolute text-center px-6"
style={{
opacity: useTransform(smoothProgress, [0.18, 0.25, 0.3], [0, 1, 0]),
y: useTransform(smoothProgress, [0.18, 0.25, 0.3], [20, 0, -20]),
}}
transition={{ delay: 4 }}
>
<Logo scale={2} />
<motion.div
className="mt-6 text-4xl md:text-6xl text-base-content/60 "
style={{
opacity: useTransform(
smoothProgress,
[0.22, 0.25, 0.35, 0.4],
[0, 1, 1, 0],
),
y: useTransform(
smoothProgress,
[0.25, 0.3, 0.35, 0.4],
[20, 0, 0, -20],
),
}}
>
is a{" "}
<span className="font-display text-primary font-extralight">
safe space
</span>
,<br />
<motion.span
className="opacity-0 text-3xl md:text-5xl"
transition={{ delay: 3 }}
whileInView={{ opacity: 1 }}
viewport={{ once: false, amount: 0.3 }}
>
where you can
</motion.span>
</motion.div>
</motion.div>
<div className="relative w-full max-w-5xl h-1/2 flex items-center justify-center mt-20">
<motion.h2
style={{
opacity: useTransform(
smoothProgress,
[0.3, 0.35, 0.4, 0.45],
[0, 1, 1, 0],
),
y: useTransform(
smoothProgress,
[0.3, 0.35, 0.4, 0.45],
[40, 0, 0, -40],
),
}}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
>
pen down your unsaid words into{" "}
<span className="font-display text-primary font-extralight">
letters
</span>
.
</motion.h2>
<motion.h2
style={{
opacity: useTransform(
smoothProgress,
[0.45, 0.5, 0.55, 0.6],
[0, 1, 1, 0],
),
y: useTransform(
smoothProgress,
[0.45, 0.5, 0.55, 0.6],
[40, 0, 0, -40],
),
}}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
>
seal it{" "}
<span className="text-secondary font-display italic font-extralight">
secure
</span>{" "}
and{" "}
<span className="text-secondary font-display font-extralight italic">
private
</span>
.
</motion.h2>
<motion.h2
style={{
opacity: useTransform(
smoothProgress,
[0.6, 0.63, 0.72, 0.75],
[0, 1, 1, 0],
),
y: useTransform(
smoothProgress,
[0.6, 0.63, 0.72, 0.75],
[40, 0, 0, -40],
),
}}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
>
send it to{" "}
<motion.span
className="font-display text-accent"
style={{
color: useTransform(
smoothProgress,
[0.67, 1],
["var(--color-accent)", "var(--color-neutral)"],
),
}}
>
someone dear
</motion.span>
<motion.span
style={{
opacity: useTransform(smoothProgress, [0.66, 0.7], [0, 1]),
}}
>
{" "}
or{" "}
<span className="font-display text-success">
yourself in the future
</span>
.
</motion.span>
</motion.h2>
<motion.h2
style={{
opacity: useTransform(
smoothProgress,
[0.75, 0.8, 0.85, 0.9],
[0, 1, 1, 0],
),
y: useTransform(
smoothProgress,
[0.75, 0.8, 0.85, 0.9],
[40, 0, 0, -40],
),
}}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
>
and even <span className="font-display text-error">burn it</span> to
release the burden.
</motion.h2>
</div>
<div className="relative h-1/4 w-full flex flex-col items-center justify-center">
<motion.div
className="z-10 absolute scale-80"
style={{
opacity: useTransform(smoothProgress, [0.3, 0.5], [0, 1]),
y: useTransform(smoothProgress, [0.3, 0.5], [200, 0]),
}}
>
<EnvelopeReveal
isInteractive={false}
ignite={ignite}
recipient={recipient}
date={formatDate(new Date().toISOString())}
onRevealComplete={() => {}}
isFlip={isEnvelopeFlipped}
/>
</motion.div>
<motion.div
className="w-48 z-100 h-48 rounded-full blur-3xl opacity-20"
transition={{
backgroundColor: { ease: "easeIn", duration: 2 },
}}
style={{
backgroundColor: useTransform(
smoothProgress,
[0.45, 0.5, 0.7, 0.75, 1],
[
"var(--color-primary)",
"var(--color-secondary)",
"var(--color-accent)",
"var(--color-success)",
"var(--color-error)",
],
),
scale: useTransform(smoothProgress, [0, 1], [0.6, 2.5]),
}}
/>
<div className="absolute border border-primary/5 w-64 h-64 rounded-full backdrop-blur-[1px]" />
</div>
</div>
</section>
);
}