I Built a WordPress Theme With AI - Here’s What Actually Worked

Quick Summary

  • An AI coding agent built the theme, but quality came from a finished prototype with a README handoff, small section units and required checks.
  • Splitting pages into 24 ACF Flexible Content layouts gave the agent bounded jobs, verified by PHP lint, WP_DEBUG renders, screenshots and scripted tests.
  • Many bugs came from WordPress conventions and plugins the agent couldn't see, so inventory every active plugin and its front-end effect first.

An AI coding agent built the WordPress theme this site now runs on. That is the least useful sentence in this article.

What decided whether the result was any good sat around the code: a finished prototype to translate, an architecture that broke the job into small units, and checks the agent had to pass before I looked at anything. Here is how that played out, including the parts that broke, when I rebuilt nahidmahamud.com in September 2026 with Claude Code, an AI coding agent that works in the terminal on your codebase.

The starting point: a page builder and a prototype

The old site ran on Elementor, stacked with add-ons: Elementor Pro, Happy Addons, Essential Addons and the Jet plugin family, plus a caching plugin and a tables plugin. Each one made sense on its own. Together they meant I owned very little of the markup my own site produced.

So I decided to stop using a page builder. I’ve written separately about building a modern WordPress theme without Elementor; here the relevant part is what replaced it.

The new design came out of Claude Design as a static HTML/CSS/JS prototype: 13 pages, a dark editorial look, a loader that fills a portrait silhouette, a curved 3D image “ribbon” in the hero, smooth scrolling with Lenis, parallax and a custom cursor.

It also came with a README “handoff”. That file mapped each section of the design to a future WordPress template part and listed what still needed real data: placeholder text, demo projects, a contact form that sent nothing.

The README turned out to be the most important file in the project. It was the spec.

An agent is only as good as its definition of done. A static prototype is an unusually precise one: the WordPress version should look and behave exactly like this, page by page.

The architecture decision: sections, not pages

The theme, NMH47-V2, is a custom classic theme with no page builder. Every page is a stack of ACF Flexible Content “sections”, with one layout per design section: 24 of them, covering heroes, featured works, services, pricing, FAQ, testimonials and the rest.

That shape suits an AI agent unusually well. Each layout is a bounded job: one section of static markup in, one template part and one set of fields out. It can be built on its own and compared against the prototype on its own.

The renderer that ties it together is short. It returns early if ACF isn’t active, and each layout name maps to a file, so featured_works renders template-parts/sections/featured-works.php:

PHP
/** Renders every section of a page: one ACF layout = one template part. */
function nmh_render_sections( $post_id = null ) {
	$post_id = $post_id ?: get_the_ID();
	if ( ! function_exists( 'have_rows' ) || ! have_rows( 'sections', $post_id ) ) {
		return;
	}
	$index = 0;
	while ( have_rows( 'sections', $post_id ) ) {
		the_row();
		$index++;
		$layout = str_replace( '_', '-', get_row_layout() );
		get_template_part( 'template-parts/sections/' . $layout, null, [
			'index'   => $index,   // for unique ids when a layout repeats
			'post_id' => $post_id,
		] );
	}
}

Three more decisions shaped everything after it:

  • Shared content lives once. Services, process, pricing, FAQ and the testimonials heading sit in an ACF options page (Theme Settings → Shared Sections). Their page layouts carry no fields of their own; they only decide where the block appears. Edit the FAQ once and every page that shows it updates.
  • The content model is in version control. ACF field groups are saved as JSON inside the theme (acf-json), so the model sits in the repo where the agent can read it and I can diff it.
  • The design’s CSS and JS stayed as they were. Anything WordPress needed went into a separate small stylesheet. The prototype remained the reference, so any visual difference was a bug in a template, not an “improvement” to the CSS.

If you want the case for this structure on its own terms, I explain why I still use ACF for custom websites.

What the AI was genuinely good at on this WordPress theme

The big, well-specified translation work is where the agent earned its keep. Turning 13 pages of static markup into template parts, with fields, escaping and matching field group definitions, is tedious for a person and exactly the kind of volume an agent handles comfortably when the target is fixed.

It was also good at not breaking what already existed. The old portfolio post type and its 45 projects kept their data. Page scripts load only where their section is used, so the home hero’s script runs only on pages that contain that hero.

The underrated win was verification. The agent didn’t just write the theme; it wrote and ran the checks against it:

  • PHP lint on every file.
  • Every page rendered with WP_DEBUG on, to catch notices a browser would hide.
  • Headless-Chrome screenshots compared against the static design at desktop and phone widths.
  • Scripted interaction tests: the portfolio filter with “load more”, live search, copy buttons, redirects.

I didn’t take the agent’s summary of its own work on trust. I trusted the checks, because I could run them again. The full sequence is in my stage-by-stage WordPress workflow with AI.

What broke, and where it was hiding

The bugs worth writing about weren’t in the translation work. They were small, WordPress-specific, and mostly invisible from inside the theme folder.

Other plugins rewrote the page. Elementor Pro’s Theme Builder was still active and silently replaced the new theme’s header and footer. A caching plugin delayed all JavaScript until the first user interaction, which broke the loader and the animations. The theme’s code was right; the page was wrong. Both plugins had to be switched off before the site rendered as designed.

Two variable names were secretly globals. Template variables called $more and $id clashed with WordPress globals, because template files run in global scope and template parts declare $id global. The result was a fatal error on single posts until they were renamed.

Backslashes disappeared. A script saving post content through wp_update_post() lost the backslashes in code blocks, because that function strips them from its input: \n became n. A byte-for-byte comparison of every code block after saving caught it on 4 posts. The fix was wp_slash().

Notice the pattern: none of these was a failure to understand the task. Each came from a convention or an environment the agent had no view of. I pulled these and a few more apart in what AI still gets wrong when building real websites.

The decisions that stayed mine

An agent will happily pick an answer for every open question. The answer will be reasonable and generic. A handful of questions in this project needed answers that were specific to me.

  • No page builder. That was the premise, not something to optimize.
  • No contact form. I contract through Upwork, and taking work outside it breaks Upwork’s rules. So the form went, and /contact/ now redirects (301) to my Upwork link.
  • Image quality over a shortcut. When photos looked smeared, the agent’s first proposal was to force the browser to always load the largest image. I asked for standard responsive-image practice instead. The real cause was an optimization plugin set too aggressively; with that fixed, the standard approach was enough.
  • Fix the plugin, keep the plugin. A drag-and-drop post ordering plugin squeezed the admin list’s Title column to about 50px. It copied column widths from the first row’s <td> cells, but WordPress renders the title cell as a <th>, so every width landed one column off. The ordering itself was worth keeping, so a small admin script patched the widths instead of dropping the plugin.
  • Hand-picked testimonials. A long review made the carousel card look odd, so the carousel shows quotes I choose, not whatever comes first.

None of those is a coding problem. They’re about my business, my taste and how I want to use my own admin every day.

Building a WordPress theme with AI: what I’d do again, and what I’d do first

I’d repeat almost all of the method:

  1. Start from a finished design with a written handoff. The prototype plus README did more for quality than any prompt.
  2. Give the agent small, comparable units. One section, one template, one screenshot to check it against.
  3. Make checks part of the task. Lint, debug renders, screenshots and byte-for-byte comparisons are cheap to write and hard to argue with.
  4. Keep the design’s code untouched. A fixed reference makes every difference a visible bug.
  5. Put the content model in version control. Field groups as JSON turn a click-through admin setup into something you can review.

One thing I’d move to the very beginning: an inventory of every active plugin and what it does to the front end. The page builder’s header override, the delayed JavaScript and the over-compressed images all came from the environment, not the theme. None of them could be seen from the code. All of them were sitting in the plugin list.

The agent did the volume. The prototype and the checks made it correct. The decisions are what made it mine.

Share on X (opens in a new tab) Share on LinkedIn (opens in a new tab) Share on Facebook (opens in a new tab)

ChatGPT (opens in a new tab) Claude (opens in a new tab) Gemini (opens in a new tab) Perplexity (opens in a new tab) Grok (opens in a new tab)

More Insights

See More