For theme developers
Guarding a theme for Dock products — by hand
Liquid required · most stores don't need this page
If you just want native buy buttons hidden on Dock products, you don't need this page: the Dock Buy Guard app embed does it with one toggle in the theme editor, on any theme, no code. This page is for theme developers who want to go further and remove those surfaces from the markup — the approach used on our reference store's custom theme. Markup removal is strictly stronger than hiding (nothing to un-hide, nothing rendered at any viewport), and it also covers cosmetic details the embed deliberately leaves alone, like the £0 placeholder price on product cards.
Why Dock products must not have native buy buttons
Dock-synced products are priced and configured only by the Dock configurator on the product page. Your theme doesn't know that: its native add-to-cart surfaces put the product's £0 placeholder base variant straight in the cart with none of the configuration Dock needs. A customer can pay for such a line, but it is never sent for production — the merchant ends up refunding it (the order is tagged soloflo-unconfigured-line and the order note names the lines). The app's checkout validation blocks these lines at checkout; guarding the theme removes the buttons that create them in the first place.
The pattern — one guard, applied everywhere
Every Dock-synced product carries a soloflo.product_id metafield (stamped by the app's product sync). Wrap every native add-to-cart surface so it only renders when that metafield's value is empty:
{% if product.metafields.soloflo.product_id.value == blank %}
{% comment %} ...the native add-to-cart surface... {% endcomment %}
{% endif %}Three rules, each learned the hard way:
- Compare
.valuetoblank— never truth-test the bare metafield drop. Sync also creates the metafield definition store-wide, and only the.value == blankcomparison behaves correctly for products that have the definition but were never synced. - Remove the surface from markup — don't CSS-hide it. A CSS-hidden button comes back at unexpected viewports (a quick-add hidden on mobile once shipped fully clickable on desktop). If you write Liquid, write the
if, not a stylesheet rule. - The merchant's own products must keep working. Products without the metafield value are untouched by the guard. Test both directions.
Where to apply it
Audit every surface that can put a variant in the cart. On Horizon-family themes (our reference implementation) that is four places:
| Surface | Horizon-family file | What to wrap |
|---|---|---|
| Collection-grid / card quick-add | snippets/quick-add.liquid | the whole snippet body (covers every render site: card gallery, featured product, hotspots) |
| Product-page buy box | blocks/buy-buttons.liquid | everything after the variable assigns (add to cart, accelerated checkout / Buy it now, quantity, pickup availability) |
| Sticky add-to-cart bar | sections/product-information.liquid | add the guard to the enable_sticky_add_to_cart condition — it usually defaults ON |
| Variant picker | blocks/variant-picker.liquid | the picker render — a synced product's variants ("Configuration" values) are internal plumbing, not customer choices |
On Dawn-family themes the equivalents are snippets/buy-buttons.liquid, snippets/card-product.liquid (the quick-add form) and sections/main-product.liquid. Also check any "quick order list" or "featured product" sections your theme ships, and anything an app added that renders its own buy button.
The £0 placeholder price
Synced products carry a £0 placeholder variant price (the real price is quoted live by the configurator), so themes that print variant prices may show "£0.00" on cards and product pages. This is the one cosmetic job the app embed deliberately doesn't attempt — price markup is too theme-specific to hide safely from the outside. In your theme you can scope it precisely: wrap the price render with the same metafield guard, or mask it with a scoped stylesheet that targets only guarded products' price elements. Keep the guard product-scoped either way — a product-blind price mask will hide the merchant's own prices too.
How this stacks with the app's own layers
- Leave the Dock Buy Guard embed ON. Your markup guards make most of its work a no-op, and it still covers surfaces you missed, theme updates that reintroduce a button, and app-injected buttons. The two never conflict. (In the theme editor the embed hides nothing — it only acts on the live storefront — so it won't fight you while you work.)
- Leave the checkout validation ON. "Dock configuration guard" (Settings → Checkout → Checkout rules) is the guarantee: even a direct
/cart/addURL can't buy an unconfigured Dock line. Theme work is UX; the validation is enforcement. - Don't touch cart editing. Customers must always be able to remove or re-quantity a Dock line in the cart/drawer — the checkout validation's messaging depends on it.
Verify it worked
- Collection page, desktop width (the shipped viewport bug showed only there): Dock product cards show no quick-add at any viewport; the merchant's own products keep theirs.
- Dock product page: only the configurator offers a purchase path — no add to cart, Buy it now, quantity, variant picker, or sticky bar on scroll.
- A merchant-own product page: full native buy path still works, end to end.
- A configurator order still lands in the cart with its
_soloflo_configline property.
If an order ever arrives tagged soloflo-unconfigured-line, a native surface slipped through — re-audit against the table above.
Questions
Email soloflo@solopress.com with your store's .myshopify.com domain and the theme you're working on — we're happy to review a guard implementation.