How I built and deployed a custom WordPress block theme using AI

photo of brown concrete building

A few weeks ago, Miriam Schwab — Head of WordPress at Elementor — published a detailed post documenting how she built her personal site (miriamschwab.me) entirely with Claude. It was an inspiring read. She walked through the whole journey: the design brief, the static prototype, the conversion to a custom WordPress theme, accessibility fixes, performance work — the lot. Matt Mullenweg even shared it widely as a great example of what AI can do with WordPress.

Miriam’s post inspired me to share my own. I’d recently been playing with a new WordPress Studio MCP server, and used it to create a new custom block theme for my personal site. Where Miriam built a classic PHP theme and used Claude for the heavy lifting, I wanted to see what was possible using the Claude with MCP (Model Context Protocol) tools and Claude’s built-in Skills, working entirely through the chat interface. No CLI, no code editor, no terminal. Just conversation.

I also wanted to build a block theme rather than a classic one — theme.json, HTML templates, and template parts. From experience I know that AI struggles with newer tech and while block themes aren’t brand new, they are new enough and rapidly evolving.

As I already had a site, I didn’t need to wire up content or anything else, I wanted to focus purely on how quickly I could get a block theme created from scratch.

The Starting Point: What I Wanted

My site at jonathanbossenger.com has needed a refresh for quite a while, but I usually just roll with whatever the latest default theme is (cobblers shoes and all that). The goal was to better represent what I do as a developer advocate — someone who writes, speaks, teaches, and builds in the WordPress ecosystem.

My requirements were straightforward:

  • A modern, distinctive homepage with a hero section, bio, and social links
  • Sections for what I do, recent blog posts, work experience, speaking and writing highlights
  • A newsletter call-to-action pointing to the WordPress.com Dev & Deploy newsletter I help manage
  • Light and dark mode support
  • Responsive across all screen sizes
  • Built as a WordPress block theme, not a classic theme

I didn’t have a specific design in mind. I wanted Claude to review my existing site content and propose something for me.

Step 1: Reviewing the Existing Site with WordPress.com MCP Tools

Rather than describing my site from memory, I let Claude examine it directly. This is where the WordPress.com MCP tools came in.

The prompt

I want to build a brand new theme for my jonathanbossenger.com site. The aim of the site is to promote myself as a developer advocate. Review the current site content and structure, and build a brand new modern theme.

What Claude did

Claude used several MCP tools to get a full picture of my site before writing a single line of code:

wpcom-mcp-site-settings — Retrieved the site’s general configuration: title, tagline, permalink structure, reading settings, timezone. This gave Claude the foundational context about the site.

wpcom-mcp-site-editor-contexttheme.active — Identified the currently active theme so Claude understood what it was replacing.

wpcom-mcp-site-editor-contexttheme.presets — Pulled the existing design tokens: colour palette, font families, spacing scale, and layout settings from the active theme. This gave Claude insight into the current design system.

wpcom-mcp-content-authoringpages.list — Listed all published pages to understand the site’s information architecture.

wpcom-mcp-content-authoringposts.list — Fetched recent blog posts to understand the content I was publishing: topics, categories, writing style.

wpcom-mcp-content-authoringcategories.list — Retrieved the category taxonomy to understand how content was organised.

web_fetch — Claude also fetched the live homepage and about page directly via their URLs to see the actual rendered content and structure, not just the data from the API.

This combination of structured data (from the WordPress.com MCP) and rendered content (from web_fetch) gave Claude a thorough understanding of what the site was, what content existed, and what the redesign needed to accommodate.

Here’s a video of the process I used on the WordPress.com YouTube channel:


Step 2: Designing the Static Prototype with the Frontend Design Skill

With the site audit complete, Claude moved into design mode. Under the hood, Claude loaded its frontend-design skill — a set of guidelines for Claude Code that push it toward distinctive, production-quality design and away from generic AI aesthetics.

What Claude built

Claude produced a complete single-file HTML prototype: all the HTML, CSS, and JavaScript in one file, ready to preview in a browser. The design decisions it made:

Typography:

  • DM Serif Display for headings — elegant and distinctive
  • Instrument Sans for body text — clean and modern
  • JetBrains Mono for code elements — a proper monospace font

Colour palette:

  • Background: #FAFAF7 (warm off-white, not sterile pure white)
  • Primary accent: #7B6AE0 (a distinctive purple)
  • Secondary accent: #E8A838 (warm amber/gold)
  • Dark mode: #0C0F14 (deep navy-black) with adjusted accent tones — #A594F9 lighter purple, yellow secondary

Design features:

  • Hero section with radial gradient background and a profile photo
  • “What I Do” section with a 3-column card grid, each card revealing a coloured top-border on hover
  • Latest Posts section using a 3-column layout
  • Work experience timeline with a gradient vertical line and dot indicators
  • Speaking and writing highlights with tag pills
  • Newsletter CTA section with a gradient background
  • Sticky header with backdrop blur
  • Fully responsive with breakpoints at 900px, 700px, and 480px
  • Light/dark mode toggle with smooth colour transitions, saving the preference to localStorage

Iteration: adding dark mode

My first prompt got me the light theme. I then asked Claude to add dark mode support with a toggle:

This is great, but it’s only a light theme. Can you also create a dark mode, with a toggle?

Claude updated the prototype with CSS custom properties scoped to [data-theme="light"] and [data-theme="dark"], a sun/moon toggle in the header, and smooth 0.4s transitions on all colour changes.

The output

Claude saved the completed prototype as an HTML artifact file that I could download and preview directly in a browser. This was the design I’d be converting into a block theme.

Step 3: Converting to a WordPress Block Theme with Studio MCP Tools

This is where my approach diverged most from Miriam’s. She used Claude Code on the command line to build a classic PHP theme. I used Claude desktop’s chat interface with a different set of MCP tools: the WordPress Developer (Studio) MCP tools.

These server was built by a colleague at Automttic and allow’s Claude to interact directly with a local WordPress site running in WordPress Studio — listing directories, reading files, and writing files, all from within the chat.

First I needed to pull the site down to my local computer, using the WordPress Studio sync feature. Then, I started a new conversation with Claude:

The prompt

Can you take this theme design, and create a WordPress block theme inside the local Jonathan Bossenger site in Studio

I uploaded the HTML prototype file from the previous chat, and Claude got to work.

What Claude did

Claude used the Studio MCP tools to explore and then build the theme:

studio_site_list — Listed all local WordPress Studio sites to find the correct one.

studio_fs_list_dir — Explored the wp-content/themes directory to see what themes existed and check for any existing jonathanbossenger theme directory.

studio_fs_read_file — Read the existing theme files (style.css, theme.json, templates, parts) to understand the current state before making changes.

Then Claude created every file for the block theme, one by one, using studio_fs_write_file.

The theme structure

theme.json (Version 3 schema) — The heart of a block theme. Claude translated the entire design system from the HTML prototype into WordPress’s configuration format: colour palette with all design tokens (background, foreground, accent purple, secondary gold, card backgrounds, muted text colours) registered as theme colours; three font families (DM Serif Display, Instrument Sans, JetBrains Mono) with fluid font sizes using clamp(); a custom spacing scale; layout constraints; and global block styles for headings, paragraphs, buttons, and links.

style.css — All the custom CSS that goes beyond what theme.json can express: the hero section’s radial gradients, card hover effects with the top-border reveal animation, the timeline’s gradient line and dot indicators, tag pill styles, newsletter CTA gradient, sticky header with backdrop-filter: blur(), and all the responsive breakpoints.

functions.php — Google Fonts enqueue (as a fallback for browsers that don’t fully support theme.json font face declarations), plus block pattern category registration.

Templates

Claude created six HTML templates, each built with WordPress block markup:

  • home.html — The full homepage matching the HTML prototype: hero with badge, heading, description, social links, and photo; “What I Do” 3-column card grid; latest posts via a Query Loop block in a 3-column grid; experience timeline with four positions; speaking and writing highlights; newsletter CTA
  • single.html — Blog post template with date, title, featured image, content, categories/tags, post navigation, and comments
  • page.html — Clean page layout
  • index.html — Blog listing fallback
  • archive.html — Archive with query title and post list
  • 404.html — Centred 404 with a home button

Template parts

  • header.html — Sticky navigation with site title and WordPress navigation block
  • footer.html — Copyright notice and social links

Activating the theme

Once all files were written, Claude used the Studio MCP tools to set the homepage to use the home template and confirmed the theme was active on the local site.

Step 4: The Human in the Loop

Claude’s generated theme was a solid starting point, but loading the local site up in a browser immediately revealed issues that needed a human eye and hands-on fixes. This is the part Miriam described well in her post too: AI gets you 80% of the way remarkably fast, but the last 20% still needs you in the loop.

I switched to Claude Code for this phase, working directly in the theme’s Git repository. Over the course of 9 commits in my spare time over two days, here’s what needed fixing:

The admin bar offset problem

The very first thing I noticed: when logged into WordPress, the admin bar overlapped the sticky header. Claude’s generated CSS positioned the header with position: fixed; top: 0;, but didn’t account for WordPress’s 32px admin bar (or the 46px mobile variant) pushing everything down.

The fix was straightforward CSS — adding .admin-bar .site-header { top: 32px; } with a media query for the mobile admin bar height — but it’s the kind of thing that only shows up when you actually log in and browse the site as an admin. Claude never sees the admin bar.

Converting raw HTML to proper block markup

This was the biggest piece of work. Claude’s initial theme wrapped most of the homepage patterns in <!-- wp:html --> blocks — essentially raw HTML inside the block editor. It worked visually, but it meant the Site Editor couldn’t interact with the content. You couldn’t click on a heading to edit it, you couldn’t use the block toolbar on paragraphs, and WordPress couldn’t parse the structure for things like search or accessibility.

With Claude Code’s help, I converted every pattern from raw HTML to proper WordPress block markup. For example, the “What I Do” section went from a single <!-- wp:html --> blob containing <section>, <div>, and <h3> tags, to properly nested wp:group, wp:columns, wp:column, wp:heading, and wp:paragraph blocks. This was repeated across all six patterns: hero, what-i-do, latest-writing, experience-timeline, speaking-writing, and newsletter-cta.

The header template part got the same treatment — the raw HTML navigation was replaced with wp:site-title and wp:navigation blocks, which meant the menu could be managed through WordPress’s standard Navigation editor rather than being hardcoded in the template.

Alongside this, a large amount of CSS was removed. The original style.css included rules for body fonts, colours, links, selection styling, and a decorative grain overlay — all things that theme.json was already handling. Stripping these duplicates out reduced the CSS by roughly a third and eliminated conflicts where CSS and theme.json were both trying to control the same properties.

Fixing the header layout

Even after converting the header to block markup, it didn’t align correctly. The site title and navigation were sitting at full-width instead of respecting the site’s content width. The fix required wrapping the header contents in a constrained layout group with an inner wide-aligned flex group — the standard WordPress pattern for full-width backgrounds with centred content. It took a couple of iterations to get the nesting right.

Updating social links and the hero image

The hero section needed real content. I added missing social platform links (Twitch, Twitter/X, Mastodon) and reordered them. I also swapped the placeholder hero photo for my actual profile image, converting it from a raw <img> tag to a proper wp:image block so it could be managed through the block editor.

Enhancing the single post template

The original single post template was minimal — title, date, content, navigation. I expanded it significantly to add ActivityPub support with Fediverse reactions, a proper comments section with avatars and threaded replies, and hideFromFeed metadata on structural groups to keep them out of RSS feeds. This brought the template in line with what a real blog post page needs.

Finishing touches

The final commits were small but important polish:

  • Theme screenshot — Added a screenshot.jpg so the theme shows a preview in the Appearance admin screen
  • Footer attribution — Added a “Theme source on GitHub” link in the footer, pointing to the public repository
  • View Transitions — Added the CSS @view-transition { navigation: auto; } rule, which enables smooth cross-page transitions in supporting browsers using the View Transitions API. I’d added this to my child theme after a talk at WordCamp Cape Town 2024. That single line of CSS creates a noticeably smoother browsing experience.

Once it was all wrapped up, I pushed the changes to the live site (via Studio sync, of course) and my site’s new theme was live.

What I Learned

Block themes work well with AI

The structure of a WordPress block theme — theme.json for the design system, HTML templates with block markup, CSS for the rest — is a natural fit for AI-assisted development. The format is declarative and well-documented, which means Claude can generate clean, standards-compliant output without the complexity of PHP template hierarchies.

MCP tools change the workflow entirely

The biggest difference between my experience and Miriam’s was the tooling. As I already had a site, the WordPress.com MCP tools let Claude audit my live site directly, and the Studio MCP tools let it write the theme files into my local development environment — no copy-pasting zip files, no manual file creation.

This matters because it keeps the entire context in one place. Claude could see the site structure, understand the content, and deploy the theme without me acting as a middleman ferrying information between systems.

The two-conversation pattern works well

Splitting the work into two conversations — one for design, one for deployment — turned out to be a natural fit. The first conversation was exploratory and iterative (reviewing the site, trying design ideas, adding dark mode). The second was more mechanical (take this design, convert it, deploy it). Different modes of work, cleanly separated.

Start with real content

I found that having Claude review the actual site content before designing was crucial. The design wasn’t generic — it was shaped around my real pages, posts, categories, and site structure. The “What I Do” cards, the experience timeline, the speaking highlights — all of those came from Claude understanding what content already existed on the site.

It’s fast, but not instant

The entire process — from first prompt to deployed theme — took two conversations and a handful of prompts. But each conversation involved Claude making dozens of tool calls, reading files, fetching web pages, and generating thousands of lines of code. The result feels like it would have taken days of manual development work, compressed into a couple of focused sessions.

It still requires a human

The AI-generated theme was genuinely impressive as a starting point. The design system, colour palette, typography, and overall layout were all spot-on. But the generated code treated WordPress more like a static HTML host than a dynamic content management system — wrapping everything in raw HTML blocks rather than using the block editor’s native components.

The human-in-the-loop work was primarily about converting that static output into something that WordPress could actually work with: editable blocks, manageable navigation, proper template hierarchy. Once that conversion was done, the Site Editor became the tool for ongoing refinement rather than a code editor.

It’s worth noting that Claude Code was instrumental in this phase too — I wasn’t writing every block markup tag by hand. But the direction, the testing, and the judgment calls about what “working properly” meant — that was all human.

What’s Next

Like Miriam said in her post: working with AI is amazing, and it allows us to aim for higher quality in ways we wouldn’t have bothered with before. But it still needs clear direction, and the results improve with each iteration.

For example, while writing this post I discovered there’s a bug in the editor I need to fix, where if I highlight text it doesn’t appear highlighted.

However, the combination of Claude, MCP tools, and WordPress’s block theme architecture made this particular build feel remarkably smooth. I’m excited to see how these tools continue to evolve.


This post was also drafted with Claude’s help, in a third conversation inside the same Claude Project that contained the two build conversations, and then manually edited and tweaked by me, because of course it was. ;-D


Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.