How do I convert my WordPress site to a static site with AI?
Convert a WordPress site to a hand-coded static site in nine phases: import the WP export into a local install, audit the rendered HTML in fonts → colors → images → layout order, copy original assets only, hand-code semantic HTML/CSS/JS replacing each plugin with a static equivalent, layer in schema and llms.txt, test locally, push to GitHub, deploy to Vercel or Nginx, then cut over DNS. The full spec is below — copy it whole and paste it into Claude as your session context.
The Spec
What this is
A nine-phase workflow for converting any WordPress site — Avada, Elementor, Divi, custom theme — into hand-coded static HTML/CSS/JS, deployed on Vercel or Nginx. Designed to be pasted into an AI coding agent (Claude, Cursor, etc.) as session context. The agent then executes the phases against your source site.
Prerequisites
- WordPress site accessible — either live or via a
.wpressexport - Local by Flywheel installed
- GitHub CLI (
gh) authenticated - Vercel CLI (
vercel) authenticated, or SSH access to a static-hosting droplet - An AI coding agent with file-system tools (Claude Code, Cursor, etc.)
Phase 1 — Source Extraction
If the source is on a hosted backup
- Get the
.wpressexport from All-in-One WP Migration (or equivalent backup) - Create a new Local site, name it
{sitename}-old - Bump upload limits in
conf/php/php.ini.hbs:upload_max_filesize = 4096Mandpost_max_size = 4096M - Restart the site in Local
- Import the
.wpressfile via WP admin → All-in-One WP Migration
If the source is already in Local or live
Skip the import. Work from the existing install.
Phase 2 — Content & Design Audit
Lock styles before building
Audit in this exact order: fonts → colors → images → layout. Lock each into a markdown doc before moving to the next. Fonts are the highest-risk to get wrong, so they get locked first. Layout audit comes last because it depends on the locked styles.
Save audit docs to {project}/_audit/:
01-FONTS.md— every font family, weight, size, line-height, letter-spacing per element (h1–h6, body, nav, buttons). Include a CSS variable mapping table.02-COLORS.md— full palette with hex values + where each color is used. Edge-case colors get noted.03-IMAGES.md— full inventory of copied originals, mapped to which pages they appear on.04-LAYOUT.md— per-page section order, container widths, breakpoints, schema for header/footer that repeats._audit/source/— raw rendered HTML pulled viacurlfrom the OLD site, kept as reference during build.
View the rendered site
Curl the rendered HTML for every page (do not mirror with wget) and save to _audit/source/:
for page in $(curl -s http://sitename-old.local/page-sitemap.xml | grep -oE '<loc>[^<]+</loc>' | sed 's|<[^>]*>||g'); do
slug=$(echo "$page" | sed 's|http://sitename-old.local/||; s|/$||; s|/|_|g')
curl -s "$page" > "_audit/source/${slug:-home}.html"
done
Extract from rendered HTML (View Source, not DevTools inspect)
- Sitemap — pull from
/page-sitemap.xml(Yoast / sitemap plugin) - Fonts — for Avada/Fusion sites, typography tokens live in
wp-content/uploads/fusion-styles/{hash}.min.css. Grep for--awb-typography{1-4}-*and--h{1-6}_typography-*variables. For other themes, check Google Fonts<link>and@font-face. - Colors — Avada palette is
--awb-color1through--awb-color8in fusion-styles CSS. For non-Avada themes, pull CSS custom properties + inlinestyle="..."attributes. - Nav structure — exact menu items and link targets
- Content per page — headings, body text, CTAs, form fields. Extract verbatim — line-for-line is the standard.
- Images — note which images are used on which pages. Full-size originals are in
wp-content/uploads/(skip the-{width}x{height}thumbnails). - Business info — address, phone, email, social URLs, GA tracking ID, Google Ads ID
- Third-party integrations — forms (Gravity Forms, Contact Form 7, Avada Fusion Forms), analytics, cookie consent, review badges
- SEO — title tags, meta descriptions, OG images, canonical URLs
Do NOT attempt
- Wget/mirror approach — generates too much WP junk (query string pages, feed XMLs, admin bar markup, plugin assets). Waste of time to clean up.
- Reverse-engineering page builder PHP (Avada Fusion Builder, Elementor, Divi) — the rendered HTML is deeply nested and class-heavy. Not worth salvaging.
- Server-side scraping of dynamically-injected content (Termageddon legal pages, etc.) — those scripts run in the browser. The text won't be in
curloutput. Build stub pages and ask for content.
Phase 3 — Asset Collection
- Create the project directory:
{site-name}/ - Create subdirectories:
images/,css/,js/ - Copy full-size images from the WP install:
wp-content/uploads/{year}/{month}/{filename}.jpg (originals only) wp-content/uploads/revslider/ (slider media if applicable) - Copy logos, favicons, badge images
- Copy any video files (
.mp4) - Do NOT copy: plugin assets, theme assets, WP core files, CSS/JS from plugins
Phase 4 — Fresh Build
Structure
{site-name}/
index.html
css/style.css
js/main.js
images/
{page-name}/index.html (one per subpage)
Standards
- No inline styles — all styling in
css/style.css - No text characters for UI elements — use inline SVGs for arrows, close buttons, icons
- SVG buttons:
padding: 0; display: grid; place-items: center;on the button,display: block;on the SVG - CSS custom properties for colors, fonts, max-widths
- Google Fonts via
<link>withpreconnect - Semantic HTML —
<header>,<nav>,<main>,<section>,<footer>,<address> - Responsive — mobile-first or desktop-first with breakpoints at 1024px, 768px, 480px
- No frameworks — pure HTML/CSS/JS
- Lazy load images below the fold (
loading="lazy") - Animations — IntersectionObserver for scroll reveals, CSS keyframes for ambient effects
Rebuild features, don't replicate plugins
| WP Plugin | Static Replacement |
|---|---|
| Revolution Slider | CSS/JS image slider with Ken Burns |
| Strong Testimonials | CSS/JS fade rotator |
| Gravity Forms / Avada Fusion Forms | HTML <form> → POST to /api/send (Vercel function + Resend) |
| Justified Image Grid | CSS grid/masonry gallery |
| Yoast SEO | Manual <title>, <meta>, OG tags, JSON-LD |
| Termageddon (cookie consent + legal injection) | Drop the script. Build static legal page stubs and ask for replacement copy. |
Form pipeline — Resend + anti-spam
Files needed:
package.json—{ "private": true, "engines": { "node": "20.x" }, "dependencies": { "resend": "^4.0.0" } }vercel.json— CORS headers restricting/api/(.*)to the production originapi/send.js— handler with origin/honeypot/timestamp/email-format/gibberish checks, thenresend.emails.send({...})js/forms.js— injects honeypot + base64 timestamp on page load, validates required fields, POSTs JSON, handles success/error UI
Required env var: RESEND_API_KEY (set via vercel env add RESEND_API_KEY production, paste value via stdin to keep it out of terminal history).
Preserve from original
- Google Analytics tracking code (gtag.js +
gtag('config', 'G-XXXXXXX')) - Google Ads conversion code (gtag config
AW-XXXXXX+ per-element click handlers) - All testimonial content (verbatim)
- All business info (address, phone, social)
- Badge links (WeddingWire, The Knot, etc.)
- Legal pages content — except when source is Termageddon (script-injected, not in HTML)
Phase 4b — GEO / Search Visibility
Apply between build and deploy. Bare minimum for a small local-business site:
Schema (JSON-LD)
Add <script type="application/ld+json"> with a @graph array to every page's <head>:
| Entity | Where | Notes |
|---|---|---|
| Organization + LocalBusiness + (vertical type) | Every page | Unified via @id="https://{domain}/#organization" |
| Person (founder) | Home + About | Nested under Organization via @id reference. Include credentials/jobTitle. |
| WebSite | Home | Identifies the site itself. |
| WebPage | Every page | Per-page entity referencing Organization via @id. |
| Service | Each service page | provider is @id reference, not a flat copy. |
| FAQPage | Pages with Q&A content | Reuse existing on-page text — do NOT invent answers. |
| BreadcrumbList | Every non-home page | Position-numbered list from Home → current page. |
Sitewide files (project root)
llms.txt— short AI-assistant index (~30 lines): identity, services, beliefs, service area, founder, key URLs, contactllms-full.txt— detailed profilesitemap.xml— every public page with priority + changefreqrobots.txt— allow all, disallow/api/and/_audit/, link to sitemap
Per-page metadata
<link rel="canonical" href="https://domain/path/" />- OG tags (
og:type,og:url,og:title,og:description,og:image) <meta name="description">- "Last updated: {date}" in footer (AEO freshness signal)
Content rules
- Image alt text — entity + service + location, e.g. "Roof repair services in Austin, TX by Acme Roofing"
- Inline cross-links — visibly distinguishable from body text (color OR underline)
- Related-services block at the bottom of each capability page
- Anchor phrases — pick 3–5 phrases that repeat naturally across pages
Phase 5 — Local Testing
- Serve locally:
python -m http.server 8080from the project root - Check every page at
http://localhost:8080 - Test responsive at 1024px, 768px, 480px
- Verify all images load
- Verify all internal links work
- Test slider/rotator auto-advance and manual controls
- Test mobile nav toggle
Phase 6 — Git & GitHub
git initin the project directorygit add -A && git commit -m "Initial build: {Site Name} static site"- Create private repo:
gh repo create {org}/client--{site-name} --private --source=. --push - Naming convention:
client--{site-name}(double dash separator)
Phase 7 — Deploy
Option A — Vercel (preferred for sites with forms)
npm installlocally to generatepackage-lock.json- Link the project:
vercel link --yes --scope={team} --project={project-name} - Set env vars via stdin:
printf "{value}" | vercel env add RESEND_API_KEY production - Deploy:
vercel deploy --prod --yes - Verify:
curl -s -o /dev/null -w "%{http_code}" https://{project-name}.vercel.app/ - Domain attach (after DNS):
vercel domains add {domain}
Option B — Nginx static (legacy / pure static)
- Drop project files into
/srv/{site-name}/on the server - Add Nginx vhost
- Run
certbotfor SSL - Deploy via SCP/rsync
- Verify health check passes
Phase 8 — DNS Cutover
- Update A record to new host IP at the domain registrar
- If TTL is high, lower to 300s 24 hours before cutover
- Wait for propagation
- Verify site loads on new IP with SSL
- Monitor for 24 hours
Phase 8b — Form Pipeline Test
If the project has a form, do a real end-to-end test before considering deploy complete:
- Temporarily change the
to:address inapi/send.jsto your inbox - Redeploy:
vercel --prod --yes - POST a test submission with a valid base64 timestamp:
Response should beTS=$(echo -n $(($(date +%s) - 5)) | base64) curl -s -X POST https://{domain}/api/send -H "Content-Type: application/json" -H "Origin: https://{domain}" -H "Referer: https://{domain}/" -d '{"name":"...","email":"...","leit_cf_ts":"'$TS'", ...}'{"ok":true}. - Confirm receipt
- Revert
to:to the client's email and redeploy
Phase 9 — Cleanup
- Confirm site is live and stable
- Remove site from old host
- Delete Local WP install (
{sitename}-old) if no longer needed - Update infrastructure registry
Conditions
When this works
- You have access to the WP install or a
.wpressexport - The site is content + media + forms + analytics — no logged-in user features
- Target hosting is Vercel (forms) or Nginx static (pure static)
- Page-builder source is fine — Avada, Elementor, Divi, custom theme all convert the same way
When it doesn't
- Active WooCommerce checkout with stock/inventory — keep WordPress, don't convert
- User accounts, login, member-only content — keep WordPress
- Server-side search or filtering you don't want to rebuild client-side
- Content injected by external scripts (Termageddon-style) — text won't be in
curloutput; you must source the copy separately
Outcome
The result is a portable site you can move to any host on earth. No proprietary lock-in, no plugin update treadmill, no DOM bloat from page builders. The same content loads in under a second instead of three to five.
Specs provided as-is. chadworks isn't responsible for how you use these prompts or any effects they may have on your code, content, infrastructure, or business. Review and test before applying.