@zalify/storefront-kit 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -95,7 +95,7 @@ export function ProductCard({ product, loading = 'lazy' }) {
95
95
  const extraCount = pickerOption
96
96
  ? pickerOption.optionValues.length - valueLimit
97
97
  : 0;
98
- return (_jsxs("div", { className: "product-card", children: [_jsxs("div", { className: "product-card__frame", children: [_jsxs(Link, { to: url, className: "product-card__media", "aria-label": product.title, children: [featuredImage ? (_jsxs(_Fragment, { children: [_jsx("img", { className: "product-card__image", src: imageUrl(featuredImage.url, 800), srcSet: cardSrcSet(featuredImage.url), sizes: CARD_SIZES, loading: loading, alt: featuredImage.altText ?? '', width: featuredImage.width ?? undefined, height: featuredImage.height ?? undefined }), secondaryImage ? (_jsx("img", { className: "product-card__image product-card__image--hover", src: imageUrl(secondaryImage.url, 800), srcSet: cardSrcSet(secondaryImage.url), sizes: CARD_SIZES, loading: "lazy", alt: "", "aria-hidden": "true", width: secondaryImage.width ?? undefined, height: secondaryImage.height ?? undefined })) : null] })) : (_jsx(PlaceholderSvg, { className: "product-card__placeholder" })), preview ? (_jsx("img", { className: `product-card__image product-card__image--preview${preview.visible ? ' is-visible' : ''}`, src: preview.src, alt: "", "aria-hidden": "true", onLoad: () => setPreview((previous) => previous ? { ...previous, visible: true } : previous) })) : null, badgePosition === 'corner' && showBadges ? (_jsx("div", { className: "product-card__badges", children: _jsx(ProductBadges, { product: product }) })) : null] }), showQuickAdd ? (_jsx("div", { className: "product-card__quick product-card__quick--overlay", children: quickAdd })) : null] }), _jsxs("div", { className: "product-card__info stack", style: { '--stack-gap': 'var(--space-2xs)' }, children: [badgePosition === 'below' && showBadges ? (_jsx("div", { className: "product-card__badges", children: _jsx(ProductBadges, { product: product }) })) : null, _jsx("h3", { className: "product-card__title", children: _jsx(Link, { to: url, children: product.title }) }), _jsx(Price, { price: product.priceRange?.minVariantPrice, compareAt: product.compareAtPriceRange?.minVariantPrice, unitPrice: product.selectedOrFirstAvailableVariant?.unitPrice, unitPriceMeasurement: product.selectedOrFirstAvailableVariant?.unitPriceMeasurement }), themeSettings.card_show_rating ? _jsx(Rating, { product: product }) : null, pickerOption ? (_jsxs("div", { className: "product-card__swatches", onMouseOut: onSwatchLeave, children: [pickerOption.optionValues.slice(0, valueLimit).map((value) => {
98
+ return (_jsxs("div", { className: "product-card", children: [_jsxs("div", { className: "product-card__frame", children: [_jsxs(Link, { to: url, className: "product-card__media", "aria-label": product.title, children: [featuredImage ? (_jsxs(_Fragment, { children: [_jsx("img", { className: "product-card__image", src: imageUrl(featuredImage.url, 800), srcSet: cardSrcSet(featuredImage.url), sizes: CARD_SIZES, loading: loading, ...(loading === 'eager' ? { fetchpriority: 'high' } : null), alt: featuredImage.altText ?? '', width: featuredImage.width ?? undefined, height: featuredImage.height ?? undefined }), secondaryImage ? (_jsx("img", { className: "product-card__image product-card__image--hover", src: imageUrl(secondaryImage.url, 800), srcSet: cardSrcSet(secondaryImage.url), sizes: CARD_SIZES, loading: "lazy", alt: "", "aria-hidden": "true", width: secondaryImage.width ?? undefined, height: secondaryImage.height ?? undefined })) : null] })) : (_jsx(PlaceholderSvg, { className: "product-card__placeholder" })), preview ? (_jsx("img", { className: `product-card__image product-card__image--preview${preview.visible ? ' is-visible' : ''}`, src: preview.src, alt: "", "aria-hidden": "true", onLoad: () => setPreview((previous) => previous ? { ...previous, visible: true } : previous) })) : null, badgePosition === 'corner' && showBadges ? (_jsx("div", { className: "product-card__badges", children: _jsx(ProductBadges, { product: product }) })) : null] }), showQuickAdd ? (_jsx("div", { className: "product-card__quick product-card__quick--overlay", children: quickAdd })) : null] }), _jsxs("div", { className: "product-card__info stack", style: { '--stack-gap': 'var(--space-2xs)' }, children: [badgePosition === 'below' && showBadges ? (_jsx("div", { className: "product-card__badges", children: _jsx(ProductBadges, { product: product }) })) : null, _jsx("h3", { className: "product-card__title", children: _jsx(Link, { to: url, children: product.title }) }), _jsx(Price, { price: product.priceRange?.minVariantPrice, compareAt: product.compareAtPriceRange?.minVariantPrice, unitPrice: product.selectedOrFirstAvailableVariant?.unitPrice, unitPriceMeasurement: product.selectedOrFirstAvailableVariant?.unitPriceMeasurement }), themeSettings.card_show_rating ? _jsx(Rating, { product: product }) : null, pickerOption ? (_jsxs("div", { className: "product-card__swatches", onMouseOut: onSwatchLeave, children: [pickerOption.optionValues.slice(0, valueLimit).map((value) => {
99
99
  const targetVariant = targetVariantFor(product, pickerOption, value.name);
100
100
  const valueUrl = targetVariant
101
101
  ? `${url}?variant=${numericId(targetVariant.id)}`
@@ -1 +1,3 @@
1
- export declare function CssVariables(): import("react").JSX.Element;
1
+ export declare function CssVariables({ nonce }?: {
2
+ nonce?: string;
3
+ }): import("react").JSX.Element;
@@ -99,7 +99,17 @@ function fontLinks() {
99
99
  hrefs.push(googleFontsHref(monoGoogle, 'wght@400;500'));
100
100
  return hrefs;
101
101
  }
102
- export function CssVariables() {
102
+ // Promotes the preloaded font stylesheets to real stylesheets. Runs
103
+ // inline so the fonts start applying as soon as the HTML streams in,
104
+ // without the external CSS ever blocking first paint (display=swap
105
+ // keeps text visible on the fallback font meanwhile).
106
+ const FONT_CSS_LOADER = [
107
+ "for(var l=document.querySelectorAll('link[data-zfy-font-css]'),i=0;i<l.length;i++){",
108
+ "var s=document.createElement('link');",
109
+ "s.rel='stylesheet';s.href=l[i].href;",
110
+ 'document.head.appendChild(s)}',
111
+ ].join('');
112
+ export function CssVariables({ nonce } = {}) {
103
113
  const { css, links } = useMemo(() => ({ css: buildCss(), links: fontLinks() }), []);
104
- return (_jsxs(_Fragment, { children: [_jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }), links.map((href) => (_jsx("link", { rel: "stylesheet", href: href }, href))), _jsx("style", { dangerouslySetInnerHTML: { __html: css } })] }));
114
+ return (_jsxs(_Fragment, { children: [_jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous" }), links.map((href) => (_jsx("link", { rel: "preload", as: "style", href: href, "data-zfy-font-css": "" }, href))), _jsx("script", { nonce: nonce, dangerouslySetInnerHTML: { __html: FONT_CSS_LOADER } }), _jsx("noscript", { children: links.map((href) => (_jsx("link", { rel: "stylesheet", href: href }, href))) }), _jsx("style", { dangerouslySetInnerHTML: { __html: css } })] }));
105
115
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalify/storefront-kit",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "The Zalify storefront SDK: framework-agnostic commerce logic (/commerce), the theme contract types and validators (/schemas), the canvas-editor bridge (/editor), and the React theme engine + shared components (/ui, /react/server). Consumed as TypeScript source inside the zalify-storefronts monorepo; published as compiled ESM + d.ts.",
6
6
  "license": "SEE LICENSE IN LICENSE.md",
@@ -176,6 +176,9 @@ export function ProductCard({product, loading = 'lazy'}: ProductCardProps) {
176
176
  srcSet={cardSrcSet(featuredImage.url)}
177
177
  sizes={CARD_SIZES}
178
178
  loading={loading}
179
+ // Above-the-fold cards are LCP candidates on collection
180
+ // pages; lowercase attribute keeps React 18 compatible.
181
+ {...(loading === 'eager' ? {fetchpriority: 'high'} : null)}
179
182
  alt={featuredImage.altText ?? ''}
180
183
  width={featuredImage.width ?? undefined}
181
184
  height={featuredImage.height ?? undefined}
@@ -120,7 +120,18 @@ function fontLinks(): string[] {
120
120
  return hrefs;
121
121
  }
122
122
 
123
- export function CssVariables() {
123
+ // Promotes the preloaded font stylesheets to real stylesheets. Runs
124
+ // inline so the fonts start applying as soon as the HTML streams in,
125
+ // without the external CSS ever blocking first paint (display=swap
126
+ // keeps text visible on the fallback font meanwhile).
127
+ const FONT_CSS_LOADER = [
128
+ "for(var l=document.querySelectorAll('link[data-zfy-font-css]'),i=0;i<l.length;i++){",
129
+ "var s=document.createElement('link');",
130
+ "s.rel='stylesheet';s.href=l[i].href;",
131
+ 'document.head.appendChild(s)}',
132
+ ].join('');
133
+
134
+ export function CssVariables({nonce}: {nonce?: string} = {}) {
124
135
  const {css, links} = useMemo(
125
136
  () => ({css: buildCss(), links: fontLinks()}),
126
137
  [],
@@ -134,8 +145,20 @@ export function CssVariables() {
134
145
  crossOrigin="anonymous"
135
146
  />
136
147
  {links.map((href) => (
137
- <link key={href} rel="stylesheet" href={href} />
148
+ <link
149
+ key={href}
150
+ rel="preload"
151
+ as="style"
152
+ href={href}
153
+ data-zfy-font-css=""
154
+ />
138
155
  ))}
156
+ <script nonce={nonce} dangerouslySetInnerHTML={{__html: FONT_CSS_LOADER}} />
157
+ <noscript>
158
+ {links.map((href) => (
159
+ <link key={href} rel="stylesheet" href={href} />
160
+ ))}
161
+ </noscript>
139
162
  <style dangerouslySetInnerHTML={{__html: css}} />
140
163
  </>
141
164
  );