1f3af8f7f5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
39 lines
1.8 KiB
TypeScript
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>
|
|
);
|
|
}
|