Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
50e8b45
Perf: Make scroll event listeners passive
Mar 16, 2026
e577139
Perf: Disable slide-reveal on mobile
Mar 16, 2026
68c63c7
Perf: Defer predictive-search.js loading
Mar 16, 2026
1e1ba8b
Perf: Optimize mobile image sizes to 50vw
Mar 16, 2026
41edaae
Optimize responsive images sizes on mobile
Mar 16, 2026
bc5cc1b
Perf: Lazy-load predictive search JS on intent
Mar 16, 2026
fe29deb
Perf: Add mobile toggle to disable heavy reveals and transitions
Mar 16, 2026
cb4600f
Merge pull request #1 from Rivka-Development/ST/scroll-listeners
Rivka-Development Mar 18, 2026
b002272
Merge pull request #2 from Rivka-Development/ST/reveal-animations
Rivka-Development Mar 18, 2026
a4947ad
Merge pull request #3 from Rivka-Development/ST/background-scripts
Rivka-Development Mar 18, 2026
66c6335
Merge pull request #4 from Rivka-Development/ST/responsive-images
Rivka-Development Mar 18, 2026
0e80358
Merge branch 'Dovetail26' of https://github.com/Rivka-Development/Dov…
Mar 18, 2026
b54fc05
predictive-search-lazyload
Mar 18, 2026
c83ce0a
Merge branch 'Dovetail26' into ST/predictive-search-lazyload
Rivka-Development Mar 18, 2026
5836053
Merge pull request #5 from Rivka-Development/ST/predictive-search-laz…
Rivka-Development Mar 18, 2026
61e6ff6
Optimize responsive images, LCP priority, and deferred mobile hydration
Mar 19, 2026
378a0a6
Merge pull request #6 from Rivka-Development/ST/responsive-images-DPR…
Rivka-Development Mar 19, 2026
2460bd1
Fine-tune performance: restore responsive images logic, add decoding=…
Mar 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
369 changes: 182 additions & 187 deletions assets/base.css

Large diffs are not rendered by default.

68 changes: 68 additions & 0 deletions assets/deferred-hydration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Deferred Hydration Utility
* Hydrates content when it enters the viewport on mobile.
*/

import { onDocumentReady } from '@theme/utilities';

class DeferredHydration extends HTMLElement {
constructor() {
super();
this.observer = null;
}

connectedCallback() {
onDocumentReady(() => {
const mobileOnly = this.hasAttribute('mobile-only');
const isMobile = window.innerWidth < 750;

if (mobileOnly && !isMobile) {
this.hydrate();
return;
}

// If it's already in viewport or we want to be safe, use IntersectionObserver
this.observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
this.hydrate();
this.observer.disconnect();
}
});
}, { rootMargin: '200px' });

this.observer.observe(this);
});
}

hydrate() {
const template = this.querySelector('template');
if (template) {
const content = template.content.cloneNode(true);
this.appendChild(content);
// Remove template to avoid re-hydration
template.remove();
this.setAttribute('hydrated', '');

// Look for any scripts that might need a nudge
this.querySelectorAll('script').forEach(script => {
const newScript = document.createElement('script');
Array.from(script.attributes).forEach(attr => newScript.setAttribute(attr.name, attr.value));
newScript.innerHTML = script.innerHTML;
if (script.parentNode) {
script.parentNode.replaceChild(newScript, script);
}
});
}
}

disconnectedCallback() {
if (this.observer) {
this.observer.disconnect();
}
}
}

if (!customElements.get('deferred-hydration')) {
customElements.define('deferred-hydration', DeferredHydration);
}
2 changes: 1 addition & 1 deletion assets/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ class HeaderComponent extends Component {
this.#observeStickyPosition(stickyMode === 'always');

if (stickyMode === 'scroll-up' || stickyMode === 'always') {
document.addEventListener('scroll', this.#handleWindowScroll);
document.addEventListener('scroll', this.#handleWindowScroll, { passive: true });
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions assets/localization.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class LocalizationFormComponent extends Component {

this.refs.search && this.refs.search.addEventListener('keydown', this.#onSearchKeyDown);
this.refs.countryList && this.refs.countryList.addEventListener('keydown', this.#onContainerKeyDown);
this.refs.countryList && this.refs.countryList.addEventListener('scroll', this.#onCountryListScroll);
this.refs.countryList && this.refs.countryList.addEventListener('scroll', this.#onCountryListScroll, { passive: true });

// Resizing the language input can be expensive for browsers that don't support field-sizing: content.
// Spliting it into separate tasks at least helps when there are multiple localization forms on the page.
Expand Down Expand Up @@ -515,7 +515,7 @@ class DrawerLocalizationComponent extends Component {
const countryList = localizationForm.querySelector('.country-selector-form__wrapper');

if (target.open) {
if (countryList) countryList.addEventListener('scroll', this.#onCountryListScroll);
if (countryList) countryList.addEventListener('scroll', this.#onCountryListScroll, { passive: true });
onAnimationEnd(target, localizationForm.focusSearchInput);
} else {
countryList?.removeEventListener('scroll', this.#onCountryListScroll);
Expand Down
32 changes: 16 additions & 16 deletions assets/scrolling.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class Scroller {
this.#onScrollEnd = options.onScrollEnd;

this.element = element;
this.element.addEventListener('scroll', this.#handleScroll);
this.element.addEventListener('scroll', this.#handleScroll, { passive: true });
}

/**
Expand Down Expand Up @@ -342,25 +342,25 @@ export function scrollIntoView(element, { ancestor, behavior = 'smooth', block =
const scrollTop =
ancestor.scrollHeight > ancestor.clientHeight
? calculateScrollOffset(
block,
ancestorRect.top,
ancestor.clientHeight,
elemRect.top,
elemRect.height,
ancestor.scrollTop
)
block,
ancestorRect.top,
ancestor.clientHeight,
elemRect.top,
elemRect.height,
ancestor.scrollTop
)
: ancestor.scrollTop;

const scrollLeft =
ancestor.scrollWidth > ancestor.clientWidth
? calculateScrollOffset(
inline,
ancestorRect.left,
ancestor.clientWidth,
elemRect.left,
elemRect.width,
ancestor.scrollLeft
)
inline,
ancestorRect.left,
ancestor.clientWidth,
elemRect.left,
elemRect.width,
ancestor.scrollLeft
)
: ancestor.scrollLeft;

ancestor.scrollTo({ top: scrollTop, left: scrollLeft, behavior });
Expand All @@ -372,7 +372,7 @@ class ScrollHint extends HTMLElement {
#rafId = null;

connectedCallback() {
this.addEventListener('scroll', this.#handleScroll);
this.addEventListener('scroll', this.#handleScroll, { passive: true });
this.#resizeObserver.observe(this);
}

Expand Down
4 changes: 2 additions & 2 deletions assets/zoom-dialog.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class ZoomDialog extends Component {

connectedCallback() {
super.connectedCallback();
this.refs.dialog.addEventListener('scroll', this.handleScroll);
this.refs.dialog.addEventListener('scroll', this.handleScroll, { passive: true });
}

disconnectedCallback() {
Expand Down Expand Up @@ -275,7 +275,7 @@ function getMostVisibleElement(elements) {
current.intersectionRatio > prev.intersectionRatio ? current : prev
);
observer.disconnect();
resolve(/** @type {HTMLElement} */ (mostVisible.target));
resolve(/** @type {HTMLElement} */(mostVisible.target));
},
{
threshold: Array.from({ length: 100 }, (_, i) => i / 100),
Expand Down
11 changes: 9 additions & 2 deletions blocks/_layered-slide.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,21 @@
{%- liquid
assign height = preview_image.width | divided_by: preview_image.aspect_ratio | round
assign sizes = '100vw'
assign widths = '832, 1200, 1600, 1920, 2560, 3840'
assign widths = '375, 450, 660, 900, 1320, 1800, 2136, 2400, 3000, 3600, 3840'
assign loading = 'lazy'
assign fetchpriority = 'auto'

if section.index == 1 and block_index == 0
assign loading = 'eager'
assign fetchpriority = 'high'
endif
-%}

{%- if block.settings.media_type_1 == 'image' -%}
{{
block.settings.image_1
| image_url: width: 3840
| image_tag: height: height, sizes: sizes, widths: widths, class: 'layered-slideshow__image', loading: loading
| image_tag: height: height, sizes: sizes, widths: widths, class: 'layered-slideshow__image', loading: loading, fetchpriority: fetchpriority
}}
{%- else -%}
{%- if block.settings.video_1.preview_image -%}
Expand All @@ -48,6 +54,7 @@
sizes: sizes,
widths: widths,
loading: loading,
fetchpriority: fetchpriority,
class: 'layered-slideshow__video-poster'
}}
{%- endif -%}
Expand Down
6 changes: 5 additions & 1 deletion blocks/accordion.liquid
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
"
{{ block.shopify_attributes }}
>
{% content_for 'blocks' %}
<deferred-hydration data-mobile-only="true">
<template>
{% content_for 'blocks' %}
</template>
</deferred-hydration>
</div>

{% stylesheet %}
Expand Down
Loading