How I used AI to build and run my portfolio website
I spend my working days arguing that AI should take the boilerplate off analysts so they can focus on decisions. At some point I had to ask whether I practise that outside the SOC. This site is the answer. It is plain HTML, CSS and a little JavaScript on GitHub Pages, and most of the changes you see on it were written by an AI assistant, Claude, working in the repository and opening pull requests that I review and merge.
This is not a story about an AI building a website while I slept. It is closer to working with a fast, tireless colleague who never touches production without me. Here is how that works, what it shipped, and the places where it simply could not do the job alone.
How the setup works
The assistant has access to one repository and nothing else. Every change happens on a branch and arrives as a pull request with a short description of what changed and why. I read the diff, check the preview, and only then merge. GitHub Pages deploys the main branch, so nothing goes live until I say so.
Before the first change, I wrote down the rules in a short brief that the assistant reads at the start of every task:
- No personal or contact details on the site. The only outbound profile links are LinkedIn and GitHub.
- Draft before publishing. New or rewritten articles come to me for review first. Small fixes can go straight through.
- Keep it fast and boring. Plain HTML, CSS and JavaScript. No frameworks and no build step.
Those three lines did more for the quality of the work than any clever prompt. An assistant that knows the boundaries does not need to ask permission for every small thing, and it knows exactly which things it must stop and ask about.
What it shipped, one pull request at a time
Here are the first nine pull requests, in order. Each one is small enough to review in a few minutes, which is the point.
| PR | Change | Why it mattered |
|---|---|---|
| #1 | Self-hosted, trimmed fonts and a hero animation that pauses off-screen | Faster first paint and less wasted CPU |
| #2 | Corrected article read times and sitemap dates | The labels said "5 min" on two-minute posts |
| #3 | AdSense slots on blog articles, loaded lazily | Monetisation without slowing the page |
| #4 | Ad unit slot IDs | Real ads instead of empty boxes |
| #5 | Privacy page and ad blocking recovery | Required disclosures, linked from every footer |
| #6 | Homepage stats that fit on narrow phones | "15m→30s" was spilling out of its cell |
| #7 | Hero buttons refocused on LinkedIn and the blog | Clearer next step for visitors |
| #8 | Search, topic and year filters, and pagination on the blog | The list can grow without becoming a wall |
| #9 | An ad banner below the blog list | Placed so filtering never moves it into the text |
Speed: fonts and a polite animation
The site used to pull its three typefaces from Google Fonts. That meant a render-blocking stylesheet and extra connections to third-party servers before a single word appeared. The first pull request replaced it with four self-hosted font files, cut down to the Latin character set, declared with font-display: swap so text shows immediately, and preloaded so the browser fetches them early. It also dropped a font weight the site never used.
The animated alert funnel at the top of the homepage got the same treatment. It now stops drawing when it scrolls out of view, using an IntersectionObserver, as well as when the tab is hidden. A single animation handle prevents two loops from running at once when it resumes. You will never notice this, which is exactly the goal. Your laptop fan might.
The mobile fix in pull request #6 is a good example of a bug I would have shipped without noticing. The "15m→30s" figure was wider than its cell on a 320-pixel screen. The fix sizes each figure relative to its own cell with container query units, with a sensible minimum, and lets it wrap after the arrow on the narrowest phones.
Search and pagination without a framework
A blog with two posts does not need search. A blog with twenty does. Pull request #8 added a search box, topic and year filters, sorting and pagination to the writing page, and it did it without turning the page into an app.
The article list is still plain HTML, so it works for search engines, for screen readers and for anyone with JavaScript turned off. A small script reads the existing items, then hides and reorders them as you filter. The current search and page live in the URL, so a filtered view can be bookmarked or shared. That is the kind of design choice I care about, and it is the kind an assistant makes well when the brief says "no frameworks" up front.
Ads and consent, done carefully
I wanted ads on the blog to help cover the time I put into writing, but not at the cost of a slow or jumpy page. The result:
- Ads only on the blog. The homepage stays ad-free.
- Lazy loading. The ad script loads only when an ad box is about to scroll into view.
- Reserved space. Each ad box has its height set in advance, so the text never jumps when an ad arrives.
- Fail closed. Until a box has a real slot ID, the box is removed and nothing loads.
- Consent first. A privacy page explains analytics and ads, and visitors in Europe, the UK, Switzerland and US states with privacy laws see the consent choices those rules require.
Where the AI needed a human
This is the part I find most interesting, because it maps closely to what I see in security automation. The code was the easy part. The hard parts were identity, access and judgement.
- Browser access. Several steps lived in web dashboards, not in the repository. The assistant was running in the cloud and could not drive the browser on my machine, where I was signed in. We had to switch to a session running on my own computer for those steps, and even then I stayed at the keyboard.
- Picking the right Google account. Analytics, Search Console and AdSense all need to sit under the same account, and like most people I have more than one. The assistant could tell me that this mattered. Only I could know which account was the right one.
- Pressing the buttons that matter. Submitting the site for ad review, verifying ownership and approving consent messages are decisions with my name on them. The assistant prepared everything. I clicked.
- Deciding what to say. What goes in an article, what stays private and what counts as confidential from work is judgement, not code. That is why every article, including this one, comes to me as a draft first.
None of those are failures of the tool. They are the boundaries I would want any automation to respect: it acts inside a scoped permission, and a person signs off on identity and irreversible steps.
What it caught that I missed
It goes the other way too. The assistant noticed that my article read times said five minutes when the posts took about two. It noticed the stats overflowing on small phones. It noticed that the fonts were costing page speed. None of these were in my original request. They came from an assistant reading the whole site rather than only the line I pointed at.
If you want to try this
- Write the rules down first. A one-page brief beats a long prompt every time.
- Keep every change small. One pull request, one idea. You will actually review it.
- Review the diff, not the summary. The description tells you what was intended. The diff tells you what happened.
- Keep the stack boring. Plain HTML is easy for you and the assistant to reason about, and it is fast for visitors.
- Check it on a real phone. Every layout bug I have shipped was obvious on a small screen.
Frequently asked questions
Can an AI assistant build a website on its own? It can write most of the code, but it still needs a person for accounts, access, approvals and deciding what to publish. On this site every change arrives as a pull request that I review before it goes live.
Why not use a website builder or a framework? Plain HTML, CSS and JavaScript load fast, are easy to review and have nothing to update or patch. They are also easy for an AI assistant to reason about, which keeps each change small and predictable.
Do ads slow the site down? Not much, if they are done carefully. Here the ad script loads only when an ad box is about to scroll into view, and each box has its space reserved so the text never jumps.
The point
Using AI on this site has felt a lot like using it in the SOC. It is excellent at the repetitive, well-scoped work: wiring up fonts, writing filters, fixing layout edge cases, writing up what changed. It still needs a human for identity, access and judgement. The win is not that I do less. It is that the hours I do spend go into the parts only I can do, like deciding what is worth writing about next.