Work and Experience
WordPress Engineering Client under NDA

Page-Builder Site Rebuilt on Bricks

A live multi-location site moved off Elementor and JetEngine, with its content model in version-controlled code

111 → 23 KB
stylesheet on the wire
Client
Multi-location US company (anonymized)
Engagement
Fixed-scope rebuild in milestones, plus live-site support
Stack
WordPressBricks BuilderElementorJetEngineACFWP-CLIPHPPythonPlaywrightRank Math
01

Context

A production WordPress site built on Elementor, with JetEngine holding its structured content: locations, events and brand assets, each with custom fields, taxonomies and public archive URLs. The content model existed only as settings rows in the plugin's database tables, invisible to version control, impossible to review, diff or roll back. The site needed rebuilding on Bricks, on a clean install, without breaking a single public URL.

The rebuild ran in milestones from mockups to a launch-ready build, while the existing site stayed live and needed its own production work along the way, including a publishing outage that turned out to have nothing to do with the obvious suspects. Every deliverable was reviewed closely by the client, so every claim in every report had to be measured, not asserted.

02

The Challenge

  • The content model lived in a plugin's settings: moving builders meant recreating it exactly, slug for slug, because every record URL depended on it.
  • Records had to move into a clean install where post and attachment IDs mean nothing, and the source plugin did not expose its field values over the REST API.
  • Bricks template exports carry identity metadata, including a Gravatar hash that fingerprints the exporting user's email, and could not be committed as they were.
  • Mid-project, the live site began returning HTTP 500 on every page publish, and the first, obvious diagnosis was wrong.
03

Scope and Approach

  1. 01

    Code-owned data layer

    Content types and taxonomies re-registered in a version-controlled MU-plugin, matching the live slugs and rewrite rules exactly, and guarded to yield rather than override if another plugin still claimed a slug. Custom fields moved to ACF field groups stored as JSON in the child theme, so a field change arrives as a reviewable diff.

  2. 02

    Evidence-backed migration

    A WP-CLI importer reading standard WXR exports, because the source meta was not available over REST. Records matched by slug, never by ID; media imported first and every image value resolved through a source-URL map, with unresolvable values logged as failures instead of guessed. Dry run first, and a per-record, per-field evidence export.

  3. 03

    Bricks build on one stylesheet

    Templates built in Bricks, exported, sanitized of identity metadata and committed. Every visual value lives in one documented child-theme stylesheet that sits outside Bricks' CSS cascade layer, so it wins without a single !important, while the builder's global classes carry structure only.

  4. 04

    Performance, search and structured data

    Oversized images re-encoded and capped, unused CSS removed, the documented stylesheet served minified only while it is current, the emoji script turned off. LocalBusiness structured data generated for every location page from its own record through the SEO plugin's JSON-LD filter, with empty values left out rather than invented.

  5. 05

    Live-site diagnostics and QA

    On the existing site: the publishing outage traced to its real mechanism and mitigated with a one-filter change; read-only regression sweeps at desktop and mobile widths; a data-completeness audit of every location record; and a regression restored with the smallest change that could be proven before it was applied.

04

Architecture flow

Legacy builder site
Elementor + JetEngine
WXR + media export
read-only source
Slug-matched import
media map, per-field evidence
Code-owned data layer
MU-plugin + ACF JSON
Bricks templates
sanitized, versioned
QA + performance
measured, not estimated
Launch-ready build
runbook + editor guide
05

Technical Deep Dive

The HTTP 500 that was not PHP memory

Saving a page wrote a revision row successfully, but the read immediately after returned nothing, so WordPress passed null into save_post and Elementor's typed handler threw a TypeError. Memory, timeouts, plugins, theme, firewall, object cache and table health were each excluded with evidence. Disabling revisions for pages only restored publishing in one reversible step, and the read-after-write gap below WordPress went to the host as a clearly labelled hypothesis with three precise questions, never presented as proven.

Why the stylesheet wins without !important

Bricks writes its own CSS inside a cascade layer, and a rule outside any layer beats every rule inside one, whatever the selectors. So the site's single stylesheet sits unlayered, the builder's global classes keep empty settings, and there is exactly one place to change any visual value. The minified copy is served only while it is newer than the source, so a forgotten rebuild costs speed, never correctness.

Measurements that nearly produced false findings

A full-page screenshot wider than the phone viewport looked like sideways scrolling; testing the actual scroll position proved the content was clipped instead, a different defect. innerText silently skipped collapsed content that textContent found. A 404 font file looked like a font fallback until a second rule for the same family turned up, loading fine. Each was caught by measuring the behaviour instead of a proxy for it.

Sanitize before commit

Template exports carry the exporter's name, profile URL, a Gravatar hash of their email and the producing site's hostname. A sanitizer strips exactly those fields, keeps every structural ID and media URL, and lists the Code elements whose signatures must be regenerated after import, Bricks signs them over the very user ID that gets removed.

06

Engineering Decisions

Slugs are identity, IDs are not

Every URL and every migration match depends on slugs, so they were preserved exactly and used as the only record key. Nothing downstream trusts a source ID.

Symptom and hypothesis are separate claims

Reports state what the evidence shows and what is suspected as two different things, and merge them only once the next layer down confirms. Adopted as a rule after an early diagnosis had to be corrected in writing.

Prove the smallest fix before applying it

When a script stopped formatting part of a page, its own logic was first replayed against the live page. It reproduced the expected output exactly, proving the code was sound and the problem was when it ran. The fix was five lines that run it again later, nothing more.

07

Results

  • The whole content model moved from plugin settings into reviewed, version-controlled code, with every public URL preserved.
  • Every migrated record accounted for field by field, nothing guessed, every gap logged.
  • Heaviest page cut by about 60 percent, page images by two thirds, and the stylesheet from 111 KB to 23 KB on the wire, all measured on the rendered pages, not estimated.
  • A live-site publishing outage traced to its real mechanism and mitigated with a single reversible filter, with the unproven part handed to the host as a hypothesis.

Have a system that needs
this kind of work?

Tell me what is breaking or what you need built. I will reply with how I would approach it and what I would check first.

Chat on WhatsApp