Skip to content

kevin-dev.com

July 20266 min readOngoing

Le siteCode source

This site is the third version of my portfolio. I made the first two by following YouTube tutorials, the first one in 2021, during my computer science degree, and the second one in 2022. That’s really how I learned HTML, CSS and JavaScript: copy, break, figure out why it broke, start again. On the second one, I was beginning to put my own touch, like the automatic dark theme or my age, which updates itself. It served me for more than four years.

Both were static sites, on Netlify. To change one line, I opened the HTML, edited the text by hand, pushed to Git and waited for the redeploy. That’s fine for updating your résumé once a year, but as soon as you want to write, it gets tedious fast. I wanted to add a project or write an article from an interface, without touching the code.

This third version is the site you’re reading: my projects, the tech I use, my résumé and a contact form, in French and English. It can also hold a blog and a list of talks. Behind it there’s an admin area just for me, protected by two-factor authentication. The code is public on GitHub.

Writing without touching the code

It’s the part nobody sees, and the one that took me the most work. At first, the editor had a button to show a preview of the Markdown. Two days into the project, I had it replaced with an editor where I just write, with Ctrl+B, Ctrl+I, tables and images, and where I can switch to the raw Markdown, colour-highlighted, at any moment. Both views work on the same text, one built with Tiptap, the other with CodeMirror. Here’s the form of this project, in French, with the switch between the two just above the toolbar:

The form of this project in the admin area: its title, its summary, then the start of the French text in the editor, with the switch between “Texte” and “Markdown” above the toolbar.

An image dragged or pasted in goes to the media library, which converts it to WebP in several sizes, and the editor asks me for its alt text. To proofread a draft, I don’t have a separate preview URL. Once logged in, I see it at its real address, with a banner.

What’s inside

The site runs on AdonisJS, first because I wanted to try another stack. I’d already done several projects with Next.js, and I wanted a more classic framework, with its ORM, validation and sessions built in. What convinced me is mostly what surrounds it. The docs are clear and up to date, the maintainers announce their choices ahead of time, and the community is small but present. For a site I want to keep for years, that stability matters more than novelty.

For the architecture, I started from Romain Lanz’s site, one of the AdonisJS maintainers. The front end is React, with no API in between. With Inertia, each controller hands its data straight to its page, with types shared end to end. The site only has one client, so an internal API would just have been one more contract to maintain.

Markdown is rendered to HTML when I save, not on every visit. Rendering goes through remark and rehype, with Shiki for code highlighting, and public pages only read that ready-made HTML. If I change how Markdown gets rendered, I have to re-save my content, and that’s on purpose, so the HTML online is always the one I proofread.

A style of my own

After two versions taken from tutorials, I wanted a design of my own. The result is plain, with a near-white background, dark ink and a single colour, a deep red taken from the curtain behind me in the home page photo. There’s also a dark theme that follows the device’s, and the site serves its fonts itself.

On my own server

It’s one of the first sites I run from A to Z. Before, my sites were on Vercel or Netlify, which took care of everything. For this one, I wanted to do it all myself, with my own services, my database and my backups, and it was a good site to start with. It runs on my VPS, under Dokploy, next to my other projects, and visits are counted by my own Umami instance.

Working with Claude

The site has been built with Claude Code since day one. On the evening of 21 July, I described what I wanted and the stack I had in mind, and asked it to ask me plenty of questions about the architecture before writing anything. That gave us a spec. The next day, we worked out the design together, starting from what I wanted on the home page, and on the 23rd, the site was online.

At the end of August, Romain Lanz published a boilerplate for building AdonisJS apps with coding agents. I wanted code tidy enough that Claude would know on its own where to create things and where to find them, so I took a good part of it. The business logic moved out of the controllers, and every database read now has a name and its own file, instead of being buried in two hundred lines of controller.

Architecture choices are written down in ADRs, short notes that say what was decided and why, and each layer of the code has its own instructions for Claude, loaded only when that code is touched. Everything that can be checked also became a lint rule. The client can’t import anything from the server, useMemo is rejected since the React compiler already handles it, and so is the em dash.

The boilerplate is a monorepo, with a separate design system, and it replaces Lucid, the AdonisJS ORM, with Kysely. I thought about both, and left them aside. shadcn/ui is meant to live inside the project, and the site is the only one using its components. Kysely would have changed the engine without tidying anything. Now that every query has its own file, I can still switch one file at a time.

Of course, there were mistakes. In early August, tests run without their config ran against the development database, and at the end every one of its tables was gone, without a single message. Since then, the tests refuse to start unless the database name ends in “_test”.

The decisions stay mine: the starting stack, the editor, the preview at the real address, what I took from the boilerplate and what I left.

Where the site stands

The site has been online since 23 July 2026. For now, I’ve put my projects on it, and the blog is waiting for its first article. I hope you like it. If you’d like to get in touch, ask a question or just say hello, the contact page is there for that.

Technologies used