Portfolio Website
| Date | 2024-04-27 |
|---|---|
| Organization | Daniel Locatelli |
| Place | Online |
| Link | daniellocatelli.com |
This is the website you are reading right now. It started in April 2024 as a small Astro site and has since grown into a playground for the way I like to build things: fast static pages, content that is easy for both humans and AI tools to read, and a few interactive pieces to spice things up.
Tech Stack
- Astro with TypeScript for the site itself, React for the few islands that need interactivity, and Tailwind CSS for styling.
- Astro Content Collections for all content, written as markdown and MDX with typed frontmatter and validated at build time.
- Claude (Anthropic) for the homepage chat, with Supabase as a vector store for retrieval.
- Three.js for the geodesic sphere.
- Cloudflare Workers with Static Assets for hosting, edge caching, and the agent-facing endpoints; prerendered pages are served straight from the edge, and the Worker only runs for the chat and MCP endpoints.
Claude Code as content management system
All the content lives as plain text files (written in markdown) in the same place as the code, the public repository on GitHub: one small file per project, research entry, publication, teaching item, or CV section, with a short header that holds the facts (title, dates, tags) above the text of the page, and a copy in each of the three languages. There is no database and no separate content system behind the pages.
The point of this setup is to make the content directly accessible to AI harnesses such as Claude Code. Because the content is just files next to the code, Claude Code can read, edit, create, and cross-check entries the same way it works on source code. In practice this means I use Claude Code as the content management system (CMS), the tool you would normally log into to add a page or fix a typo: I describe a new project or a correction in a sentence, and it writes or updates the files, keeps the headers consistent, and checks the related entries in the other languages. This very page was written that way. Everything on this site is co-created, from the code to the content.
Keeping the content in the repository as plain text has a second payoff: it is straightforward to chunk it, embed it, and feed it to a language model. That is what makes the AI chat on the homepage possible (more on it below).
Translation by Claude Code
The site is available in English, Portuguese, and German. There is no translation service in the pipeline: when a content file changes in one locale, Claude Code translates it and updates the corresponding files in the other two. Structural fields such as dates, links, and places are kept in sync, while translatable fields such as country and city names are localised. The same applies to the interface strings, which live as typed objects per locale.
AI chat on the homepage
The homepage opens with a chat powered by Claude. Visitors can ask what I am working on, where I studied, which tools I use, or anything else covered by the site, and get an answer grounded in the actual content rather than a generic reply.

Under the hood, a knowledge pipeline turns the content collections into small text chunks per locale (individual pages, CV entries, a chronological timeline, and a set of pre-written FAQ answers for the most common visitor questions), embeds them with Voyage AI, and stores the vectors in Supabase. When a question comes in, the API endpoint retrieves the most similar chunks and passes them to Claude as context. Whenever content changes, a single command regenerates the knowledge files and uploads fresh embeddings, and a benchmark script runs a fixed set of common questions against the chat to make sure it still answers all of them correctly.
The geodesic sphere
Further down the home page, between the service offerings and the “Architect + Programmer” section, sits a geodesic sphere rendered with Three.js. It follows the construction Buckminster Fuller made famous: start from an icosahedron, subdivide each face, project the vertices onto a sphere, and take the dual, so that the twelve original vertices become pentagons and everything else becomes hexagons. The sphere rotates as you scroll, tying the motion of the page to the geometry. The green polygon edges are drawn as thin screen-space strips rather than raw one-pixel GL lines, so they stay smooth and evenly thick on any screen, and the faces are nudged back slightly in depth so the edges never flicker against the surface. A light fog toward the black page background fades the faces at the back of the sphere, giving the view a sense of depth.
It is also a nod to my own path: geodesic and lightweight structures are a recurring theme in the projects and research on this site, from the O3 Pavilion, where it started for real, through Common Sky and my master thesis Building Across Scales to my doctoral work on timber structures. Three.js is fetched right after the first screen has painted, in an idle moment, so it never sits on the critical path of the initial page load but is ready by the time you scroll down to the sphere.
Presentation mode
Content items can carry a slide deck that lives alongside the writeup, in the same folder and the same repository. Decks are authored in MDX with a small YAML shorthand for the common slide types (title, text, image, image row, video, overlays), and rendered in the browser with keyboard navigation, an overview of all slides, and a presenter window. I use this for teaching and talks, so that a lecture and its slides are published together, versioned together, and translated together.

Agent-ready on Cloudflare
Since much of the traffic to a site like this will increasingly come from AI agents rather than browsers, the site exposes its content in the formats agents expect:
- an
llms.txtindex per locale, generated from the content collections at build time; - a markdown companion for every content page (append
.mdto the URL), plus content negotiation so that a request withAccept: text/markdownreceives markdown directly; - a
robots.txtthat explicitly welcomes AI crawlers, a sitemap with image entries, and an API catalog under/.well-known/; - a small read-only MCP server, so that agents can query the site’s content as tools;
- DNS-AID discovery records (
_mcp._agentsand_index._agentsSVCB records, DNSSEC-signed), so that agents can find the MCP endpoint from the domain name alone; - a skills index under
/.well-known/agent-skills/, following Cloudflare’s Agent Skills discovery RFC, with twoSKILL.mdfiles in the Agent Skills format that teach an agent how to query the site via MCP or read it as markdown.
Getting content negotiation to work on prerendered pages took some digging into how Cloudflare’s request pipeline, Workers Static Assets, and Astro’s build-time middleware interact; the solution is a zone-level Cloudflare Snippet that rewrites the URL before it reaches the Worker. On isitagentready.com, the checker that accompanies Cloudflare’s agent-readiness guide, the site went from a 25% score to 71/100, “Level 5, Agent-Native”, with full marks for discoverability, content, and bot access control. The remaining points sit in the API and auth category and are deliberately left open: OAuth discovery, protected-resource metadata, and an auth.md only make sense when there is something to log in to, an A2A agent card describes an agent that offers services to other agents, and WebMCP exposes in-page actions such as forms or checkouts. A read-only portfolio has none of these, so the checker keeps listing them and the site keeps declining them.

Performance and Lighthouse
Astro renders the site to mostly static HTML, which already gives it a head start. Lighthouse scores of 100 for performance, accessibility, best practices, and SEO then come from not loading what the visitor does not need yet:
- Images ship in responsive sizes with explicit dimensions, lazy-loaded just ahead of the viewport; fonts are subset and preloaded.
- Three.js loads during an idle moment and only redraws the sphere while it is moving.
- The chat window is fetched only once a visitor starts typing, so the hero input itself ships a few kilobytes of JavaScript.
- The skills-map logos are separate lazy-loaded images instead of inline SVG, which cut the homepage HTML from about 350 KB to under 70 KB.

A personal toolbox behind the public pages
The site also hosts pages that are not linked from anywhere and exist mainly for my own use. The short CV, the full CV, and the PhD-oriented CV live at unlisted URLs, are rendered from the same content collections as the rest of the site (so an experience or publication only ever needs to be entered once), and carry print styles so that saving the page as a PDF produces a clean, up-to-date document whenever one is needed. A couple of similarly unlisted pages serve as title cards for recorded lectures. In this way the site doubles as a small workspace, not only a showcase for visitors.

Smaller details
- Link previews at build time. External links listed on a page are rendered as preview cards. Their titles, descriptions, images, and favicons are fetched once and cached in the repository, so the build is reproducible and no third-party request happens at page load.
- Tooltips everywhere. One small tooltip system (a popover panel with an arrow, positioned and flipped by a few lines of JavaScript) serves every tooltip on the site: markdown footnotes show the note inline on hover so readers do not have to jump to the bottom of the page; each tool in the homepage Map of Knowledge explains where and how I use it; the social icons in the footer, the contact section and the CV header name their destination; and the slide-deck controls show their keyboard shortcut. No native browser tooltips remain.
Other Links



