@orderlyshop/web-components 0.1.0-build.7066 → 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.
Files changed (70) hide show
  1. package/AGENTS.md +22 -34
  2. package/README.md +168 -61
  3. package/bin/orderly-generate-server-renderers.mjs +69 -11
  4. package/bin/orderly-hydrate-static-pages.mjs +47 -13
  5. package/bin/orderly-init-shop.mjs +50 -7
  6. package/custom-elements.json +34 -9
  7. package/dist/browser/orderly-web-components.define.global.js +585 -120
  8. package/dist/browser/orderly-web-components.define.global.js.br +0 -0
  9. package/dist/browser/orderly-web-components.define.global.js.gz +0 -0
  10. package/dist/browser/orderly-web-components.define.global.js.map +1 -1
  11. package/dist/browser/orderly-web-components.global.js +585 -120
  12. package/dist/browser/orderly-web-components.global.js.br +0 -0
  13. package/dist/browser/orderly-web-components.global.js.gz +0 -0
  14. package/dist/browser/orderly-web-components.global.js.map +1 -1
  15. package/dist/{default-shop-BDktbMzl.d.ts → default-shop-CQuz1DKx.d.ts} +6 -2
  16. package/dist/default-shop.d.ts +2 -2
  17. package/dist/default-shop.js +1 -905
  18. package/dist/default-shop.js.br +0 -0
  19. package/dist/default-shop.js.gz +0 -0
  20. package/dist/default-shop.js.map +1 -1
  21. package/dist/define-DThgfT4n.d.ts +10 -0
  22. package/dist/define.d.ts +1 -1
  23. package/dist/define.js +721 -9933
  24. package/dist/define.js.br +0 -0
  25. package/dist/define.js.gz +0 -0
  26. package/dist/define.js.map +1 -1
  27. package/dist/index.d.ts +38 -9
  28. package/dist/index.js +650 -10698
  29. package/dist/index.js.br +0 -0
  30. package/dist/index.js.gz +0 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/navigation.d.ts +10 -2
  33. package/dist/navigation.js +9 -1357
  34. package/dist/navigation.js.br +0 -0
  35. package/dist/navigation.js.gz +0 -0
  36. package/dist/navigation.js.map +1 -1
  37. package/dist/query.js +1 -114
  38. package/dist/query.js.br +0 -0
  39. package/dist/query.js.gz +0 -0
  40. package/dist/query.js.map +1 -1
  41. package/dist/shop-CYr-svkP.d.ts +313 -0
  42. package/dist/shop-query.js +1 -99
  43. package/dist/shop-query.js.br +0 -0
  44. package/dist/shop-query.js.gz +0 -0
  45. package/dist/shop-query.js.map +1 -1
  46. package/dist/shop.d.ts +3 -3
  47. package/dist/shop.js +773 -10506
  48. package/dist/shop.js.br +0 -0
  49. package/dist/shop.js.gz +0 -0
  50. package/dist/shop.js.map +1 -1
  51. package/dist/stores.d.ts +1 -1
  52. package/dist/stores.js +1 -375
  53. package/dist/stores.js.br +0 -0
  54. package/dist/stores.js.gz +0 -0
  55. package/dist/stores.js.map +1 -1
  56. package/dist/taxonomy.d.ts +2 -2
  57. package/dist/taxonomy.js +1 -246
  58. package/dist/taxonomy.js.br +0 -0
  59. package/dist/taxonomy.js.gz +0 -0
  60. package/dist/taxonomy.js.map +1 -1
  61. package/dist/{types-CXEwL2xS.d.ts → types-CeigwmOf.d.ts} +7 -1
  62. package/docs/components/README.md +74 -5
  63. package/docs/components/product-grid.md +7 -1
  64. package/docs/shop-best-practices.md +145 -0
  65. package/html-custom-data.json +28 -5
  66. package/package.json +3 -3
  67. package/server/README.md +2 -0
  68. package/server/php/orderly-product.php +25 -2
  69. package/dist/define-BgDPJcdv.d.ts +0 -9
  70. package/dist/shop-MI-N1X7L.d.ts +0 -153
package/AGENTS.md CHANGED
@@ -5,29 +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.
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.
12
11
 
13
- ## Discovery
12
+ ## Required Reading and Intake
14
13
 
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/components/product-rail.md` and `docs/components/product-grid.md` before implementing product listing UI.
19
- - Read `custom-elements.json` for machine-readable custom element metadata.
20
- - Read `html-custom-data.json` when generating vanilla HTML or configuring editor autocomplete.
21
- - Inspect `examples/shop` for a complete vanilla TypeScript shop that customizes the defaults without replacing package logic.
22
- - 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.
23
-
24
- ## New Shop Intake
25
-
26
- - Ask the user for the Orderly account id so `src/shop-query.ts` can tenant-scope the default `SearchQuery`.
27
- - 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.
28
- - 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.
29
- - Put the resulting theme in `src/style.css` or the consuming shop stylesheet, starting with `DESIGN.md` tokens before adding component-specific selectors.
30
- - 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.
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.
31
21
 
32
22
  ## Query Rules
33
23
 
@@ -50,27 +40,25 @@ Use this package as a headless storefront component library. Prefer native custo
50
40
 
51
41
  ## Implementation Defaults
52
42
 
53
- - Keep components headless. Add behavior and default light-DOM markup, not bundled theme CSS.
54
- - 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.
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.
55
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.
56
- - Preserve template override support through `data-orderly-template`, `data-orderly-bind`, `data-orderly-slot`, and `data-orderly-action`.
57
- - 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`.
58
- - Keep browser-only package code free of Node HTTP modules.
59
- - Do not store tokens, API keys, OAuth responses, or cookie values.
60
- - 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.
61
51
  - Product images should default to `fit="contain"`, white background, and square aspect ratio in product tiles.
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.
62
53
  - Product grids should support continuation-token based dynamic paging without requiring a visible load-more button.
63
- - Use `orderly-home-page` for default storefront homepages instead of generating product rails in app code.
64
- - 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.
65
- - Keep `orderly-navigation` sticky by default. Use `sticky="false"` only when a shop intentionally wants navigation to scroll away.
66
- - Use `orderly-checkout-page` for checkout pages instead of wiring `orderly-checkout` and `orderly-basket` manually in app code.
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.
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.
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.
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`.
67
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`.
68
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.
69
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.
70
- - Use `navigation.ts` for shop category/navigation structure. Export `navigationDefinitions: NavigationDefinition[]`.
71
- - 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.
72
- - 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 `description`, optional `query`, and optional `children`.
73
- - 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"] }`.
74
62
 
75
63
  ## Common Patterns
76
64
 
package/README.md CHANGED
@@ -17,39 +17,24 @@ Inputs:
17
17
  - Orderly account id: ASK
18
18
  - Visual style reference: ASK
19
19
  - Deployment target: ASK
20
-
21
- Use any provided input above without asking again. Ask only for values that are still `ASK`.
22
-
23
- Install and setup:
24
- 1. Install @orderlyshop/core-client and @orderlyshop/web-components if they are not already installed.
25
- 2. Read node_modules/@orderlyshop/web-components/AGENTS.md, DESIGN.md, README.md, and docs/components/README.md before changing shop files.
26
- 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.
27
-
28
- Navigation:
29
- 4. Use the Orderly account id to tenant-scope src/shop-query.ts.
30
- 5. Run npx orderly-init-navigation --suggest --account-id <account-id> when product sampling is possible.
31
- 6. Build src/navigation.ts manually from navigation-products.json with at most two category levels, stable unique slugs, and plain SearchQueryInput objects.
32
-
33
- Visual style:
34
- 7. Use the visual style reference as design direction for palette, radius, spacing, density, typography feel, product tiles, navigation, checkout, and page rhythm.
35
- 8. Start src/style.css from DESIGN.md semantic tokens. Add component-specific selectors only for details that tokens cannot express.
36
- 9. Do not copy proprietary assets, text, logos, or brand marks from the reference unless I explicitly confirm they may be used.
37
-
38
- Pages and behavior:
39
- 10. 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.
40
- 11. Keep product/category SSR and generated category URLs working unless the deployment target requires a different routing approach.
41
-
42
- Deployment:
43
- 12. Configure the build/publish flow for the deployment target.
44
- - For static hosting, use npm run build and document the dist upload.
45
- - For FTP, use npx orderly-publish-site --target ftp.
46
- - For Apache/PHP or Nginx/PHP, keep the package-generated server renderers and document the required server helper files.
47
- - For any other target, document the exact build output and any remaining deployment assumptions.
48
-
49
- Validation:
50
- 13. Run the nearest relevant build/test command.
51
- 14. Start the dev server when useful and inspect desktop and mobile layouts.
52
- 15. Report what changed, what was verified, and any missing backend, credential, or deployment prerequisite.
20
+ - Google Tag Manager container id: NONE
21
+ - Google Tag Manager data layer name: dataLayer
22
+ - Analytics consent categories: statistics
23
+ - Cookie policy URL: /information/cookies/
24
+
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
+
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.
53
38
  ```
54
39
 
55
40
  ## Install
@@ -74,7 +59,7 @@ Agents and developers should use `npx orderly-init-shop` before hand-writing sho
74
59
  npx orderly-init-shop --account-id 00000000-0000-0000-0000-000000000000
75
60
  ```
76
61
 
77
- 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`. Treat the reference 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.
78
63
 
79
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.
80
65
 
@@ -82,7 +67,7 @@ The scaffold uses Vite for local development and static builds, but Vite is adde
82
67
 
83
68
  ## Browser Standalone Bundle
84
69
 
85
- The package build also emits browser-ready standalone bundles with `@orderlyshop/core-client`, Connect, and protobuf runtime code included. These files are shipped in the npm package and are what the `unpkg` and `jsdelivr` package fields point at.
70
+ The package build emits optimized ESM output and browser-ready standalone bundles with `@orderlyshop/core-client`, Connect, and protobuf runtime code included. JavaScript output is minified, and `npm run build` writes `.br` and `.gz` files next to every generated `dist/**/*.js` file for hosts that can serve precompressed assets. The standalone browser files are shipped in the npm package and are what the `unpkg` and `jsdelivr` package fields point at.
86
71
 
87
72
  Use the configurable bundle when a vanilla HTML site needs to set shop options before registering elements:
88
73
 
@@ -118,23 +103,86 @@ Use the auto-register bundle for the built-in default shop or pages that configu
118
103
 
119
104
  The normal package entrypoints remain ESM for bundlers. The standalone files are classic browser scripts and expose `window.OrderlyWebComponents`.
120
105
 
121
- `configureShop()` is the recommended one-call setup for HTML head scripts. Its parameter is strongly typed as `ShopConfig`, so VS Code/TypeScript can provide completions directly on the object literal. All fields are optional: `uiLanguage`, `defaultShop`, `storefrontRouter`, `pageLayout`, `responsiveTemplates`, `shopFooter`, `storedImageUrls`, and `components`. It applies configuration first and then registers custom elements. The built-in SPA storefront router is enabled by default; pass `storefrontRouter: { enabled: false }` only when a shop needs traditional full-page navigation for every internal link. Pass `components: false` only when elements are registered elsewhere.
106
+ `configureShop()` is the recommended one-call setup for HTML head scripts. Its parameter is strongly typed as `ShopConfig`, so VS Code/TypeScript can provide completions directly on the object literal. All fields are optional: `uiLanguage`, `defaultShop`, `storefrontRouter`, `pageLayout`, `responsiveTemplates`, `shopFooter`, `storedImageUrls`, `consent`, `analytics`, and `components`. It applies configuration first and then registers custom elements. The built-in SPA storefront router is enabled by default; pass `storefrontRouter: { enabled: false }` only when a shop needs traditional full-page navigation for every internal link. Pass `components: false` only when elements are registered elsewhere.
122
107
 
123
108
  Built-in component copy is Danish by default (`uiLanguage: "DA"`). Set `uiLanguage: "EN"` to switch built-in labels, empty states, checkout copy, navigation controls, and page defaults to English. Shop-specific attributes, templates, and `defaultShop` label fields still take precedence over language defaults.
124
109
 
110
+ ## Cookie Consent
111
+
112
+ Use `configureShop({ consent })` when the shop uses optional cookies, local storage identifiers, Google Tag Manager, analytics, campaign measurement, personalization, or similar tracking. The package-owned consent runtime gives visitors a first-layer banner with accept, reject, and customize actions, optional categories that are off by default, a persistent "Cookieindstillinger" button, versioned localStorage persistence, and first-party cookie cleanup when consent is rejected or withdrawn:
113
+
114
+ ```ts
115
+ configureShop({
116
+ consent: {
117
+ enabled: true,
118
+ version: "2026-04-30",
119
+ policyHref: "/information/cookies/",
120
+ managedCookies: [
121
+ {
122
+ name: "_ga",
123
+ category: "statistics",
124
+ provider: "Google Analytics",
125
+ purpose: "Skelner mellem besøgende i statistikmåling.",
126
+ duration: "Typisk op til 2 år"
127
+ },
128
+ {
129
+ name: "_ga_*",
130
+ category: "statistics",
131
+ provider: "Google Analytics",
132
+ purpose: "Bevarer sessionsstatus for statistikmåling.",
133
+ duration: "Typisk op til 2 år"
134
+ }
135
+ ]
136
+ }
137
+ });
138
+ ```
139
+
140
+ Default categories are `necessary`, `preferences`, `statistics`, and `marketing`. `necessary` is always on; all other categories require an affirmative choice. Override `categories` and `texts` when a shop needs different purpose wording, and change `version` when purposes, vendors, or the cookie policy materially change so visitors are asked again. `managedCookies` accepts exact first-party cookie names or simple wildcard patterns such as `_ga_*`; include `provider`, `purpose`, and `duration` so the preferences layer can disclose the cookies it manages.
141
+
142
+ Programmatic helpers are exported for custom code: `hasCookieConsent(category)`, `currentCookieConsent()`, `saveCookieConsent(categories)`, `openCookieConsentPreferences()`, `setOrderlyCookie(name, value, { category })`, `deleteOrderlyCookie(name)`, and `clearCookiesForCategory(category)`. Use `setOrderlyCookie` for shop-owned optional cookies so they are only written when the category has consent.
143
+
144
+ The package can enforce consent for package-owned browser behavior, analytics hooks, and configured first-party cookies. The shop owner still has to keep the cookie policy/vendor list accurate, configure any server-side or third-party deletion outside the package, and ensure any custom scripts also check consent before reading or writing optional cookies.
145
+
146
+ ## Analytics And Google Tag Manager
147
+
148
+ Use `configureShop({ analytics })` to install Google Tag Manager and publish GA4-compatible navigation and ecommerce events to `dataLayer`:
149
+
150
+ ```ts
151
+ configureShop({
152
+ consent: {
153
+ enabled: true,
154
+ policyHref: "/information/cookies/"
155
+ },
156
+ analytics: {
157
+ googleTagManagerId: import.meta.env.VITE_ORDERLY_GOOGLE_TAG_MANAGER_ID,
158
+ defaultCurrency: "DKK",
159
+ affiliation: "My Shop"
160
+ }
161
+ });
162
+ ```
163
+
164
+ When `googleTagManagerId` is set, the package initializes `dataLayer` and injects the GTM script. The generated starter shop wires this to `VITE_ORDERLY_GOOGLE_TAG_MANAGER_ID`; omit the env var to leave tracking off. When package cookie consent is configured, analytics defaults to requiring the `statistics` category before GTM loads or package analytics events are pushed. Set `analytics.consentCategories` to another category, an array such as `["statistics", "marketing"]`, or `false` only when the shop handles consent outside the package.
165
+
166
+ With package cookie consent enabled, analytics also queues Google Consent Mode v2 `default` and `update` commands. The default command denies optional storage until the visitor has chosen; the update command is pushed immediately when the visitor saves or changes consent.
167
+
168
+ The analytics integration listens to package-owned DOM events and pushes these GA4 event names: `page_view`, `view_item_list`, `select_item`, `view_item`, `add_to_cart`, `remove_from_cart`, `view_cart`, `begin_checkout`, `add_shipping_info`, `add_payment_info`, and `purchase`. Ecommerce events clear the previous `ecommerce` object before pushing the new event, so GTM GA4 tags can read the current event payload cleanly. Purchase tracking fires from `orderly-payment-success-page` with `transaction_id`; configure the GA4 tag in GTM to use the data layer ecommerce object and avoid also firing a separate duplicate purchase tag on the same page.
169
+
170
+ For custom analytics, keep `analytics.enabled: true` without `googleTagManagerId` to use the same hooks with an existing GTM/dataLayer install, or pass `onEvent` to forward package events to another destination.
171
+
125
172
  ## API Discovery
126
173
 
127
174
  Developers and coding agents should start with these package-owned docs:
128
175
 
129
176
  - [`DESIGN.md`](./DESIGN.md) defines the package-wide design tokens and the recommended theming workflow for consistent styling across all components.
130
177
  - [`docs/components/README.md`](./docs/components/README.md) lists every custom element, common attributes, typed properties, events, and template hooks.
178
+ - [`docs/shop-best-practices.md`](./docs/shop-best-practices.md) is the practical checklist for turning the package scaffold into a branded, editorial storefront without replacing package-owned component behavior.
131
179
  - [`docs/components/product-grid.md`](./docs/components/product-grid.md) documents search loading, sorting, paging, default query merging, and declarative query markup.
132
180
  - [`docs/components/product-rail.md`](./docs/components/product-rail.md) documents homepage/category rails and their query configuration.
133
181
  - [`AGENTS.md`](./AGENTS.md) gives coding agents the conventions for building shops with this package.
134
182
  - [`custom-elements.json`](./custom-elements.json) is the Custom Elements Manifest for tools that understand web component metadata.
135
183
  - [`html-custom-data.json`](./html-custom-data.json) is VS Code compatible custom data for HTML autocomplete and hover docs.
136
184
 
137
- 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.
138
186
 
139
187
  Enable HTML editor hints in a consuming project by adding the package custom data file to `.vscode/settings.json`:
140
188
 
@@ -359,19 +407,17 @@ npx orderly-init-navigation --path src/navigation.ts
359
407
 
360
408
  Agent workflow for a new shop:
361
409
 
362
- 1. Ask the user for the new shop's Orderly account id.
363
- 2. Ask the user for a reference website URL for the desired visual style, or a short style description when no reference site exists.
364
- 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:
365
412
 
366
413
  ```sh
367
414
  npx orderly-init-navigation --suggest --account-id <account-id>
368
415
  ```
369
416
 
370
- 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.
371
- 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 `description`, optional `query`, and optional recursive `children`. Use plain `SearchQueryInput` objects for `query`, such as `{ query: "sko sneakers", tags: ["sko"] }`.
372
- 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.
373
- 7. Configure the shop scope with a Core `SearchQuery` and pass it to `configureShop({ defaultShop: { defaultQuery } })`.
374
- 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.
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.
375
421
 
376
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.
377
423
 
@@ -399,7 +445,7 @@ Navigation data has three layers:
399
445
 
400
446
  - `NavigationDefinition` is the authored data in `navigation.ts`. It is the only type most shops need to write. Every item needs an explicit stable `slug`; labels are display text and can change without changing URLs.
401
447
  - `ResolvedNavigationItem` is created by `createCategoryNavigation()` and consumed by default page components. It adds stable ids, hrefs, normalized Core `SearchQuery` values, and derived metadata.
402
- - `NavigationMetadata` contains those derived page values: slug, parent id, path segments, normalized query, search text, description, hero image, and page root.
448
+ - `NavigationMetadata` contains those derived page values: slug, parent id, path segments, normalized query, search text, description, hero image, hero layout, hero cover count, and page root.
403
449
 
404
450
  The older `CategoryDefinition`, `CategoryNavigationItem`, and `CategoryMetadata` names remain as compatibility aliases only.
405
451
 
@@ -431,7 +477,7 @@ Build with generated category pages and then clean the generated source files. S
431
477
  npx orderly-build-category-pages
432
478
  ```
433
479
 
434
- `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.
435
481
 
436
482
  Hydrate an already-built site manually, for example in a nightly job:
437
483
 
@@ -443,6 +489,20 @@ Use `--skip-products` for metadata-only hydration, `--strict-products` when a ni
443
489
 
444
490
  The category generator expects `src/navigation.ts` or `src/navigation.js` to export `navigationDefinitions` and the template page to contain `<orderly-category-page></orderly-category-page>`. TypeScript navigation files use the current shop project's `typescript` dependency for one-off transpilation. Useful options include `--navigation`, `--template`, `--categories-dir`, `--site-title`, `--component-tag`, and `--export`. The older `--taxonomy` flag and `categoryDefinitions` export are still accepted as compatibility aliases.
445
491
 
492
+ ## Static Vanilla Deployment
493
+
494
+ For static hosting, keep the generated shop as a normal Vite site and upload the built `dist` directory, not `src` or `node_modules`.
495
+
496
+ Recommended path-based setup:
497
+
498
+ 1. Keep `productUrlMode: "path"` or configure `storefrontRouter.product.mode = "path"` when the host can serve `/products/.../` through the provided server helper or a static fallback.
499
+ 2. Use real category URLs by exporting `navigationDefinitions` and running `npx orderly-build-category-pages` from the shop project. This generates `src/categories/**/index.html` during build, hydrates `dist/index.html` plus `dist/categories/**/index.html`, and cleans generated source files afterwards.
500
+ 3. Upload every file in `dist`, including hashed assets under `dist/assets` and precompressed `.br`/`.gz` files when the host supports them.
501
+ 4. Configure the host to serve `index.html`, generated category `index.html` files, checkout/payment callback pages, and product fallback routing. Apache/PHP, Nginx/PHP-FPM, and Node helpers live in `node_modules/@orderlyshop/web-components/server`.
502
+ 5. Use hash product URLs only when the deployment target cannot route unknown product paths. Hash URLs still work with the SPA router, but path URLs are clearer for static/server-rendered SEO entry points.
503
+
504
+ For local dry runs use `npm run build`, then the host's static preview command or `npm run preview` from the generated shop. For one-off publish flows, use `npx orderly-publish-site --target local --dir published-site` or `npx orderly-publish-site --target ftp`.
505
+
446
506
  ## Local SSR Dev Mode
447
507
 
448
508
  Scaffolded shops use SSR in dev mode by default through the package Vite middleware:
@@ -502,7 +562,7 @@ npm run build
502
562
  npx orderly-generate-server-renderers --site-title "My Shop"
503
563
  ```
504
564
 
505
- Scaffolded shops run that generator from `npm run build` by default. It writes `dist/product.php`, `dist/category.php`, `dist/.htaccess`, and `dist/orderly-ssr-manifest.json`. The PHP renderers call `SearchService.Search` over gRPC-web, inject semantic HTML into the built `product.html` or `category.html`, and preserve the normal web-component scripts and styles.
565
+ Scaffolded shops run that generator from `npm run build` by default. It writes `dist/product.php`, `dist/category.php`, `dist/.htaccess`, and `dist/orderly-ssr-manifest.json`. The PHP renderers call `SearchService.Search` over gRPC-web, inject semantic HTML into the built `product.html` or `category.html`, and preserve the normal web-component scripts and styles. The generated `.htaccess` sets long immutable cache headers for fingerprinted static assets and keeps HTML/PHP revalidatable; configure equivalent cache headers manually when deploying to hosts that ignore `.htaccess`.
506
566
 
507
567
  Runtime options:
508
568
 
@@ -511,7 +571,7 @@ Runtime options:
511
571
  - `ORDERLY_SSR_HEADERS` can add internal server-side request headers. Do not expose API keys or bearer tokens to browser JavaScript.
512
572
  - `ORDERLY_SSR_MANIFEST` can point PHP to a manifest outside the web root.
513
573
 
514
- 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.
515
575
 
516
576
  ## Define Elements
517
577
 
@@ -520,7 +580,14 @@ import { configureShop } from "@orderlyshop/web-components";
520
580
 
521
581
  configureShop({
522
582
  responsiveTemplates: { mobileMediaQuery: "(max-width: 860px)" },
523
- storedImageUrls: { baseUrl: "https://static.example-cdn.com/" }
583
+ storedImageUrls: {
584
+ baseUrl: "https://static.example-cdn.com/",
585
+ roles: {
586
+ "product-tile": { width: 500, height: 500 },
587
+ "product-detail": { width: 900, height: 900 },
588
+ fullsize: false
589
+ }
590
+ }
524
591
  });
525
592
  ```
526
593
 
@@ -548,7 +615,7 @@ style.textContent = ORDERLY_LAZY_LOAD_CSS;
548
615
  document.head.append(style);
549
616
  ```
550
617
 
551
- `defineOrderlyWebComponents()` installs the same lazy-load CSS at runtime as a fallback, but head inclusion is what prevents raw slotted links from flashing before JavaScript has loaded. The default component CSS is exported as `ORDERLY_DEFAULT_CSS` and installed automatically by `defineOrderlyWebComponents()`.
618
+ `defineOrderlyWebComponents()` installs the same lazy-load CSS at runtime as a fallback, but head inclusion is what prevents raw slotted links from flashing before JavaScript has loaded. The optional hydration fade is one-shot: it runs only for the first rendered component tree after a real page load, then the package removes `data-orderly-hydration-fade` so SPA navigation, browser back, product overlays, and other later renders do not fade or visually re-layout. The default component CSS is exported as `ORDERLY_DEFAULT_CSS` and installed automatically by `defineOrderlyWebComponents()`.
552
619
 
553
620
  ## Global Templates
554
621
 
@@ -611,25 +678,38 @@ collection.client = client;
611
678
  collection.query = searchQuery;
612
679
  ```
613
680
 
681
+ ## Content and Brand Cookbook
682
+
683
+ The package defaults are a foundation, not the desired finished look for a branded shop. Start with the reference website and catalog, then make deliberate content and layout choices before replacing package templates.
684
+
685
+ For a fuller real-shop checklist, read [Shop Best Practices](./docs/shop-best-practices.md) before the first implementation pass.
686
+
687
+ - Homepage: combine `orderly-home-page` or product rails with shop-owned editorial sections, seasonal/category rails, trust copy, store context, and strong imagery from the catalog or approved brand assets.
688
+ - Category pages: use `NavigationDefinition.description`, `heroImage`, `heroLayout: "covers"` or `heroLayout: "split"`, and `heroCoverCount` to create category headers with context and product-cover previews before adding custom HTML.
689
+ - Product pages: use `defaultShop.productDetail.trustNote`, `secondaryCta`, and detail slots for store visits, secondhand curation notes, return policy, pickup information, or other shop-specific confidence builders.
690
+ - Navigation: use `slot="menu-before"` and `slot="menu-after"` for store finder, donation, volunteer, campaign, or brand links; use `persist-state` for vertical desktop menus with nested categories.
691
+ - Basket: use `basket-mode="inline-desktop"` on `orderly-home-page`, `orderly-category-page`, or `orderly-product-detail-page` when the desktop design needs a right rail that only appears after products are added. Use `<orderly-basket-icon href="/checkout.html" placement="mobile-fixed" hide-when-empty>` for a package-owned mobile cart/checkout button with count.
692
+ - Checkout: keep platform copy out of the user-facing flow. Configure labels through `defaultShop.checkoutLabels` and `basketLabels`, then use scoped CSS and the checkout responsive override rules rather than replacing the checkout template.
693
+
614
694
  ## Components
615
695
 
616
696
  - `orderly-page-layout` provides reusable page regions for header, header actions, responsive primary navigation placement, left/right sidebars, content, footer, and overlay content. It includes a default logo image using `https://orderly.shop/home/App_Icon.svg`; override it with `logo-src`, `logo-alt`, and `logo-href`. Use `primary-nav-mobile-placement="header"` for the common mobile header burger pattern without a duplicate mobile layout template.
617
- - `orderly-home-page` composes page layout, responsive primary navigation, one product rail per top-level category, basket icon, basket drawer, product detail dialog, and footer. Configure it with `configureShop({ defaultShop })` plus attributes such as `title`, `eyebrow`, `rail-cta-label`, and `product-href`. The product dialog expand link follows the configured product URL mode, and compact `https://orderly.shop/...` product URLs are preserved when it builds hash or path routes.
618
- - `orderly-category-page` composes page layout, navigation, search, product grid, product detail, basket icon, and basket around a navigation category. Without properties it uses the package default shop configuration. Override with `configureShop({ defaultShop })` once per shop, set `base-url` or `product-href` in markup, or assign `category`, `navigationItems`, `sortOptions`, `client`, `defaultQuery`, and `basketController` from JavaScript for advanced cases. Configure `defaultShop.navigationLayout` or top-level `navigationLayout` to switch the package-owned category page from desktop side navigation to top horizontal category navigation while keeping the mobile burger in the header. The product dialog expand link uses the configured product URL mode and still supports explicit per-page `product-href` overrides when a shop wants a different physical product page URL.
697
+ - `orderly-home-page` composes page layout, responsive primary navigation, one product rail per top-level category, basket icon, basket drawer, optional inline desktop basket rail, product detail dialog, and footer. Configure it with `configureShop({ defaultShop })` plus attributes such as `title`, `eyebrow`, `rail-cta-label`, `product-href`, and `basket-mode="inline-desktop"`. The product dialog is fullscreen on mobile, keeps a bounded image column on desktop through `--orderly-product-dialog-image-size`, and pushes a same-URL history entry when opened, so browser back closes the overlay before leaving the homepage. The expand link follows the configured product URL mode, and compact `https://orderly.shop/...` product URLs are preserved when it builds hash or path routes.
698
+ - `orderly-category-page` composes page layout, navigation, search, product grid, product detail, basket icon, and basket around a navigation category. Without properties it uses the package default shop configuration. Override with `configureShop({ defaultShop })` once per shop, set `base-url` or `product-href` in markup, or assign `category`, `navigationItems`, `sortOptions`, `client`, `defaultQuery`, and `basketController` from JavaScript for advanced cases. Configure `defaultShop.navigationLayout` or top-level `navigationLayout` to switch the package-owned category page from desktop side navigation to top horizontal category navigation while keeping the mobile burger in the header. Set `basket-mode="inline-desktop"` when the desktop category page should show a right basket rail only after the basket has lines. The product dialog is fullscreen on mobile, keeps a bounded image column on desktop through `--orderly-product-dialog-image-size`, and pushes a same-URL history entry when opened, so browser back closes the overlay before leaving the category. The expand link uses the configured product URL mode and still supports explicit per-page `product-href` overrides when a shop wants a different physical product page URL.
619
699
  - `orderly-checkout-page` composes page layout, responsive primary navigation, checkout form, basket order summary, footer, shared basket state, delivery loading, and order-created cleanup. Delivery methods render as radio cards after address entry, service points render as radio-card pickup choices when required, and delivery changes verify the draft so totals update. If a persisted draft already contains a complete address, delivery methods are restored on mount; if it also contains a delivery choice, the draft is verified so the basket summary can show backend shipping and total prices. Configure copy through `configureShop({ defaultShop: { checkoutPageTitle, checkoutPageDescription, checkoutOrderTitle, checkoutTermsHref, checkoutLabels, basketLabels } })`, or assign `client`, `basketController`, `navigationItems`, `checkoutLabels`, and `basketLabels` from JavaScript.
620
- - `orderly-product-detail-page` composes page layout, navigation, product detail, basket drawer, and footer for a product route. Set `share-url`, the `shareUrl` property, or pass `#url=<encoded-share-url>` in the page URL; compact Orderly slugs are restored to `https://orderly.shop/...` before it calls `SearchService.Search` with `SearchQuery.ShareUrl` and renders the resolved `SearchObject`. It forwards `details-before`, `purchase-note`, `secondary-cta`, and `details-after` slots to the nested product page. Shops can tune outer gutters with `--orderly-product-detail-page-padding`, `--orderly-product-detail-page-mobile-padding`, `--orderly-product-detail-page-inline-padding`, and `--orderly-product-detail-page-mobile-inline-padding`.
621
- - `orderly-stored-image` accepts `image: StoredImage`, resolves image URLs from the configured prefix, and applies `RotationDeg`, crop, and full-size metadata in the browser.
700
+ - `orderly-product-detail-page` composes page layout, navigation, product detail, basket drawer, optional inline desktop basket rail, and footer for a product route. Set `share-url`, the `shareUrl` property, or pass `#url=<encoded-share-url>` in the page URL; compact Orderly slugs are restored to `https://orderly.shop/...` before it calls `SearchService.Search` with `SearchQuery.ShareUrl` and renders the resolved `SearchObject`. It forwards `details-before`, `purchase-note`, `secondary-cta`, and `details-after` slots to the nested product page. Set `basket-mode="inline-desktop"` when a product page should keep the basket visible as a desktop right rail after add-to-basket. Shops can tune outer gutters with `--orderly-product-detail-page-padding`, `--orderly-product-detail-page-mobile-padding`, `--orderly-product-detail-page-inline-padding`, and `--orderly-product-detail-page-mobile-inline-padding`.
701
+ - `orderly-stored-image` accepts `image: StoredImage`, resolves image URLs from the configured prefix, and applies `RotationDeg`, crop, fit, and role-based CDN sizing in the browser. Default roles include `product-tile` 500x500, `product-detail` 900x900, 180x180 thumbnails, and `fullsize` without size params.
622
702
  - `orderly-credit` accepts `credit: Credit` or declarative `amount` and `currency` attributes, then renders stylable money markup. DKK values are formatted as `kr. <pris>`, for example `kr. 100,00`.
623
- - `orderly-product-tile` accepts `product: SearchObject` and emits `orderly-product-selected`, `orderly-add-to-basket`, and `orderly-remove-from-basket`. Templates can bind the product share URL with `data-orderly-bind="share-url"`; anchors without an existing `href` keep the raw share URL, while anchors that already point at a product page are rewritten to the configured physical product route using hash or path mode. Default tiles and statically hydrated category tiles include `schema.org/Product` and `schema.org/Offer` microdata for name, image, URL, description, SKU, brand, price, currency, and in-stock availability when those fields are present. When a `BasketController` is assigned through `basketController`, the default action button toggles between add and remove, using an icon-only button next to the price. Keyboard focus is indicated on the product title instead of a frame around the whole tile; custom tile templates should keep a title element with `data-orderly-bind="title"` or `.orderly-product-tile__title`.
624
- - `orderly-product-page` accepts `product: SearchObject`, renders all product image thumbnails, switches the selected image on thumbnail click, and exposes product details plus add-to-basket behavior. Product-page templates bind `title`, `description`, `brand`, `size`, `color`, `condition`, `price`, `image`, `addLabel`, and `closeLabel`; they also support `basket-action-label`, `basket-action-icon`, and `basket-action-state` so product-page add-to-basket markup can share the same binding names as product tiles. The default product template has extension slots `details-before`, `purchase-note`, `secondary-cta`, and `details-after`; use these for shop-specific notices or links without replacing the whole product template. For simple default content, configure `defaultShop.productDetail.trustNote` and `defaultShop.productDetail.secondaryCta`. The fullscreen image viewer is package-owned, so custom product templates only need inline image markup plus optional thumbnail markup; the overlay, click/wheel zoom behavior, labels, focus handling, square image viewport, and overlay thumbnails are injected by the component itself unless `image-viewer="false"` is set. When opened inside an existing product dialog, the viewer is portaled into that open `<dialog>` so it stays above the product overlay; otherwise it is portaled to `document.body`. Main image frame styling lives on the default wrapper, not the inner `orderly-stored-image`, and shops can tune spacing/image framing with `--orderly-product-page-gap`, `--orderly-product-page-mobile-gap`, `--orderly-product-page-media-gap`, `--orderly-product-page-padding`, `--orderly-product-page-mobile-padding`, `--orderly-product-page-details-padding`, `--orderly-product-page-mobile-details-padding`, `--orderly-product-page-image-border`, `--orderly-product-page-image-radius`, `--orderly-product-page-image-background`, and `--orderly-product-page-thumbnail-border`.
703
+ - `orderly-product-tile` accepts `product: SearchObject` and emits `orderly-product-selected`, `orderly-add-to-basket`, and `orderly-remove-from-basket`. Templates can bind the product share URL with `data-orderly-bind="share-url"`; anchors without an existing `href` keep the raw share URL, while anchors that already point at a product page are rewritten to the configured physical product route using hash or path mode. Default tiles, generated starter-shop tile templates, and statically hydrated category tiles include `schema.org/Product`, nested `schema.org/Brand`, and `schema.org/Offer` microdata for Google product snippets: name, image, URL, description, SKU, brand, price, currency, and in-stock availability are emitted when those fields are present. Custom tile templates should preserve the `data-orderly-bind="schema-*"` placeholders plus the `data-orderly-schema-brand` and `data-orderly-schema-offer` wrappers. When a `BasketController` is assigned through `basketController`, the default action button toggles between add and remove, using an icon-only button next to the price. Keyboard focus is indicated on the product title instead of a frame around the whole tile; custom tile templates should keep a title element with `data-orderly-bind="title"` or `.orderly-product-tile__title`.
704
+ - `orderly-product-page` accepts `product: SearchObject`, renders all product image thumbnails, switches the selected image on thumbnail click, and exposes product details plus add-to-basket behavior. It emits `orderly-product-viewed` once per displayed product, so analytics integrations can track `view_item` without replacing the template. Product-page templates bind `title`, `description`, `brand`, `size`, `color`, `condition`, `price`, `image`, `addLabel`, and `closeLabel`; they also support `basket-action-label`, `basket-action-icon`, and `basket-action-state` so product-page add-to-basket markup can share the same binding names as product tiles. The default product template has extension slots `details-before`, `purchase-note`, `secondary-cta`, and `details-after`; use these for shop-specific notices or links without replacing the whole product template. For simple default content, configure `defaultShop.productDetail.trustNote` and `defaultShop.productDetail.secondaryCta`. The fullscreen image viewer is package-owned, so custom product templates only need inline image markup plus optional thumbnail markup; the overlay, click/wheel zoom behavior, labels, focus handling, square image viewport, and overlay thumbnails are injected by the component itself unless `image-viewer="false"` is set. When opened inside an existing product dialog, the viewer is portaled into that open `<dialog>` so it stays above the product overlay; otherwise it is portaled to `document.body`. Main image frame styling lives on the default wrapper, not the inner `orderly-stored-image`, and shops can tune spacing/image framing with `--orderly-product-page-gap`, `--orderly-product-page-mobile-gap`, `--orderly-product-page-media-gap`, `--orderly-product-page-padding`, `--orderly-product-page-mobile-padding`, `--orderly-product-page-details-padding`, `--orderly-product-page-mobile-details-padding`, `--orderly-product-page-image-border`, `--orderly-product-page-image-radius`, `--orderly-product-page-image-background`, and `--orderly-product-page-thumbnail-border`.
625
705
  - `orderly-search-box` binds to a target `orderly-product-grid`. Use `mode="textbox"` for an inline search input or `mode="icon"` for a header icon that opens a full-page search overlay. Icon mode searches automatically with a 500ms debounce and renders matching product tiles below the search field. Default home and category pages use icon mode next to the basket icon.
626
706
  - `orderly-product-grid` accepts `query: SearchQuery`, merges the configured shop query scope, calls `SearchService.Search`, renders its own sort control from `sortOptions`, supports opaque `ContinuationToken` paging, manual paging, dynamic/infinite scroll, and default loading placeholders while the first page is pending.
627
707
  - `orderly-product-rail` accepts `query: SearchQuery`, title, CTA label, and CTA href, then renders the search as a horizontal scroll list by composing `orderly-product-grid`. It is useful for homepages and editorial rows where several category searches should be stacked vertically.
628
- - `orderly-collection-page` accepts `query: SearchQuery`, title, description, and hero image, then delegates fetching to `orderly-product-grid`.
708
+ - `orderly-collection-page` accepts `query: SearchQuery`, title, description, hero image, `hero-layout`, and `hero-cover-count`, then delegates fetching to `orderly-product-grid`. Use `hero-layout="covers"` to show product-cover previews from the loaded category products without custom category header HTML.
629
709
  - `orderly-shop-footer` renders configurable logo, about text, address, contact information, opening hours, and information links.
630
- - `orderly-basket-icon` and `orderly-basket` share a `BasketController` backed by `DraftOrder` persistence. Basket state events such as `orderly-basket-open`, `orderly-basket-change`, and `orderly-basket-verified` use the current `DraftOrder` directly as `event.detail`, so consumers can derive counts and totals from Core contracts instead of an internal basket data shape. `orderly-basket` renders an item overview, verifies non-empty persisted drafts on load plus later basket mutations through `OrderService.VerifyDraft` when a client or `base-url` is configured, includes a configurable checkout link through `checkout-href` and `checkout-label`, and only exposes quantity selection for lines where `MaxQuantity > 1`. Summary rows now render only backend-provided `DraftOrder` values, never local subtotal/total math, and the component renders both `DraftOrder.Errors` and `DraftOrderLine.Errors`. Custom basket templates can project order-level errors through `data-orderly-slot="errors"`.
710
+ - `orderly-basket-icon` and `orderly-basket` share a `BasketController` backed by `DraftOrder` persistence. Basket state events such as `orderly-basket-open`, `orderly-basket-change`, and `orderly-basket-verified` use the current `DraftOrder` directly as `event.detail`, so consumers can derive counts and totals from Core contracts instead of an internal basket data shape. `orderly-basket-icon` can also render a checkout/cart link with `href`, `placement="mobile-fixed"`, and `hide-when-empty` for a package-owned mobile button with count. `orderly-basket-checkout` includes `{ draft, count, href }` when the checkout action is triggered. `orderly-basket` renders an item overview, verifies non-empty persisted drafts on load plus later basket mutations through `OrderService.VerifyDraft` when a client or `base-url` is configured, includes a configurable checkout link through `checkout-href` and `checkout-label`, and only exposes quantity selection for lines where `MaxQuantity > 1`. Summary rows now render only backend-provided `DraftOrder` values, never local subtotal/total math, and the component renders both `DraftOrder.Errors` and `DraftOrderLine.Errors`. Custom basket templates can project order-level errors through `data-orderly-slot="errors"`.
631
711
  - `orderly-checkout` persists checkout contact and address fields directly on `DraftOrder.Transport`, defaults the phone country picker to `+45`, validates email and phone values with built-in component logic, looks up Danish city names from the entered postal code through Dataforsyningen's `https://api.dataforsyningen.dk/postnumre/{postnr}` endpoint, loads delivery methods and service points, verifies the draft, and calls `OrderService.Create`. On mount it restores delivery methods for a persisted complete address and verifies a persisted delivery/service-point choice once, so shared basket summaries receive backend shipping and total prices without waiting for another address edit.
632
- - `orderly-navigation` uses `NavigationController` and can be subclassed with site-specific `NavigationItem[]`. It is sticky by default, including when placed in `left`, `right`, or `primary-nav`; set `sticky="false"` to opt out. Use `layout="vertical"` for disclosure side navigation, `layout="horizontal"` for the tiered desktop bar, or `layout="burgermenu"` for an icon trigger that expands into the same nested disclosure menu inside a floating panel. Use `layout-desktop="horizontal" layout-mobile="burgermenu"` with `orderly-page-layout primary-nav-mobile-placement="header"` for desktop horizontal nav plus inline mobile burger.
712
+ - `orderly-navigation` uses `NavigationController` and can be subclassed with site-specific `NavigationItem[]`. It is sticky by default, including when placed in `left`, `right`, or `primary-nav`; set `sticky="false"` to opt out. Use `layout="vertical"` for disclosure side navigation, `layout="horizontal"` for the tiered desktop bar, or `layout="burgermenu"` for an icon trigger that expands into the same nested disclosure menu inside a floating panel. Use `layout-desktop="horizontal" layout-mobile="burgermenu"` with `orderly-page-layout primary-nav-mobile-placement="header"` for desktop horizontal nav plus inline mobile burger. Add `persist-state state-key="main-menu"` to remember expanded category branches across navigation, and use `slot="menu-before"`/`slot="menu-after"` for shop-owned links such as store finder, campaigns, or volunteer pages.
633
713
  - `orderly-search-box`, `orderly-sort-select`, `orderly-filter-panel`, and `orderly-load-more` bind to `orderly-product-grid`.
634
714
 
635
715
  ## Navigation Patterns
@@ -667,6 +747,33 @@ configureShop({
667
747
  </orderly-page-layout>
668
748
  ```
669
749
 
750
+ ## Basket Layout And Count
751
+
752
+ Use the package basket primitives before writing custom state glue:
753
+
754
+ ```html
755
+ <orderly-category-page basket-mode="inline-desktop"></orderly-category-page>
756
+ <orderly-basket-icon href="/checkout.html" placement="mobile-fixed" hide-when-empty label="Kurv"></orderly-basket-icon>
757
+ ```
758
+
759
+ `basket-mode="inline-desktop"` is supported by `orderly-home-page`, `orderly-category-page`, and `orderly-product-detail-page`. It keeps the existing drawer behavior on mobile, but on desktop it shows a right rail only when `BasketController.count > 0`.
760
+
761
+ Basket count contract:
762
+
763
+ - `BasketController.count` is derived from `DraftOrder.OrderLines[].Quantity`.
764
+ - `orderly-basket-icon`, `orderly-basket`, product tiles/grids/rails, product pages, and checkout should share the same `BasketController` instance.
765
+ - `orderly-basket-change` and `orderly-basket-verified` expose the current `DraftOrder` as `event.detail`; custom UI can derive counts from that draft without copying package state.
766
+ - Verified and persisted drafts still use the same `DraftOrder`; backend-provided prices and errors are rendered by `orderly-basket`.
767
+
768
+ ## Checkout Responsive Overrides
769
+
770
+ When customizing checkout layout, do not override the package mobile collapse accidentally:
771
+
772
+ - Keep desktop-only grid changes inside `@media (min-width: 768px)` or the same breakpoint configured through `configureShop({ responsiveTemplates })`.
773
+ - Avoid global `grid-template-columns` overrides on `.orderly-checkout-page__grid`, `.orderly-checkout__field-grid`, `.orderly-checkout__delivery-grid`, and `.orderly-checkout__option-grid--delivery` without a matching mobile reset.
774
+ - Prefer CSS variables and scoped spacing/color rules on `.orderly-checkout-page`, `.orderly-checkout-page__card`, `.orderly-checkout`, and `.orderly-checkout__option`.
775
+ - Keep `.orderly-checkout-page__card--summary` as a normal layout container; the package moves submit/terms controls there for the responsive checkout summary.
776
+
670
777
  ## Rendering And Styling
671
778
 
672
779
  The package ships baseline default CSS and uses light DOM. Default markup uses stable `orderly-*` class names and CSS variables, so host CSS can style or replace the default look directly.
@@ -885,7 +992,7 @@ Stored images can be rendered directly:
885
992
  productImage.image = searchObject.Images[0];
886
993
  ```
887
994
 
888
- `orderly-stored-image` uses `DEFAULT_IMAGE_BASE_URL`, currently `https://orderlyproduction.azureedge.net/`, when no custom image URL configuration is supplied. `configureStoredImageUrls({ baseUrl })` maps normal product images to `baseUrl/thumbnails/<StoredImage.Name>` and full/object variants to `baseUrl/objects/<StoredImage.Name>`. The component also supports `ImageDataBase64`, absolute URLs, and a custom resolver through `configureStoredImageUrls({ resolver })`.
995
+ `orderly-stored-image` uses `DEFAULT_IMAGE_BASE_URL`, currently `https://orderlyproduction.azureedge.net/`, when no custom image URL configuration is supplied. `configureStoredImageUrls({ baseUrl })` maps normal product images to `baseUrl/thumbnails/<StoredImage.Name>` and full/object variants to `baseUrl/objects/<StoredImage.Name>`. The component also supports `ImageDataBase64`, absolute URLs, and a custom resolver through `configureStoredImageUrls({ resolver })`. Image URLs use configured roles rather than runtime layout measurement: default roles are `product-tile` 500x500, `product-detail` 900x900, `thumbnail`, `basket-line`, and `checkout-line` 180x180, and `fullsize` with no `width`/`height` parameters. Set `image-role` on the component, pass `{ role }` to `storedImageUrl()`, or override role sizes with `configureShop({ storedImageUrls: { roles } })`. Custom resolvers receive `{ variant, role, width, height }` as the second argument and should preserve those dimensions in the returned URL when the target CDN supports resizing. Use `loading`, `decoding`, and `fetchpriority` on custom image templates when a shop knows which image is above the fold; the default product detail main image is eager/high priority, while normal stored images stay lazy by default.
889
996
 
890
997
  By default, `orderly-stored-image` does not render a frame border. Add the boolean `border` attribute when a bordered image is wanted:
891
998