Speed
The engineering rules behind the published speed score (mobile Lighthouse >=85 on the
flagship demo with products loaded, re-measured per release). Like the update-safety
contract, these are release blockers.
Budgets (CI-enforced)
- Total JS under 100KB across the built package.
scripts/check-js-budget.mjsruns in - Mobile Lighthouse >= 85 performance, >= 90 accessibility on a live preview, enforced
CI and on release against dist/assets/*.js and fails the build on breach.
by shopify/lighthouse-ci-action in CI and re-verified on the exact shipped package by
the release workflow.
Per-section JS loading (convention)
A section's JavaScript loads only on pages where the section is present:
- Each JS-carrying section owns one asset file (
assets/section-<name>.js) and loads it
from inside its own Liquid file:
```liquid
<script src="{{ 'section-mega-menu.js' | asset_url }}" defer></script>
```
Because the tag lives in the section, pages without the section never request the file.
Shopify deduplicates repeated tags when a section appears twice.
- No global bundles.
layout/theme.liquidships zero JS by default; anything added there - Prefer CSS and native elements (
<details>,popover,:has()) over JS. A behavior that - All scripts are
defer(ortype="module", which defers implicitly). Nothing blocks
needs a written justification in the PR and counts against the budget like everything else.
can be CSS is CSS.
parsing.
Render-blocking rules
critical.cssis the only render-blocking stylesheet and is preloaded.- Section-specific styles use
{% stylesheet %}(Shopify concatenates and serves them - Fonts: preconnect + preload base variants only,
font-display: swap(wired in - Images: always through
snippets/image.liquid/image_tagwith explicit dimensions
efficiently) - never a per-section <link> in the head.
theme.liquid / css-variables.liquid).
(no CLS) and lazy loading below the fold.
Measurement policy
Every release publishes its measured mobile Lighthouse result in the release notes and
changelog. The number printed is from CI on the exact shipped package - never a local run,
never a desktop score.