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.
| Module | Size (min) | Responsibility |
|---|---|---|
cart-drawer.js | ~3.2 KB | Cart drawer open/close, quantity updates, item removal, Section Rendering API calls |
header.js | ~1.8 KB | Sticky scroll behavior, mobile drawer toggle, mega menu hover handling |
predictive-search.js | ~2.1 KB | Debounced search input, Shopify Predictive Search API calls, results rendering |
media-gallery.js | ~2.4 KB | Product image gallery thumbnail navigation, zoom on click, video play |
variant-picker.js | ~1.9 KB | Variant selection, availability state, URL history pushState, price update |
scroll-reveal.js | ~0.8 KB | IntersectionObserver-based scroll reveal animations for sections |
announcement-bar.js | ~0.6 KB | Auto-rotation, dismiss button, sessionStorage persistence |
quantity-input.js | ~0.4 KB | Increment/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 type | Strategy | Rationale |
|---|---|---|
| Hero image (above fold) | eager + high priority | LCP element. Must load as fast as possible. |
| Below-fold images | lazy | Not needed at initial load. Deferred until near viewport. |
| Product card images | lazy | Collection grids are below fold and paginated. |
| Lookbook images | lazy | Always below fold in context of full page. |
| Video (background) | lazy + muted autoplay | Deferred until near viewport. Muted autoplay does not trigger data usage block on mobile. |
| Non-critical scripts | defer / module | All scripts use defer or type=module, which defers execution until after HTML parsing. |
| Cart drawer HTML | on-demand | Cart 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.