Phase 8:
Performance

Render-Blocking CSS, LCP Optimization, CLS Prevention & content-visibility

WinSport Theme · Core Web Vitals · Deferred Loading · CSS Containment

Dawn Baseline

Dawn already provides solid image optimization. Phase 8 builds on top of it.

srcset
Responsive images
lazy
Below-fold loading
WebP
Via Shopify CDN
w/h
CLS prevention
  • image.liquid snippet — srcset with 7 breakpoints (165w–1066w), lazy/eager toggle, fetchpriority, width/height
  • product-media-gallery — LCP image set to lazy_load: false
  • image-bannerfetchpriority: 'high' for first section
  • Shopify CDN automatically serves WebP for supported browsers via image_url filter

Three Optimizations

render-blocking

Deferred CSS

Non-critical stylesheets (custom theme CSS, VAT pricing, cart drawer) load asynchronously via print/onload pattern. Only base.css blocks rendering.

content-visibility

Skip Off-Screen

Below-fold sections use content-visibility: auto with contain-intrinsic-size. Browser skips layout/paint until scrolled into view.

LCP & CLS

Core Web Vitals

Hero fallback image gets fetchpriority="high". CSS containment on card media reduces paint area. Lazy images get intrinsic sizing.

Deferred CSS Loading

Before (Render-Blocking)

{{ 'base.css' | stylesheet_tag }} {{ 'price-vat.css' | stylesheet_tag }} {{ 'winsport.css' | stylesheet_tag }} {{ 'cart-drawer.css' | stylesheet_tag }} {{ 'cart.css' | stylesheet_tag }} {{ 'totals.css' | stylesheet_tag }} {{ 'price.css' | stylesheet_tag }} {{ 'discounts.css' | stylesheet_tag }}

After (Only base.css blocks)

{{ 'base.css' | stylesheet_tag }} <link href="price-vat.css" media="print" onload="this.media='all'"> <link href="winsport.css" media="print" onload="this.media='all'"> <link href="cart-drawer.css" media="print" onload="this.media='all'">

7 render-blocking stylesheets → 1. The rest load after first paint.

content-visibility: auto

/* Skip rendering of below-fold sections */
.shopify-section:not(:first-child) {
  content-visibility: auto;
  contain-intrinsic-size: auto 500px;
}

/* CSS containment for card paint */
.card__media {
  will-change: auto;
  contain: layout style;
}
  • content-visibility: auto — browser skips layout, style, and paint computations for off-screen sections
  • contain-intrinsic-size: auto 500px — provides estimated height so scrollbar doesn’t jump (CLS prevention)
  • :not(:first-child) — hero section always renders immediately
  • contain: layout style on cards — isolates reflow/repaint to individual cards during hover animations
  • Browser remembers actual size after first render via the auto keyword

Image Pipeline Summary

<!-- image.liquid snippet -->
<img
  srcset="
    {{ image | image_url: 165 }} 165w,
    {{ image | image_url: 360 }} 360w,
    {{ image | image_url: 533 }} 533w,
    {{ image | image_url: 720 }} 720w,
    {{ image | image_url: 940 }} 940w,
    {{ image | image_url: 1066 }} 1066w"
  sizes="(min-width: 990px) 25vw,     (min-width: 750px) 33vw, 50vw"
  loading="lazy"
  decoding="async"
  width="{{ image.width }}"
  height="{{ image.height }}"
>
<!-- Hero: fetchpriority high -->
<img
  src="{{ 'hero-bg.jpg' | asset_url }}"
  loading="eager"
  fetchpriority="high"
  width="1920"
  height="1280"
>

<!-- Banner: dynamic priority -->
{{ image | image_tag:
  fetchpriority: fetch_priority,
  widths: '375,750,...,3840',
  sizes: '100vw'
}}

Key Takeaways

  • 1 render-blocking stylesheet instead of 8 — non-critical CSS deferred via print/onload pattern
  • content-visibility: auto on below-fold sections — browser skips layout/paint until scrolled
  • CLS prevention — contain-intrinsic-size, width/height on all images, aspect-ratio via CSS
  • LCP optimization — fetchpriority="high" on hero, eager loading, font preloads
  • CSS containment on cards — isolates reflow during hover animations
  • Dawn baseline preserved — srcset, WebP via CDN, responsive sizes, lazy loading below fold

WinSport Theme · Phase 8 Complete · Dawn v15.4.1 · Shopify 2.0