Skip to content

Building a WooCommerce Store on a Block Theme: Real Patterns, Real Decisions

By Jasper Frumau WooCommerce

This is a building-in-public post. We’re mid-way through adding a WooCommerce store vertical to the Elayne block theme — a generic, reusable store layout that any product-based business can use. Here’s what that means in plain English for store owners, and a genuine technical deep-dive for developers and agencies.

For Store Owners: What This Store Vertical Gives You

The Elayne store vertical is a generic, design-quality WooCommerce layout — not tied to any specific product category. The demo uses a leather accessories store (portfolios, professional goods with colour and style variants) because it exercises the full feature set: multiple product images, colour swatches, add-on options, trust badges. Swap the products and it works equally well for clothing, homeware, cosmetics, gifts, or anything else you sell.

The design targets the quality level of premium e-commerce stores: vertical thumbnail gallery, colour swatch selectors, add-on options, trust badges, sticky mobile ATC bar. Features that are expected on a well-built store, on WordPress, without platform lock-in.

What You Get Out of the Box

  • A product page that converts: Large gallery with thumbnail navigation, colour swatches, style options, prominent Add to Cart — all above the fold on desktop
  • Mobile-first sticky bar: On phones, an ATC bar sticks to the bottom of the screen so customers never have to scroll back up to buy
  • Trust signals built in: Free shipping threshold, return policy, and warranty display automatically on every product page
  • No platform fees: You own the store, the data, and the code — WooCommerce on WordPress, no transaction cut, no monthly platform subscription
  • Colour and variant selectors: Active state rings, JS-driven label updates, sold-out states — the patterns that make variant selection feel premium
  • Works across verticals: The same store patterns can be restyled for existing Elayne verticals — nail salon, spa, legal — wherever a shop component fits the client’s site

Current Status (May 2026)

We’re about 80% through the build. The product page patterns are complete. The store homepage, category grid, and shop landing are done. The remaining work is wiring the single-product template together and responsive/cross-browser testing. We’re documenting everything openly — including the decisions that didn’t work first time.

Pricing: €65/hour. A WooCommerce store setup with the Elayne theme typically runs 15–25 hours depending on product count and customisation depth.

For Developers: The Real Technical Decisions

The Elayne theme is a full-site editing block theme — no page builder, no Classic Editor, no PHP template files beyond what’s necessary. Adding WooCommerce to a block theme in a way that doesn’t break the design system requires a set of deliberate architectural choices. Here’s what we made and why.

1. Isolated on a Multisite Subsite

The store runs on demo.imagewize.test/store/ — a directory-based multisite subsite, not the main demo site. This prevents WooCommerce from polluting the main demo’s front-end (cart fragments, WC body classes, extra CSS) and lets us keep the Elayne theme activated on both sites independently.

Consequence: every WP-CLI command targeting the store needs --url=demo.imagewize.test/store/. Skip it and you’re querying the wrong site. Running wp plugin list --path=web/wp without the URL flag shows network-level status — WooCommerce might show as inactive even though it’s active on the subsite. Lesson learned the hard way.

# Always use --url for the store subsite
trellis vm shell --workdir /srv/www/demo.imagewize.com/current -- \
  wp plugin list --path=web/wp --url=demo.imagewize.test/store/

# Disable WooCommerce's default "coming soon" mode on new stores
trellis vm shell --workdir /srv/www/demo.imagewize.com/current -- \
  wp option update woocommerce_coming_soon no \
  --path=web/wp --url=demo.imagewize.test/store/

2. Three-Tier WooCommerce Pattern Strategy

WooCommerce ships its own block patterns in wp-content/plugins/woocommerce/patterns/. The temptation is to ignore them and build everything custom. Don’t. We use a three-tier decision flow:

  • Tier 1 — Use as-is: WooCommerce plugin patterns (product-collection 2/3/4 columns, filters) work fine and are maintained by Automattic. Use them directly. They’re exempt from our theme’s compliance rules.
  • Tier 2 — CSS override: When a WC block’s output is structurally correct but doesn’t match the design, override via assets/styles/woocommerce.css or block-style CSS files. No pattern duplication needed.
  • Tier 3 — Custom pattern: Only when Tiers 1–2 genuinely can’t deliver the required UI — colour swatches, engraving add-on, sticky mobile ATC bar. These live in patterns/woocommerce/ with a woo- prefix.

Tier 3 is where most tutorials start and end. Starting there means rebuilding everything WooCommerce already gives you for free.

3. Store Colour Palette Without Touching theme.json

The store palette — charcoal, cream, orange, gold — is completely different from Elayne’s default teal brand colours. The naive fix is to edit theme.json. The right fix is styles/store.json — a style variation file.

The variation remaps the same semantic slugs (primary, base, orange, gold) to the store palette. All patterns reference slugs, not hex values. Switch the variation on and the whole store recolours. Switch it off and the rest of the theme is untouched.

// styles/store.json excerpt — semantic slugs remapped to store palette
{
  "settings": {
    "color": {
      "palette": [
        { "slug": "primary",     "color": "#1A1A1A", "name": "Charcoal" },
        { "slug": "base",        "color": "#FDFBF7", "name": "Cream" },
        { "slug": "orange",      "color": "#E65C00", "name": "Orange" },
        { "slug": "gold",        "color": "#C8A96E", "name": "Gold" },
        { "slug": "main-accent", "color": "#6B6560", "name": "Muted" }
      ]
    }
  }
}

One gotcha: if the variation change doesn’t appear in the Site Editor, you need to switch away and back via Styles → Browse styles. The block editor caches the active variation aggressively during a session.

4. Ten Custom Product Page Patterns (and Why We Needed Each)

The single product page is where WooCommerce’s default block output diverges most from a premium design. We built ten patterns to cover what WooCommerce blocks can’t deliver natively:

  • woo-product-gallery: Wraps WC’s product-image-gallery block with a custom container. The gallery images are rendered dynamically via a PHP template override (woocommerce/single-product/product-image.php) — desktop shows vertical thumbnails on the left, mobile shows horizontal scroll thumbnails below. No hardcoded placeholder slots.
  • woo-product-top: Collection label (category tag in orange), star rating + count, product title with semantic italic subtitle, price block with compare price and save amount, shipping note
  • woo-product-color-swatches: 40×40px swatches with active border ring, JS-driven label update showing the selected colour name
  • woo-product-style-options: Button-style variant selectors with active/default/sold-out states (opacity + line-through for unavailable)
  • woo-product-engraving-option: Clickable add-on card with price — the entire box is the click target, not just a checkbox
  • woo-product-atc-section: Quantity selector + ATC button + wishlist heart + Buy Now in a flex layout, using native WC quantity-selector and add-to-cart-button blocks for functional integrity
  • woo-product-trust-badges: 3-column grid with SVG icons (truck, return arrow, shield) and muted uppercase labels — cream background, 1px borders between columns
  • woo-product-accordion-tabs: Description / Specifications / Shipping & Returns / Care — single-open accordion with ± icon animation via JS
  • woo-product-related: “You May Also Like” section using woocommerce/product-collection with a related products query — 4 columns desktop, 2 on tablet/mobile
  • woo-product-mobile-atc-bar: Fixed bottom bar (position: fixed; bottom: 0), visible only below 768px, with backdrop blur — hides the regular ATC section on mobile so there’s no duplicate

5. Dynamic Gallery via WC Template Override

The gallery pattern started with hardcoded placeholder slots — seven product images baked into the PHP. That broke immediately the moment the actual product had a different number of images. The fix: a WooCommerce PHP template override at woocommerce/single-product/product-image.php that renders the gallery images dynamically from the product’s actual attachment IDs, wrapped in the custom CSS classes the JS expects.

The pattern itself is now thin — just a wrapper group block around wp:woocommerce/product-image-gallery. All the layout complexity is in the CSS and the template override. This means the pattern validates cleanly and the gallery always reflects real product data.

// The pattern is intentionally minimal after the refactor

6. Conditional JS and Font Loading

The product page JS (gallery switching, colour swatches, accordion, wishlist toggle) is only enqueued on single product pages. The store fonts — Jost (body) and Cormorant Garamond (headings) — are also loaded conditionally. Nothing touches pages outside the store.

// functions.php — conditional enqueue
function elayne_enqueue_product_page_scripts() {
    if ( ! is_product() ) {
        return;
    }
    wp_enqueue_script(
        'elayne-woocommerce-product-page',
        get_template_directory_uri() . '/assets/js/woocommerce-product-page.js',
        array(),
        '1.0.0',
        true
    );
}
add_action( 'wp_enqueue_scripts', 'elayne_enqueue_product_page_scripts' );

7. Creating Demo Products with WP-CLI — Use a PHP File, Not wp eval

Standard wp post create creates raw WordPress posts without WooCommerce metadata. You need WC_Product_Simple to set price, stock status, catalog visibility, and product attributes correctly. wp eval with inline PHP containing arrays and dollar signs requires escaping that breaks silently across different shells.

The reliable pattern: write a PHP script to /tmp/ first, then run it with wp eval-file. No escaping, no surprises.

# Step 1: write the script to the VM (heredoc avoids all shell escaping)
trellis vm shell --workdir /srv/www/demo.imagewize.com/current -- bash -c "cat > /tmp/create-products.php << 'PHPEOF'
set_name( 'Sterling Advocate Portfolio' );
\$product->set_status( 'publish' );
\$product->set_catalog_visibility( 'visible' );
\$product->set_price( '285.00' );
\$product->set_regular_price( '285.00' );
\$product->set_stock_status( 'instock' );
\$id = \$product->save();
echo 'Created ID ' . \$id . PHP_EOL;
PHPEOF
"
# Step 2: execute it against the store subsite
trellis vm shell --workdir /srv/www/demo.imagewize.com/current -- \
  wp eval-file /tmp/create-products.php \
  --path=web/wp --url=demo.imagewize.test/store/

8. pt-cli: Two-Pass Pattern Validation

Gutenberg block patterns have two distinct failure modes: structural errors (unbalanced delimiters, malformed block JSON, bad nesting) and compliance errors (hardcoded pixel values, raw hex colours, unregistered block style slugs, missing translation wrappers). These require different validators.

Pass 1 is the WP-CLI structural validator — it requires a WordPress database connection, so it runs inside the Trellis VM. Pass 2 is pt-cli, a custom PHP CLI tool we built that runs on the host with no WordPress dependency. It checks Elayne-specific compliance rules: semantic spacing slugs, registered colour references, SVG-only icons, i18n wrappers.

pt-cli v2 absorbed the earlier elayne-cli scaffolding commands — pattern:create, layout:create, style:create — into a single binary. One install, one tool. Scaffolding still knows about Elayne’s design system via --theme=elayne.

# Pass 1 — structural validation (requires Trellis VM)
trellis vm shell --workdir /srv/www/demo.imagewize.com/current -- \
  wp pattern validate web/app/themes/elayne/patterns/woocommerce/ --fix \
  --path=web/wp

# Pass 2 — compliance check (runs on host, no WordPress needed)
cd demo/web/app/themes/elayne && composer check

# Or directly:
pt-cli check patterns/woocommerce/ --theme=elayne --autofix

Running only one pass misses a whole class of bugs. Pass 1 won’t catch a hardcoded #E65C00 where a semantic orange slug should be. Pass 2 won’t catch a mismatched closing block comment. Both matter for WP.org submission.

9. Block Editor Whitespace Is Not Forgiving

One rule that bit us repeatedly: WordPress block serialization is whitespace-strict. A newline or tab between an opening <div> and a <!-- wp:... --> comment — or between a closing </div> and <!-- /wp:... --> — causes a block validation error in the editor. The block renders, but the editor marks it as invalid and offers to “Attempt block recovery.”

Write block markup compact, the way the editor serialises it. Not pretty-printed.

What’s Done and What’s Left

  • Store homepage: Hero, ticker, featured products grid (responsive 4→2→1), categories grid, testimonials, newsletter
  • Shop landing / category pages: Filters sidebar, product collection grid
  • Product page patterns (10 total): Gallery, top section, colour swatches, style options, engraving, ATC, trust badges, accordion tabs, related products, mobile ATC bar
  • JavaScript: Gallery switching, swatch active state, style options, engraving toggle, accordion, wishlist
  • Store style variation: Store colour palette via styles/store.json
  • Pattern compliance: pt-cli Pass 2 + WP-CLI Pass 1 — all patterns valid
  • 🔄 single-product.html template: Wiring all patterns into the template (Phase 4)
  • 🔄 Dynamic swatch/variation integration: Currently static; requires WooCommerce variation form hooks
  • Responsive + cross-browser testing
  • Sticky gallery on desktop
  • Buy Now button (add to cart + immediate checkout redirect)

Why This Approach Matters for Agencies

The usual WooCommerce agency workflow leans on page builders or heavy child themes that fight the block editor. This implementation is block-native from the start: patterns, style variations, conditional asset loading, a compliance CI pipeline. When WooCommerce updates, the core blocks update with it. The custom patterns are the minimum surface area that genuinely needed custom work.

The three-tier strategy and the two-pass validation workflow are transferable to any block theme + WooCommerce project. The pt-cli tooling is open for reuse — it’s theme-configurable via --theme flag and the compliance rules are defined in JSON.

We’ll publish a follow-up once the single-product template phase is complete and tested. If you’re building something similar or want to use the Elayne theme for a WooCommerce project, get in touch.

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

Related Reading

Leave a Reply

Your email address will not be published.