@zalify/storefront-kit 0.1.0 → 0.1.2
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.
- package/dist/react/components/ProductCard.js +1 -1
- package/dist/react/engine/CssVariables.d.ts +3 -1
- package/dist/react/engine/CssVariables.js +12 -2
- package/dist/react/engine/fonts.js +6 -1
- package/package.json +1 -1
- package/src/react/components/ProductCard.tsx +3 -0
- package/src/react/engine/CssVariables.tsx +25 -2
- package/src/react/engine/fonts.ts +6 -1
|
@@ -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)}`
|
|
@@ -99,7 +99,17 @@ function fontLinks() {
|
|
|
99
99
|
hrefs.push(googleFontsHref(monoGoogle, 'wght@400;500'));
|
|
100
100
|
return hrefs;
|
|
101
101
|
}
|
|
102
|
-
|
|
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
|
}
|
|
@@ -29,5 +29,10 @@ export function parseFontHandle(handle) {
|
|
|
29
29
|
}
|
|
30
30
|
export function googleFontsHref(family, axes = 'ital,wght@0,400;0,500;0,700;1,400') {
|
|
31
31
|
const param = family.trim().replace(/ /g, '+');
|
|
32
|
-
|
|
32
|
+
// display=optional: the font never swaps in after first paint, so a
|
|
33
|
+
// late-arriving font file can't reflow text (zero font CLS). With the
|
|
34
|
+
// stylesheet loaded async (CssVariables), swap would otherwise shift
|
|
35
|
+
// layout on every cold load; optional shows the fallback first visit
|
|
36
|
+
// and the brand font from the cache on later views.
|
|
37
|
+
return `https://fonts.googleapis.com/css2?family=${param}:${axes}&display=optional`;
|
|
33
38
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zalify/storefront-kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
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
|
-
|
|
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
|
|
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
|
);
|
|
@@ -38,5 +38,10 @@ export function googleFontsHref(
|
|
|
38
38
|
axes = 'ital,wght@0,400;0,500;0,700;1,400',
|
|
39
39
|
): string {
|
|
40
40
|
const param = family.trim().replace(/ /g, '+');
|
|
41
|
-
|
|
41
|
+
// display=optional: the font never swaps in after first paint, so a
|
|
42
|
+
// late-arriving font file can't reflow text (zero font CLS). With the
|
|
43
|
+
// stylesheet loaded async (CssVariables), swap would otherwise shift
|
|
44
|
+
// layout on every cold load; optional shows the fallback first visit
|
|
45
|
+
// and the brand font from the cache on later views.
|
|
46
|
+
return `https://fonts.googleapis.com/css2?family=${param}:${axes}&display=optional`;
|
|
42
47
|
}
|