@orderlyshop/web-components 0.1.0-build.7067 → 0.1.0-build.7068

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/AGENTS.md CHANGED
@@ -5,35 +5,19 @@ Use this package as a headless storefront component library. Prefer native custo
5
5
  ## Agent Operating Contract
6
6
 
7
7
  - These instructions are optimized for GPT-5.5-class coding agents and other modern LLMs. Work from the user's requested storefront outcome, choose the package-owned component or CLI that owns that outcome, and keep edits scoped to that path.
8
- - Read stable package context before shop-specific files: `README.md`, `DESIGN.md`, component docs, then `custom-elements.json` or `html-custom-data.json` for exact API names. Inspect the consuming shop after that.
9
8
  - Prefer package configuration, typed properties, declarative templates, semantic tokens, and package CLI tools over copying component internals into a shop.
10
9
  - Do not infer attribute names, event names, template slots, or CSS hooks from memory. Use the docs and metadata in this package as the source of truth.
11
- - Stop when the requested behavior is implemented and verified with the nearest relevant script. If validation needs data, account ids, credentials, or a running backend, report the exact missing prerequisite.
12
-
13
- ## Discovery
14
-
15
- - Read `README.md` for setup and package-wide concepts.
16
- - Read `DESIGN.md` before changing the visual theme. It defines the semantic design tokens that should be changed first.
17
- - Read `docs/components/README.md` for component APIs.
18
- - Read `docs/shop-best-practices.md` before building or heavily customizing a shop. Use it as the checklist for moving from the scaffold to a branded, editorial storefront while preserving package-owned components.
19
- - Read `docs/components/product-rail.md` and `docs/components/product-grid.md` before implementing product listing UI.
20
- - Read `custom-elements.json` for machine-readable custom element metadata.
21
- - Read `html-custom-data.json` when generating vanilla HTML or configuring editor autocomplete.
22
- - Inspect `examples/shop` for a complete vanilla TypeScript shop that customizes the defaults without replacing package logic.
23
- - When creating a new shop from scratch, run `npx orderly-init-shop` first. It scaffolds the vanilla HTML/TypeScript/Vite structure that the package expects, including `src/includes/head.html`, `src/navigation.ts`, `src/shop-query.ts`, page templates, and package scripts.
24
-
25
- ## New Shop Intake
26
-
27
- - Ask the user for the Orderly account id so `src/shop-query.ts` can tenant-scope the default `SearchQuery`.
28
- - Ask the user for a reference website URL for the desired visual style. If they do not have one, ask for a short description of the target look.
29
- - Use the reference website as design direction only: extract palette, density, radius, typography feel, spacing, and navigation patterns into semantic Orderly tokens and shop CSS. Do not copy proprietary assets, text, logos, or brand marks unless the user confirms they own or may use them.
30
- - Do not deliver a shop that mostly looks like the package default with minor token changes. The default scaffold is a functional baseline, not the creative target.
31
- - Inspect the product catalog and use online research when available to understand the shop, category, product type, audience, competitors, and current visual conventions before committing to a design direction.
32
- - Build a distinctive first version: homepage and category pages should combine package components with shop-owned editorial sections, catalog-driven product groupings, contextual copy, category storytelling, and imagery or content choices that make the shop feel specific.
33
- - Use `docs/shop-best-practices.md` as the practical standard for the first version, especially when deciding what belongs in shop CSS/config/slots versus custom component logic.
34
- - Research current high-end frontend execution before finalizing when network access is available. Aim for the technical polish expected from premium product marketing sites: responsive art direction, strong imagery, smooth interaction states, thoughtful motion where appropriate, performance, and accessibility. Use this as a quality bar, not as permission to copy a specific site.
35
- - Put the resulting theme in `src/style.css` or the consuming shop stylesheet, starting with `DESIGN.md` tokens before adding component-specific selectors.
36
- - After scaffolding, run the nearest local dev/build command and visually compare the shop against the requested reference at desktop and mobile widths when a browser is available.
10
+ - Stop only when the behavior is implemented, verified with the nearest relevant script, and visually inspected in a browser when available. If validation needs data, account ids, credentials, a browser, or a running backend, report the exact missing prerequisite.
11
+
12
+ ## Required Reading and Intake
13
+
14
+ - Read `README.md`, `DESIGN.md`, `docs/shop-best-practices.md`, `docs/components/README.md`, then `custom-elements.json` or `html-custom-data.json` before shop-specific edits. Read `docs/components/product-rail.md` and `docs/components/product-grid.md` before product listing work.
15
+ - Use `docs/shop-best-practices.md` as the completion checklist for branded shop work. A finished shop must not look like the default scaffold with minor token changes.
16
+ - For new shops, run `npx orderly-init-shop` first. Ask for the Orderly account id and a reference website URL; if no reference exists, ask for a short style description.
17
+ - Use the account id for tenant-scoped `src/shop-query.ts`. Use the reference, catalog inspection, and online research as design direction for `src/style.css`; never copy proprietary assets, text, logos, or brand marks without user confirmation.
18
+ - Build the first version around catalog-driven content: homepage sections, category introductions, product context, useful navigation, and a visual system based on `DESIGN.md` tokens plus scoped CSS.
19
+ - Aim for current premium frontend craft: responsive art direction, strong imagery, clear interaction states, appropriate motion, performance, and accessibility.
20
+ - Before reporting completion, run the local dev/build command and visually inspect homepage, category, product, basket, and checkout on desktop and mobile when a browser is available.
37
21
 
38
22
  ## Query Rules
39
23
 
@@ -56,32 +40,25 @@ Use this package as a headless storefront component library. Prefer native custo
56
40
 
57
41
  ## Implementation Defaults
58
42
 
59
- - Keep components headless. Add behavior and default light-DOM markup, not bundled theme CSS.
60
- - Start theme work from the semantic tokens in `DESIGN.md`. Use component-local selectors and hooks only for deliberate exceptions after the shared tokens are set.
61
- - Treat visual differentiation as part of the implementation, not a later polish task. Configure package components, slots, templates, and shop CSS so the first working shop already matches the design target as closely as possible.
62
- - Prefer shop-owned editorial page sections around package components over replacing package templates wholesale. Preserve package-owned product tiles, checkout, product detail behavior, image viewers, structured data, accessibility, and generated URLs unless the user explicitly asks for a different structure.
43
+ - Keep components headless. Prefer configuration, typed properties, templates, slots, semantic tokens, and scoped shop CSS over copying package internals.
44
+ - Start theme work from `DESIGN.md`. Visual differentiation is part of the first implementation, not later polish.
45
+ - Add shop-owned editorial sections around package components. Preserve package-owned product tiles, checkout, product detail behavior, image viewers, structured data, accessibility, and generated URLs unless the user explicitly asks for a different structure.
63
46
  - Built-in UI copy defaults to Danish. Use `configureShop({ uiLanguage: "EN" })` for English shops, and prefer shop attributes/templates for wording that is unique to one storefront.
64
- - Preserve template override support through `data-orderly-template`, `data-orderly-bind`, `data-orderly-slot`, and `data-orderly-action`.
65
- - Put shop-wide templates in a shared document-level registry, for example template files included from `body-start.html`, using `data-orderly-template` plus `data-orderly-for`.
66
- - Keep browser-only package code free of Node HTTP modules.
67
- - Do not store tokens, API keys, OAuth responses, or cookie values.
68
- - Use `configureShop({ consent })` before enabling optional analytics, marketing, personalization, or shop-owned cookies. Keep cookie/vendor/purpose/duration text accurate for the specific shop, and use `setOrderlyCookie()` or `hasCookieConsent()` for custom optional browser state instead of writing cookies directly.
69
- - Treat localStorage basket and checkout profile data as user-owned state. Do not add secret persistence.
47
+ - Preserve `data-orderly-template`, `data-orderly-bind`, `data-orderly-slot`, and `data-orderly-action`. Put shared templates in a document-level registry using `data-orderly-for`.
48
+ - Keep browser code free of Node HTTP modules. Do not store tokens, API keys, OAuth responses, cookie values, or other secrets.
49
+ - Use `configureShop({ consent })` before enabling optional analytics, marketing, personalization, or shop-owned cookies. Use `setOrderlyCookie()` or `hasCookieConsent()` for optional browser state.
50
+ - Treat localStorage basket and checkout profile data as user-owned state.
70
51
  - Product images should default to `fit="contain"`, white background, and square aspect ratio in product tiles.
71
52
  - Use `orderly-stored-image` for package/product images when possible and set the appropriate `image-role` (`product-tile`, `product-detail`, `thumbnail`, `basket-line`, `checkout-line`, or `fullsize`) so image URLs reuse configured CDN dimensions.
72
53
  - Product grids should support continuation-token based dynamic paging without requiring a visible load-more button.
73
54
  - Use `orderly-home-page` for default storefront homepages instead of generating product rails in app code. Set `basket-mode="inline-desktop"` when the design needs a package-owned desktop basket rail instead of custom portal glue.
74
55
  - Use `orderly-category-page` for category routes. Default category links should use `/categories/<category-path>/` so builds can generate and hydrate server-rendered category pages. Use `/category.html#id=<encoded-category-path-or-slug>` only for shops that explicitly opt into hash routing. Prefer navigation `heroImage`, `heroLayout: "covers"`, `heroCoverCount`, and category `description` before replacing the category hero template.
75
56
  - Keep `orderly-navigation` sticky by default. Use `sticky="false"` only when a shop intentionally wants navigation to scroll away. Use `persist-state state-key="main-menu"` for vertical desktop menus that should remember open branches, and use `slot="menu-before"`/`slot="menu-after"` for store finder, campaign, volunteer, or other menu promo links.
76
- - Use `<orderly-basket-icon href="/checkout.html" placement="mobile-fixed" hide-when-empty>` for a package-owned mobile cart/checkout button with count before building a shop-specific fixed basket badge.
77
- - Use `orderly-checkout-page` for checkout pages instead of wiring `orderly-checkout` and `orderly-basket` manually in app code.
57
+ - Use package-owned basket and checkout surfaces first: `<orderly-basket-icon href="/checkout.html" placement="mobile-fixed" hide-when-empty>`, `basket-mode="inline-desktop"`, and `orderly-checkout-page`.
78
58
  - Use `orderly-product-detail-page` for full product routes. Set `share-url` to the product `SearchObject.ShareURL.URL`; the component loads the product through `SearchQuery.ShareUrl`.
79
59
  - Keep category page generation in the normal shop build. Use `orderly-build-category-pages` without `--no-hydrate` so homepage/category pages get initial HTML for SEO and less visible loading shift. Pass `--base-url` or set `VITE_ORDERLY_BASE_URL` to override the default `https://service.orderly.shop` snapshot backend.
80
60
  - For real product URLs on traditional hosting, use the package-owned helpers in `server/`: Apache `.htaccess`, Nginx rewrite config, PHP product renderer, and optional Node product snapshot endpoint. Do not copy this logic into example-shop.
81
- - Use `navigation.ts` for shop category/navigation structure. Export `navigationDefinitions: NavigationDefinition[]`.
82
- - Use `npx orderly-init-navigation` to create the default file or `npx orderly-init-navigation --suggest --account-id <account-id>` to dump sampled product data for agent-assisted navigation design.
83
- - Navigation definitions are recursive but should stay at most two category levels deep for normal shops. Each item must include `label` and a stable globally unique `slug`, and can include optional `heroImage`, optional `heroLayout`, optional `heroCoverCount`, optional `description`, optional `query`, and optional `children`.
84
- - Use plain `SearchQueryInput` objects in navigation definitions, for example `query: { query: "sko sneakers", tags: ["sko"] }`.
61
+ - Use `navigation.ts` for category/navigation structure. Run `npx orderly-init-navigation --suggest --account-id <account-id>` when sampling helps, then export `navigationDefinitions: NavigationDefinition[]` with stable unique slugs, at most two levels, optional hero/content fields, and plain `SearchQueryInput` values such as `query: { query: "sko sneakers", tags: ["sko"] }`.
85
62
 
86
63
  ## Common Patterns
87
64
 
package/README.md CHANGED
@@ -24,40 +24,17 @@ Inputs:
24
24
 
25
25
  Use any provided input above without asking again. Ask only for values that are still `ASK`. Treat Google Tag Manager container id `NONE` or an empty value as an explicit choice to leave GTM/analytics disabled.
26
26
 
27
- Install and setup:
28
- 1. Install @orderlyshop/core-client and @orderlyshop/web-components if they are not already installed.
29
- 2. Read node_modules/@orderlyshop/web-components/AGENTS.md, DESIGN.md, README.md, docs/components/README.md, and docs/shop-best-practices.md before changing shop files.
30
- 3. Run npx orderly-init-shop for a new shop. Pass --account-id when the Orderly account id is provided. Preserve existing files unless I explicitly approve overwrite.
31
- 4. Google Tag Manager is optional. If Google Tag Manager container id is `NONE` or empty, leave GTM/analytics disabled and do not ask for a container id. If it is provided and is not `ASK`, wire it through `VITE_ORDERLY_GOOGLE_TAG_MANAGER_ID` and package config rather than pasting raw GTM snippets. Use `configureShop({ consent, analytics })`, set `consent.policyHref` from Cookie policy URL, set `analytics.dataLayerName` when Google Tag Manager data layer name is not `dataLayer`, and set `analytics.consentCategories` from Analytics consent categories. Keep package Google Consent Mode enabled unless I explicitly ask for a custom CMP/tag setup.
32
-
33
- Navigation:
34
- 5. Use the Orderly account id to tenant-scope src/shop-query.ts.
35
- 6. Run npx orderly-init-navigation --suggest --account-id <account-id> when product sampling is possible.
36
- 7. Build src/navigation.ts manually from navigation-products.json with at most two category levels, stable unique slugs, and plain SearchQueryInput objects.
37
-
38
- Visual style:
39
- 8. Use the visual style reference as design direction for palette, radius, spacing, density, typography feel, product tiles, navigation, checkout, and page rhythm.
40
- 9. Do not ship the package default look and feel with minor color changes. Read the package docs, inspect the catalog, research the reference site and comparable modern shops, then create a distinct visual direction that matches the design target.
41
- 10. Build an editorially useful storefront: make the homepage and category pages visually and textually interesting with catalog-driven product groupings, contextual copy, category storytelling, and researched context for the shop, category, or product type.
42
- 11. Research current high-end frontend patterns when network access is available, and aim for the technical polish of a premium product marketing site: strong art direction, responsive composition, high-quality imagery, smooth interaction states, thoughtful motion where appropriate, and excellent performance/accessibility.
43
- 12. Start src/style.css from DESIGN.md semantic tokens. Add component-specific selectors only for details that tokens cannot express.
44
- 13. Do not copy proprietary assets, text, logos, or brand marks from the reference or research sources unless I explicitly confirm they may be used.
45
-
46
- Pages and behavior:
47
- 14. Use package-owned components and configuration: orderly-home-page, orderly-category-page, orderly-product-detail-page, orderly-checkout-page, configureShop(...), templates, and the generated Vite setup.
48
- 15. Keep product/category SSR and generated category URLs working unless the deployment target requires a different routing approach.
49
-
50
- Deployment:
51
- 16. Configure the build/publish flow for the deployment target.
52
- - For static hosting, use npm run build and document the dist upload.
53
- - For FTP, use npx orderly-publish-site --target ftp.
54
- - For Apache/PHP or Nginx/PHP, keep the package-generated server renderers and document the required server helper files.
55
- - For any other target, document the exact build output and any remaining deployment assumptions.
56
-
57
- Validation:
58
- 17. Run the nearest relevant build/test command.
59
- 18. Start the dev server when useful and inspect desktop and mobile layouts.
60
- 19. Report what changed, what was verified, and any missing backend, credential, research, analytics, or deployment prerequisite.
27
+ Workflow:
28
+ 1. Install @orderlyshop/core-client and @orderlyshop/web-components if needed. Run npx orderly-init-shop for a new shop, pass --account-id when available, and preserve existing files unless I approve overwrite.
29
+ 2. Read node_modules/@orderlyshop/web-components/AGENTS.md, DESIGN.md, README.md, docs/components/README.md, and docs/shop-best-practices.md before changing shop files. Treat docs/shop-best-practices.md as a completion checklist, not optional inspiration.
30
+ 3. Scope the shop with the Orderly account id in src/shop-query.ts. When product sampling is possible, run npx orderly-init-navigation --suggest --account-id <account-id>, then build src/navigation.ts manually with stable unique slugs, at most two category levels, and plain SearchQueryInput objects.
31
+ 4. Use package-owned components and configuration first: orderly-home-page, orderly-category-page, orderly-product-detail-page, orderly-checkout-page, configureShop(...), templates, generated Vite setup, generated category URLs, and package SSR helpers.
32
+ 5. Use the visual style reference as design direction, not as copy material. Start src/style.css from DESIGN.md semantic tokens, then add scoped component selectors only where tokens are insufficient.
33
+ 6. Do not ship the package default look and feel with minor color changes. Inspect the catalog, research the reference site and comparable modern shops, and create homepage/category/product context that is visually and editorially specific to this shop.
34
+ 7. Aim for premium modern frontend craft: strong art direction, responsive composition, high-quality imagery, clear interaction states, appropriate motion, performance, and accessibility. Do not copy proprietary assets, text, logos, or brand marks unless I explicitly confirm they may be used.
35
+ 8. Google Tag Manager is optional. Treat Google Tag Manager container id `NONE` or an empty value as an explicit choice to leave GTM/analytics disabled. If a container id is provided and is not `ASK`, wire it through `VITE_ORDERLY_GOOGLE_TAG_MANAGER_ID` and package config rather than raw GTM snippets. Use `configureShop({ consent, analytics })`, Cookie policy URL, Google Tag Manager data layer name, and Analytics consent categories from the inputs.
36
+ 9. Configure the build/publish flow for the deployment target: npm run build for static hosting, npx orderly-publish-site --target ftp for FTP, package server helpers for Apache/PHP or Nginx/PHP, or explicit documented assumptions for other targets.
37
+ 10. Run the nearest relevant build/test command. Start the dev server when a browser is available and visually inspect homepage, category, product, basket, and checkout on desktop and mobile before declaring the shop done. Report command and visual verification plus any missing backend, credential, research, analytics, browser, or deployment prerequisite.
61
38
  ```
62
39
 
63
40
  ## Install
@@ -82,7 +59,7 @@ Agents and developers should use `npx orderly-init-shop` before hand-writing sho
82
59
  npx orderly-init-shop --account-id 00000000-0000-0000-0000-000000000000
83
60
  ```
84
61
 
85
- For agent-assisted shop creation, ask for both the Orderly account id and a reference website URL for visual style before customizing the scaffold. Use the account id for `src/shop-query.ts`; use the reference website to choose semantic design tokens, radius, spacing, navigation treatment, and component-level exceptions in `src/style.css`. Do not stop at the package default look and feel with a few token changes. Read the package docs, inspect the catalog, research the reference site and comparable modern shops when network access is available, and create a distinctive visual direction that matches the design target from the first pass. The homepage and category pages should be visually and editorially useful: combine catalog-driven sections with contextual copy, category storytelling, and researched context for the shop, category, or product type. Aim for the level of polish expected from premium product marketing sites, including responsive composition, strong imagery, thoughtful interaction states, appropriate motion, performance, and accessibility. Treat the reference and research as design direction, not as permission to copy proprietary assets, text, logos, or brand marks.
62
+ For agent-assisted shop creation, ask for both the Orderly account id and a reference website URL for visual style before customizing the scaffold. Use the account id for `src/shop-query.ts`, use the reference website and catalog research to shape `src/style.css`, and follow [`docs/shop-best-practices.md`](./docs/shop-best-practices.md) as the completion checklist. The first pass should already feel visually distinct, editorially useful, and technically polished; it should not look like the default package scaffold with small token changes.
86
63
 
87
64
  Re-running `npx orderly-init-shop` in an existing shop is non-destructive by default. Existing scaffold files are left unchanged with warnings, while missing new scaffold files are added. Use `--force` only when you explicitly want scaffold files overwritten.
88
65
 
@@ -205,7 +182,7 @@ Developers and coding agents should start with these package-owned docs:
205
182
  - [`custom-elements.json`](./custom-elements.json) is the Custom Elements Manifest for tools that understand web component metadata.
206
183
  - [`html-custom-data.json`](./html-custom-data.json) is VS Code compatible custom data for HTML autocomplete and hover docs.
207
184
 
208
- These docs are written for GPT-5.5-class coding agents as well as humans: start with the outcome the shop needs, use the package-owned API surface, and verify with the nearest package or shop script before changing broader app structure.
185
+ These docs are written for GPT-5.5-class coding agents as well as humans: start with the outcome the shop needs, use the package-owned API surface, follow `docs/shop-best-practices.md` as the shop quality bar, and verify with both the nearest package/shop script and visual browser inspection before calling a shop finished.
209
186
 
210
187
  Enable HTML editor hints in a consuming project by adding the package custom data file to `.vscode/settings.json`:
211
188
 
@@ -430,22 +407,17 @@ npx orderly-init-navigation --path src/navigation.ts
430
407
 
431
408
  Agent workflow for a new shop:
432
409
 
433
- 1. Ask the user for the new shop's Orderly account id.
434
- 2. Ask the user for a reference website URL for the desired visual style, or a short style description when no reference site exists.
435
- 3. Run the navigation helper to dump sampled product information:
410
+ 1. Ask for the Orderly account id and a reference website URL for the desired visual style, or a short style description when no reference site exists.
411
+ 2. Run the navigation helper when product sampling is possible:
436
412
 
437
413
  ```sh
438
414
  npx orderly-init-navigation --suggest --account-id <account-id>
439
415
  ```
440
416
 
441
- 4. Read the generated `navigation-products.json`. The helper does not infer categories. It writes sampled product records with `title`, `brand`, and `tags` only, plus sampling metadata.
442
- 5. Create `navigation.ts` manually from the dump. Export `navigationDefinitions: NavigationDefinition[]`. Each entry must have `label` and a stable, globally unique `slug`, and can also have optional `heroImage`, optional `heroLayout`, optional `heroCoverCount`, optional `description`, optional `query`, and optional recursive `children`. Use plain `SearchQueryInput` objects for `query`, such as `{ query: "sko sneakers", tags: ["sko"] }`.
443
- 6. Keep navigation to at most two category levels. Categories should be reasonably granular, but avoid empty categories or categories backed by only a few products.
444
- 7. Configure the shop scope with a Core `SearchQuery` and pass it to `configureShop({ defaultShop: { defaultQuery } })`.
445
- 8. Translate the reference website into `src/style.css` by setting `DESIGN.md` semantic tokens first, then add scoped component selectors only for visual details the tokens cannot express.
446
- 9. Do not leave the shop looking like the package scaffold. Use the sampled catalog, the visual reference, and online research when available to create a distinct visual and editorial concept for the homepage, category pages, and product context.
447
- 10. Add useful shop-owned content around package components where appropriate: editorial hero sections, curated category introductions, product-context sections, buying guidance, trust content, and category-specific imagery or copy derived from the catalog and research.
448
- 11. Research current high-end frontend execution before finalizing when network access is available. The result should feel technically polished, responsive, and intentional, with strong art direction, smooth interaction states, appropriate motion, performance, and accessibility.
417
+ 3. Create `navigation.ts` manually from `navigation-products.json`. Export `navigationDefinitions: NavigationDefinition[]`; each item needs `label`, a globally unique `slug`, optional hero/content fields, optional `children`, and plain `SearchQueryInput` values such as `{ query: "sko sneakers", tags: ["sko"] }`. Keep navigation to at most two category levels and avoid empty or tiny categories.
418
+ 4. Configure shop scope with a Core `SearchQuery` through `configureShop({ defaultShop: { defaultQuery } })`.
419
+ 5. Read and follow `docs/shop-best-practices.md`, translate the reference into `DESIGN.md` tokens plus scoped CSS, and add homepage/category content from catalog and research.
420
+ 6. Run the dev server when a browser is available and visually inspect homepage, category, product, basket, and checkout on desktop and mobile. Fix visible issues before reporting completion; visual QA is required for finished shop work.
449
421
 
450
422
  The helper is intentionally an explicit tool for developers and coding agents. It is not a postinstall action. Agents should keep user-owned navigation edits, ask before overwriting files, and treat the product dump as source material for human-reviewable navigation.
451
423
 
@@ -505,7 +477,7 @@ Build with generated category pages and then clean the generated source files. S
505
477
  npx orderly-build-category-pages
506
478
  ```
507
479
 
508
- `orderly-build-category-pages` also hydrates the built `dist/index.html` and generated `dist/categories/**/index.html` pages with SEO-friendly fallback HTML. The hydration step embeds semantic product cards per category using `SearchService.Search`; it reads `--base-url`, `VITE_ORDERLY_BASE_URL`, or `ORDERLY_BASE_URL`, and otherwise uses the default `https://service.orderly.shop` backend. Live web components read the semantic fallback as initial state, hide it, and then continue as normal client components.
480
+ `orderly-build-category-pages` also hydrates the built `dist/index.html` and generated `dist/categories/**/index.html` pages with SEO-friendly fallback HTML. The hydration step embeds semantic product cards per category using `SearchService.Search`; it reads `--base-url`, `VITE_ORDERLY_BASE_URL`, or `ORDERLY_BASE_URL`, and otherwise uses the default `https://service.orderly.shop` backend. Live web components read the semantic fallback as initial visible state, hide the raw fallback markup, and then continue as normal client components. Product grids still run their first live `SearchService.Search()` after hydration when a client or backend URL is configured, so fallback products are replaced by current backend data and continuation-token paging starts from the live result set.
509
481
 
510
482
  Hydrate an already-built site manually, for example in a nightly job:
511
483
 
@@ -599,7 +571,7 @@ Runtime options:
599
571
  - `ORDERLY_SSR_HEADERS` can add internal server-side request headers. Do not expose API keys or bearer tokens to browser JavaScript.
600
572
  - `ORDERLY_SSR_MANIFEST` can point PHP to a manifest outside the web root.
601
573
 
602
- The semantic fallback uses real headings, links, images, schema.org Product/Offer metadata, and small `data-orderly-*` attributes only for behavior-critical values. Components hydrate from that HTML, then hide the fallback and continue as normal interactive web components. Generated SSR output keeps the same HTML for users and crawlers, hides fallback visibility for normal browser hydration, and serves a visible fallback layout to Googlebot. See [`server/README.md`](./server/README.md) for details.
574
+ The semantic fallback uses real headings, links, images, schema.org Product/Offer metadata, and small `data-orderly-*` attributes only for behavior-critical values. Components hydrate from that HTML, then hide the fallback and continue as normal interactive web components. Product grids treat fallback products as temporary visual content and replace them with the first live search result when a backend client is available; without a live search target, the fallback remains visible. Generated SSR output keeps the same HTML for users and crawlers, hides fallback visibility for normal browser hydration, and serves a visible fallback layout to Googlebot. See [`server/README.md`](./server/README.md) for details.
603
575
 
604
576
  ## Define Elements
605
577
 
@@ -4925,7 +4925,7 @@ button.orderly-product-page__image-button:active {
4925
4925
  <span class="orderly-payment-result-page__line-title" data-orderly-bind="title"></span>
4926
4926
  <span class="orderly-payment-result-page__line-quantity" data-orderly-bind="quantity"></span>
4927
4927
  <${e} class="orderly-payment-result-page__line-price" data-orderly-bind="price"></${e}>
4928
- `,Qe(o,"thumbnail",t.Images[0]),ve(o,"thumbnail",ge(t.Images[0],"thumbnail",aA)),h(o,"title",t.Title??t.SKU?.Value??c("itemFallbackTitle")),h(o,"quantity",`${c("quantityShortLabel")} ${t.Quantity??0}`),ie(o,"price",O(t.Price??t.TotalItemPrice??t.PricePerPiece)),o}function dA(t,e,r){t.querySelectorAll("[data-orderly-order-card]").forEach(o=>{o instanceof HTMLElement&&(o.hidden=!e)}),t.querySelectorAll(".orderly-payment-result-page__empty").forEach(o=>{o instanceof HTMLElement&&(o.hidden=e)}),t.querySelectorAll('[data-orderly-bind="paymentLink"]').forEach(o=>{o instanceof HTMLAnchorElement&&(r?(o.href=r,o.hidden=!1):(o.removeAttribute("href"),o.hidden=!0))})}function og(t){return t?.Reference??t?.Payment?.Reference??t?.Id?.Id?.Value??""}function cA(){return typeof location>"u"?"":new URL(location.href).searchParams.get("orderid")??""}function uA(){return typeof location>"u"?"/":location.pathname}var Ps="orderly-product-tile",Ao=class extends L{static observedAttributes=["base-url","paging","sort-label","clear-search-label","error-label","loading-label","results-label","searching-label",...Ke];currentQuery;currentDefaultQuery;currentProducts=[];continuation;loading=!1;error;observer;totalCount=0;estimatedTotalCount;controller;currentSortOptions=[];currentInitialProducts;initialProductsConsumed=!1;paging="button";productTileTagName=Ps;get defaultQuery(){return this.currentDefaultQuery}set defaultQuery(e){this.currentDefaultQuery=e,this.refresh()}get shopQuery(){return this.defaultQuery}set shopQuery(e){this.defaultQuery=e}get sortOptions(){return[...this.currentSortOptions]}set sortOptions(e){this.currentSortOptions=[...e],this.requestRender()}get sortLabel(){return this.getAttribute("sort-label")??c("sortLabel")}set sortLabel(e){this.setAttribute("sort-label",e)}get clearSearchLabel(){return this.getAttribute("clear-search-label")??c("clearSearchLabel")}set clearSearchLabel(e){this.setAttribute("clear-search-label",e)}get errorLabel(){return this.getAttribute("error-label")??c("productsLoadError")}set errorLabel(e){this.setAttribute("error-label",e)}get loadingLabel(){return this.getAttribute("loading-label")??c("loadingLabel")}set loadingLabel(e){this.setAttribute("loading-label",e)}get resultsLabel(){return this.getAttribute("results-label")??c("productsLabel")}set resultsLabel(e){this.setAttribute("results-label",e)}get searchingLabel(){return this.getAttribute("searching-label")??c("searchingLabel")}set searchingLabel(e){this.setAttribute("searching-label",e)}get basketController(){return this.controller}set basketController(e){this.controller=e,this.requestRender()}get query(){return this.currentQuery}set query(e){this.currentQuery=e,this.refresh()}get products(){return[...this.currentProducts]}set products(e){this.currentProducts=[...e],this.totalCount=e.length,this.estimatedTotalCount=e.length,this.continuation=void 0,this.error=void 0,this.loading=!1,this.initialProductsConsumed=!0,this.requestRender(),F(this,"orderly-results",this.state)}get initialProducts(){return this.currentInitialProducts?[...this.currentInitialProducts]:void 0}set initialProducts(e){this.currentInitialProducts=e?[...e]:void 0,this.initialProductsConsumed=!1}get continuationToken(){return this.continuation}get hasMore(){return Ks(this.continuation)}get state(){return{loading:this.loading,error:this.error,products:this.products,count:this.totalCount,estimatedTotalCount:this.estimatedTotalCount,hasMore:this.hasMore}}connectedCallback(){if(super.connectedCallback(),!this.currentInitialProducts?.length){let e=Lt(this);e.length>0&&(this.currentInitialProducts=e)}this.hasAttribute("paging")&&(this.paging=ng(this.getAttribute("paging"),this.paging)),this.productTileTagName===Ps&&(this.productTileTagName=G(this,"product-grid","product-tile")),!this.currentQuery&&Ne(this)&&this.refresh()}disconnectedCallback(){this.observer?.disconnect(),super.disconnectedCallback()}attributeChangedCallback(e){if(e==="paging"&&(this.paging=ng(this.getAttribute("paging"),this.paging)),Ke.includes(e)){this.isConnected&&this.refresh();return}this.requestRender()}async refresh(){if(!this.resolvedQuery()){this.currentProducts=[],this.continuation=void 0,this.requestRender();return}this.consumeInitialProducts()||(this.currentProducts=[],this.continuation=void 0,await this.fetchPage(!1))}async loadNextPage(){!this.resolvedQuery()||this.loading||!this.hasMore||await this.fetchPage(!0)}async setSort(e){let r=this.resolvedQuery();if(!r)return;let o=K(Y,r);o.OrderBy=[...e],this.currentQuery=o,await this.refresh()}async clearSearch(){let e=this.resolvedQuery();if(!e)return;let r=K(Y,e);r.Query="",this.currentQuery=r,await this.refresh()}render(){let e=Z(this,"layout")??fA();h(e,"status",this.error?this.errorLabel:this.loading?this.loadingLabel:""),_(e,"context",this.renderContext()),_(e,"sort",this.renderSort()),this.configureSortContainer(e),_(e,"items",this.renderProducts()),this.configureLoadMore(e);let r=x(this,e);r.toggleAttribute("aria-busy",this.loading),this.toggleAttribute("data-orderly-loading",this.loading),this.configureInfiniteScroll(r)}renderProducts(){let e=document.createDocumentFragment();if(this.loading&&this.currentProducts.length===0){let r=Z(this,"loading");return e.append(r??bA()),e}if(!this.loading&&!this.error&&this.currentProducts.length===0){let r=Z(this,"empty");e.append(r??gA(c("emptyLabel")))}for(let r of this.currentProducts)e.append(this.renderProduct(r));return e}async fetchPage(e){this.loading=!0,this.error=void 0,this.requestRender();try{let r=Xr(this.resolvedQuery(),this.currentDefaultQuery);e?r.Continuation=this.continuation:r.Continuation=void 0;let o=await this.getClient().services.searchService.search(r),n=sa(o);this.currentProducts=e?[...this.currentProducts,...n]:n,this.continuation=pA(o.Continuation),this.totalCount=o.Count,this.estimatedTotalCount=o.EstimatedTotalCount,F(this,"orderly-results",this.state)}catch(r){this.error=r,F(this,"orderly-error",{error:r})}finally{this.loading=!1,this.requestRender()}}consumeInitialProducts(){return this.initialProductsConsumed||!this.currentInitialProducts?.length?!1:(this.currentProducts=[...this.currentInitialProducts],this.continuation=void 0,this.totalCount=this.currentInitialProducts.length,this.estimatedTotalCount=this.currentInitialProducts.length,this.error=void 0,this.loading=!1,this.initialProductsConsumed=!0,this.hideInitialMarkup(),this.requestRender(),F(this,"orderly-results",this.state),!0)}hideInitialMarkup(){this.querySelectorAll(":scope > [data-orderly-product], :scope > [data-orderly-products], :scope > [data-orderly-ssr-fallback]").forEach(e=>{e instanceof HTMLElement&&(e.hidden=!0)})}renderProduct(e){let r=Z(this,"product");if(r){let n=document.createElement("div");n.append(r),customElements.upgrade(n),lr(n,e);let a=n.querySelector(this.productTileTagName)??n.querySelector(Ps);a&&(a.basketController=this.controller,a.product=e);let i=document.createDocumentFragment();return SA(n),i.append(...n.childNodes),i}let o=document.createElement(this.productTileTagName);return o.className="orderly-product-grid__item",ag(o),o.basketController=this.controller,o.product=e,o}renderSort(){if(this.currentSortOptions.length===0)return;let e=Z(this,"sort")??yA();h(e,"sort-label",this.sortLabel);let r=e.querySelector('[data-orderly-action="sort"]')??e.querySelector("select");if(r instanceof HTMLSelectElement){r.replaceChildren(...this.currentSortOptions.map((n,a)=>{let i=document.createElement("option");return i.value=String(a),i.textContent=n.label,i}));let o=this.selectedSortIndex();o>=0?r.value=String(o):r.selectedIndex=-1,r.disabled=!this.resolvedQuery(),r.addEventListener("change",()=>{let n=this.currentSortOptions[Number(r.value)];n&&this.setSort(n.orderBy)})}return e}renderContext(){let e=Z(this,"context")??hA(),r=this.resolvedQuery(),o=r?.Query?.trim()??"",n=this.estimatedTotalCount??this.totalCount??this.currentProducts.length;return h(e,"resultCount",r?`${n} ${this.resultsLabel}`:""),h(e,"activeQuery",o?`${this.searchingLabel} "${o}"`:""),h(e,"clearSearchLabel",this.clearSearchLabel),h(e,"loadingLabel",this.loading?this.loadingLabel:""),e.querySelectorAll('[data-orderly-action="clear-search"]').forEach(a=>{a instanceof HTMLElement&&(a.hidden=!o,a.addEventListener("click",()=>{this.clearSearch()}))}),e.querySelectorAll("[data-orderly-loading-indicator]").forEach(a=>{a instanceof HTMLElement&&(a.hidden=!this.loading)}),e}selectedSortIndex(){let e=this.resolvedQuery()?.OrderBy??[];return this.currentSortOptions.findIndex(r=>vA(r.orderBy,e))}resolvedQuery(){return this.currentQuery??Ne(this)}configureSortContainer(e){this.currentSortOptions.length>0||e.querySelectorAll("[data-orderly-sort-container]").forEach(r=>r.remove())}configureLoadMore(e){e.querySelectorAll('[data-orderly-action="load-more"]').forEach(r=>{if(r instanceof HTMLElement){if(this.paging!=="button"||!this.hasMore){r.remove();return}r instanceof HTMLButtonElement&&(r.disabled=this.loading),r.addEventListener("click",()=>{this.loadNextPage()})}})}configureInfiniteScroll(e){if(this.observer?.disconnect(),!mA(this.paging)||!this.hasMore||typeof IntersectionObserver>"u")return;let r=e.querySelector("[data-orderly-sentinel]");r&&(this.observer=new IntersectionObserver(o=>{o.some(n=>n.isIntersecting)&&this.loadNextPage()}),this.observer.observe(r))}};function Ks(t){return!!t?.Token}function pA(t){return Ks(t)?t:void 0}function ng(t,e){return t==="dynamic"||t==="infinite"?"infinite":t==="manual"||t==="button"?t:e}function mA(t){return t==="dynamic"||t==="infinite"}function sa(t){return t.data.map(e=>bt(e,un)).filter(e=>!!e)}function gA(t){let e=document.createElement("p");return e.className="orderly-product-grid__message",e.textContent=t,e}function yA(){let t=document.createDocumentFragment(),e=document.createElement("label");return e.className="orderly-product-grid__sort",e.innerHTML=`
4928
+ `,Qe(o,"thumbnail",t.Images[0]),ve(o,"thumbnail",ge(t.Images[0],"thumbnail",aA)),h(o,"title",t.Title??t.SKU?.Value??c("itemFallbackTitle")),h(o,"quantity",`${c("quantityShortLabel")} ${t.Quantity??0}`),ie(o,"price",O(t.Price??t.TotalItemPrice??t.PricePerPiece)),o}function dA(t,e,r){t.querySelectorAll("[data-orderly-order-card]").forEach(o=>{o instanceof HTMLElement&&(o.hidden=!e)}),t.querySelectorAll(".orderly-payment-result-page__empty").forEach(o=>{o instanceof HTMLElement&&(o.hidden=e)}),t.querySelectorAll('[data-orderly-bind="paymentLink"]').forEach(o=>{o instanceof HTMLAnchorElement&&(r?(o.href=r,o.hidden=!1):(o.removeAttribute("href"),o.hidden=!0))})}function og(t){return t?.Reference??t?.Payment?.Reference??t?.Id?.Id?.Value??""}function cA(){return typeof location>"u"?"":new URL(location.href).searchParams.get("orderid")??""}function uA(){return typeof location>"u"?"/":location.pathname}var Ps="orderly-product-tile",Ao=class extends L{static observedAttributes=["base-url","paging","sort-label","clear-search-label","error-label","loading-label","results-label","searching-label",...Ke];currentQuery;currentDefaultQuery;currentProducts=[];continuation;loading=!1;error;observer;totalCount=0;estimatedTotalCount;controller;currentSortOptions=[];currentInitialProducts;initialProductsConsumed=!1;paging="button";productTileTagName=Ps;get defaultQuery(){return this.currentDefaultQuery}set defaultQuery(e){this.currentDefaultQuery=e,this.refresh()}get shopQuery(){return this.defaultQuery}set shopQuery(e){this.defaultQuery=e}get sortOptions(){return[...this.currentSortOptions]}set sortOptions(e){this.currentSortOptions=[...e],this.requestRender()}get sortLabel(){return this.getAttribute("sort-label")??c("sortLabel")}set sortLabel(e){this.setAttribute("sort-label",e)}get clearSearchLabel(){return this.getAttribute("clear-search-label")??c("clearSearchLabel")}set clearSearchLabel(e){this.setAttribute("clear-search-label",e)}get errorLabel(){return this.getAttribute("error-label")??c("productsLoadError")}set errorLabel(e){this.setAttribute("error-label",e)}get loadingLabel(){return this.getAttribute("loading-label")??c("loadingLabel")}set loadingLabel(e){this.setAttribute("loading-label",e)}get resultsLabel(){return this.getAttribute("results-label")??c("productsLabel")}set resultsLabel(e){this.setAttribute("results-label",e)}get searchingLabel(){return this.getAttribute("searching-label")??c("searchingLabel")}set searchingLabel(e){this.setAttribute("searching-label",e)}get basketController(){return this.controller}set basketController(e){this.controller=e,this.requestRender()}get query(){return this.currentQuery}set query(e){this.currentQuery=e,this.refresh()}get products(){return[...this.currentProducts]}set products(e){this.currentProducts=[...e],this.totalCount=e.length,this.estimatedTotalCount=e.length,this.continuation=void 0,this.error=void 0,this.loading=!1,this.initialProductsConsumed=!0,this.requestRender(),F(this,"orderly-results",this.state)}get initialProducts(){return this.currentInitialProducts?[...this.currentInitialProducts]:void 0}set initialProducts(e){this.currentInitialProducts=e?[...e]:void 0,this.initialProductsConsumed=!1}get continuationToken(){return this.continuation}get hasMore(){return Ks(this.continuation)}get state(){return{loading:this.loading,error:this.error,products:this.products,count:this.totalCount,estimatedTotalCount:this.estimatedTotalCount,hasMore:this.hasMore}}connectedCallback(){if(super.connectedCallback(),!this.currentInitialProducts?.length){let e=Lt(this);e.length>0&&(this.currentInitialProducts=e)}this.hasAttribute("paging")&&(this.paging=ng(this.getAttribute("paging"),this.paging)),this.productTileTagName===Ps&&(this.productTileTagName=G(this,"product-grid","product-tile")),!this.currentQuery&&Ne(this)&&this.refresh()}disconnectedCallback(){this.observer?.disconnect(),super.disconnectedCallback()}attributeChangedCallback(e){if(e==="paging"&&(this.paging=ng(this.getAttribute("paging"),this.paging)),Ke.includes(e)){this.isConnected&&this.refresh();return}this.requestRender()}async refresh(){if(!this.resolvedQuery()){this.currentProducts=[],this.continuation=void 0,this.requestRender();return}let r=this.consumeInitialProducts();r&&!this.hasLiveSearchTarget()||(r||(this.currentProducts=[],this.continuation=void 0),await this.fetchPage(!1))}async loadNextPage(){!this.resolvedQuery()||this.loading||!this.hasMore||await this.fetchPage(!0)}async setSort(e){let r=this.resolvedQuery();if(!r)return;let o=K(Y,r);o.OrderBy=[...e],this.currentQuery=o,await this.refresh()}async clearSearch(){let e=this.resolvedQuery();if(!e)return;let r=K(Y,e);r.Query="",this.currentQuery=r,await this.refresh()}render(){let e=Z(this,"layout")??fA();h(e,"status",this.error?this.errorLabel:this.loading?this.loadingLabel:""),_(e,"context",this.renderContext()),_(e,"sort",this.renderSort()),this.configureSortContainer(e),_(e,"items",this.renderProducts()),this.configureLoadMore(e);let r=x(this,e);r.toggleAttribute("aria-busy",this.loading),this.toggleAttribute("data-orderly-loading",this.loading),this.configureInfiniteScroll(r)}renderProducts(){let e=document.createDocumentFragment();if(this.loading&&this.currentProducts.length===0){let r=Z(this,"loading");return e.append(r??bA()),e}if(!this.loading&&!this.error&&this.currentProducts.length===0){let r=Z(this,"empty");e.append(r??gA(c("emptyLabel")))}for(let r of this.currentProducts)e.append(this.renderProduct(r));return e}async fetchPage(e){this.loading=!0,this.error=void 0,this.requestRender();try{let r=Xr(this.resolvedQuery(),this.currentDefaultQuery);e?r.Continuation=this.continuation:r.Continuation=void 0;let o=await this.getClient().services.searchService.search(r),n=sa(o);this.currentProducts=e?[...this.currentProducts,...n]:n,this.continuation=pA(o.Continuation),this.totalCount=o.Count,this.estimatedTotalCount=o.EstimatedTotalCount,F(this,"orderly-results",this.state)}catch(r){this.error=r,F(this,"orderly-error",{error:r})}finally{this.loading=!1,this.requestRender()}}consumeInitialProducts(){return this.initialProductsConsumed||!this.currentInitialProducts?.length?!1:(this.currentProducts=[...this.currentInitialProducts],this.continuation=void 0,this.totalCount=this.currentInitialProducts.length,this.estimatedTotalCount=this.currentInitialProducts.length,this.error=void 0,this.loading=!1,this.initialProductsConsumed=!0,this.hideInitialMarkup(),this.requestRender(),F(this,"orderly-results",this.state),!0)}hasLiveSearchTarget(){return!!(this.client||this.baseUrl||this.getDefaultBaseUrl())}hideInitialMarkup(){this.querySelectorAll(":scope > [data-orderly-product], :scope > [data-orderly-products], :scope > [data-orderly-ssr-fallback]").forEach(e=>{e instanceof HTMLElement&&(e.hidden=!0)})}renderProduct(e){let r=Z(this,"product");if(r){let n=document.createElement("div");n.append(r),customElements.upgrade(n),lr(n,e);let a=n.querySelector(this.productTileTagName)??n.querySelector(Ps);a&&(a.basketController=this.controller,a.product=e);let i=document.createDocumentFragment();return SA(n),i.append(...n.childNodes),i}let o=document.createElement(this.productTileTagName);return o.className="orderly-product-grid__item",ag(o),o.basketController=this.controller,o.product=e,o}renderSort(){if(this.currentSortOptions.length===0)return;let e=Z(this,"sort")??yA();h(e,"sort-label",this.sortLabel);let r=e.querySelector('[data-orderly-action="sort"]')??e.querySelector("select");if(r instanceof HTMLSelectElement){r.replaceChildren(...this.currentSortOptions.map((n,a)=>{let i=document.createElement("option");return i.value=String(a),i.textContent=n.label,i}));let o=this.selectedSortIndex();o>=0?r.value=String(o):r.selectedIndex=-1,r.disabled=!this.resolvedQuery(),r.addEventListener("change",()=>{let n=this.currentSortOptions[Number(r.value)];n&&this.setSort(n.orderBy)})}return e}renderContext(){let e=Z(this,"context")??hA(),r=this.resolvedQuery(),o=r?.Query?.trim()??"",n=this.estimatedTotalCount??this.totalCount??this.currentProducts.length;return h(e,"resultCount",r?`${n} ${this.resultsLabel}`:""),h(e,"activeQuery",o?`${this.searchingLabel} "${o}"`:""),h(e,"clearSearchLabel",this.clearSearchLabel),h(e,"loadingLabel",this.loading?this.loadingLabel:""),e.querySelectorAll('[data-orderly-action="clear-search"]').forEach(a=>{a instanceof HTMLElement&&(a.hidden=!o,a.addEventListener("click",()=>{this.clearSearch()}))}),e.querySelectorAll("[data-orderly-loading-indicator]").forEach(a=>{a instanceof HTMLElement&&(a.hidden=!this.loading)}),e}selectedSortIndex(){let e=this.resolvedQuery()?.OrderBy??[];return this.currentSortOptions.findIndex(r=>vA(r.orderBy,e))}resolvedQuery(){return this.currentQuery??Ne(this)}configureSortContainer(e){this.currentSortOptions.length>0||e.querySelectorAll("[data-orderly-sort-container]").forEach(r=>r.remove())}configureLoadMore(e){e.querySelectorAll('[data-orderly-action="load-more"]').forEach(r=>{if(r instanceof HTMLElement){if(this.paging!=="button"||!this.hasMore){r.remove();return}r instanceof HTMLButtonElement&&(r.disabled=this.loading),r.addEventListener("click",()=>{this.loadNextPage()})}})}configureInfiniteScroll(e){if(this.observer?.disconnect(),!mA(this.paging)||!this.hasMore||typeof IntersectionObserver>"u")return;let r=e.querySelector("[data-orderly-sentinel]");r&&(this.observer=new IntersectionObserver(o=>{o.some(n=>n.isIntersecting)&&this.loadNextPage()}),this.observer.observe(r))}};function Ks(t){return!!t?.Token}function pA(t){return Ks(t)?t:void 0}function ng(t,e){return t==="dynamic"||t==="infinite"?"infinite":t==="manual"||t==="button"?t:e}function mA(t){return t==="dynamic"||t==="infinite"}function sa(t){return t.data.map(e=>bt(e,un)).filter(e=>!!e)}function gA(t){let e=document.createElement("p");return e.className="orderly-product-grid__message",e.textContent=t,e}function yA(){let t=document.createDocumentFragment(),e=document.createElement("label");return e.className="orderly-product-grid__sort",e.innerHTML=`
4929
4929
  <span class="orderly-product-grid__sort-label" data-orderly-bind="sort-label"></span>
4930
4930
  <select class="orderly-product-grid__sort-select" data-orderly-action="sort"></select>
4931
4931
  `,t.append(e),t}function hA(){let t=document.createDocumentFragment(),e=document.createElement("div");return e.className="orderly-product-grid__context",e.innerHTML=`