refactor: modularize envelope reveal and logo components for easier usage across the site

This commit is contained in:
ramvignesh-b
2026-04-26 00:55:19 +05:30
parent f352c298e7
commit dadb688c50
4 changed files with 147 additions and 151 deletions
+10 -10
View File
@@ -1,26 +1,26 @@
import { DotIcon } from "@phosphor-icons/react";
import "@fontsource/knewave/400.css";
export default function Logo() {
export default function Logo({ scale = 2 }) {
return (
<span
<div
role="img"
aria-label="Pi Ku"
className="inline-flex items-baseline justify-center leading-none select-none"
style={{ fontFamily: "'Knewave', serif" }}
style={{ fontFamily: "'Knewave', serif", scale }}
>
<span className="text-2xl font-light text-accent">&nbsp;Pi</span>
<span className={`text-xl font-light text-accent`}>&nbsp;Pi</span>
<DotIcon
weight="fill"
size={12}
className="text-accent translate-y-[0.3em] -mx-px"
size={6}
className={`text-primary translate-y-1 -mx-px`}
/>
<span className="text-2xl font-light text-accent">&nbsp;Ku</span>
<span className={`text-xl font-light text-accent`}>&nbsp;Ku</span>
<DotIcon
weight="fill"
size={12}
className="text-accent translate-y-[0.3em] -mx-px"
size={6}
className={`text-primary translate-y-1 -mx-px`}
/>
</span>
</div>
);
}
@@ -8,6 +8,7 @@ export interface EnvelopeRevealProps {
date?: string;
onRevealComplete: () => void;
ignite: boolean;
isFlip?: boolean;
}
export function EnvelopeReveal({
@@ -15,9 +16,14 @@ export function EnvelopeReveal({
date,
onRevealComplete,
ignite,
isFlip,
}: EnvelopeRevealProps) {
const [revealLetter, setRevealLetter] = useState(false);
const [isFlipped, setIsFlipped] = useState(false);
const [isFlipped, setIsFlipped] = useState(!!isFlip);
useEffect(() => {
setIsFlipped(!!isFlip);
}, [isFlip]);
const [burn, setBurn] = useState<{ width: number; height: number }>({
width: 0,
@@ -27,7 +33,10 @@ export function EnvelopeReveal({
const flapCheckbox = useRef<HTMLInputElement>(null);
useEffect(() => {
if (!ignite) return;
if (!ignite) {
setBurn({ width: 0, height: 0 });
return;
}
const burnInterval = setInterval(() => {
setBurn((prev) => ({ width: prev.width + 4, height: prev.height + 6 }));
}, 100);
@@ -43,8 +52,7 @@ export function EnvelopeReveal({
};
return (
<div className="h-screen mx-auto flex-col items-center flex justify-center">
<div className="perspective-distant scale-80 duration-1000 transition-all animate-[pulse_2s_linear_1]">
<>
<div
className={`relative h-70 w-105 transform-3d transition-transform duration-2000 ${isFlipped ? "rotate-y-180" : ""}`}
>
@@ -101,9 +109,7 @@ export function EnvelopeReveal({
<span className={"text-neutral-content/60 font-xs font-display"}>
to
</span>
<h1 className="text-3xl font-bold text-base-content">
{recipient}
</h1>
<h1 className="text-3xl font-bold text-base-content">{recipient}</h1>
<p className="text-base-content/60 font-display mt-8">{date}</p>
<img
src={stamp}
@@ -122,11 +128,10 @@ export function EnvelopeReveal({
/>
</button>
</div>
</div>
{ignite && (
<div className="absolute w-90 h-60 bg-transparent z-100 overflow-hidden flex align-baseline">
<div className="absolute w-115 h-70 z-100 overflow-hidden flex align-baseline -translate-y-70 -translate-x-5">
<div
className="absolute border-2 border-amber-200 -bottom-3 -right-3 w-0 h-0 transition-all duration-500 bg-base-100 rounded-tl-full rounded-bl-full origin-bottom-right"
className="absolute z-1000 border-2 border-amber-200 -bottom-3 -right-3 w-0 h-0 transition-all duration-500 bg-base-100 rounded-tl-full rounded-bl-full origin-bottom-right"
style={{
width: 2 * burn.width,
height: 2 * burn.height,
@@ -134,6 +139,6 @@ export function EnvelopeReveal({
></div>
</div>
)}
</div>
</>
);
}
+5 -18
View File
@@ -7,50 +7,39 @@
prefersdark: true;
color-scheme: dark;
/* ── Base surfaces ── */
--color-base-100: oklch(14% 0.012 35); /* was 0.018 hue 50 */
--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
); /* aged parchment, not crisp white */
--color-base-content: oklch(82% 0.02 70);
/* ── Primary: old lamp gold — warm, incandescent ── */
--color-primary: oklch(67% 0.11 78);
--color-primary-content: oklch(15% 0.03 70);
/* ── Secondary: dusty plum ── */
--color-secondary: oklch(48% 0.08 305);
--color-secondary-content: oklch(92% 0.01 305);
/* ── Accent: muted lavender-clay ── */
--color-accent: oklch(55% 0.06 325);
--color-accent-content: oklch(18% 0.03 295);
/* ── Neutral: warm stone ── */
--color-neutral: oklch(28% 0.02 45);
--color-neutral-content: oklch(80% 0.015 60);
/* ── Semantic — desaturated, no alarm ── */
--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); /* honey, not caution-sign amber */
--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);
/* ── Shape ── */
--radius-selector: 0.5rem;
--radius-field: 0.375rem;
--radius-box: 0.5rem;
/* ── Effects ── */
--depth: 1;
--noise: 0.03;
/* ── Border ── */
--border: 1px;
}
@@ -58,12 +47,10 @@
--font-display: "Playwrite HR Lijeva Variable", cursive;
--font-sans: "Jost Variable", sans-serif;
--font-serif: "Playfair Display Variable", serif;
--color-glass-bg: rgba(
28,
--color-glass-bg: rgba(28,
22,
16,
0.45
); /* slightly deeper to match new base */
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);
+6 -2
View File
@@ -244,13 +244,15 @@ export default function Reader() {
<section className="min-h-fit w-full bg-base-100 px-4 py-8 md:py-16 font-serif relative overflow-hidden">
<div className="fixed inset-0 bg-[radial-gradient(circle_at_center,transparent_0%,rgba(0,0,0,0.5)_100%)] pointer-events-none z-0" />
<div
className={`transition-all duration-1000 relative ${
className={`transition-all delay-300 duration-1000 relative ${
revealState === "revealed"
? "opacity-0 w-0 h-0 overflow-hidden invisible"
: "opacity-100"
}`}
>
{revealState === "sealed" && (
<div className="h-[80vh] mx-auto flex-col items-center flex justify-center">
<div className="perspective-distant scale-80 duration-1000 transition-all animate-[pulse_2s_linear_1]">
<EnvelopeReveal
recipient={metadata?.recipient || "Someone dear"}
date={
@@ -261,6 +263,8 @@ export default function Reader() {
onRevealComplete={() => setRevealState("revealed")}
ignite={ignite}
/>
</div>
</div>
)}
</div>
@@ -275,7 +279,7 @@ export default function Reader() {
/>
{revealState === "revealed" && (
<div className="max-w-4xl m-8 mx-auto space-y-8 relative inset-0 z-100">
<div className="max-w-4xl m-8 mx-auto space-y-8 h-full relative inset-0 z-100">
<div className="relative group perspective-1000">
<div className="absolute inset-0 bg-primary/5 blur-3xl rounded-full scale-75 opacity-0 group-hover:opacity-100 transition-opacity duration-1000 pointer-events-none" />