Skip to content

How We Sped Up WooCommerce Pattern Development with Pattern Tools CLI

· Upd. By Jasper Frumau WooCommerce

The Problem: Block Validation Was Killing Productivity

Building the WooCommerce store vertical for the Elayne theme took 47 commits and around 8,300 lines. A large chunk of that effort wasn’t spent on design or content — it was spent fighting WordPress 6.6+ block validation errors. At one point we had 46 validation failures in a single pattern file.

The root causes were consistent. Inline gap values stripped from rendered HTML. core/cover attributes missing from block comments. The wrong CSS class for font sizes. Button attribute ordering that failed silently. None of these are obvious from the block editor UI — they only surface when you insert a pattern and check the browser console.

The round-trip cost was high: write a pattern, insert it in the editor, reload the page, spot the validation error in DevTools, re-edit the PHP file, reload again. That loop added days to what should have been hours of work.

The Solution: Scaffold First, Customize Second

The initial fix was to flip the order. Instead of writing block JSON from scratch and then debugging validation errors, you start with a scaffolded template that already has all the required attributes in place. Then you customize content only — text, colors, copy — without touching the block structure.

That was pt-cli v1.4.0 (originally elayne-cli). It worked. But the workflow evolved further: v1.7.0 added a --shell-only flag that makes the WP block editor itself the primary authoring tool, eliminating hand-written block JSON entirely. v1.8.0 added layout:create for structure-first scaffolding. v2.0.0 (2026-05-08) absorbed elayne-cli and became pt-cli. And most recently, a WP-CLI Gutenberg structural validator was added to catch the class of issues that regex cannot — structural problems that only WordPress’s own parser can detect. More on all of that below.

pt-cli: What’s Inside

pt-cli is a Composer-installed tool that ships with the Elayne theme at vendor/imagewize/pt-cli/. The current release (v2.0.0+) ships 23 templates, 8 layout skeletons, 12 snippets, a CSS stub system, and two scaffolding modes: layout:create for structure-first authoring and --shell-only for editor-first authoring. For full details see the pt-cli repository and the workflow documentation at docs/elayne/store/workflow-improvements.md (internal usage).

23 Templates

11 general-purpose templates cover the patterns all verticals share: hero covers, feature grids, two-column text/image layouts, stats bars, testimonial grids, CTA sections, and contact sections. 12 WooCommerce-specific templates cover every store page section built for the store vertical:

  • woo-hero — full-bleed hero with image and CTA buttons
  • woo-ticker — animated marquee with category labels
  • woo-shop-categories — flat CSS grid with featured bento card
  • woo-featured-products — product collection block with menu_order query
  • woo-our-story — two-column brand section with watermark CSS
  • woo-testimonials — testimonial grid with avatar circles
  • woo-newsletter — newsletter signup section
  • woo-shop-landing — pattern shell that loads all section sub-patterns
  • woo-cart — wrapper for the native woocommerce/cart block
  • woo-checkout — wrapper for the native woocommerce/checkout block
  • woo-filters-sidebar — sticky sidebar with price slider, colour-chip attribute, and checkbox filter lists
  • woo-product-grid — filter-aware product collection grid with sort toolbar and pagination

12 Snippets (the Validation Guards)

Six layout snippets cover reusable UI sub-patterns: eyebrow + heading + body, button pairs, 3-column grid wrappers, stat items, testimonial cards, and cover cards with floating badges.

Six WP 6.6+ validation guard snippets cover the specific blocks that most commonly fail validation:

  • valid-cover.txtwp:cover with all required attributes including dimRatio, backgroundColor, and root-level integer minHeight
  • valid-columns-wp66.txtwp:columns without inline gap or margin; isStackedOnMobile:false maps to the is-not-stacked-on-mobile class
  • responsive-grid-min-width.txtwp:group grid layout using minimumColumnWidth (preferred over wp:columns for 3+ columns)
  • valid-button-attr-order.txtwp:button with className and colors before style; font size via style.typography.fontSize, never root-level fontSize
  • valid-fullwidth-section.txtalignfull outer group with margin reset and constrained inner group
  • valid-heading-with-preset.txtwp:heading with a font size slug in JSON and the matching has-{slug}-font-size CSS class in HTML

The Pattern Compliance Checker: Now With Autofix

The compliance checker (now part of pt-cli check, see .claude/commands/pattern.md for usage) grew by 405 lines during the store vertical branch. Every hard-fought bug fix turned into a new automated check. The checker is now the most valuable artifact that branch produced.

Version 1.4.0 ships an --autofix flag. One command, run against the entire patterns directory, auto-fixes roughly 80% of validation issues without human review:

pt-cli check demo/web/app/themes/elayne/patterns/ --theme=elayne --autofix

What autofix handles automatically:

  • Inject inline gap: on flex group divs (was the dominant failure mode — ~30 patterns affected)
  • Strip inline gap: from constrained or default groups
  • Strip inline margin: from flex groups and columns
  • Reorder wp:button JSON keys so className comes before style
  • Inject the correct has-{slug}-font-size class on heading and paragraph blocks
  • Migrate root-level fontSize on buttons to style.typography.fontSize

What autofix intentionally skips (requires human judgment): hardcoded hex colors, untranslated text strings, hardcoded media IDs, and external URLs. Those need to be reviewed before fixing.

In one pass against the store vertical patterns, autofix raised the pass rate from 60 to 90 patterns out of 111. The remaining 12 failures all fell into the non-autofixable buckets — and were fixed manually in a single focused session.

The Faster Path: Editor-First Authoring with --shell-only

The full-template scaffold workflow works, but it still puts you in hand-written block JSON territory — which is exactly where WP 6.6+ validation errors live. Version 1.7.0 added a --shell-only flag that sidesteps this entirely.

The insight: the WP block editor never emits invalid block markup. If you build a section in the editor and copy the result, you get structurally valid JSON by definition. The compliance checker then acts as a safety net rather than a primary debugging tool.

The --shell-only command scaffolds only the PHP header and a companion CSS stub — no block JSON at all:

composer pattern:create -- \
  --template=woo-hero \
  --title="My Section" \
  --slug="elayne/my-section" \
  --category="elayne/woocommerce" \
  --shell-only \
  --with-style \
  --style-dir=assets/styles/block-styles/

See .claude/commands/pattern.md for the full pattern generation workflow.

This produces a PHP file with the correct header (slug, category, keywords, inserter flag) and a <!-- PASTE BLOCKS HERE: Build in WP editor → Copy all blocks → replace this comment --> placeholder, plus a companion CSS stub ready for customization.

The four-step editor-first workflow:

  1. Scaffold the shell and CSS stub with --shell-only --with-style
  2. Build the section in the WP block editor, referencing the design HTML files for layout intent
  3. Copy all blocks from the editor → paste over the PASTE BLOCKS HERE placeholder
  4. Add esc_html_e() wrappers on text strings, then run the compliance checker as a final safety net

The 23 existing full templates are now primarily reference examples — useful for understanding what valid block JSON looks like for a given section type, but no longer the starting point for new patterns.

The Even Faster Path: Layout-Based Scaffolding with layout:create

Version 1.8.0 added a second command: layout:create. Where --shell-only gives you an empty header and a paste marker, layout:create gives you a structurally valid skeleton for the section — two-column hero, three-column feature grid, sidebar+content, and so on. The block structure is already correct; what remains is content and CSS.

This turns out to be the sweet spot for most new patterns. The structure (column count, alignment, full-width margin reset) is the part that produces WP 6.6+ validation errors. Once a layout handles that, the remaining customization is almost entirely design-level: font choices, colors, spacing — exactly the kind of changes where an AI prompt against the design HTML is most effective.

composer layout:create -- \
  --title="My Hero Section" \
  --slug="elayne/my-hero-section" \
  --layout=hero-image-right \
  --category="elayne/woocommerce" \
  --output-dir=demo/web/app/themes/elayne/patterns/woocommerce/

See docs/elayne/store/workflow-improvements.md for details on layout-based scaffolding.

The 8 available layouts cover the shapes that appear in almost every vertical:

  • full-width — single column, constrained — simplest starting point
  • two-column — 50/50 columns block
  • three-column — grid with 3 equal groups
  • sidebar-left — narrow left (33%) + wide content (66%)
  • sidebar-right — wide content (66%) + narrow right (33%)
  • hero-image-left — cover image left + heading, text, CTA right
  • hero-image-right — heading, text, CTA left + cover image right
  • landing-page — hero + 3-column features + CTA, no header/footer wrapper

The four-step layout-based workflow:

  1. Scaffold with layout:create — structurally valid block markup, tokens replaced at generation time
  2. Replace placeholder text and images with design content
  3. Ask AI to adjust CSS (fonts, colors, spacing) based on design specs in the HTML source files
  4. Run the compliance checker as a final safety net

The Workflow in Practice

For a full new vertical (new industry, new design), the /vertical command (internal usage for now but may be published at a later stage) orchestrates everything in sequence: HTML prototype generation, design token extraction, style variation JSON, and all core patterns scaffolded and validated. That’s the right entry point when starting from zero.

For a single new pattern within an existing vertical, there are now three paths depending on the section’s shape and complexity:

Path A: Layout-based (recommended when the section fits a known shape)

  1. Run composer layout:create -- --layout=<name> --title="..." --slug="elayne/..." --category="elayne/woocommerce" --output-dir=patterns/woocommerce/
  2. Replace placeholder text and images with design content
  3. Adjust CSS for fonts, colors, and spacing using the design HTML as reference
  4. Run the compliance checker before committing

Path B: Editor-first (for complex or WooCommerce-specific sections)

  1. Run composer pattern:create -- --template=woo-hero --shell-only --with-style --style-dir=assets/styles/block-styles/ to scaffold the PHP header and CSS stub
  2. Build the section in the WP block editor using the design HTML as reference
  3. Copy all blocks from the editor → paste into the PHP shell
  4. Add esc_html_e() wrappers on text strings, run the compliance checker

Path C: Full template scaffold (for WooCommerce-specific block types)

  1. Run composer pattern:create -- --template=<name> --title="..." --slug="elayne/..." --category="elayne/woocommerce" --output-dir=patterns/woocommerce/ --with-style
  2. Read the validation guard snippets from vendor/imagewize/pt-cli/snippets/ relevant to the blocks involved
  3. Customize content only — text, image references, colors — without altering block structure
  4. Run pt-cli check --theme=elayne --autofix before committing

The Pre-Commit Hook

The compliance checker is also wired into a pre-commit Git hook at .git/hooks/pre-commit. It runs automatically on any staged pattern PHP file before the commit lands. This changes the feedback loop: errors surface at commit time (2 seconds) instead of at editor-insert time (30+ seconds per round-trip including reload and DevTools inspection).

#!/usr/bin/env bash
staged=$(git diff --cached --name-only --diff-filter=ACM | grep '^demo/web/app/themes/elayne/patterns/.*\.php$')
[ -z "$staged" ] && exit 0
pt-cli check $staged --theme=elayne

The Last Mile: Gutenberg Structural Validation

Even with scaffolded templates, editor-first authoring, and autofix in place, one class of issues remained hard to catch: structural block problems. Unbalanced delimiters, malformed JSON attributes, and improperly nested blocks all produce the same “Block contains unexpected or invalid content” error in the Site Editor — but regex cannot reliably detect them before that point.

The fix is to use WordPress’s own parser. The block editor’s recovery feature runs on two WordPress core functions: parse_blocks(), which parses block HTML into a structured array, and serialize_blocks(), which serializes that array back to canonical markup. The round-trip reveals structural issues: if the output differs from the input, WordPress normalized something — meaning the original had a problem.

This is now available as a WP-CLI command registered in demo/wp-cli.yml, so it’s available in any wp call from the demo directory. Pass 1 must run inside the Trellis VM because WordPress requires a database connection. Files sync automatically via Lima, so patterns edited on the host are immediately available:

# Dry run — show which files need normalization
cd ~/code/imagewize.com/trellis && trellis vm shell \
  --workdir /srv/www/demo.imagewize.com/current -- \
  wp pattern validate web/app/themes/elayne/patterns/ --diff

# Auto-fix structural issues (unbalanced delimiters, malformed JSON, bad nesting)
cd ~/code/imagewize.com/trellis && trellis vm shell \
  --workdir /srv/www/demo.imagewize.com/current -- \
  wp pattern validate web/app/themes/elayne/patterns/ --fix

After Pass 1, run Pass 2 on the host as usual:

pt-cli check demo/web/app/themes/elayne/patterns/ --theme=elayne --autofix

The two validators are genuinely complementary — neither can replace the other:

Issue typeGutenberg validatorCompliance checker
Unbalanced block delimiters✅ Catches and fixes❌ Cannot detect reliably
Malformed block JSON attributes✅ Catches and fixes❌ Cannot detect reliably
Improper block nesting✅ Catches and fixes❌ Partial at best
Whitespace/formatting divergence✅ Normalizes❌ Not applicable
Hardcoded font sizes / px values❌ Theme-agnostic✅ Flags and autofixes
Missing margin reset on full-width groups❌ Theme-agnostic✅ Flags
Spacer blocks, wrong layout type❌ Theme-agnostic✅ Flags
WooCommerce-specific rules❌ Theme-agnostic✅ Flags
Untranslated strings / hardcoded emails❌ Theme-agnostic✅ Flags

Running both passes in sequence — Pass 1 in the VM, Pass 2 on the host — gives the highest possible confidence before committing a pattern. Pass 1 handles structural correctness (the class of issues that produce “Block contains unexpected or invalid content” in the Site Editor). Pass 2 handles theme-specific compliance (the class of issues that cause WP.org submission rejection or design inconsistency).

What This Means for Future Verticals

The store vertical was built before these tools existed in their current form. The pattern set was built by hand, debugging validation errors one by one. The workflow improvements are a direct response to that experience — every pain point from the store branch was turned into a tool, a template, a snippet, or an automated check.

Future verticals start differently. For most section shapes, layout:create provides a structurally valid skeleton immediately — the two-column hero, the three-column feature grid, the sidebar layout are already solved. For sections with no matching layout, --shell-only keeps the WP block editor as the author. The two-pass validation system (Gutenberg structural validator + pt-cli check --theme=elayne --autofix) catches whatever remains before it reaches the Site Editor. The pre-commit hook ensures nothing lands in git without passing at least the compliance check. pt-cli handles all the boilerplate throughout. The effort is content and design — which is where it should have been all along.

Note: The WooCommerce store demo is currently in development and not yet publicly launched. Once live, it will be available at demo.imagewize.com/store/ running Elayne with the Store style variation (Cormorant Garamond headings, charcoal and cream palette) and the full homepage pattern suite including hero, marquee, categories grid, featured products, brand story, testimonials, and newsletter sections.

Need a WooCommerce Developer for Your Store?

We build and optimize WooCommerce stores for SMEs — from custom checkout flows and payment integrations to performance tuning and ongoing maintenance. Fixed-price quotes available.

  • Custom checkout and cart optimization
  • Payment gateway integration (Stripe, Mollie, PayPal)
  • WooCommerce performance and speed optimization
  • Ongoing store maintenance and support

Leave a Reply

Your email address will not be published.