Event site
Formatura Medicina TXXXI - UNICID
The information hub for a medical class's graduation: four events, a searchable FAQ, documents, and a form that lands in the committee's inbox.
Overview
A medical class of 140 graduates finishes at the end of 2026. The graduation committee ran everything through a WhatsApp group with the graduates and their guests, and that group became the only source of information: dinner times, dress code for the ball, form deadlines. The same questions came back every week, because digging an old answer out of hundreds of messages takes more effort than asking again.
The site takes that information out of the conversation and gives it a fixed address. It holds the four graduation events, a searchable FAQ, the guests’ portal, the committee’s newsletters and documents, contacts for the companies involved, and a question form.
Technical decisions
Four events, four content files
The information arrived in pieces and out of order, so I needed to change content without opening a component, and reorder a section without touching layout. That is why I chose Astro.
Each event is a YAML file validated by a schema and rendered by one dynamic route. The file declares its own sections as a discriminated union: general information, dress code, schedule, menu, permissions, list, links, text and FAQ. A renderer picks the component for each type. The newsletters and documents sit in Cloudflare R2 behind their own subdomain, so publishing a new one is an upload and a link in the YAML.
What it cost: as long as new information fits a section type that already exists, it is a block of YAML. When it doesn’t, it is a new schema entry, a new component and a new case in the renderer.
Static throughout, with one exception
The site is fully prerendered except for one route, the one that receives the question form. It runs on the server, checks a Cloudflare Turnstile token before anything else, and sends the email through Resend from the graduation’s own domain. Both keys are declared in Astro’s env schema, so a missing key breaks the build instead of the form of the first graduate who tries to use it.
What it cost: the project stopped running as a purely static site and picked up an adapter and two external accounts. In exchange, the committee gets the questions in its own inbox with no dashboard to learn, and it is much harder for a bot to flood it.
The animation runs on top of a page that is already finished
The root opens with the class crest, the line and the countdown, animated with GSAP and split-type,
with Lenis handling smooth scrolling. The HTML leaves the server in its final state: the script only
sets the starting points and animates up to where the page already was. The opening screen clears
itself through a CSS animation, so a script that fails to load leaves nobody stuck behind it. Under
prefers-reduced-motion that screen never appears at all.
What it cost: GSAP, split-type and Lenis add JavaScript to the first page anyone opens, and Lenis swaps the scrolling the browser gives for free for one it controls itself. I took the cost because the root exists to give the graduate a moment: the class crest, the line, and how long is left until the ceremony. None of the three loads on the pages people actually consult.
Results and lessons
The four events, the FAQ and the guests’ portal all came out of the same section types, and publishing new material became editing YAML and uploading a file to R2.
The question form sends through Resend with the graduation’s domain verified, from an address on that domain. That is what keeps the message out of the committee’s spam folder, which a generic sender does not guarantee.



