refactor: add image logo variant and subtle texts on home

This commit is contained in:
me
2026-05-07 05:52:52 +05:30
parent 167b1d2875
commit 06f5c2d119
2 changed files with 425 additions and 415 deletions
+46 -39
View File
@@ -2,47 +2,54 @@ import { DotIcon } from "@phosphor-icons/react";
import "@fontsource/knewave/400.css"; import "@fontsource/knewave/400.css";
interface LogoProps { interface LogoProps {
scale?: number; scale?: number;
type?: "inline" | "mono" | "logo"; type?: "inline" | "mono" | "logo" | null;
ul?: boolean;
} }
export default function Logo({ scale = 1, type = "logo" }: LogoProps) { export default function Logo({ scale = 1, type = null, ul = false }: LogoProps) {
if (type === "inline") { if (type === "inline") {
return ( return (
<span className={"text-accent font-display italic "}> <span className={"text-accent font-display italic "}>
pi<span className="text-primary">.</span>&nbsp;ku pi<span className="text-primary">.</span>&nbsp;ku
<span className="text-primary">.</span>&nbsp; <span className="text-primary">.</span>&nbsp;
</span> </span>
); );
} }
if (type === "mono") { if (type === "mono") {
return ( return (
<span className="font-display italic font-bold border-b-3 border-dashed border-stone-800/50"> <span className="font-display italic font-bold border-b-3 border-dashed border-stone-800/50">
pi. ku. pi. ku.
</span> </span>
); );
} }
return ( if (type === "logo") {
<div return (
role="img" <img src="/android-chrome-512x512.png" alt="Pi. Ku. logo" className="mx-auto" width={scale * 100} />
aria-label="Pi. Ku. logo" );
className="inline-flex items-baseline justify-center leading-none select-none" }
style={{ fontFamily: "'Knewave', serif", scale }}
> return (
<span className={`text-3xl font-light text-accent`}>Pi</span> <div
<DotIcon role="img"
weight="fill" aria-label="Pi. Ku. logo"
size={12} className={`inline-flex items-baseline justify-center leading-none select-none ${ul ? "ul-wavy" : ""}`}
className={`text-primary translate-y-1 -mx-px`} style={{ fontFamily: "'Knewave', serif", scale }}
/> >
<span className={`text-3xl font-light text-accent`}>&nbsp;Ku</span> <span className="text-3xl font-light text-accent">Pi</span>
<DotIcon <DotIcon
weight="fill" weight="fill"
size={12} size={12}
className={`text-primary translate-y-1 -mx-px`} className="text-primary translate-y-1 -mx-px"
/> />
</div> <span className="text-3xl font-light text-accent">&nbsp;Ku</span>
); <DotIcon
weight="fill"
size={12}
className="text-primary translate-y-1 -mx-px"
/>
</div>
);
} }
+379 -376
View File
@@ -1,10 +1,10 @@
import { InfoIcon } from "@phosphor-icons/react"; import { InfoIcon } from "@phosphor-icons/react";
import { ReactLenis } from "lenis/react"; import { ReactLenis } from "lenis/react";
import { import {
motion, motion,
useMotionValueEvent, useMotionValueEvent,
useScroll, useScroll,
useTransform, useTransform,
} from "motion/react"; } from "motion/react";
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@@ -14,384 +14,387 @@ import Saajan from "../components/ui/Saajan.tsx";
import { ROUTES } from "../config/routes.ts"; import { ROUTES } from "../config/routes.ts";
import { formatDate } from "../utils/dateFormat.ts"; import { formatDate } from "../utils/dateFormat.ts";
import "@fontsource/space-mono/index.css";
import "@fontsource/architects-daughter/index.css";
export default function Home() { export default function Home() {
const sectionContainer1 = useRef<HTMLDivElement>(null); const sectionContainer1 = useRef<HTMLDivElement>(null);
const { scrollYProgress } = useScroll({ const { scrollYProgress } = useScroll({
target: sectionContainer1, target: sectionContainer1,
}); });
const [isEnvelopeFlipped, setIsEnvelopeFlipped] = useState(true); const [isEnvelopeFlipped, setIsEnvelopeFlipped] = useState(true);
const [flapOpen, setFlapOpen] = useState(false); const [flapOpen, setFlapOpen] = useState(false);
const [recipient, setRecipient] = useState("someone dear"); const [recipient, setRecipient] = useState("someone dear");
const [ignite, setIgnite] = useState(false); const [ignite, setIgnite] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
useMotionValueEvent(scrollYProgress, "change", (latestScrollValue) => { useMotionValueEvent(scrollYProgress, "change", (latestScrollValue) => {
if (latestScrollValue > 0.54) { if (latestScrollValue > 0.54) {
setFlapOpen(false); setFlapOpen(false);
} else { } else {
setFlapOpen(true); setFlapOpen(true);
} }
if (latestScrollValue <= 0.6) { if (latestScrollValue <= 0.6) {
setIsEnvelopeFlipped(true); setIsEnvelopeFlipped(true);
} else { } else {
setIsEnvelopeFlipped(false); setIsEnvelopeFlipped(false);
} }
if (latestScrollValue > 0.68) { if (latestScrollValue > 0.68) {
setRecipient("future me"); setRecipient("future me");
} else { } else {
setRecipient("someone dear"); setRecipient("someone dear");
} }
if (latestScrollValue > 0.77) { if (latestScrollValue > 0.77) {
setIgnite(true); setIgnite(true);
} else { } else {
setIgnite(false); setIgnite(false);
} }
}); });
return ( return (
<ReactLenis root options={{ lerp: 0.1, duration: 1.5, smoothWheel: true }}> <ReactLenis root options={{ lerp: 0.1, duration: 1.5, smoothWheel: true }}>
<section <section
ref={sectionContainer1} ref={sectionContainer1}
className="relative w-full h-[850vh] bg-base-100 font-serif" 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"> <div className="sticky top-0 h-screen w-full flex flex-col items-center justify-center overflow-hidden">
{/* Intro */} {/* Intro */}
<motion.div <motion.div
className="absolute flex flex-col items-center justify-center pointer-events-none" className="absolute flex flex-col items-center justify-center pointer-events-none"
style={{ style={{
opacity: useTransform(scrollYProgress, [0, 0.12, 1], [1, 0, 0]), opacity: useTransform(scrollYProgress, [0, 0.12, 1], [1, 0, 0]),
scale: useTransform(scrollYProgress, [0, 0.12], [1, 10]), scale: useTransform(scrollYProgress, [0, 0.12], [1, 10]),
}} }}
> >
<h1 className="text-neutral-content/40 text-4xl md:text-6xl text-center px-6"> <h1 className="text-neutral text-4xl md:text-6xl text-center px-6">
You've been carrying something You've been carrying something
</h1> </h1>
<h2 className="text-primary text-5xl md:text-7xl font-extralight mt-4 italic font-display animate-pulse"> <motion.h2 className="text-primary text-5xl md:text-7xl mt-4 italic font-display font-light">
unsaid unsaid
</h2> </motion.h2>
</motion.div> </motion.div>
<motion.div <motion.div
className="absolute text-center" className="absolute text-center"
style={{ style={{
opacity: useTransform(scrollYProgress, [0, 0.15, 0.2], [0, 1, 0]), opacity: useTransform(scrollYProgress, [0, 0.15, 0.2], [0, 1, 0]),
y: useTransform(scrollYProgress, [0, 0.15, 0.2], [40, 0, -40]), y: useTransform(scrollYProgress, [0, 0.15, 0.2], [40, 0, -40]),
scale: useTransform(scrollYProgress, [0, 0.15, 0.2], [0.8, 1, 3]), scale: useTransform(scrollYProgress, [0, 0.15, 0.2], [0.8, 1, 3]),
}} }}
> >
<div className="mt-6 text-4xl md:text-6xl text-base-content/60 italic"> <div className="mt-6 text-4xl md:text-6xl text-base-content/60 italic">
and that's okay... and that's okay...
</div> </div>
</motion.div> </motion.div>
{/* pi. ku. */} {/* pi. ku. */}
<motion.div <motion.div
className="absolute text-center px-6" className="absolute text-center px-6"
style={{ style={{
opacity: useTransform( opacity: useTransform(
scrollYProgress, scrollYProgress,
[0.18, 0.25, 0.3], [0.18, 0.25, 0.3],
[0, 1, 0], [0, 1, 0],
), ),
y: useTransform(scrollYProgress, [0.18, 0.25, 0.3], [20, 0, -20]), y: useTransform(scrollYProgress, [0.18, 0.25, 0.3], [20, 0, -20]),
}} }}
transition={{ delay: 4 }} transition={{ delay: 4 }}
> >
<Logo scale={2} /> <Logo type="logo" scale={1.5} ul={true} />
<motion.div <motion.div
className="mt-6 text-4xl md:text-6xl text-base-content/60 " className="font-serif font-extralight mt-6 text-4xl md:text-6xl text-neutral "
style={{ style={{
opacity: useTransform( opacity: useTransform(
scrollYProgress, scrollYProgress,
[0.22, 0.25, 0.35, 0.4], [0.22, 0.25, 0.35, 0.4],
[0, 1, 1, 0], [0, 1, 1, 0],
), ),
y: useTransform( y: useTransform(
scrollYProgress, scrollYProgress,
[0.25, 0.3, 0.35, 0.4], [0.25, 0.3, 0.35, 0.4],
[20, 0, 0, -20], [20, 0, 0, -20],
), ),
}} }}
> >
is a{" "} is a{" "}
<span className="font-display text-primary font-extralight"> <span className="font-display text-primary font-extralight">
safe space safe space
</span> </span>
,<br /> ,<br />
<motion.span <motion.span
className="opacity-0 text-3xl md:text-5xl" className="opacity-0 text-2xl md:text-4xl font-hand tracking-[0.4em] text-neutral"
transition={{ delay: 3 }} transition={{ delay: 5 }}
whileInView={{ opacity: 1 }} whileInView={{ opacity: 1 }}
viewport={{ once: false, amount: 0.3 }} viewport={{ once: false, amount: 0.3 }}
> >
where you can where you can
</motion.span> </motion.span>
</motion.div> </motion.div>
</motion.div> </motion.div>
<div className="relative w-full max-w-5xl h-1/2 flex items-center justify-center mt-20"> <div className="relative w-full max-w-5xl h-1/2 flex items-center justify-center mt-20">
<motion.h2 <motion.h2
style={{ style={{
opacity: useTransform( opacity: useTransform(
scrollYProgress, scrollYProgress,
[0.3, 0.35, 0.4, 0.45], [0.3, 0.35, 0.4, 0.45],
[0, 1, 1, 0], [0, 1, 1, 0],
), ),
y: useTransform( y: useTransform(
scrollYProgress, scrollYProgress,
[0.3, 0.35, 0.4, 0.45], [0.3, 0.35, 0.4, 0.45],
[40, 0, 0, -40], [40, 0, 0, -40],
), ),
}} }}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight" className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
> >
pen down your unsaid words into{" "} pen down your unsaid words into{" "}
<span className="font-display text-primary font-extralight"> <span className="font-display text-primary font-extralight">
letters letters
</span> </span>
. .
</motion.h2> </motion.h2>
{/* Seal */} {/* Seal */}
<motion.h2 <motion.h2
style={{ style={{
opacity: useTransform( opacity: useTransform(
scrollYProgress, scrollYProgress,
[0.45, 0.5, 0.55, 0.6], [0.45, 0.5, 0.55, 0.6],
[0, 1, 1, 0], [0, 1, 1, 0],
), ),
y: useTransform( y: useTransform(
scrollYProgress, scrollYProgress,
[0.45, 0.5, 0.55, 0.6], [0.45, 0.5, 0.55, 0.6],
[40, 0, 0, -40], [40, 0, 0, -40],
), ),
}} }}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight" className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
> >
seal it{" "} seal it{" "}
<span className="text-secondary font-display italic font-extralight"> <span className="text-success font-mono tracking-tighter font-extrabold">
secure secure
</span>{" "} </span>{" "}
and{" "} and{" "}
<span className="text-secondary font-display font-extralight italic"> <span className="text-info font-mono tracking-tighter">
private private
</span> </span>
. .
</motion.h2> </motion.h2>
{/* Send / vault */} {/* Send / vault */}
<motion.h2 <motion.h2
style={{ style={{
opacity: useTransform( opacity: useTransform(
scrollYProgress, scrollYProgress,
[0.6, 0.63, 0.72, 0.75], [0.6, 0.63, 0.72, 0.75],
[0, 1, 1, 0], [0, 1, 1, 0],
), ),
y: useTransform( y: useTransform(
scrollYProgress, scrollYProgress,
[0.6, 0.63, 0.72, 0.75], [0.6, 0.63, 0.72, 0.75],
[40, 0, 0, -40], [40, 0, 0, -40],
), ),
}} }}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight" className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
> >
send it to{" "} send it to{" "}
<motion.span <motion.span
className="font-display text-accent" className="font-display text-accent"
style={{ style={{
color: useTransform( color: useTransform(
scrollYProgress, scrollYProgress,
[0.67, 1], [0.67, 1],
["var(--color-accent)", "var(--color-neutral)"], ["var(--color-accent)", "var(--color-neutral)"],
), ),
}} }}
> >
someone dear someone dear
</motion.span> </motion.span>
<motion.span <motion.span
style={{ style={{
opacity: useTransform(scrollYProgress, [0.66, 0.7], [0, 1]), opacity: useTransform(scrollYProgress, [0.66, 0.7], [0, 1]),
}} }}
> >
<motion.span <motion.span
className="font-display text-accent" className="font-display text-accent"
style={{ style={{
color: useTransform( color: useTransform(
scrollYProgress, scrollYProgress,
[0.67, 1], [0.67, 1],
["var(--color-accent)", "var(--color-neutral)"], ["var(--color-accent)", "var(--color-neutral)"],
), ),
}} }}
> >
{" "} {" "}
or{" "} or{" "}
</motion.span> </motion.span>
<span className="font-display text-success"> <span className="font-display text-success">
yourself in the future yourself in the future
</span> </span>
. .
</motion.span> </motion.span>
</motion.h2> </motion.h2>
{/* Burn */} {/* Burn */}
<motion.h2 <motion.h2
style={{ style={{
opacity: useTransform( opacity: useTransform(
scrollYProgress, scrollYProgress,
[0.75, 0.8, 0.85, 0.9], [0.75, 0.8, 0.85, 0.9],
[0, 1, 1, 0], [0, 1, 1, 0],
), ),
y: useTransform( y: useTransform(
scrollYProgress, scrollYProgress,
[0.75, 0.8, 0.85, 0.9], [0.75, 0.8, 0.85, 0.9],
[40, 0, 0, -40], [40, 0, 0, -40],
), ),
}} }}
className="absolute text-4xl md:text-6xl text-center px-10 leading-tight" className="absolute text-4xl md:text-6xl text-center px-10 leading-tight"
> >
and even <span className="font-display text-error">burn it</span>{" "} and even <span className="font-display text-error">burn it</span>{" "}
to release the burden. to release the burden.
</motion.h2> </motion.h2>
{/* Outro */} {/* Outro */}
<motion.h2 <motion.h2
className={ className={
"italic absolute text-4xl md:text-6xl text-center px-10 leading-tight" "italic absolute text-4xl md:text-6xl text-center px-10 leading-tight text-neutral-content/50"
} }
style={{ style={{
opacity: useTransform(scrollYProgress, [0.9, 1], [0, 1]), opacity: useTransform(scrollYProgress, [0.9, 1], [0, 1]),
y: useTransform(scrollYProgress, [0.9, 1], [80, 0]), y: useTransform(scrollYProgress, [0.9, 1], [80, 0]),
}} }}
> >
You've been carrying it long enough. You've been carrying it long enough.
</motion.h2> </motion.h2>
{/* CTA */} {/* CTA */}
<motion.div <motion.div
className={ className={
"z-100 absolute -bottom-12 md:bottom-0 font-display flex flex-wrap md:flex-nowrap gap-4 md:gap-12 justify-center" "z-100 absolute -bottom-12 md:bottom-0 font-hand flex flex-wrap md:flex-nowrap gap-4 md:gap-12 justify-center"
} }
style={{ style={{
opacity: useTransform(scrollYProgress, [0.98, 1], [0, 1]), opacity: useTransform(scrollYProgress, [0.98, 1], [0, 1]),
y: useTransform(scrollYProgress, [0.98, 1], [80, 0]), y: useTransform(scrollYProgress, [0.98, 1], [80, 0]),
display: useTransform( display: useTransform(
scrollYProgress, scrollYProgress,
[0.96, 1], [0.96, 1],
["none", "flex"], ["none", "flex"],
), ),
}} }}
> >
<button <button
className={ className={
"md:opacity-50 hover:opacity-100 btn btn-ghost btn-wide md:btn-xl rounded-full font-extralight md:grayscale hover:grayscale-0 hover:-translate-y-1 transition-all duration-1000" "md:opacity-50 hover:opacity-100 btn btn-ghost btn-wide md:btn-xl rounded-full font-extralight md:grayscale hover:grayscale-0 hover:-translate-y-1 transition-all duration-1000"
} }
type={"button"} type={"button"}
onClick={() => navigate(ROUTES.ABOUT, { replace: true })} onClick={() => navigate(ROUTES.ABOUT, { replace: true })}
> >
<InfoIcon className={"text-primary"} /> <InfoIcon className={"text-primary"} />
Tell me More Tell me More
</button> </button>
<button <button
className={ className={
"md:opacity-50 hover:opacity-100 btn rounded-full btn-primary btn-wide md:btn-xl md:grayscale hover:grayscale-0 hover:-translate-y-1 transition-all duration-1000" "md:opacity-50 hover:opacity-100 btn rounded-full btn-primary btn-wide md:btn-xl md:grayscale-50 hover:grayscale-0 focus:grayscale-0 active:grayscale-0 hover:-translate-y-1 transition-all duration-1000"
} }
type={"button"} type={"button"}
onClick={() => navigate(ROUTES.ONBOARD, { replace: true })} onClick={() => navigate(ROUTES.ONBOARD, { replace: true })}
> >
I'm ready I'm ready
</button> </button>
</motion.div> </motion.div>
</div> </div>
<div className="relative h-1/4 w-full flex flex-col items-center justify-center pointer-events-none"> <div className="relative h-1/4 w-full flex flex-col items-center justify-center pointer-events-none">
<motion.div <motion.div
className={"z-21 absolute"} className={"z-21 absolute"}
style={{ style={{
opacity: useTransform( opacity: useTransform(
scrollYProgress, scrollYProgress,
[0.3, 0.4, 0.5, 0.52], [0.3, 0.4, 0.5, 0.52],
[0, 1, 0.1, 0], [0, 1, 0.1, 0],
), ),
y: useTransform( y: useTransform(
scrollYProgress, scrollYProgress,
[0.3, 0.45, 0.5], [0.3, 0.45, 0.5],
[300, 0, 200], [300, 0, 200],
), ),
scale: useTransform( scale: useTransform(
scrollYProgress, scrollYProgress,
[0.3, 0.4, 0.5], [0.3, 0.4, 0.5],
[1, 1, 0.6], [1, 1, 0.6],
), ),
}} }}
> >
<div className="mockup-phone w-[75vw] border-primary"> <div className="mockup-phone w-[75vw] border-primary">
<div className="mockup-phone-camera"></div> <div className="mockup-phone-camera"></div>
<div className="mockup-phone-display"> <div className="mockup-phone-display">
<img alt="letter" src="/screenshots/letter.webp" /> <img alt="letter" src="/screenshots/letter.webp" />
</div>
</div>
</motion.div>
{/* Envelope */}
<motion.div
className="absolute scale-50 md:scale-80 z-10"
style={{
opacity: useTransform(
scrollYProgress,
[0.4, 0.45, 0.5, 0.7, 0.9, 1],
[0, 0.6, 1, 1, 0.3, 0],
),
y: useTransform(scrollYProgress, [0.45, 0.5, 1], [600, 200, 0]),
}}
>
<EnvelopeReveal
isInteractive={false}
ignite={ignite}
recipient={recipient}
date={formatDate(new Date().toISOString())}
onRevealComplete={() => { }}
isFlip={isEnvelopeFlipped}
openFlap={flapOpen}
/>
</motion.div>
{/* Saajan */}
<motion.div
className="fixed bottom-0 z-10 font-sans -mb-6 scale-85 md:scale-100 md:mb-0"
style={{
opacity: useTransform(
scrollYProgress,
[0.98, 0.995, 1],
[0, 0.5, 1],
),
y: useTransform(scrollYProgress, [0.98, 1], [50, -10]),
}}
>
<Saajan
message={
"I think we forget things\nif there is nobody to tell them."
}
position={"top"}
/>
</motion.div>
{/* Orb */}
<motion.div
className="w-48 z-100 h-48 rounded-full blur-3xl opacity-20"
transition={{
backgroundColor: { ease: "easeIn", duration: 2 },
}}
style={{
backgroundColor: useTransform(
scrollYProgress,
[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(scrollYProgress, [0, 1], [0.6, 2.5]),
}}
/>
<div className="absolute border border-primary/5 w-64 h-64 rounded-full backdrop-blur-[1px]" />
</div>
</div> </div>
</div> </section>
</motion.div> </ReactLenis>
{/* Envelope */} );
<motion.div
className="absolute scale-50 md:scale-80 z-10"
style={{
opacity: useTransform(
scrollYProgress,
[0.4, 0.45, 0.5, 0.7, 0.9, 1],
[0, 0.6, 1, 1, 0.3, 0],
),
y: useTransform(scrollYProgress, [0.45, 0.5, 1], [600, 200, 0]),
}}
>
<EnvelopeReveal
isInteractive={false}
ignite={ignite}
recipient={recipient}
date={formatDate(new Date().toISOString())}
onRevealComplete={() => {}}
isFlip={isEnvelopeFlipped}
openFlap={flapOpen}
/>
</motion.div>
{/* Saajan */}
<motion.div
className="fixed bottom-0 z-10 font-sans -mb-6 scale-85 md:scale-100 md:mb-0"
style={{
opacity: useTransform(
scrollYProgress,
[0.98, 0.995, 1],
[0, 0.5, 1],
),
y: useTransform(scrollYProgress, [0.98, 1], [50, -10]),
}}
>
<Saajan
message={
"I think we forget things\nif there is nobody to tell them."
}
position={"top"}
/>
</motion.div>
{/* Orb */}
<motion.div
className="w-48 z-100 h-48 rounded-full blur-3xl opacity-20"
transition={{
backgroundColor: { ease: "easeIn", duration: 2 },
}}
style={{
backgroundColor: useTransform(
scrollYProgress,
[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(scrollYProgress, [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>
</ReactLenis>
);
} }