Skip to content

For developers

This chapter is for the developer a merchant hires to extend Strata: how the theme is put together, what you can hook into, and how to change it without losing the change at the next theme update. Everything a merchant can do is in the rest of the documentation; this is the layer underneath.

Most requests do not need a code change. Check these first, in this order:

  1. The setting already exists. Search this documentation for the thing the merchant wants — Strata has more layout options than its first screen suggests.
  2. Custom CSS. Every section has a Custom CSS field in the theme editor, and Theme settings has one for the whole store. CSS written there survives theme updates.
  3. The Custom Liquid section, or an app block in the Apps section, for markup that has no setting.
  4. A metafield read through a dynamic source, for per-product content.

Only when none of these fit, edit the files. Then write the change down: Shopify copies settings and templates into a theme update automatically, but a code edit that conflicts with the update has to be reapplied by hand.

Strata is a standard Online Store 2.0 theme: JSON templates, section groups for the header and footer, theme blocks for the mega menu.

Where What
layout/theme.liquid Loads one stylesheet and one deferred script for the whole store
assets/bvl-theme.css All of the theme’s CSS, in one readable (not minified) file
assets/bvl-theme.js The global script: core, drawers, menu, cart, search, header, reveal, product cards
assets/bvl-product.js, assets/bvl-collection.js, … Per-section scripts, loaded only by the section that needs them
snippets/css-variables.liquid Turns Theme settings into CSS custom properties — the single source of every design token
sections/header-group.json, sections/footer-group.json The header and footer section groups
blocks/ Theme blocks: mega-menu and its private children (_mega-column, _mega-promo, …)
locales/ Every storefront string; nothing is hard-coded in Liquid

Everything the theme defines is namespaced so it cannot collide with yours: CSS classes start with bvl-, custom properties with --bvl-, data attributes with data-bvl-, and the JavaScript lives on window.Bvl. Use your own prefix for anything you add, and you will never be overwritten.

Every themeable value is a custom property on :root, written by snippets/css-variables.liquid from Theme settings. Read them rather than hard-coding values, and your additions will follow the merchant’s settings — and the preset — automatically.

Family Examples
Typography --bvl-font-heading, --bvl-font-body, --bvl-h1 … --bvl-h6, --bvl-text-sm, --bvl-leading-body
Spacing --bvl-space-1 … --bvl-space-7, --bvl-gutter, --bvl-grid-gap, --bvl-section-spacing, --bvl-page-width
Shape --bvl-button-radius, --bvl-card-radius, --bvl-input-radius, --bvl-image-radius, --bvl-radius-pill
Elevation --bvl-shadow-xs … --bvl-shadow-lg
Motion --bvl-anim-duration, --bvl-anim-fast, --bvl-anim-slow, --bvl-ease, --bvl-ease-out

Colours are never global: each colour scheme is a class, .bvl-scheme--<scheme id>, that sets the colour tokens for everything inside it. <body> carries the first scheme; every section carries the scheme the merchant picked for it.

Token Role
--bvl-color-bg, --bvl-color-surface Background, and the slightly raised surface of cards and panels
--bvl-color-text, --bvl-color-text-muted Text, and secondary text
--bvl-color-accent, --bvl-color-accent-contrast Accent, and text that sits on the accent
--bvl-color-border Hairlines and field borders
--bvl-color-button-primary-bg, --bvl-color-button-primary-text Primary button
--bvl-color-button-secondary-* Secondary button (-bg, -text, -border)
--bvl-color-success, --bvl-color-warning, --bvl-color-error Status colours

So a component you add that uses color: var(--bvl-color-text) is legible in every scheme, on every preset, with no extra work.

.acme-note {
padding: var(--bvl-space-3);
border: 1px solid var(--bvl-color-border);
border-radius: var(--bvl-box-radius);
background: var(--bvl-color-surface);
color: var(--bvl-color-text);
}
Class Set on When
bvl-ready <html> The global script has run
is-drawer-open <body> Any drawer (cart, menu, search, quick view) is open
is-open [data-bvl-drawer] That drawer is open

The global script defines one object, window.Bvl. Every feature registers itself on it as a module, and modules talk to each other through it — never through shared variables — so you can do the same.

Member What it is
Bvl.modules The registered modules, by name
Bvl.ready(fn) Runs fn once the DOM is ready (immediately if it already is)
Bvl.register(name, module) Registers a module and calls its init(). Registering a name twice returns the first module, so a script loaded twice does no harm
Bvl.utils.qs / qsa querySelector / querySelectorAll (the latter as an array)
Bvl.utils.on(el, event, fn) addEventListener that returns its own remover
Bvl.utils.debounce(fn, ms) Debounce, 150 ms by default

Modules registered by the theme: announcement, carousel, cart, countdown, drag, drawer, facets, header, menu, personalize, popup, product, recommendations, reveal, search, slideshow, spotlight, tabs, video (plus addresses on the account pages). Per-section modules exist only on pages where their section is.

Bvl.modules.drawer.open('cart'); // also 'navigation', 'search', 'quick-view'
Bvl.modules.drawer.close('cart');
Bvl.modules.drawer.closeAll();

Or with no JavaScript at all — any element with the attribute opens or closes a drawer:

<button type="button" data-bvl-drawer-open="cart">View cart</button>
<button type="button" data-bvl-drawer-close>Close</button>

One drawer is open at a time; opening one closes the others. Escape closes the open drawer.

Any form on the page that posts to /cart/add is intercepted, validated and sent with AJAX — so a custom add-to-cart form, or an app’s, gets the cart drawer for free. Give it the usual Shopify fields (id, quantity, properties[...]) and nothing else.

// Add the contents of a product form, re-render the cart, open the drawer.
await Bvl.modules.cart.addItem(formElement);
// Set a line's quantity (0 removes it) and re-render the cart.
await Bvl.modules.cart.changeQty(lineItemKey, 2);

When Cart behavior is set to Page, forms submit normally and Shopify goes to /cart.

If you change the cart outside the theme (an app calling /cart/add.js itself), re-render the cart drawer with the Section Rendering API — its section id is cart-drawer.

Event Fired on detail
bvl:media:show The product media slide that became active (bubbles) { id, scroll } — the media id, and whether the gallery scrolled to it
bvl:tabs:change The tab set (bubbles) { tab, panel } — the active tab button and its panel

Strata also responds to the theme editor’s own events (shopify:section:load, shopify:section:unload, shopify:block:select), so a section re-initialises correctly when the merchant changes a setting. Your own scripts should do the same if they set anything up on load.

Load your file deferred, after the theme, and wait for Bvl:

<script src="{{ 'acme-custom.js' | asset_url }}" defer></script>
Bvl.ready(() => {
Bvl.utils.qsa('[data-acme-thing]').forEach((el) => { /* … */ });
});

Every image goes through snippets/image.liquid, which writes the srcset, the lazy loading and the alt text. Use it for your own images and they will behave like the theme’s:

{% render 'image',
image: product.featured_media.preview_image,
ratio: 1,
sizes: '(min-width: 1024px) 33vw, 100vw'
%}

Pass an accurate sizes (and ratio to crop to a frame). Pass priority: true only for the one image at the top of the page — the theme already marks its own, and two “priority” images compete for bandwidth.

These sections accept app blocks (@app): Apps, Product (the main section of the product template) and Custom quote. Anything a merchant installs from the Shopify App Store that ships an app block can be placed there without code.

The theme reads its product metafields by key, so they work without any setup in the editor. The full list — namespaces, keys, types and what appears without each — is on the Metafields page.

Strata is built to stay above the Theme Store’s Lighthouse minimums. Two habits keep an extended store there:

  • Do not add a second copy of a library the theme already does without. Strata has no jQuery, no slider library and no animation library.
  • Defer everything. A script without defer (or async) in the <head> blocks the first paint for every visitor.