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.
Before you edit code
Section titled “Before you edit code”Most requests do not need a code change. Check these first, in this order:
- The setting already exists. Search this documentation for the thing the merchant wants — Strata has more layout options than its first screen suggests.
- 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.
- The Custom Liquid section, or an app block in the Apps section, for markup that has no setting.
- 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.
Design tokens
Section titled “Design tokens”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 |
Colour schemes
Section titled “Colour schemes”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);}State classes
Section titled “State classes”| 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 |
JavaScript
Section titled “JavaScript”The Bvl object
Section titled “The Bvl object”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.
Drawers
Section titled “Drawers”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.
Events
Section titled “Events”| 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.
Adding a script
Section titled “Adding a script”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) => { /* … */ });});Liquid
Section titled “Liquid”Images
Section titled “Images”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.
App blocks
Section titled “App blocks”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.
Metafields
Section titled “Metafields”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.
Performance
Section titled “Performance”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(orasync) in the<head>blocks the first paint for every visitor.
© 2026 Bevel · Shopify is a trademark of Shopify Inc.; Bevel is an independent theme studio.