Skip to content

Performance and Core Web Vitals

Hyprism is built to clear Shopify Theme Store performance gates with margin. This chapter covers the optimizations baked in, what targets to expect, and how to verify performance on your store.

Hyprism aims for these performance targets across all 13 templates:

MetricMobile targetDesktop targetTheme Store gate
Lighthouse Performance≥85 (median 92)≥95 (median 99–100)≥75 mobile
Largest Contentful Paint (LCP)<2.5s<1.5s<2.5s
Cumulative Layout Shift (CLS)<0.1<0.1<0.1
Total Blocking Time (TBT)<300ms<100ms<300ms
Interaction to Next Paint (INP)<200ms<200ms<200ms

The median across all 13 templates after 4 iterative Lighthouse runs (on a development store with apparel demo content) was:

  • Mobile: 92 (Performance), 96–100 (A11y), 100 (SEO) — only Cart shows SEO=69 because /cart is intentionally blocked from indexing via robots.txt.
  • Desktop: 99–100 (Performance), 96–100 (A11y), 100 (SEO).

The theme’s critical above-the-fold CSS (the styles needed before any section renders) is in assets/critical.css. It’s loaded synchronously in <head> with <link rel="preload"> so the browser fetches it early.

Non-critical CSS lives in {% stylesheet %} blocks inside individual section/block Liquid files — Shopify bundles these into per-section CSS files that load only when that section is on the page.

In layout/theme.liquid <head>:

<link rel="preconnect" href="https://fonts.shopifycdn.com">
<link rel="preload" as="style" href="critical.css">
<link rel="preload" as="font" type="font/woff2" href="..."> <!-- body + heading fonts -->

Browser starts connection + font / CSS download before parsing the rest of the page.

⚠️ Preconnect to cdn.shopify.com was removed in v0.7 — Lighthouse flagged it as “unused” because Shopify serves theme assets same-origin (no DNS resolution needed). Removing the preconnect freed up critical-path latency by ~1500ms on the home page. Lesson: only preconnect to origins you actually fetch from cross-origin.

  • All images use image_url filter for the Shopify CDN, which auto-converts to AVIF/WebP based on browser support.
  • Section-position-aware fetchpriority: hero/first-section images get fetchpriority="high" for LCP; below-fold images get loading="lazy" for delayed loading.
  • decoding="async" on every <img> — browser decodes images off the main thread, so image rendering doesn’t block scroll responsiveness.
  • Subgrid card alignment keeps all product cards in a grid identical-height (no layout shift as images load at different speeds).

All scroll, touch, and wheel listeners use { passive: true }:

window.addEventListener('scroll', handler, { passive: true });
section.addEventListener('touchstart', handler, { passive: true });

This tells the browser “I won’t preventDefault” — the browser can start scrolling immediately without waiting for the JS to run. Massively improves INP.

Every theme JS script uses defer:

<script src="hyprism.js" defer></script>
<script src="hyprism-features.js" defer></script>

JS doesn’t block HTML parsing. It executes after DOMContentLoaded.

Instead of scroll listeners that fire on every scroll event, Hyprism uses IntersectionObservers for:

  • Sticky Product Bar (watches the main ATC)
  • Mobile Sticky Cart Bar (watches the in-flow summary)
  • Off-screen card visibility-hiding in slider carousels
  • Recently-viewed tracking (when a product page enters viewport, the product is added to localStorage)

IntersectionObservers fire only when an element crosses a threshold — orders of magnitude cheaper than scroll listeners.

  • Transform-only animations (translate, scale, rotate) — the browser composites these on GPU without main-thread cost
  • will-change only when transitioning — not set permanently (which would keep elements on a GPU layer always)
  • backdrop-filter only on explicit glass elements — not on overlay-wrappers (which would force the entire page through GPU compositor)

A few things that could be optimized but aren’t, because the trade-offs aren’t worth it:

Preload-link for product / article featured images (REVERTED)

Section titled “Preload-link for product / article featured images (REVERTED)”

Tried in v0.7-Round-2: <link rel="preload" as="image"> for the product page’s main image. On throttled-mobile (Lighthouse’s mobile profile = 4× CPU + Slow-4G), the preload competed with critical-path resources for bandwidth, and FCP regressed +0.6s. Net-negative — reverted.

Lesson: preload helps only for late-discovered resources (JS-driven images, dynamically-imported fonts). For in-the-HTML images, the browser’s default discovery is already optimal.

Tried in v0.7-Round-2: deprioritize the header logo to free bandwidth for the hero. On throttled-mobile, this delayed FCP because the logo is part of the first paint. Browser-default heuristics (auto-prio for above-fold images) were already correct. Reverted.

Lesson: don’t speculatively deprioritize above-fold resources — the browser knows better.

This score is constant across all 13 Hyprism templates because the failing items are Shopify-platform-level, not theme-fixable:

Failed ItemCauseFixable?
CSP script-src and object-src missingShopify edge sets Content-Security-Policy header that Hyprism can’t overrideNo — Shopify-level
HSTS max-age too lowShopify edge sets HSTS headerNo
No COOP (Cross-Origin-Opener-Policy)Shopify edgeNo
No Trusted TypesShopify edgeNo
Third-party cookies (shop.app)Shopify ecosystemNo

Dawn and Horizon score 77 too — reviewers are familiar with this plateau and don’t reject themes for it.

In Chrome / Edge:

  1. Open the page you want to test.
  2. DevTools (F12) → Lighthouse tab.
  3. Pick “Mobile” + “Performance, Accessibility, Best Practices, SEO” + “Slow 4G + 4× CPU”.
  4. Click “Analyze page load”.

Run 3–5 times and take the median — Lighthouse Mobile has natural variance (±5–15 points per run, even with no code changes).

Shopify Admin → Online Store → Themes → Customize → Performance tab. Shopify tests every published theme weekly and reports CWV.

Once your store is live with real traffic, Shopify’s Performance tab shows real-user CWV data (anonymized aggregate). This is more meaningful than Lighthouse because it reflects what real visitors experience.

Things merchants control that affect performance:

ChoiceImpactRecommendation
Hero video file sizeHighCompress to ≤2MB for autoplay-loops
Hero image sizeMediumUpload at 2× display size; let Shopify CDN serve optimized formats
Number of sections on home pageLowGenerally fine; Hyprism uses section-scoped CSS so unused sections aren’t loaded
Number of apps installedHighEach app injects JS. Apps often hurt performance more than themes.
Custom Liquid section contentVariableCustom Liquid runs server-side, so it’s free at runtime. JavaScript inside Custom Liquid can be costly.

INP is Google’s newer metric (replacing FID in 2024). It measures the longest input-to-paint delay during a page session — basically “how snappy does this page feel?”

Hyprism INP targets <200ms. To achieve this:

  • All JS listeners are passive where applicable
  • Main-thread tasks are <50ms each
  • Animations are GPU-composited (transform / opacity / filter)
  • IntersectionObserver replaces scroll listeners
  • AJAX cart updates are event-driven, no polling

If your INP is high on a specific page, check:

  1. Apps with heavy JS — Shopify Apps tab → Performance tab shows per-app impact
  2. Custom Liquid sections — large for loops in Liquid run server-side (fine) but JSON output rendered as <script> and parsed in JS can be slow if huge
  3. Marketing pixels — Meta Pixel, TikTok Pixel, etc. can be heavy

Chapter 15 — Localization — multi-language store setup.