Lisbon offsite 2026
6 photos · 16 to 18 September
photo-gallery.tsx
"use client";
import { useState } from "react";
import { LuUpload } from "react-icons/lu";
import { Button, Carousel, CarouselSlide, Image, ImageTile, Modal } from "@nexera-ui/react";
const PHOTOS = [
{ alt: "Team dinner on the Lisbon rooftop", sky: "fill-chart-1", ground: "fill-chart-5" },
{ alt: "Kayaking on the Tagus river", sky: "fill-chart-2", ground: "fill-chart-3" },
{ alt: "Hackathon demos in the main hall", sky: "fill-chart-4", ground: "fill-chart-6" },
{ alt: "Morning yoga on the beach", sky: "fill-chart-3", ground: "fill-chart-7" },
{ alt: "Group photo at Belém Tower", sky: "fill-chart-6", ground: "fill-chart-2" },
{ alt: "Award night for the Customer team", sky: "fill-chart-8", ground: "fill-chart-1" },
];
/** Placeholder artwork from theme tokens; replace with your photos' `src`. */
function Art({ sky, ground }: { sky: string; ground: string }) {
return (
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid slice" aria-hidden>
<rect width="400" height="300" className={sky} opacity="0.35" />
<circle cx="300" cy="80" r="36" className="fill-surface" opacity="0.8" />
<path d="M0 230 L110 140 L190 210 L260 160 L400 250 V300 H0 Z" className={ground} />
</svg>
);
}
/** Photo gallery: a grid of event photos that opens into a full-screen viewer you can swipe through. */
export default function PhotoGallery() {
const [open, setOpen] = useState<number | null>(null);
return (
<div className="@container bg-page px-4 py-8 @3xl:px-8">
<section aria-labelledby="gallery-title" className="mx-auto grid max-w-5xl gap-5">
<header className="flex flex-wrap items-end justify-between gap-3">
<div className="grid gap-1">
<h2 id="gallery-title" className="text-heading-h3 text-primary">
Lisbon offsite 2026
</h2>
<p className="text-body-small text-secondary">
{PHOTOS.length} photos · 16 to 18 September
</p>
</div>
<Button variant="secondary" size="md" leadingIcon={<LuUpload />}>
Add photos
</Button>
</header>
<ul className="grid grid-cols-2 gap-3 @3xl:grid-cols-3 @5xl:grid-cols-4">
{PHOTOS.map((photo, i) => (
<li key={photo.alt}>
<ImageTile
src={null}
alt={photo.alt}
fallback={<Art {...photo} />}
onPress={() => {
setOpen(i);
}}
/>
</li>
))}
</ul>
</section>
<Modal
title="Lisbon offsite 2026"
subtitle="Use the arrows or swipe to move between photos."
size="fullscreen"
isOpen={open !== null}
onOpenChange={(isOpen) => {
if (!isOpen) setOpen(null);
}}
>
<Carousel
aria-label="Offsite photos"
defaultIndex={open ?? 0}
indicator="counter"
className="mx-auto w-full max-w-4xl"
>
{PHOTOS.map((photo) => (
<CarouselSlide key={photo.alt}>
<figure className="grid gap-2">
<Image src={null} alt={photo.alt} ratio="16-9" fallback={<Art {...photo} />} />
<figcaption className="text-body-default text-secondary">{photo.alt}</figcaption>
</figure>
</CarouselSlide>
))}
</Carousel>
</Modal>
</div>
);
}