My new website wasn't designed for humans first
- ai
- agents
- build-in-public
- web
Every page on this site now exists twice. One version for you, one in plain markdown for the AI agents that increasingly read it first. The actual architecture, what it cost, and the one thing I still can't measure.
A growing share of visits to this site will never render a pixel of it. Not this page either. The reader is a recruiter's AI assistant, a research tool, a model someone asked about me. It fetches the text, takes what it needs, and leaves.
I am a product designer. I have had a personal site for most of my 17 years doing this, and every version of it was built for a person with 40 seconds and a scroll wheel. This month I rebuilt this one for the other reader. Every page now exists twice, once as the page you are looking at and once as plain markdown at its own URL. And there is a feature for the humans too: you can now interview me without me being in the room. I'll get to that.
This post is the actual architecture. Not a prediction about AI and the open web.
The reader changed and the page didn't
When someone wants to know about me now, there is a decent chance they never open this site. They paste the link into a chat and ask.
What that reader gets from a modern site is bad. A JS bundle, nav chrome, a consent banner, a footer, and somewhere inside it all, a few hundred words that are actually about me. The content is a small fraction of what it has to parse, and the parse is lossy.
I make the page. I know exactly what is on it. Giving that reader clean text is not doing a robot a favor. It is the same job as writing a good page title: make the thing legible to whoever is actually reading.
Every page exists twice
Add .md to the URL in your address bar. Right now, on this page. You get this exact post as plain markdown, frontmatter and all.
That works everywhere on the site. mahdif.com/index.md is the home page, mahdif.com/work.md the work page, and so on down. Real headings, and internal links that point at other .md files, so a model can traverse the whole site markdown to markdown and never touch HTML. (One plumbing note: the .md rewrite has to run before the router matches, or this post's .md URL would get swallowed by the blog's slug route. That cost me an hour.)
Two more doors. /llms.txt is a curated map of the site linking the markdown mirrors. /llms-full.txt is the entire site concatenated into one file, for a reader that would rather take everything at once than crawl.
One serializer, or the whole thing is a lie
This was the real decision.
All of it renders from a single server side function. The .md routes, llms.txt, llms-full.txt, the "copy for AI" action you can see on this page, and an animated agent view you can toggle from the rail. Five surfaces, one serializer.
What you see in agent view is byte for byte what a crawler gets.
That constraint is the point. Two code paths means two sites, and the machine one drifts, quietly, until it is describing a person I stopped being a year ago. It also forces the gating down into the serializer: password protected case studies are excluded server side in the same function that builds the markdown, so there is no side entrance where the mirror hands out what the page won't.
The markdown declares itself as an alternate of each page, not a competitor to it. Every page emits <link rel="alternate" type="text/markdown">. Getting that wrong is how you hand a search engine two versions of yourself and let it pick.
You can interview me without me in the room
The other half of the rebuild, and the half built for people.
The site has a chat, grounded in the same content, that answers as me. Ask it about a project, how I think about a problem, why something got built the way it did.
A portfolio has always been one directional. I publish it and hope the right person opens the right page in the right order. Someone with a specific question about my work had no way to ask it without getting on my calendar. Now they can, at 11pm, on a Sunday, without me.
The part I can't measure
Here is the honest section.
I can see requests to the .md routes in my analytics. I cannot see whether a model used what it fetched, or whether a single sentence I wrote made it into an answer about me. There is no attribution layer. The loop that makes normal design work, ship it and watch what people do, does not exist here yet.
llms.txt is a convention somebody proposed, not a standard anyone committed to. It may go the way of every other well meaning text file at the root of a domain. I built on it anyway, because the cost was a weekend and the downside is a few files nobody reads.
And the title oversells one thing. "Not designed for humans first" is true about the architecture and a little dishonest about the effort. The visible site got most of the work. The shader on the home page, the three click sounds I auditioned before picking one, the chat frame that preloads in the background so the first open has no loading state. I did not stop caring about the version you are reading. I stopped assuming it was the only one.
If you want to try it
Start with one page. Serve your home page as markdown at a stable URL and add the alternate link tag. That is an afternoon, and it is most of the value.
Generate it from the same source as the page. Hand written markdown will be wrong within a month, and a stale machine version is worse than none.
Put your gating in the generator, not the template. The markdown route is a door, and doors need the same lock as the front one.
The test I didn't mean to build
Designing for a reader that cannot see did something to the work itself. The markdown version of this site has no layout, no shader, no sound, no photography, no order I chose for visual reasons. It is just the claims, in sequence.
If the work doesn't hold up in plain text, the design was carrying it.