← sidv.co

Build ยท 14 min read

How I Rebuilt allymatter.com With Claude, Errors and All

On the evening of 22 October 2026, at 6:29 PM IST, the new allymatter.com went live. The site itself took eight days to build. Getting to those eight days took four months and four false starts. This is the whole story, including the parts that broke.

The new AllyMatter home page: a headline reading The policy management center for every policy your auditor asks about, with Start a Trial and Book a Demo buttons and an animated policy card on the right.
The new home page. The card on the right is not a screenshot. It is built in code and animates through a policy's life.
187pages live
244old addresses redirected
119blog posts moved
8days of building
$0monthly hosting

Part one: four ways not to build a website

AllyMatter is a policy management center. Our old site ran on WordPress and had grown the way most startup sites grow: a page here, a plugin there, around 186 blog posts of mixed quality, and nobody quite sure which pages still mattered. I wanted a site that explained the product clearly and turned visitors into trials. What I did not have was a clear idea of how to build it.

  1. June 2026 Attempt one: a hand-coded static site. My first session with Claude Code produced a light, typography-first design in plain HTML and CSS, black and white with our orange. It looked good. It was also never committed to git, never pushed anywhere, and quietly disappeared from my machine a few weeks later. Lesson one: work that is not in version control does not exist.
  2. Mid June Attempt two: Tailwind. I redid the home page with Tailwind, borrowing the feel of tailwindcss.com, and built a first blog article template. Useful for learning what I liked. Not a site.
  3. 29 June Attempt three: Next.js and Sanity. This one sounded the most "proper". A Next.js app, a Sanity content system, hosting on Vercel. I scaffolded the app and moved one blog post into Sanity. Then I noticed I had written down two different stacks on the same day, Astro in one note and Next.js in another. That contradiction was the tell: I was choosing tools before I knew what the site needed.
  4. 4 August Attempt four: stay on WordPress. When I asked what Sanity would cost with proper editor roles, and thought about migrating every blog post and mapping 133 old addresses to new ones, I retreated. The decision was a custom WordPress theme on our existing host. About 90% of visitors land on blog posts, so a botched migration was the biggest risk in the whole project. Staying put removed it.

Through August and September we ran the WordPress plan like a normal agency project: a writing stage, a review stage, a design stage and a development stage, tracked as over a hundred tickets. The launch date slipped from 21 August to 10 September to 21 September. A refreshed site did go live on 21 September. A week later a full review found missing pages behind the menus, product claims that were no longer true, and five of fourteen items in one menu leading nowhere.

That review changed my mind. The problem was not the platform. It was the distance between deciding something and seeing it on the page. Every change went through four hands.

Part two: the decision to build it in code

On 10 October I asked a blunt question: what if the whole site, blog included, was just code, built and changed directly with Claude, with nothing on WordPress at all? I ran it through the same sceptical review I use for product decisions. It came back as a cautious yes. On 14 October I made it final: WordPress would go away, and the site would live in a GitHub repository, served by Cloudflare Pages.

Two rules came with it. First, nothing on the live domain would change until the entire site, blog and all, was built and tested on a staging address. Then one switch. Second, every existing blog address would stay exactly the same, so the redirect risk that scared me in August mostly disappeared.

Before any code, I spent a day writing instructions. I collected reference sites for layout, motion and colour, and wrote down rulings as I went: a fixed narrow page width, a grey background, a specific orange, real product screens instead of stock art, dark mode everywhere, and an accessibility bar of WCAG 2.2 AA on every page. The instruction file ended up with well over a hundred numbered rules. That file mattered more than any line of code. It meant every new page started from decisions already made.

Part three: eight days of building

  1. 14 October Design tokens first: one file holding every colour, size and spacing value. Then the hero, an outcomes grid, an industries switcher, the consultant section, the call-to-action bands and the footer, all in one day.
  2. 15 October The menus, including a large product dropdown and a solutions menu with photos washed in our orange. The product page, where each feature is shown as a small working mock of the app built in code, so it never goes stale like a screenshot does. The blog home and article template. A single body font, Figtree, across the site. The first blog posts moved over: two, then twenty, then ten more.
  3. 16 October A page generator. Instead of hand-writing every product sub-page, a small script builds them from a template and a content file. A header-art system for blog posts, six styles that cycle so no two neighbouring posts look alike.
  4. 17 and 18 October Six department pages, four use-case pages, a consistent thin icon set with a rule that no icon repeats on a page. Then 29 standards pages, nine comparison pages and a "What's new" changelog. Claude's background helpers hit a usage limit in the afternoon and were simply re-run after it reset.
  5. 19 October A count: 118 pages built in staging, 33 of them blog articles.
  6. 21 October The heavy day. The rest of the blog moved in one go: 86 posts in a day. A smaller, cheaper Claude model did the moving, a larger one reviewed every post, and every post passed a word-for-word check against the original. The blog stood at 119 posts. Then the first push to GitHub, 895 files. I connected Cloudflare Pages and the whole site appeared on a staging address. The same day: a real 404 page, legal pages, our own cookie banner, self-hosted fonts, a working contact form, a double opt-in newsletter, and every old address mapped to its new home.
  7. 22 October Security headers and bot protection on the forms. 117 links added between related posts. Six passes through the blog removing claims about features we do not ship. A glossary of 82 terms. A consultants page. Then, at 6:29 PM, go live. Within fifteen minutes Claude had checked the real domain: all 187 pages loaded and all 244 old addresses redirected correctly.

Part four: everything that broke

This is the part most build stories leave out. Nothing below was dramatic. Each one was a small thing that would have quietly hurt the site if nobody had checked.

Cloudflare could not find the repository

When I first tried to connect Cloudflare Pages, the new repository simply was not in the list.

Fix: Cloudflare's GitHub app had access to some repositories but not this one. Granting it access solved it.

Missing pages pretended to exist

Any address that did not exist showed the home page, with a "success" status. Search engines would have indexed endless copies of the home page.

Fix: without a 404 page, Cloudflare Pages treats a site like a single-page app. Adding a generated 404 page that returns a proper "not found" status fixed it.

Redirects silently stopped working

We wrote every old-to-new address into Cloudflare's redirects file. Only part of it took effect: 124 rules worked and the rest were ignored, with no warning.

Fix: Claude ran a test deploy to find where the cut-off was, and found the file had a size limit. All the redirects moved into a small piece of server code with a map of 471 entries, plus a guard so no address can redirect to itself. A separate routes file keeps images and pages from touching that code, which keeps us inside the free plan.

Pages the old site had that nobody had listed

The redirect map came from a snapshot of the old site. A full crawl of the live site found pages it missed, including the About page, several hubs and seven posts. One of the missing posts had links pointing to it from other websites.

Fix: I ruled on each one, keep, merge or redirect. The backlinked post was pulled from the old site's API and migrated. The first attempt failed on a missing author field, the second worked.

Staging links jumped to the live site

Blog posts carried links written out in full, with the domain. Clicking around the staging site kept dropping me onto the old live site.

Fix: every internal link was rewritten as a relative path, so the same files work on any address.

Link previews were blank

Sharing a staging link in Slack or LinkedIn showed no preview at all.

Fix: the staging site blocked all robots, including the preview fetchers. Staging now lets the preview bots of LinkedIn, X, Facebook, Slack and WhatsApp through, and nothing else.

The contact form said yes and did nothing

Submissions saved, but the alert email to us never arrived, and the form reported no error.

Fix: the email step was swallowing its own error message. Once it reported properly, we found two problems in sequence: the sending domain was set up without the ".com", and the API key was wrong. I also learned that Cloudflare only picks up a changed secret after a fresh deploy.

The spam check rejected our own site

The bot check on the forms failed twice: first with "domain not allowed", then with "invalid key".

Fix: the first error came from reusing an old widget set up for a different domain. The second was a copy-paste that dropped the last character of the new key. A new widget and a careful paste fixed both.

A safety check that never ran

The site checker had a test for our security policy, but it sat after the line that ends the script, so it never executed. On launch day, a second check silently did nothing because it relied on a command this Mac does not have.

Fix: both were caught before the security policy was switched on for real. The test moved above the exit line and the check was re-run properly. Lesson: a check that passes too easily deserves suspicion.

Staging's "do not index" could have shipped to live

Staging pages tell search engines not to index them. The normal build would have carried that into the live site and quietly removed us from Google.

Fix: a separate launch build that strips the staging-only settings, and a standing rule that live always goes out through it.

www still pointed at the old host

After the switch, allymatter.com showed the new site but www.allymatter.com still answered from the old WordPress host, and dropped tracking parameters on the way.

Fix: www was added to Cloudflare as its own domain with a permanent redirect that keeps the full address. Cleaning up DNS the same day also turned up old records pointing at servers we no longer used, which we removed.

Claude's own front-end slips

A handful, each caught by looking at the page: a new helper class shared a name with an existing one and made rows vanish from the e-signature card; another reused class made the approval-workflow figure jump out of its box; one extra closing tag put a stray section on all 40 pages; a call-to-action had 88 pixels of space above it instead of 32; and a few layouts broke on phones.

Fix: each was renamed, removed or re-spaced the same day. The class clashes produced a lasting rule: search the code for a name before adding it.

Misreading me

A few times Claude did the wrong thing confidently. It built a trial sign-up card when I had asked for a newsletter card. It read "option 1" as a different design than the one I meant and deleted the one I wanted. Once it removed a line of copy it could not trace, which turned out to be correct.

Fix: it said so plainly each time and rebuilt the right thing from its saved work. The pattern taught me to say exactly which element I mean, not "the first one".

Tools that hit their limits

Our Figma connection on a view-only seat allowed twenty calls a month, which ran out in a day. Headless Chrome, used to capture animations, ran CSS and JavaScript animations on different clocks, so captures came out wrong. There was no video tool on the machine to inspect a reference animation frame by frame.

Fix: a full Figma seat, captures in real time through Chrome's developer protocol instead, and redrawing product screens directly in HTML when the design file moved under us.

Part five: how the site actually works

There is no content management system. Every page is a plain HTML file generated by small Python scripts from templates and content files. Blog posts are content files too. A checker runs on every build and follows every link and asset, around 26,000 of them, so a broken link never ships.

The code lives in a private GitHub repository. Every push to the main branch is picked up by Cloudflare Pages and published worldwide in about a minute. The only server code is a handful of small functions: the contact form, the newsletter sign-up, and the redirect map. The hosting cost is zero.

When I want a change, I describe it to Claude, look at it on the staging address, and push it. The distance between deciding something and seeing it live went from weeks to minutes. That was the whole point.

What I would tell someone starting today

What is still open

The 29 standards pages are built but hidden until we are happy with them. Seven blog posts are on hold. Two product pages were removed for launch and will come back. A German version is waiting. A few very old image links from the WordPress days no longer load. None of these needed to block the launch, and all of them are now changes that take minutes rather than tickets.

See the result.

Visit allymatter.com

Sid Varma, October 2026. Questions or ideas: [email protected]