Files
ttvfx/components/Frame.tsx
T
twotalesanimation 1f3af8f7f5 Initial ttvfx.com site: Next.js static export
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 18:17:41 +02:00

39 lines
1.8 KiB
TypeScript

import type { Project } from "@/content/site";
// A project still. Uses the project image when present, otherwise renders a
// stylised "viewer" frame (gradient plate + safe-area guide + slate overlay).
export default function Frame({ project, priority = false }: { project: Project; priority?: boolean }) {
const h = project.hue;
return (
<div className="relative aspect-[2.39/1] w-full overflow-hidden bg-ink-2">
{project.image ? (
// eslint-disable-next-line @next/next/no-img-element
<img
src={project.image}
alt={project.title}
loading={priority ? "eager" : "lazy"}
className="absolute inset-0 h-full w-full object-cover transition-transform duration-[1.2s] ease-out group-hover:scale-[1.04]"
/>
) : (
<div
className="absolute inset-0 transition-transform duration-[1.2s] ease-out group-hover:scale-[1.06]"
style={{
background: `radial-gradient(ellipse 60% 80% at 70% 60%, hsl(${h} 70% 45% / .55), transparent 70%),
radial-gradient(ellipse 50% 60% at 20% 30%, hsl(${(h + 40) % 360} 60% 30% / .5), transparent 70%),
linear-gradient(180deg, hsl(${h} 25% 9%), hsl(${h} 30% 4%))`,
}}
>
<div className="absolute inset-x-0 bottom-0 h-1/3 bg-gradient-to-t from-black/70 to-transparent" />
</div>
)}
<div className="pointer-events-none absolute inset-[6%] border border-white/10" />
<div className="pointer-events-none absolute top-3 left-4 font-mono text-[10px] tracking-widest text-white/50 uppercase">
{project.category} · {project.year}
</div>
<div className="pointer-events-none absolute top-3 right-4 font-mono text-[10px] tracking-widest text-white/50">
{project.shots ? `${project.shots} SHOTS` : "2.39:1"}
</div>
</div>
);
}