Performance

Performance

Mast is engineered for Lighthouse scores above 90. This page documents every performance decisión made in the theme and why.


Performance targets

These are the targets Mast is designed to hit on a store with a reasonable product catalog (fewer than 100 products) and standard Shopify-hosted images on a mid-range mobile device with a 4G connection.

Lighthouse Performance

90+

Mobile

Largest Contentful Paint

< 2.5s

Mobile

Total Blocking Time

< 200ms

Desktop

Cumulative Layout Shift

< 0.1

All

JS bundle (minified)

~14 KB

gzip ~5 KB

CSS (above fold)

inline

Critical path

First render fonts

FOFT

No invisible text

Image formats

WebP + AVIF

Shopify CDN

Note

Performance scores degrade when third-party apps are installed. Each app adds its own scripts and stylesheets. Common culprits include review apps (Yotpo, Okendo), chat widgets, and loyalty program scripts. Monitor your Lighthouse score after each app installation.

Critical CSS inlining

Render-blocking CSS is one of the biggest contributors to poor Lighthouse scores on Shopify themes. Mast addresses this with a critical CSS strategy: the styles needed to render the above-the-fold content are inlined directly in the <head> of every page.

This means the browser can paint the first meaningful content without waiting for any stylesheet to download. The full stylesheet loads asynchronously after the critical paint has occurred.

<!-- Critical CSS: inlined in <head> --> <style> /* Reset, layout, header, above-fold section */ :root { --color-paper: #DEDBC8; ... } body { background: var(--color-paper); } .site-header { ... } .image-banner { ... } </style> <!-- Full stylesheet: non-blocking --> <link rel="preload" href="/assets/base.css" as="style" onload="this.onload=null;this.rel='stylesheet'"> <noscript> <link rel="stylesheet" href="/assets/base.css"> </noscript>

The non-blocking stylesheet loading pattern uses rel="preload" with an onload handler to switch to rel="stylesheet" once the file is downloaded. A <noscript> fallback ensures the stylesheet loads normally for users with JavaScript disabled.

Font loading optimization

Web fonts are a common source of layout shift and invisible text (FOIT). Mast uses a Flash Of Faux Text (FOFT) strategy that shows system fonts immediately and swaps to the web font when it arrives, without invisible text at any point.

Preconnect to font origins

A preconnect hint for the Shopify CDN and Google Fonts origins is rendered in the <head>. This establishes the TCP connection and TLS handshake before the browser encounters the font request, reducing latency by 100–300ms on the first load.

<link rel="preconnect" href="https://fonts.shopifycdn.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

Preload above-fold fonts

The heading font used in the first visible section is preloaded with a rel="preload" hint. This gives the heading font the highest fetch priority and minimizes the time between first paint and font swap.

<link rel="preload" href="{{ settings.type_heading_font | font_url }}" as="font" type="font/woff2" crossorigin>

font-display: swap

All @font-face declarations use font-display: swap. The browser shows the fallback system font immediately and swaps to the web font once it is available, eliminating invisible text.

Subsetting

Where possible, Mast requests only the Latin character subset from font providers. Full Unicode font files can be 3–5x larger than Latin-only subsets. For stores serving non-Latin scripts, the subsetting is adjusted accordingly.

Image optimization

Images are the largest payload on any ecommerce site. Mast uses the Shopify CDN image transformation API to serve appropriately sized, modern-format images at every breakpoint.

Responsive srcset

Every image rendered by Mast uses a srcset with multiple width descriptors and a sizes attribute that accurately describes the rendered width at each breakpoint. The browser selects the smallest image that meets the display density requirement.

{%- assign widths = '400,600,800,1000,1200,1600,2000' -%} <img src="{{ image | image_url: width: 800 }}" srcset=" {%- for w in widths | split: ',' -%} {{ image | image_url: width: w }} {{ w }}w, {%- endfor -%} " sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw" loading="lazy" alt="{{ image.alt }}" width="{{ image.width }}" height="{{ image.height }}" >

fetchpriority on hero images

The first image in the viewport (the Image Banner hero) is marked with fetchpriority="high" and loading="eager". This gives it the highest fetch priority in the browser's resource queue and ensures it contributes positively to the Largest Contentful Paint metric.

Lazy loading all other images

All images below the fold use loading="lazy", which defers their download until they are close to entering the viewport. This reduces the initial page payload and speeds up the first contentful paint without any JavaScript.

Explicit width and height attributes

Every image tag includes explicit width and height attributes matching the intrinsic dimensions of the source image. Combined with aspect-ratio: auto in CSS, this reserves the correct space in the layout before the image loads, preventing Cumulative Layout Shift.

WebP and AVIF vía Shopify CDN

The Shopify CDN automatically serves images in WebP format to browsers that support it, and AVIF where supported. No configuration is required. The image_url Liquid filter enables CDN-side format negotiation. AVIF files are typically 30–50% smaller than equivalent WebP files.

JavaScript bundle

Mast's JavaScript footprint is approximately 14 KB minified (about 5 KB gzip). This is one of the smallest bundles among full-featured Shopify themes.

The bundle is kept small by three principles: no jQuery or utility libraries, no framework (plain ES modules only), and shipping only what is genuinely required for interactivity.

ModuleSize (min)Responsibility
cart-drawer.js~3.2 KBCart drawer open/close, quantity updates, item removal, Section Rendering API calls
header.js~1.8 KBSticky scroll behavior, mobile drawer toggle, mega menu hover handling
predictive-search.js~2.1 KBDebounced search input, Shopify Predictive Search API calls, results rendering
media-gallery.js~2.4 KBProduct image gallery thumbnail navigation, zoom on click, video play
variant-picker.js~1.9 KBVariant selection, availability state, URL history pushState, price update
scroll-reveal.js~0.8 KBIntersectionObserver-based scroll reveal animations for sections
announcement-bar.js~0.6 KBAuto-rotation, dismiss button, sessionStorage persistence
quantity-input.js~0.4 KBIncrement/decrement buttons on cart and product form

Module loading strategy

Each module is loaded as a <script type="module"> tag adjacent to the component it controls. Modules not needed on the current template are not loaded. For example, variant-picker.js only loads on product templates.

No polyfills

Mast targets browsers that support ES modules natively. This eliminates the need for Babel transpilation or polyfill bundles. The browser support baseline is Safari 14, Chrome 80, Firefox 75, Edge 80 — which covers over 99% of Shopify's user base.

Scroll reveal

Mast uses IntersectionObserver for scroll-triggered reveal animations. This is a native browser API that does not require JavaScript polling or scroll event listeners, making it significantly more performant than alternatives.

Sections with scroll reveal add a data-reveal attribute to their root element. The scroll-reveal.js module observes all elements with this attribute and adds a is-revealed class when they enter the viewport, triggering a CSS transition.

The reveal animation (a subtle fade and translate) is disabled when the user has prefers-reduced-motion enabled. In that case, elements appear at their final position immediately.

Section Rendering API for cart

The cart drawer uses Shopify's Section Rendering API to update its contents after add-to-cart, quantity changes, and item removal. Instead of a full page reload, the theme sends a fetch request to the current URL with a sections parameter, receives only the HTML for the cart section, and replaces the drawer's inner HTML.

This approach is faster than a full AJAX cart because it returns rendered HTML from Shopify's Liquid renderer rather than JSON that must be processed by JavaScript on the client.

// Add to cart then fetch updated cart section const response = await fetch(window.Shopify.routes.root + 'cart', { headers: { 'sections': 'cart-drawer', 'X-Requested-With': 'XMLHttpRequest' } }); const data = await response.json(); cartDrawer.innerHTML = data['cart-drawer'];

Lazy loading strategy

Mast applies lazy loading selectively rather than universally, because lazy loading the first-viewport content actively hurts LCP scores.

Content typeStrategyRationale
Hero image (above fold)eager + high priorityLCP element. Must load as fast as possible.
Below-fold imageslazyNot needed at initial load. Deferred until near viewport.
Product card imageslazyCollection grids are below fold and paginated.
Lookbook imageslazyAlways below fold in context of full page.
Video (background)lazy + muted autoplayDeferred until near viewport. Muted autoplay does not trigger data usage block on mobile.
Non-critical scriptsdefer / moduleAll scripts use defer or type=module, which defers execution until after HTML parsing.
Cart drawer HTMLon-demandCart drawer markup is only fetched when the cart icon is first clicked.

How to test performance

  • Google PageSpeed Insights pagespeed.web.dev

    Run your live store URL through PageSpeed Insights for a real-world Lighthouse score. Use the mobile report as the primary benchmark.

  • Shopify Theme Analyzer In Shopify Admin

    Go to Online Store > Themes > Analyze your theme. Shopify runs its own performance audit and flags issues specific to the theme store review criteria.

  • Chrome DevTools Lighthouse Browser DevTools

    Run locally against your development store with the Network tab throttled to Slow 4G. Check the Opportunities and Diagnostics sections for theme-specific issues.

  • WebPageTest webpagetest.org

    Deeper waterfall analysis. Useful for diagnosing specific render-blocking resources, font swap timing, and third-party script impact.