"use client";
import ParallaxPortal, {
ParallaxPortalLayer,
} from "@/components/ui/parallax-portal";
import { useId, useMemo } from "react";
function seeded(seed: number) {
let a = seed;
return () => {
a = (a + 0x6d2b79f5) | 0;
let t = Math.imul(a ^ (a >>> 15), 1 | a);
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
function Stars({ count, seed }: { count: number; seed: number }) {
const stars = useMemo(() => {
const rand = seeded(seed);
return Array.from({ length: count }, () => ({
x: rand() * 400,
y: rand() * 250,
r: rand() ** 3 * 1.6 + 0.3,
o: 0.4 + rand() * 0.6,
}));
}, [count, seed]);
return (
<>
{stars.map((s, i) => (
<circle key={i} cx={s.x} cy={s.y} r={s.r} fill="white" opacity={s.o} />
))}
</>
);
}
function SpaceWindow() {
const id = useId().replace(/:/g, "");
return (
<svg
viewBox="0 0 400 250"
className="h-full w-full"
preserveAspectRatio="xMidYMid slice"
aria-hidden="true"
>
<defs>
<radialGradient id={`${id}-neb`} cx="30%" cy="40%" r="60%">
<stop offset="0" stopColor="var(--color-accent)" stopOpacity="0.35" />
<stop offset="1" stopColor="var(--color-accent)" stopOpacity="0" />
</radialGradient>
<radialGradient id={`${id}-planet`} cx="35%" cy="30%" r="75%">
<stop
offset="0"
stopColor="color-mix(in oklab, var(--color-accent) 70%, white)"
/>
<stop
offset="0.55"
stopColor="color-mix(in oklab, var(--color-accent) 60%, #123)"
/>
<stop offset="1" stopColor="#050810" />
</radialGradient>
</defs>
<rect width="400" height="250" fill="#04060c" />
<rect width="400" height="250" fill={`url(#${id}-neb)`} />
<Stars count={160} seed={3} />
<circle cx="290" cy="170" r="92" fill={`url(#${id}-planet)`} />
<ellipse
cx="290"
cy="170"
rx="150"
ry="18"
fill="none"
stroke="color-mix(in oklab, var(--color-accent) 50%, white)"
strokeOpacity="0.45"
strokeWidth="3"
transform="rotate(-14 290 170)"
/>
</svg>
);
}
const HULL =
"color-mix(in oklab, var(--color-ink) 18%, var(--color-surface-muted))";
const PANELS = `repeating-linear-gradient(90deg, transparent 0 78px, color-mix(in oklab, var(--color-ink) 20%, transparent) 78px 80px), repeating-linear-gradient(0deg, transparent 0 58px, color-mix(in oklab, var(--color-ink) 14%, transparent) 58px 60px), ${HULL}`;
const SHIP_WALLS = {
back: (
<div className="absolute inset-0 p-[7%]" style={{ background: PANELS }}>
<div className="relative h-full w-full overflow-hidden rounded-[var(--radius-banner)] border-[6px] border-[color-mix(in_oklab,var(--color-ink)_60%,var(--color-surface))] shadow-[inset_0_0_30px_black]">
<SpaceWindow />
<div className="absolute inset-y-0 left-1/3 w-2 bg-[color-mix(in_oklab,var(--color-ink)_60%,var(--color-surface))]" />
<div className="absolute inset-y-0 left-2/3 w-2 bg-[color-mix(in_oklab,var(--color-ink)_60%,var(--color-surface))]" />
</div>
</div>
),
left: (
<div className="absolute inset-0" style={{ background: PANELS }}>
<div className="absolute inset-x-0 top-[62%] h-1 bg-accent shadow-[0_0_14px_var(--color-accent)]" />
</div>
),
right: (
<div className="absolute inset-0" style={{ background: PANELS }}>
<div className="absolute inset-x-0 top-[62%] h-1 bg-accent shadow-[0_0_14px_var(--color-accent)]" />
</div>
),
floor: (
<div
className="absolute inset-0 bg-[repeating-linear-gradient(0deg,transparent_0_10px,color-mix(in_oklab,var(--color-ink)_22%,transparent)_10px_12px),repeating-linear-gradient(90deg,transparent_0_10px,color-mix(in_oklab,var(--color-ink)_22%,transparent)_10px_12px)]"
style={{ backgroundColor: HULL }}
/>
),
ceiling: (
<div className="absolute inset-0" style={{ background: PANELS }}>
<div className="absolute inset-y-0 left-1/2 w-3 -translate-x-1/2 bg-surface shadow-[0_0_24px_color-mix(in_oklab,white_80%,transparent)]" />
</div>
),
};
export default function ParallaxPortalStarshipDemo() {
const depth = 420;
return (
<div className="flex w-full items-center justify-center bg-canvas p-10">
<ParallaxPortal
label="The bridge of a starship, looking out at a ringed planet"
walls={SHIP_WALLS}
depth={depth}
intensity={0.8}
className="h-[340px] w-full max-w-xl"
>
<ParallaxPortalLayer
depth={Math.round(depth * 0.55)}
x={0.5}
y={0.46}
anchor="center"
>
<svg
viewBox="0 0 100 100"
className="w-[110px] opacity-80 drop-shadow-[0_0_12px_var(--color-accent)]"
aria-hidden="true"
>
<circle
cx="50"
cy="50"
r="40"
fill="none"
stroke="var(--color-accent)"
strokeWidth="1.2"
/>
<ellipse
cx="50"
cy="50"
rx="40"
ry="14"
fill="none"
stroke="var(--color-accent)"
strokeWidth="1"
/>
<ellipse
cx="50"
cy="50"
rx="14"
ry="40"
fill="none"
stroke="var(--color-accent)"
strokeWidth="1"
/>
<ellipse
cx="50"
cy="50"
rx="30"
ry="40"
fill="none"
stroke="var(--color-accent)"
strokeWidth="0.6"
/>
</svg>
</ParallaxPortalLayer>
<ParallaxPortalLayer depth={70} x={0.5}>
<div className="flex h-[54px] w-[300px] items-center justify-center gap-2 rounded-t-[var(--radius-card)] bg-[color-mix(in_oklab,var(--color-ink)_70%,var(--color-surface))] px-4">
{Array.from({ length: 9 }, (_, i) => (
<span
key={i}
className="h-2 w-5 rounded-full"
style={{
background:
i % 3
? "var(--color-accent)"
: "color-mix(in oklab, var(--color-ink-inverse) 40%, transparent)",
}}
/>
))}
</div>
</ParallaxPortalLayer>
<ParallaxPortalLayer depth={-110} x={0.5} y={1.1}>
<svg viewBox="0 0 220 150" className="w-[220px]" aria-hidden="true">
<path
d="M40 150 V70 C40 20 180 20 180 70 V150 Z"
fill="color-mix(in oklab, var(--color-ink) 85%, var(--color-surface))"
/>
<path
d="M58 150 V78 C58 42 162 42 162 78 V150 Z"
fill="color-mix(in oklab, var(--color-ink) 70%, var(--color-accent))"
/>
</svg>
</ParallaxPortalLayer>
</ParallaxPortal>
</div>
);
}