Code of Maat
Code of Maat is my agency: software, AI and design. I built our starter theme and the GitHub Actions pipeline that deploys client sites.
- ROLE
- Founder
- PLATFORM
- Vite, Tailwind v4, ACF page builder, GitHub Actions
Challenge
My agency: software, AI and design. Websites, web and mobile apps, AI integrations. I built our starter theme (Vite, Tailwind v4, an ACF page builder) and the delivery pipeline: GitHub Actions with lint, type-check, build, atomic deploys with rollback and route checks, behind Cloudflare. Eight client sites built on it so far: clinics, a music school, artists and cultural organisations.
The client-site server has no Node. The build runs in CI, and only compiled output reaches the server. One extra parenthesis in a template reaches the server as a 500. An error in a page template had gone unnoticed while only the homepage was checked.
The agency’s own site was rebuilt, and its home page was checked against an approved design, section by section. The integration plan was written so that several engineers could work in parallel without talking to each other.
Approach
The agency theme renders every page as a stack of ACF flexible-content blocks, styled with Tailwind v4 utilities and built with Vite 8. A block is three files with the same name: field definition, renderer and an optional admin thumbnail. Renderers receive plain arrays and never call get_sub_field(). So the same renderer also works from a PHP array on the 404 page, the case single and the closing screen.
Each task in the integration plan owned listed files, and the shared contracts were fixed in the document. Section redesigns ran as rounds of standalone HTML artefacts, one per assigned direction, each screenshotted at 1440×900 and 390×844. Visual agents wrote the four service-header animations in parallel. A fix pass then worked from a verifier’s report on the four headers and fixed all high and medium findings.
Architecture
The client-site deploy I built runs in GitHub Actions on every push to main. It runs php -l on every PHP file, builds, and fails if manifest.json or the compiled CSS is missing. The theme is synced into a new directory. Two consecutive mv commands at the end switch it live, so the site never runs a half-copied theme. Then the job requests a list of routes and fails on any status other than 200, or on a PHP fatal-error message.
The agency theme has three entries: main on every page, home only where a visible hero_corridor or doors_3d row is, and styles. three.js r128 is vendored, never bundled, and printed only with the home entry. Inner pages request no three.js and create no WebGL context. Lazy hooks mount each module when its markup comes near the viewport, so pages without a block never download its code.
When three.js is missing or WebGL is unavailable, the hero adds html.no-gl, shows a poster, and the scene returns early. A hero-go fallback in the page head shows the hero copy after 2.6 s, even if the hero script never runs.
Decisions
- Services are child pages, not a custom post type: “There are only four.” Each needs its own block composition, and a custom post type would need its own templates and an archive replacement.
- Content lives in seed files: running one twice leaves the database byte-identical.
- three.js stays at r128, because the approved scene depends on r128 colour handling and on APIs that were removed later. The vendored file is served from the site’s own origin, not a CDN. That avoids an extra DNS and TLS handshake on the hero intro’s critical path.
- I asked for no WebGL on service pages and the 3D devices only on the homepage. The per-service 3D headers came out, and inner pages went back to no WebGL context and no MAAT3D code.
- The homepage section “What do you want to build” moved from the 3D devices scene to an abstract WebGL line, then to four canvas 2D columns. I approved the columns with two tweaks: bigger, redder diagrams and no swipe carousel on phones. With the columns, Home creates 2 contexts, one fewer than with the line.
- Only one deploy runs at a time, and a running one is never cancelled. Two runs would rewrite the same files, and a stop mid-sync would leave the theme incomplete.
- The server’s host key is pinned from a secret, so a man-in-the-middle cannot receive the deploy.
- The deploy stops if the ACF field-group JSON on the server changed since the last deploy. An admin edit then reaches git first instead of being deleted.
- Hashed JS and CSS files from the previous build that changed within the last week stay next to the new ones. Tabs opened before the deploy can still load their chunks.
# Keep the previous build's hashed files next to the new ones: tabs opened
# before this deploy can still load the chunks they ask for.
# (-p keeps their date, so after a week they drop out instead of piling up)
if [ -d "$THEME_DIR/assets/dist" ]; then find "$THEME_DIR/assets/dist" -maxdepth 1 -type f ( -name '*.js' -o -name '*.css' ) -mtime -7 -exec cp -p --update=none {} "$RELEASE/assets/dist/" ; || true; fi
mkdir -p "$(dirname "$BACKUP")"
rm -rf "$BACKUP"
if [ -d "$THEME_DIR" ]; then mv "$THEME_DIR" "$BACKUP"; fi
mv "$RELEASE" "$THEME_DIR"
Outcome
site.spec.js checks every page for a skip link, visible focus, alt on every image, one H1 and no sideways scroll. The integration pass recorded content-rules.mjs --all at 29 loads, 0 failing; php-lint.sh at 107 files, 0 errors; and site.spec.js at 59 passed, 1 skipped. Verification measured Home parity at 0–0.017 % for every DOM and canvas section.
The performance table passed 48 of 56 rows, with TTFB 125 ms and CSS 28.7 / main 10 / home 24.5 KB gzipped. The desktop TBT rows were recorded as FAIL on a machine with load average 8–15. In the same session, the approved file measured a desktop TBT of 131 ms and the built site 132 ms. Autoloading the site options cut MySQL Com_select per logged-out /about/ request to 43, from about 124.
A verifier’s cleanup script once deleted the real case studies, because name__in is ignored. The seeds restored them, and the rule became: delete only by recorded IDs.
> OUTPUT: Multilingual sites, schema.org graphs, llms.txt and WebGL heroes with fallbacks, plus a GDPR-grade patient portal with magic-link login and private uploads. The agency's own site is designed with multi-agent design pipelines.



