Shop template recipes
These snippets use the version 1 interface. Add the matching HTML elements and run each initialisation inside Bottle.ready. They are alternatives to adapt, not scripts to paste on top of the walkthrough without checking IDs and listeners.
Follow the shop’s configured menu
Section titled “Follow the shop’s configured menu”Create <nav id="menu" aria-label="Shop"></nav> in HTML. Use the supplied paths rather than guessing them:
Bottle.ready(function (data) { var menu = document.getElementById('menu'); function addLink(item, container) { var link = document.createElement('a'); link.href = item.path; link.textContent = item.label; if (item.path === data.page.path) link.setAttribute('aria-current', 'page'); link.addEventListener('click', function (event) { event.preventDefault(); Bottle.navigate(item.path); }); container.append(link); } data.navigation.forEach(function (item) { var group = document.createElement('div'); addLink(item, group); item.children.forEach(function (child) { addLink(child, group); }); menu.append(group); });});This simple version displays children without hover menus. For a collapsible menu, use a real button with aria-expanded and a controlled region. Make it operable by keyboard and touch; do not reveal essential links only on hover.
Search and sort without changing the snapshot
Section titled “Search and sort without changing the snapshot”Use a search input and labelled buttons such as Name, Price: low to high and Price: high to low. A <select> is not supported in an uploaded template.
function selectProducts(products, query, sort) { var needle = query.trim().toLocaleLowerCase('en-GB'); var selected = products.filter(function (product) { return (product.name + ' ' + (product.description || '')) .toLocaleLowerCase('en-GB') .includes(needle); }); selected.sort(function (a, b) { if (sort === 'price-low') return a.pricePence - b.pricePence; if (sort === 'price-high') return b.pricePence - a.pricePence; return a.name.localeCompare(b.name, 'en-GB'); }); return selected;}filter creates a new array, so sorting does not mutate the read-only data. Update the result count with role="status", show No products match your search, and provide a Clear search button if useful. Avoid replacing or announcing the entire grid on every keystroke through an assertive live region.
If catalogueTruncated is true, show More products are available. Choose a category to narrow your search. The interface does not provide a second page of products.
Category links and category pages
Section titled “Category links and category pages”Render data.categories as links or buttons calling Bottle.navigate(category.path). On a category page, data.products is already filtered. Use data.page.title for the heading and data.page.categorySlug for an active category indicator.
For a temporary on-page filter on the homepage, test membership with product.categorySlugs.includes(slug). This filters only the supplied snapshot; it is not a substitute for visiting a category when the catalogue is truncated.
Product pictures and unavailable products
Section titled “Product pictures and unavailable products”Use the first entry in product.images if present. Set its dimensions, use its supplied alt, and keep the approved src unchanged. If it is absent, use a neutral placeholder or omit the picture. Do not invent an image URL from the product ID.
Use text labels for stock state:
var availabilityLabels = { in_stock: 'In stock', low: 'Low stock', out: 'Out of stock', backorder: 'Available to order',};Keep View product available so shoppers can check Bottle’s supported product page. Availability is a summary, not a guarantee or a precise inventory count. Bottle makes the final purchase decisions on its own pages.
Show a published content page
Section titled “Show a published content page”Declare pages.page and include an h1 with ID content-title and a container with ID content-body. Initialise them as follows:
Bottle.ready(function (data) { if (data.page.type !== 'page') return; document.getElementById('content-title').textContent = data.page.title; document.getElementById('content-body').innerHTML = data.page.html || '';});Only page.html is supplied as sanitised HTML. Product descriptions, names and menu labels must use textContent. This content projection includes text-section text, not the editor’s full picture/gallery/product widgets. Omit the page layout to retain the native section renderer.
If your content container includes internal links, intercept those clicks with your navigation handler. Do not assume external editorial links can be opened from the isolated template. Keep legal and contact information reachable through Bottle’s supported navigation.
Add a bundled hero picture
Section titled “Add a bundled hero picture”Place hero.webp in an images folder and use it in HTML:
<img src="images/hero.webp" alt="A delivery vehicle at our yard" width="1200" height="800" />Or reference it from a root-level stylesheet:
.hero { background-image: url('images/hero.webp');}Keep meaningful text outside decorative backgrounds. Compress the picture before upload and stay within the size limits. SVG, remote image URLs and external fonts are not supported.
Keep the template fast
Section titled “Keep the template fast”Render only the products you need on the homepage and offer View all products. Build DOM nodes before attaching them, use lazy loading for below-the-fold pictures, avoid repeated observers and expensive animations, and debounce expensive searches if necessary. Avoid adding a framework for a simple catalogue.
Use normal document flow and responsive CSS. The frame resizes automatically, including when search reduces the results. Don’t add another page-sized scroll region. Browser scripts can still be slow; test on a phone, not only a powerful desktop.