@tokenoftrust/storefront-runner 2.0.40 → 2.0.42
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/apps/storefront/integrations/lifecycle-host-asset.mjs +20 -6
- package/apps/storefront/package.json +1 -0
- package/apps/storefront/platform-reconcile-tenants.json +3 -0
- package/apps/storefront/public/favicon.ico +0 -0
- package/apps/storefront/public/platform/google-measurement/v2/entry.mjs +1 -1
- package/apps/storefront/public/shared/commerce-chrome.css +147 -16
- package/apps/storefront/src/components/AsyncStylesheet.astro +26 -0
- package/apps/storefront/src/components/Breadcrumbs.astro +8 -1
- package/apps/storefront/src/components/Img.astro +6 -5
- package/apps/storefront/src/components/ProductCard.astro +6 -4
- package/apps/storefront/src/components/Seo.astro +2 -1
- package/apps/storefront/src/components/chrome/SiteFooter.astro +45 -11
- package/apps/storefront/src/components/chrome/SiteHeader.astro +83 -13
- package/apps/storefront/src/components/commerce/RatingStars.astro +1 -1
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +2 -2
- package/apps/storefront/src/components/content/PromoTiles.astro +107 -0
- package/apps/storefront/src/components/content/TrustStrip.astro +30 -14
- package/apps/storefront/src/components/home/Hero.astro +156 -4
- package/apps/storefront/src/components/islands/AgeGate.tsx +135 -26
- package/apps/storefront/src/components/islands/ImageGallery.tsx +6 -9
- package/apps/storefront/src/components/islands/IsolatedAgeGate.tsx +194 -46
- package/apps/storefront/src/components/islands/SearchTypeahead.tsx +10 -4
- package/apps/storefront/src/components/marketing/Faq.astro +9 -2
- package/apps/storefront/src/config/devTenantSeed.ts +1 -1
- package/apps/storefront/src/config/kvTenantResolver.ts +7 -1
- package/apps/storefront/src/config/publicationSpaceConfig.ts +12 -1
- package/apps/storefront/src/config/publicationSpaceRegistry.ts +5 -0
- package/apps/storefront/src/config/tenants.ts +22 -0
- package/apps/storefront/src/layouts/Layout.astro +69 -44
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +24 -8
- package/apps/storefront/src/lib/activity/recordActivity.ts +15 -0
- package/apps/storefront/src/lib/asyncCss.ts +23 -0
- package/apps/storefront/src/lib/auth/loginGate.ts +22 -11
- package/apps/storefront/src/lib/basePath.ts +70 -9
- package/apps/storefront/src/lib/chrome/managedCart.ts +50 -0
- package/apps/storefront/src/lib/chrome/model.ts +4 -0
- package/apps/storefront/src/lib/chrome/newsletterAgeAttestation.ts +9 -0
- package/apps/storefront/src/lib/content/trustStrip.ts +38 -3
- package/apps/storefront/src/lib/cspStaticHashes.ts +4 -1
- package/apps/storefront/src/lib/diag/capture.ts +161 -0
- package/apps/storefront/src/lib/diag/consoleLine.ts +76 -0
- package/apps/storefront/src/lib/diag/debugConsole.ts +118 -0
- package/apps/storefront/src/lib/diag/debugMode.ts +163 -0
- package/apps/storefront/src/lib/diag/debugWindow.ts +111 -0
- package/apps/storefront/src/lib/diag/reference.ts +148 -0
- package/apps/storefront/src/lib/diag/referenceStore.ts +113 -0
- package/apps/storefront/src/lib/diag/report.ts +109 -0
- package/apps/storefront/src/lib/diag/session.ts +93 -0
- package/apps/storefront/src/lib/featuredProductsPresentation.ts +5 -0
- package/apps/storefront/src/lib/heroSlideshow.ts +41 -0
- package/apps/storefront/src/lib/homeVisualParity.ts +107 -0
- package/apps/storefront/src/lib/imageAttrs.ts +9 -6
- package/apps/storefront/src/lib/integration/nonceStamp.ts +23 -1
- package/apps/storefront/src/lib/integration/renderIntegrationState.ts +36 -0
- package/apps/storefront/src/lib/integration/tenantAnalyticsDeclarations.ts +17 -12
- package/apps/storefront/src/lib/jsonScript.ts +13 -0
- package/apps/storefront/src/lib/pinnedRouteRewriteBoundary.ts +31 -0
- package/apps/storefront/src/lib/previewReload.ts +53 -9
- package/apps/storefront/src/lib/rawChrome.ts +148 -19
- package/apps/storefront/src/lib/rawMarketingHtml.ts +120 -7
- package/apps/storefront/src/lib/storyblok/content-model.ts +46 -3
- package/apps/storefront/src/lib/styleGuideThemes.ts +107 -0
- package/apps/storefront/src/lib/tenantRouting.ts +15 -4
- package/apps/storefront/src/lib/tot/syncState.ts +21 -0
- package/apps/storefront/src/lib/tracking/publishSafetyNet.ts +29 -3
- package/apps/storefront/src/middleware/controlPlane.ts +9 -5
- package/apps/storefront/src/middleware/index.ts +13 -1
- package/apps/storefront/src/middleware/renderChain.ts +54 -11
- package/apps/storefront/src/middleware/routeBranch.ts +58 -12
- package/apps/storefront/src/middleware/staticBundle.ts +37 -3
- package/apps/storefront/src/pages/[...slug].astro +6 -2
- package/apps/storefront/src/pages/api/request-access.ts +5 -4
- package/apps/storefront/src/pages/blog/[slug].astro +11 -1
- package/apps/storefront/src/pages/cart.ts +23 -0
- package/apps/storefront/src/pages/index.astro +64 -85
- package/apps/storefront/src/pages/products/[handle].astro +8 -6
- package/apps/storefront/src/pages/sitemap.xml.ts +8 -2
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +14 -25
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +10 -17
- package/apps/storefront/src/pages/style-guide/[tenant]/guide/[theme].astro +9 -16
- package/apps/storefront/src/pages/style-guide/[tenant]/index.astro +9 -15
- package/apps/storefront/src/pages/the-build/[slug].astro +7 -1
- package/apps/storefront/src/styles/fonts.css +7 -0
- package/apps/storefront/src/styles/global.css +397 -3
- package/package.json +4 -1
- package/packages/cli/src/visual-parity.mjs +321 -0
- package/packages/public-runtime/package.json +2 -0
- package/packages/public-runtime/schemas/publication-space/v1/manifest.schema.json +1 -1
- package/packages/public-runtime/schemas/publication-space/v1/normalized-mount.schema.json +1 -1
- package/packages/public-runtime/src/chrome.ts +44 -7
- package/packages/public-runtime/src/csp.ts +12 -1
- package/packages/public-runtime/src/extension-contract-values.d.mts +19 -0
- package/packages/public-runtime/src/extension-contract-values.mjs +62 -0
- package/packages/public-runtime/src/extension-contract.ts +20 -6
- package/packages/public-runtime/src/index.ts +8 -0
- package/packages/public-runtime/src/integration/consent-broker.ts +10 -7
- package/packages/public-runtime/src/integration/consent-config.ts +9 -0
- package/packages/public-runtime/src/integration/contact-sales-action-source.ts +56 -10
- package/packages/public-runtime/src/integration/csp-compiler.ts +6 -3
- package/packages/public-runtime/src/integration/governed-event-properties.ts +62 -0
- package/packages/public-runtime/src/integration/index.ts +1 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +34 -6
- package/packages/public-runtime/src/integration/lifecycle-host-asset.d.mts +2 -0
- package/packages/public-runtime/src/integration/lifecycle-host-asset.mjs +17 -3
- package/packages/public-runtime/src/integration/native-amplitude.ts +12 -3
- package/packages/public-runtime/src/integration/native-ga4-sandbox-asset.d.mts +2 -0
- package/packages/public-runtime/src/integration/native-ga4-sandbox-asset.mjs +35 -1
- package/packages/public-runtime/src/integration/native-ga4-sandbox-entry.ts +45 -12
- package/packages/public-runtime/src/integration/native-ga4-sandbox.ts +28 -7
- package/packages/public-runtime/src/integration/native-ga4.ts +18 -2
- package/packages/public-runtime/src/integration/platform-adapters.ts +14 -3
- package/packages/public-runtime/src/integration/render-phases.ts +28 -2
- package/packages/public-runtime/src/publication-redirects.d.mts +45 -0
- package/packages/public-runtime/src/publication-redirects.mjs +379 -0
- package/packages/public-runtime/src/publication-space.ts +24 -3
- package/packages/public-runtime/src/static-bundle.ts +145 -26
- package/packages/public-runtime/src/tenant-embed-defaults.d.mts +3 -0
- package/packages/public-runtime/src/tenant-embed-defaults.mjs +40 -0
- package/packages/public-runtime/src/tenant.ts +59 -1
- package/pnpm-lock.runner.yaml +12 -0
- package/pnpm-workspace.yaml +4 -0
- package/scripts/dev/git-status.mjs +1 -1
- package/scripts/dev/publish.mjs +6 -0
- package/scripts/lib/tenant-asset-copy.mjs +4 -2
- package/scripts/tenant/materialize-tenant-content.mjs +1 -0
- package/scripts/tenant/validate.mjs +171 -8
- package/apps/storefront/src/lib/cfImage.ts +0 -55
- package/apps/storefront/src/workers/pruneRollupWorker.ts +0 -120
- package/tenants/home/public/fonts/inter-latin-400-normal.woff2 +0 -0
- package/tenants/home/public/fonts/inter-latin-500-normal.woff2 +0 -0
- package/tenants/home/public/fonts/inter-latin-600-normal.woff2 +0 -0
- package/tenants/home/public/fonts/inter-latin-ext-400-normal.woff2 +0 -0
- package/tenants/home/public/fonts/inter-latin-ext-500-normal.woff2 +0 -0
- package/tenants/home/public/fonts/inter-latin-ext-600-normal.woff2 +0 -0
- package/tenants/home/public/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/tenants/home/public/fonts/jetbrains-mono-latin-500-normal.woff2 +0 -0
- package/tenants/home/public/fonts/jetbrains-mono-latin-ext-400-normal.woff2 +0 -0
- package/tenants/home/public/fonts/jetbrains-mono-latin-ext-500-normal.woff2 +0 -0
- package/tenants/home/public/fonts/plus-jakarta-sans-latin-500-normal.woff2 +0 -0
- package/tenants/home/public/fonts/plus-jakarta-sans-latin-600-normal.woff2 +0 -0
- package/tenants/home/public/fonts/plus-jakarta-sans-latin-700-normal.woff2 +0 -0
- package/tenants/home/public/fonts/plus-jakarta-sans-latin-800-normal.woff2 +0 -0
- package/tenants/home/public/fonts/plus-jakarta-sans-latin-ext-500-normal.woff2 +0 -0
- package/tenants/home/public/fonts/plus-jakarta-sans-latin-ext-600-normal.woff2 +0 -0
- package/tenants/home/public/fonts/plus-jakarta-sans-latin-ext-700-normal.woff2 +0 -0
- package/tenants/home/public/fonts/plus-jakarta-sans-latin-ext-800-normal.woff2 +0 -0
- package/tenants/home/public/js/storefront.js +0 -129
- package/tenants/home/public/logo-wordmark.png +0 -0
- package/tenants/home/public/pages/storefront.css +0 -235
- package/tenants/home/theme.json +0 -6
|
@@ -59,6 +59,19 @@
|
|
|
59
59
|
--color-field-border: var(--color-field-border, color-mix(in srgb, var(--color-border) 52%, var(--color-text)));
|
|
60
60
|
--color-star: var(--color-star, var(--color-accent));
|
|
61
61
|
|
|
62
|
+
/* Conversion-role aliases (style-concept.md §Semantic tokens). The palette
|
|
63
|
+
above names a COLOR; these name the JOB a color does on a conversion
|
|
64
|
+
surface, so a reskin can move the buy button without dragging chrome,
|
|
65
|
+
headings and ::selection with it (all of which read --color-primary).
|
|
66
|
+
Each defaults to the palette role it plays today, so nothing renders
|
|
67
|
+
differently until a tenant supplies one — a tenant brand-tunes by emitting
|
|
68
|
+
color.cta / color.cta-hover / color.cta-contrast / color.trust, which
|
|
69
|
+
themeToCssVars serializes like any other --color-<k>. */
|
|
70
|
+
--color-cta: var(--color-cta, var(--color-primary));
|
|
71
|
+
--color-cta-hover: var(--color-cta-hover, var(--color-primary-hover));
|
|
72
|
+
--color-cta-contrast: var(--color-cta-contrast, var(--color-primary-contrast));
|
|
73
|
+
--color-trust: var(--color-trust, var(--color-success));
|
|
74
|
+
|
|
62
75
|
--font-display: var(--font-display, "Space Grotesk"), system-ui, sans-serif;
|
|
63
76
|
--font-body: var(--font-body, "Source Serif 4"), Georgia, serif;
|
|
64
77
|
--font-mono: var(--font-mono, "Space Mono"), ui-monospace, monospace;
|
|
@@ -85,6 +98,21 @@
|
|
|
85
98
|
injected --tap-target (schema.ts, from shape.tapTarget) wins when present,
|
|
86
99
|
else 44px. Components size touch controls with var(--tap-target). (F6.) */
|
|
87
100
|
--tap-target: var(--tap-target, 44px);
|
|
101
|
+
|
|
102
|
+
/* Role aliases that are NOT colors and so cannot live in @theme (Tailwind
|
|
103
|
+
v4 owns the --text-* namespace for font sizes — a --text-muted there would
|
|
104
|
+
collide with the existing `text-muted` color utility). Declared here for
|
|
105
|
+
the same reason --tap-target is, and read by the conversion components
|
|
106
|
+
below so they survive a reskin:
|
|
107
|
+
--focus-ring the whole keyboard-focus outline, not just its color, so
|
|
108
|
+
a theme can change the ring's weight/style too
|
|
109
|
+
--surface-card raised card/panel fill (testimonials, proof cards)
|
|
110
|
+
--text-muted secondary copy (help text, attributions, fine print)
|
|
111
|
+
Each resolves through an optional --color-<k> override first, so a theme
|
|
112
|
+
brand-tunes it over the normal token channel without a new mechanism. */
|
|
113
|
+
--focus-ring: var(--border-thick, 2px) solid var(--color-focus-ring);
|
|
114
|
+
--surface-card: var(--color-surface-card, var(--color-surface));
|
|
115
|
+
--text-muted: var(--color-text-muted, var(--color-muted));
|
|
88
116
|
}
|
|
89
117
|
|
|
90
118
|
html {
|
|
@@ -124,7 +152,9 @@
|
|
|
124
152
|
dedicated --color-focus-ring token (defaults to the accent spark) so a theme
|
|
125
153
|
can brand-tune the ring without touching the accent used elsewhere. */
|
|
126
154
|
:focus-visible {
|
|
127
|
-
|
|
155
|
+
/* --focus-ring is the whole outline; its own default is built from
|
|
156
|
+
--color-focus-ring, so the dedicated ring color still drives it. */
|
|
157
|
+
outline: var(--focus-ring, var(--border-thick, 2px) solid var(--color-focus-ring));
|
|
128
158
|
outline-offset: 2px;
|
|
129
159
|
border-radius: 2px;
|
|
130
160
|
}
|
|
@@ -136,6 +166,36 @@
|
|
|
136
166
|
|
|
137
167
|
img { max-width: 100%; height: auto; }
|
|
138
168
|
|
|
169
|
+
/* Disabled + busy controls (style-concept.md §Full interactive states).
|
|
170
|
+
--color-disabled / --color-disabled-contrast were declared for exactly this
|
|
171
|
+
and nothing read them, so an unstyled `<button disabled>` was pixel-identical
|
|
172
|
+
to a live one. This is a @layer base default: any component rule or Tailwind
|
|
173
|
+
utility (utilities layer) still wins, so controls that already carry their
|
|
174
|
+
own disabled treatment are untouched.
|
|
175
|
+
Checkboxes, radios, ranges, colors and file inputs are excluded — a
|
|
176
|
+
background-color on those replaces the native widget rather than tinting it. */
|
|
177
|
+
:where(button, [role="button"], a, input, select, textarea):disabled,
|
|
178
|
+
:where(button, [role="button"], a, input, select, textarea)[aria-disabled="true"] {
|
|
179
|
+
cursor: not-allowed;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
:is(
|
|
183
|
+
button,
|
|
184
|
+
[role="button"],
|
|
185
|
+
select,
|
|
186
|
+
textarea,
|
|
187
|
+
input:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"])
|
|
188
|
+
):disabled {
|
|
189
|
+
background-color: var(--color-disabled);
|
|
190
|
+
color: var(--color-disabled-contrast);
|
|
191
|
+
border-color: var(--color-disabled);
|
|
192
|
+
box-shadow: none;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Loading/busy: the control holds its place and stops accepting input. */
|
|
196
|
+
[aria-busy="true"] { cursor: progress; }
|
|
197
|
+
:is(button, [role="button"], form, fieldset)[aria-busy="true"] { pointer-events: none; }
|
|
198
|
+
|
|
139
199
|
/* Honor reduced motion (brief §6/§8). */
|
|
140
200
|
@media (prefers-reduced-motion: reduce) {
|
|
141
201
|
*, *::before, *::after {
|
|
@@ -147,6 +207,22 @@
|
|
|
147
207
|
}
|
|
148
208
|
}
|
|
149
209
|
|
|
210
|
+
@layer components {
|
|
211
|
+
.skip-link {
|
|
212
|
+
position: fixed;
|
|
213
|
+
inset-block-start: 0;
|
|
214
|
+
inset-inline-start: 0;
|
|
215
|
+
z-index: 2147483646;
|
|
216
|
+
transform: translateY(-150%);
|
|
217
|
+
border-radius: var(--radius-md);
|
|
218
|
+
background: var(--color-primary);
|
|
219
|
+
color: var(--color-primary-contrast);
|
|
220
|
+
padding: .75rem 1rem;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.skip-link:focus-visible { transform: translateY(0); }
|
|
224
|
+
}
|
|
225
|
+
|
|
150
226
|
/* ---- shared component primitives ----------------------------------------- */
|
|
151
227
|
@layer components {
|
|
152
228
|
.container-prose {
|
|
@@ -158,7 +234,7 @@
|
|
|
158
234
|
|
|
159
235
|
.eyebrow {
|
|
160
236
|
font-family: var(--font-mono);
|
|
161
|
-
font-size: 0.
|
|
237
|
+
font-size: 0.875rem;
|
|
162
238
|
letter-spacing: 0.16em;
|
|
163
239
|
text-transform: uppercase;
|
|
164
240
|
color: var(--color-muted);
|
|
@@ -249,7 +325,10 @@
|
|
|
249
325
|
.link-accent {
|
|
250
326
|
color: var(--color-text);
|
|
251
327
|
background-image: linear-gradient(var(--color-accent-ink), var(--color-accent-ink));
|
|
252
|
-
|
|
328
|
+
/* The underline sits against the CONTENT box, so `.link-accent--cta`'s
|
|
329
|
+
padding cannot push it away from the text. */
|
|
330
|
+
background-origin: content-box;
|
|
331
|
+
background-position: 0 calc(100% + 2px);
|
|
253
332
|
background-repeat: no-repeat;
|
|
254
333
|
background-size: 100% 1.5px;
|
|
255
334
|
transition: color 0.2s ease, background-size 0.25s cubic-bezier(0.22, 1, 0.36, 1);
|
|
@@ -258,6 +337,17 @@
|
|
|
258
337
|
.link-accent:focus-visible {
|
|
259
338
|
color: var(--color-accent-ink);
|
|
260
339
|
}
|
|
340
|
+
/* Standalone CTA form — a link on its own line, so it carries the
|
|
341
|
+
--tap-target minimum. Vertical padding on an INLINE box grows the hit area
|
|
342
|
+
and the measured rect without adding a pixel to the line box, so nothing
|
|
343
|
+
around it moves; keep these links inline, because an inline-block would lay
|
|
344
|
+
that padding out. 0.875rem clears 44px over the 18px content area of a
|
|
345
|
+
14px/1.25 line (measured in Chrome at 390px). Deliberately NOT on the base
|
|
346
|
+
class: `.link-accent` also marks up links inside a sentence, where the
|
|
347
|
+
padding would reach into the hit area of the line above. */
|
|
348
|
+
.link-accent--cta {
|
|
349
|
+
padding-block: 0.875rem;
|
|
350
|
+
}
|
|
261
351
|
@media (prefers-reduced-motion: no-preference) {
|
|
262
352
|
.link-accent { background-size: 0% 1.5px; }
|
|
263
353
|
.link-accent:hover,
|
|
@@ -310,3 +400,307 @@
|
|
|
310
400
|
to { opacity: 1; transform: none; }
|
|
311
401
|
}
|
|
312
402
|
}
|
|
403
|
+
|
|
404
|
+
/* ---- conversion patterns -------------------------------------------------- */
|
|
405
|
+
/*
|
|
406
|
+
* The CTA hierarchy, trust/social-proof and accessible-form families the
|
|
407
|
+
* ecommerce quality bar asks of every theme
|
|
408
|
+
* (playbook/site-migration/style-concept.md §"Ecommerce quality bar"), built
|
|
409
|
+
* once at platform level so every tenant inherits them rather than re-deriving
|
|
410
|
+
* them per migration.
|
|
411
|
+
*
|
|
412
|
+
* Every value reads a contract token, so a reskin moves the whole set and a
|
|
413
|
+
* tenant never hand-writes a hex here.
|
|
414
|
+
*
|
|
415
|
+
* The class names are deliberately NOT `.btn` / `.field`: admin.css and several
|
|
416
|
+
* page-scoped <style> blocks already own those, and a bare `.btn` here would
|
|
417
|
+
* reach into them.
|
|
418
|
+
*/
|
|
419
|
+
@layer components {
|
|
420
|
+
/* --- CTA hierarchy -------------------------------------------------------
|
|
421
|
+
Three weights so a page can carry exactly ONE primary ask and route every
|
|
422
|
+
other action down a tier:
|
|
423
|
+
.btn-primary the conversion action (buy, start, book) — one per view
|
|
424
|
+
.btn-secondary the supporting action (compare, learn more)
|
|
425
|
+
.btn-tertiary the low-commitment out (quiet/text; nav, footer, cards)
|
|
426
|
+
Placement rules for hero / nav / pricing / footer / mobile-sticky live in
|
|
427
|
+
the playbook; the treatments live here. */
|
|
428
|
+
.btn-primary,
|
|
429
|
+
.btn-secondary,
|
|
430
|
+
.btn-tertiary {
|
|
431
|
+
display: inline-flex;
|
|
432
|
+
align-items: center;
|
|
433
|
+
justify-content: center;
|
|
434
|
+
gap: 0.5em;
|
|
435
|
+
/* WCAG 2.2 §2.5.8 — the same minimum every other touch control uses. */
|
|
436
|
+
min-height: var(--tap-target);
|
|
437
|
+
padding: 0.75rem 1.5rem;
|
|
438
|
+
border: var(--border-thin, 1px) solid transparent;
|
|
439
|
+
border-radius: var(--radius-full);
|
|
440
|
+
font-family: var(--font-display);
|
|
441
|
+
font-size: 0.875rem;
|
|
442
|
+
font-weight: 500;
|
|
443
|
+
line-height: 1.2;
|
|
444
|
+
letter-spacing: 0.08em;
|
|
445
|
+
text-transform: uppercase;
|
|
446
|
+
text-align: center;
|
|
447
|
+
cursor: pointer;
|
|
448
|
+
transition:
|
|
449
|
+
background-color 0.2s ease,
|
|
450
|
+
border-color 0.2s ease,
|
|
451
|
+
color 0.2s ease;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.btn-primary {
|
|
455
|
+
background-color: var(--color-cta);
|
|
456
|
+
color: var(--color-cta-contrast);
|
|
457
|
+
}
|
|
458
|
+
.btn-primary:hover { background-color: var(--color-cta-hover); }
|
|
459
|
+
.btn-primary:active {
|
|
460
|
+
background-color: color-mix(in srgb, var(--color-cta-hover) 90%, #000);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.btn-secondary {
|
|
464
|
+
background-color: transparent;
|
|
465
|
+
border-color: var(--color-cta);
|
|
466
|
+
color: var(--color-cta);
|
|
467
|
+
}
|
|
468
|
+
.btn-secondary:hover {
|
|
469
|
+
background-color: color-mix(in srgb, var(--color-cta) 10%, transparent);
|
|
470
|
+
border-color: var(--color-cta-hover);
|
|
471
|
+
color: var(--color-cta-hover);
|
|
472
|
+
}
|
|
473
|
+
.btn-secondary:active {
|
|
474
|
+
background-color: color-mix(in srgb, var(--color-cta) 18%, transparent);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.btn-tertiary {
|
|
478
|
+
background-color: transparent;
|
|
479
|
+
padding-inline: 0.5rem;
|
|
480
|
+
color: var(--color-text);
|
|
481
|
+
text-decoration: underline;
|
|
482
|
+
text-decoration-color: transparent;
|
|
483
|
+
text-underline-offset: 4px;
|
|
484
|
+
transition: color 0.2s ease, text-decoration-color 0.2s ease;
|
|
485
|
+
}
|
|
486
|
+
.btn-tertiary:hover {
|
|
487
|
+
color: var(--color-accent-ink);
|
|
488
|
+
text-decoration-color: currentColor;
|
|
489
|
+
}
|
|
490
|
+
.btn-tertiary:active { color: var(--color-cta-hover); }
|
|
491
|
+
|
|
492
|
+
/* Busy CTA: holds its box (no reflow mid-submit), reads as inactive, and the
|
|
493
|
+
base layer has already removed pointer events. */
|
|
494
|
+
.btn-primary[aria-busy="true"],
|
|
495
|
+
.btn-secondary[aria-busy="true"],
|
|
496
|
+
.btn-tertiary[aria-busy="true"] {
|
|
497
|
+
opacity: 0.7;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/* Full-width CTA — the mobile/sticky and in-card form. */
|
|
501
|
+
.btn-block { display: flex; width: 100%; }
|
|
502
|
+
|
|
503
|
+
/* --- Trust & social proof ------------------------------------------------
|
|
504
|
+
Boundary-safe SHELLS only. They style a claim; they never assert one. Copy
|
|
505
|
+
stays placeholder or `needs-review` until sourced (style-concept.md §Trust
|
|
506
|
+
and social proof components). */
|
|
507
|
+
|
|
508
|
+
/* Horizontal strip of short proof points under a hero or above a footer. */
|
|
509
|
+
.proof-strip {
|
|
510
|
+
display: flex;
|
|
511
|
+
flex-wrap: wrap;
|
|
512
|
+
align-items: center;
|
|
513
|
+
justify-content: center;
|
|
514
|
+
gap: 1rem 2rem;
|
|
515
|
+
padding-block: 1rem;
|
|
516
|
+
color: var(--text-muted);
|
|
517
|
+
font-family: var(--font-mono);
|
|
518
|
+
font-size: 0.8125rem;
|
|
519
|
+
letter-spacing: 0.08em;
|
|
520
|
+
text-transform: uppercase;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/* Review / rating display. The stars are decorative; the accessible value
|
|
524
|
+
belongs in the .rating__value text, never in color alone. */
|
|
525
|
+
.rating {
|
|
526
|
+
display: inline-flex;
|
|
527
|
+
align-items: center;
|
|
528
|
+
gap: 0.5em;
|
|
529
|
+
}
|
|
530
|
+
.rating__stars {
|
|
531
|
+
color: var(--color-star);
|
|
532
|
+
letter-spacing: 0.08em;
|
|
533
|
+
line-height: 1;
|
|
534
|
+
}
|
|
535
|
+
.rating__value {
|
|
536
|
+
color: var(--text-muted);
|
|
537
|
+
font-family: var(--font-mono);
|
|
538
|
+
font-size: 0.8125rem;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
/* Customer testimonial / pull quote. */
|
|
542
|
+
.testimonial {
|
|
543
|
+
display: flex;
|
|
544
|
+
flex-direction: column;
|
|
545
|
+
gap: 0.75rem;
|
|
546
|
+
padding: 1.5rem;
|
|
547
|
+
border: var(--border-thin, 1px) solid var(--color-border);
|
|
548
|
+
border-radius: var(--radius-lg);
|
|
549
|
+
background: var(--surface-card);
|
|
550
|
+
box-shadow: var(--shadow-sm);
|
|
551
|
+
}
|
|
552
|
+
.testimonial__quote {
|
|
553
|
+
margin: 0;
|
|
554
|
+
font-size: var(--fs-md, 1.125rem);
|
|
555
|
+
line-height: 1.5;
|
|
556
|
+
text-wrap: pretty;
|
|
557
|
+
}
|
|
558
|
+
.testimonial__attribution {
|
|
559
|
+
display: flex;
|
|
560
|
+
align-items: center;
|
|
561
|
+
gap: 0.6rem;
|
|
562
|
+
color: var(--text-muted);
|
|
563
|
+
font-family: var(--font-mono);
|
|
564
|
+
font-size: 0.8125rem;
|
|
565
|
+
}
|
|
566
|
+
.testimonial__avatar {
|
|
567
|
+
flex: none;
|
|
568
|
+
width: 2.25rem;
|
|
569
|
+
height: 2.25rem;
|
|
570
|
+
border-radius: var(--radius-full);
|
|
571
|
+
object-fit: cover;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/* Security / compliance / certification badge. Reads --color-trust so the
|
|
575
|
+
reassurance palette moves independently of the CTA palette. */
|
|
576
|
+
.trust-badge {
|
|
577
|
+
display: inline-flex;
|
|
578
|
+
align-items: center;
|
|
579
|
+
gap: 0.45em;
|
|
580
|
+
padding: 0.35rem 0.7rem;
|
|
581
|
+
border: var(--border-thin, 1px) solid
|
|
582
|
+
color-mix(in srgb, var(--color-trust) 42%, transparent);
|
|
583
|
+
border-radius: var(--radius-full);
|
|
584
|
+
background: color-mix(in srgb, var(--color-trust) 10%, transparent);
|
|
585
|
+
color: var(--color-trust);
|
|
586
|
+
font-family: var(--font-mono);
|
|
587
|
+
font-size: 0.6875rem;
|
|
588
|
+
letter-spacing: 0.12em;
|
|
589
|
+
text-transform: uppercase;
|
|
590
|
+
white-space: nowrap;
|
|
591
|
+
}
|
|
592
|
+
.trust-badge__icon {
|
|
593
|
+
flex: none;
|
|
594
|
+
width: 0.95em;
|
|
595
|
+
height: 0.95em;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/* Guarantee / returns / refund / warranty callout — the risk-reversal line
|
|
599
|
+
that sits next to the buy button. */
|
|
600
|
+
.reassurance {
|
|
601
|
+
display: flex;
|
|
602
|
+
align-items: flex-start;
|
|
603
|
+
gap: 0.6rem;
|
|
604
|
+
padding: 0.75rem 1rem;
|
|
605
|
+
border-inline-start: var(--border-thick, 2px) solid var(--color-trust);
|
|
606
|
+
border-radius: var(--radius-sm);
|
|
607
|
+
background: color-mix(in srgb, var(--color-trust) 7%, transparent);
|
|
608
|
+
font-size: 0.875rem;
|
|
609
|
+
line-height: 1.45;
|
|
610
|
+
}
|
|
611
|
+
.reassurance__icon {
|
|
612
|
+
flex: none;
|
|
613
|
+
width: 1.1em;
|
|
614
|
+
height: 1.1em;
|
|
615
|
+
margin-top: 0.15em;
|
|
616
|
+
color: var(--color-trust);
|
|
617
|
+
}
|
|
618
|
+
.reassurance__note {
|
|
619
|
+
display: block;
|
|
620
|
+
color: var(--text-muted);
|
|
621
|
+
font-size: 0.8125rem;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/* "As seen in" / customer-logo strip. Logos are desaturated at rest so they
|
|
625
|
+
read as texture, not as endorsements competing with the CTA. */
|
|
626
|
+
.logo-strip {
|
|
627
|
+
display: flex;
|
|
628
|
+
flex-wrap: wrap;
|
|
629
|
+
align-items: center;
|
|
630
|
+
justify-content: center;
|
|
631
|
+
gap: 1.25rem 2.5rem;
|
|
632
|
+
padding-block: 1.25rem;
|
|
633
|
+
}
|
|
634
|
+
.logo-strip__item {
|
|
635
|
+
max-height: 2rem;
|
|
636
|
+
width: auto;
|
|
637
|
+
filter: grayscale(1);
|
|
638
|
+
opacity: 0.65;
|
|
639
|
+
transition: filter 0.2s ease, opacity 0.2s ease;
|
|
640
|
+
}
|
|
641
|
+
.logo-strip__item:hover {
|
|
642
|
+
filter: none;
|
|
643
|
+
opacity: 1;
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
/* --- Accessible forms ----------------------------------------------------
|
|
647
|
+
Lead-gen / contact / newsletter fields with every state the contract names:
|
|
648
|
+
label, help text, required + optional markers, validation error, success,
|
|
649
|
+
disabled and loading (the last two come from the base layer). Errors are
|
|
650
|
+
announced through aria-describedby + aria-invalid, never color alone. */
|
|
651
|
+
.form-field {
|
|
652
|
+
display: grid;
|
|
653
|
+
gap: 0.375rem;
|
|
654
|
+
}
|
|
655
|
+
.form-field__label {
|
|
656
|
+
font-family: var(--font-mono);
|
|
657
|
+
font-size: 0.75rem;
|
|
658
|
+
letter-spacing: 0.1em;
|
|
659
|
+
text-transform: uppercase;
|
|
660
|
+
color: var(--color-text);
|
|
661
|
+
}
|
|
662
|
+
/* Required/optional markers. The word is the signal; the color is decoration,
|
|
663
|
+
so the marker text must stay in the DOM for screen readers. */
|
|
664
|
+
.form-field__required { color: var(--color-error); }
|
|
665
|
+
.form-field__optional { color: var(--text-muted); font-weight: 400; }
|
|
666
|
+
|
|
667
|
+
.form-control {
|
|
668
|
+
width: 100%;
|
|
669
|
+
min-height: var(--tap-target);
|
|
670
|
+
padding: 0.625rem 0.85rem;
|
|
671
|
+
border: var(--border-thin, 1px) solid var(--color-field-border);
|
|
672
|
+
border-radius: var(--radius-md);
|
|
673
|
+
background: var(--color-surface);
|
|
674
|
+
color: var(--color-text);
|
|
675
|
+
font-family: inherit;
|
|
676
|
+
font-size: 1rem; /* >=16px: iOS zooms the page on focus below this. */
|
|
677
|
+
line-height: 1.4;
|
|
678
|
+
transition: border-color 0.15s ease;
|
|
679
|
+
}
|
|
680
|
+
.form-control::placeholder { color: var(--text-muted); }
|
|
681
|
+
.form-control:hover { border-color: var(--color-text); }
|
|
682
|
+
|
|
683
|
+
.form-control[aria-invalid="true"] { border-color: var(--color-error); }
|
|
684
|
+
.form-control[data-state="success"] { border-color: var(--color-success); }
|
|
685
|
+
|
|
686
|
+
/* Help text — the persistent hint, wired with aria-describedby. */
|
|
687
|
+
.form-field__help {
|
|
688
|
+
color: var(--text-muted);
|
|
689
|
+
font-size: 0.8125rem;
|
|
690
|
+
line-height: 1.4;
|
|
691
|
+
}
|
|
692
|
+
/* Validation messages. Both carry a glyph so the state survives a
|
|
693
|
+
monochrome/color-blind read. */
|
|
694
|
+
.form-field__error,
|
|
695
|
+
.form-field__success {
|
|
696
|
+
display: flex;
|
|
697
|
+
align-items: flex-start;
|
|
698
|
+
gap: 0.4em;
|
|
699
|
+
font-size: 0.8125rem;
|
|
700
|
+
line-height: 1.4;
|
|
701
|
+
}
|
|
702
|
+
.form-field__error { color: var(--color-error); }
|
|
703
|
+
.form-field__error::before { content: "\26A0"; }
|
|
704
|
+
.form-field__success { color: var(--color-success); }
|
|
705
|
+
.form-field__success::before { content: "\2713"; }
|
|
706
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/storefront-runner",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.42",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE",
|
|
5
5
|
"description": "World-shareable storefront runner: multi-tenant renderer on Astro/Cloudflare. No control plane.",
|
|
6
6
|
"packageManager": "pnpm@11.9.0",
|
|
@@ -17,6 +17,9 @@
|
|
|
17
17
|
"typecheck": "pnpm -r typecheck",
|
|
18
18
|
"depcruise": "pnpm --filter @tot/storefront-runner depcruise"
|
|
19
19
|
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"sharp": "^0.34.5"
|
|
22
|
+
},
|
|
20
23
|
"devDependencies": {
|
|
21
24
|
"typescript": "^5.6.3"
|
|
22
25
|
},
|