02The story
Context
Arvale is a store I built for myself: an invented brand selling clothes, bags, footwear and homeware "made to be kept", with a full catalogue, a journal and the help pages a real shop needs. No client, so no brief to hide behind. The goal was to show, in one live site, the kind of WooCommerce store clients usually ask me for, and how I build it.
The rule I set was that everything a premium store theme sells — colour swatches, AJAX filters, a mega menu, quick view, wishlist, compare, an off-canvas cart — had to be built on WooCommerce's own machinery, and every page had to stay editable in Elementor by someone who never opens the code.
How it's put together
Three layers, each with one job.
A deliberately small child theme. Arvale is a child of Hello Elementor. It carries the brand stylesheet, the motion system and not much else. It doesn't hold templates or store logic.
A custom plugin, Arvale Core, for everything the store does. It's made of 13 modules — swatches, filters, wishlist, compare, quick view, mini-cart, shipping, media, search, product page, icons, Elementor and demo mode. Each one checks its own dependencies and can be switched off from a settings page, and each loads its CSS and JS only on pages where it actually renders. It declares compatibility with WooCommerce's HPOS order tables.
Elementor Pro for every layout. Header, footer, the product card, the shop archive, the single product page, the journal, the 404 and the newsletter popup are all Theme Builder templates. The plugin adds its own Arvale Store panel to the editor: 18 custom widgets (mega menu, category index, shop the look, product media, product filters, product gallery, reviews, key details and more) and 4 dynamic tags (sale percentage, stock status, "spend $X more for free shipping", category count), so the store-specific pieces drop into a layout like any native widget.
Built on WooCommerce, not around it
This is the part I care about most, because it's where store builds usually break on the next WooCommerce update.
- Filters speak WooCommerce's own URL language (
filter_color,min_price,rating_filter), so the main shop query does the filtering and every filtered view is a real, shareable, crawlable URL. JavaScript only swaps the results in place. Each facet's counts ignore its own selection, so choosing Black never hides Navy from the colour list, and the counts are cached against WooCommerce's own product version. - Swatches replace variation dropdowns with accessible buttons, but the
native
<select>stays in the form, so WooCommerce's variation script still handles matching, price, stock and image changes. - The off-canvas cart goes through
WC()->cartand arrives as a standard cart fragment, with quantity steppers and a free-shipping progress bar. - Quick view renders WooCommerce's own single-product functions, so price, variations and add to cart behave exactly as they do on the product page.
- Live search is a normal product search form underneath. Press Enter, or turn JavaScript off, and you land on WooCommerce's search results.
- The wishlist lives in user meta for customers and a cookie for guests, merged at login. Compare (up to four products) lives in the browser, so both keep working on fully cached pages.
- The product page adds badges, extra data tabs and a sticky add-to-cart bar that drives the page's real cart form, not a copy of it.
Reproducible, not hand-clicked
The whole store can be rebuilt from a WP-CLI command. wp arvale all
configures the store the way a merchant would (currency, tax, shipping zones,
payment methods, attributes, brands, coupons) and generates the 300-product
catalogue through WooCommerce's CRUD classes, not raw SQL, so lookup tables,
price caches and variation sync all behave as if someone had typed the
products in. It then imports the photography by folder, adds reviews, and
writes the design into Elementor: the kit's colours and type, the Theme
Builder templates, the product card and every page. It saves all of that
through Elementor's Document API, so the result can't be told apart from work
done in the editor, and it's safe to run again.
Details that make it feel finished
- A mega menu built from an ordinary WordPress menu. Product categories pick up their counts and thumbnails automatically, and below 1025px the menu becomes a drawer with the same structure as accordions.
- A typographic category index on the homepage with live product counts and an image preview that follows the pointer. Keyboard and reduced-motion users get the image beside the row instead.
- Shop the look: an editorial photo with hotspots that open live product cards, with price, stock and add to cart.
- Its own line-icon set, registered as a library in Elementor's icon picker so every icon control can use it.
- One motion system: sections that rise into view, headlines revealed line by line, image wipes, a marquee, and a header that hides on scroll down. It never hides content when JavaScript fails, and none of it runs for people who prefer reduced motion.
- The contact form and dropdowns run on my own plugins, Defer Forms for Contact Form 7 and Nice Select for WP.
Tuned for free hosting
It runs on free shared hosting, and that shaped a few decisions. The media module stops WordPress generating image sizes the theme never uses and saves the ones it does need as WebP, which also matters on hosts that cap the number of files you can store. WooCommerce's zoom, slider and lightbox scripts never load, because the product gallery is a native scroll-snap slider. Block editor styles load only on journal posts. A demo-mode module offers only offline payment methods, shows a quiet notice at checkout and tags every order, so anyone can walk through a full checkout and nothing is ever charged.
Why it's here
Client sites belong to the client, and they change after handover. Arvale is a store I control end to end, so it shows how I build WooCommerce sites when nothing is decided for me: Elementor Pro handles the layout so a client's own team can edit it, and one clean, modular plugin handles the store logic without fighting WooCommerce, so it keeps working after the next update.

