Writing — Will Curran

How I Rebuilt My Website With Claude Design, Claude Code and the HubSpot API

Written by Will Curran | Aug 21, 2026, 11:55:05 PM

This site ran on an off-the-shelf HubSpot theme for years. Dark, competent, generic. Roboto and Lato, a coral accent I never chose, and a layout that looked like a hundred other sites built from the same marketplace listing. It worked. It just was not mine.

Worth noting

This is not a story about AI building a website while I slept. I made every decision about what this site says and how it behaves. What changed is that the distance between deciding something and seeing it live went from weeks to minutes.

Over a handful of working sessions I rebuilt the whole thing — seven pages plus this blog — against a design system I built myself, deployed straight into my live HubSpot instance by an AI agent with API access. No agency, no dev shop, no ticket sitting in someone else’s queue for two weeks.

Here is how it actually worked, including the parts that broke.

The design came first, and that is the whole trick

Before a single line of code, I built the Will Curran Design System in Claude Design. Not a mood board. An actual system: a palette down to the hex, a type scale, spacing steps, radii, shadows, motion timings, and a component set — buttons, badges, tags, cards, the waveform, the glow backdrop, the wordmark.

Three colors carry the whole site. Obsidian #0E0E11 for ground, marigold #E1873C for anything that wants your attention, cloud #F2F0EB for type. Space Grotesk sets headlines, Inter sets body copy, Space Mono handles anything that reads like data — dates, counts, labels. That is it. Every screen on this site is built from those decisions.

Then I designed the pages themselves in the same tool: the homepage, the portfolio, the press board, favorites, the speaking and consulting flows, and later this blog with all twelve of its states — listing, post, category, author, search, subscribe, unsubscribe, password prompt, the notification email.

That order matters more than anything else in this post.

An agent working without a design system will invent a new grey in every file. An agent working from one makes the same decision you would have made, on the fortieth screen as on the first.

When the agent needed a card, it did not have to guess at a border color or a corner radius. Both were already named. When it needed an accent, there was exactly one. The consistency you see across these pages is not the model being clever. It is the model having somewhere to look things up.

Wiring the agent into HubSpot

The build side was Claude Code, talking to the HubSpot API directly.

This is where I lost the most time, and it had nothing to do with design. HubSpot Personal Access Keys cannot grant the content scope. You can generate one, authenticate perfectly, read every page in your portal — and never create or update a single one. The permission simply is not available on that credential type, and nothing in the flow tells you so. You just get a scope error at the end of a long chain of work that looked like it was going fine.

The answer is a HubSpot Service Key, which can hold content along with source code read and write. I generated one, and everything unblocked at once.

After that the deploy path was simple:

  • A child theme, will-curran-brand, extending the marketplace theme it replaced — so error pages, search, and the password prompt stay inherited instead of being rebuilt from nothing.
  • Source files pushed to /cms/v3/source-code/published/content/ as they changed.
  • Pages, redirects, forms, blog posts, tags and authors all created through their own endpoints.

What actually got built

Fifteen editable modules. Around sixteen hundred lines of CSS and JavaScript. Seven pages and a blog.

  • Portfolio — twenty-five projects across five filters, from sixteen years of Endless Events through wearable hardware and conversational AI to the open source I contribute to now.
  • Press — one hundred appearances in a single filterable board. Talks, podcasts, articles, mentions.
  • Favorites — twenty-eight apps and pieces of gear I actually paid for and kept using.
  • Speaking, Consulting and Hire — three real HubSpot forms wired to my CRM, each confirming on the page rather than dumping you somewhere else.

One rule I set early and did not bend on: every section had to stay drag-and-drop editable inside HubSpot. I did not want a site that only an agent could change. If I want to reorder my portfolio on a plane, I should be able to.

The audit I would never have done by hand

My press page had a hundred outbound links, some of them a decade old. I asked for a check on all of them.

Thirty-one were dead. Twenty-one had captures in the Wayback Machine and now point there. Three had been quietly republished at new URLs, found by searching for the headline. The rest were genuinely gone and got retired.

I have known that page had rot in it for years. It is exactly the kind of task that is too boring to ever reach the top of a list and too tedious to hand to a person. It took minutes.

The parts that were not magic

If you take one thing from this, take this section. The model was rarely the hard part. The platform was.

  • HubSpot reserves body, label, value, text and name as module field names. Use one and the upload fails with an internal error that does not name the field.
  • {% module_attribute %} is silently ignored inside a drag-and-drop area. No error. The page just renders the defaults and you stare at it.
  • Pages snapshot their content when they are created. Editing the template afterwards changes nothing until you force a fresh snapshot.
  • HubL’s and and or return booleans rather than the operands, so href="{{ a and b or c }}" renders as href="true". Two of my links quietly pointed at a page called true.
  • The source code API is case-insensitive on paths. Upload Blog-Post.html, delete blog-post.html, and you have just deleted the file you uploaded thirty seconds ago.
  • One undefined CSS custom property zeroed out three separate declarations. An invalid var() does not fall back — it invalidates the whole line. My paragraphs lost their spacing and a card lost all its padding, in total silence.
  • Pages are prerendered and edge-cached, so a template change can take twenty minutes to appear. Debugging on a delay like that feels like arguing with someone over satellite.

Every one of those is the sort of thing that eats an afternoon whether a person or an agent hits it. The difference is that the agent hit them at three in the morning and kept a written record of each one.

Where I was still the bottleneck, and should have been

I found that CSS bug by looking at the page and saying “the bottom of a post looks weird.” I found the broken portfolio filters the same way. Nobody told me to check; I just knew the thing was not behaving like the thing I had designed.

That is the job now. Every judgment call on this rebuild was mine:

  • What the navigation should be, and that Press keeps its old /media URL because a decade of inbound links point there.
  • What to cut — four pages retired and redirected rather than kept alive out of sentiment.
  • How dates should age. “Booking 2026” follows the current year; “2027 Event Trends” follows the next one. Two different rules that look identical until January, when one of them embarrasses you.
  • Which of a hundred press appearances actually represent me, and which repositories I own versus contribute to. Getting that wrong is not a bug, it is a lie.

Nineteen years in event technology and sixteen running a company is what tells me a booking page needs to answer “are you available and what does it cost” in the first screen. No model knows that about my business. It knows how to build the page once I have decided what the page is for.

The agent is fast, tireless and has no taste. I have taste, context and a limited number of hours. That trade is extremely good.

If you are about to try this

  1. Build the design system first. Everything downstream gets cheaper and more consistent. Skip it and you will spend your time reviewing arbitrary decisions instead of real ones.
  2. Sort your credentials before you start. Find out what your platform’s token types can actually do. Mine cost me hours of work that looked like progress.
  3. Look at the page. Not the diff, not the summary. The rendered page, on a phone as well as a laptop. Most of what I caught was caught this way.
  4. Keep it editable by a human. If the only way to change a heading is another agent session, you have not built a website, you have built a dependency.
  5. Expect the platform to be the hard part. Not the model. Never the model.

The site you are reading is the result. Same CMS I was on before, same CRM behind the forms, entirely different thing to look at — and I can change any part of it myself, in an afternoon, without asking anyone.

If you are working through something similar and want to compare notes, I am easy to reach.