@orderlyshop/web-components 0.1.0-build.7063 → 0.1.0-build.7067
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 +15 -4
- package/README.md +192 -32
- package/bin/orderly-generate-server-renderers.mjs +69 -11
- package/bin/orderly-hydrate-static-pages.mjs +47 -13
- package/bin/orderly-init-shop.mjs +50 -7
- package/custom-elements.json +48 -11
- package/dist/browser/orderly-web-components.define.global.js +759 -157
- package/dist/browser/orderly-web-components.define.global.js.br +0 -0
- package/dist/browser/orderly-web-components.define.global.js.gz +0 -0
- package/dist/browser/orderly-web-components.define.global.js.map +1 -1
- package/dist/browser/orderly-web-components.global.js +759 -157
- package/dist/browser/orderly-web-components.global.js.br +0 -0
- package/dist/browser/orderly-web-components.global.js.gz +0 -0
- package/dist/browser/orderly-web-components.global.js.map +1 -1
- package/dist/{default-shop-CwBPPNF3.d.ts → default-shop-CQuz1DKx.d.ts} +75 -2
- package/dist/default-shop.d.ts +2 -2
- package/dist/default-shop.js +1 -861
- package/dist/default-shop.js.br +0 -0
- package/dist/default-shop.js.gz +0 -0
- package/dist/default-shop.js.map +1 -1
- package/dist/define-DThgfT4n.d.ts +10 -0
- package/dist/define.d.ts +1 -1
- package/dist/define.js +870 -9805
- package/dist/define.js.br +0 -0
- package/dist/define.js.gz +0 -0
- package/dist/define.js.map +1 -1
- package/dist/index.d.ts +37 -9
- package/dist/index.js +819 -10551
- package/dist/index.js.br +0 -0
- package/dist/index.js.gz +0 -0
- package/dist/index.js.map +1 -1
- package/dist/navigation.d.ts +10 -2
- package/dist/navigation.js +9 -1357
- package/dist/navigation.js.br +0 -0
- package/dist/navigation.js.gz +0 -0
- package/dist/navigation.js.map +1 -1
- package/dist/query.js +1 -114
- package/dist/query.js.br +0 -0
- package/dist/query.js.gz +0 -0
- package/dist/query.js.map +1 -1
- package/dist/shop-CYr-svkP.d.ts +313 -0
- package/dist/shop-query.js +1 -99
- package/dist/shop-query.js.br +0 -0
- package/dist/shop-query.js.gz +0 -0
- package/dist/shop-query.js.map +1 -1
- package/dist/shop.d.ts +3 -3
- package/dist/shop.js +895 -10314
- package/dist/shop.js.br +0 -0
- package/dist/shop.js.gz +0 -0
- package/dist/shop.js.map +1 -1
- package/dist/stores.d.ts +1 -1
- package/dist/stores.js +1 -375
- package/dist/stores.js.br +0 -0
- package/dist/stores.js.gz +0 -0
- package/dist/stores.js.map +1 -1
- package/dist/taxonomy.d.ts +2 -2
- package/dist/taxonomy.js +1 -246
- package/dist/taxonomy.js.br +0 -0
- package/dist/taxonomy.js.gz +0 -0
- package/dist/taxonomy.js.map +1 -1
- package/dist/{types-CXEwL2xS.d.ts → types-CeigwmOf.d.ts} +7 -1
- package/docs/components/README.md +100 -5
- package/docs/shop-best-practices.md +128 -0
- package/html-custom-data.json +30 -7
- package/package.json +3 -3
- package/server/README.md +2 -0
- package/server/php/orderly-product.php +25 -2
- package/dist/define-qZ8TVIjH.d.ts +0 -9
- package/dist/shop-TvSqmBjM.d.ts +0 -190
package/AGENTS.md
CHANGED
|
@@ -15,6 +15,7 @@ Use this package as a headless storefront component library. Prefer native custo
|
|
|
15
15
|
- Read `README.md` for setup and package-wide concepts.
|
|
16
16
|
- Read `DESIGN.md` before changing the visual theme. It defines the semantic design tokens that should be changed first.
|
|
17
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.
|
|
18
19
|
- Read `docs/components/product-rail.md` and `docs/components/product-grid.md` before implementing product listing UI.
|
|
19
20
|
- Read `custom-elements.json` for machine-readable custom element metadata.
|
|
20
21
|
- Read `html-custom-data.json` when generating vanilla HTML or configuring editor autocomplete.
|
|
@@ -26,6 +27,11 @@ Use this package as a headless storefront component library. Prefer native custo
|
|
|
26
27
|
- Ask the user for the Orderly account id so `src/shop-query.ts` can tenant-scope the default `SearchQuery`.
|
|
27
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.
|
|
28
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.
|
|
29
35
|
- Put the resulting theme in `src/style.css` or the consuming shop stylesheet, starting with `DESIGN.md` tokens before adding component-specific selectors.
|
|
30
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.
|
|
31
37
|
|
|
@@ -52,24 +58,29 @@ Use this package as a headless storefront component library. Prefer native custo
|
|
|
52
58
|
|
|
53
59
|
- Keep components headless. Add behavior and default light-DOM markup, not bundled theme CSS.
|
|
54
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.
|
|
55
63
|
- 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
64
|
- Preserve template override support through `data-orderly-template`, `data-orderly-bind`, `data-orderly-slot`, and `data-orderly-action`.
|
|
57
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`.
|
|
58
66
|
- Keep browser-only package code free of Node HTTP modules.
|
|
59
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.
|
|
60
69
|
- Treat localStorage basket and checkout profile data as user-owned state. Do not add secret persistence.
|
|
61
70
|
- Product images should default to `fit="contain"`, white background, and square aspect ratio in product tiles.
|
|
71
|
+
- 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
72
|
- 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.
|
|
73
|
+
- 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
|
+
- 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
|
+
- 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.
|
|
66
77
|
- Use `orderly-checkout-page` for checkout pages instead of wiring `orderly-checkout` and `orderly-basket` manually in app code.
|
|
67
78
|
- 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
79
|
- 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
80
|
- 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
81
|
- Use `navigation.ts` for shop category/navigation structure. Export `navigationDefinitions: NavigationDefinition[]`.
|
|
71
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.
|
|
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`.
|
|
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`.
|
|
73
84
|
- Use plain `SearchQueryInput` objects in navigation definitions, for example `query: { query: "sko sneakers", tags: ["sko"] }`.
|
|
74
85
|
|
|
75
86
|
## Common Patterns
|
package/README.md
CHANGED
|
@@ -17,39 +17,47 @@ Inputs:
|
|
|
17
17
|
- Orderly account id: ASK
|
|
18
18
|
- Visual style reference: ASK
|
|
19
19
|
- Deployment target: ASK
|
|
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/
|
|
20
24
|
|
|
21
|
-
Use any provided input above without asking again. Ask only for values that are still `ASK`.
|
|
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.
|
|
22
26
|
|
|
23
27
|
Install and setup:
|
|
24
28
|
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,
|
|
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.
|
|
26
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.
|
|
27
32
|
|
|
28
33
|
Navigation:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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.
|
|
32
37
|
|
|
33
38
|
Visual style:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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.
|
|
37
45
|
|
|
38
46
|
Pages and behavior:
|
|
39
|
-
|
|
40
|
-
|
|
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.
|
|
41
49
|
|
|
42
50
|
Deployment:
|
|
43
|
-
|
|
51
|
+
16. Configure the build/publish flow for the deployment target.
|
|
44
52
|
- For static hosting, use npm run build and document the dist upload.
|
|
45
53
|
- For FTP, use npx orderly-publish-site --target ftp.
|
|
46
54
|
- For Apache/PHP or Nginx/PHP, keep the package-generated server renderers and document the required server helper files.
|
|
47
55
|
- For any other target, document the exact build output and any remaining deployment assumptions.
|
|
48
56
|
|
|
49
57
|
Validation:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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.
|
|
53
61
|
```
|
|
54
62
|
|
|
55
63
|
## Install
|
|
@@ -74,7 +82,7 @@ Agents and developers should use `npx orderly-init-shop` before hand-writing sho
|
|
|
74
82
|
npx orderly-init-shop --account-id 00000000-0000-0000-0000-000000000000
|
|
75
83
|
```
|
|
76
84
|
|
|
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.
|
|
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.
|
|
78
86
|
|
|
79
87
|
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
88
|
|
|
@@ -82,7 +90,7 @@ The scaffold uses Vite for local development and static builds, but Vite is adde
|
|
|
82
90
|
|
|
83
91
|
## Browser Standalone Bundle
|
|
84
92
|
|
|
85
|
-
The package build
|
|
93
|
+
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
94
|
|
|
87
95
|
Use the configurable bundle when a vanilla HTML site needs to set shop options before registering elements:
|
|
88
96
|
|
|
@@ -118,16 +126,79 @@ Use the auto-register bundle for the built-in default shop or pages that configu
|
|
|
118
126
|
|
|
119
127
|
The normal package entrypoints remain ESM for bundlers. The standalone files are classic browser scripts and expose `window.OrderlyWebComponents`.
|
|
120
128
|
|
|
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.
|
|
129
|
+
`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
130
|
|
|
123
131
|
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
132
|
|
|
133
|
+
## Cookie Consent
|
|
134
|
+
|
|
135
|
+
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:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
configureShop({
|
|
139
|
+
consent: {
|
|
140
|
+
enabled: true,
|
|
141
|
+
version: "2026-04-30",
|
|
142
|
+
policyHref: "/information/cookies/",
|
|
143
|
+
managedCookies: [
|
|
144
|
+
{
|
|
145
|
+
name: "_ga",
|
|
146
|
+
category: "statistics",
|
|
147
|
+
provider: "Google Analytics",
|
|
148
|
+
purpose: "Skelner mellem besøgende i statistikmåling.",
|
|
149
|
+
duration: "Typisk op til 2 år"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
name: "_ga_*",
|
|
153
|
+
category: "statistics",
|
|
154
|
+
provider: "Google Analytics",
|
|
155
|
+
purpose: "Bevarer sessionsstatus for statistikmåling.",
|
|
156
|
+
duration: "Typisk op til 2 år"
|
|
157
|
+
}
|
|
158
|
+
]
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
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.
|
|
164
|
+
|
|
165
|
+
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.
|
|
166
|
+
|
|
167
|
+
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.
|
|
168
|
+
|
|
169
|
+
## Analytics And Google Tag Manager
|
|
170
|
+
|
|
171
|
+
Use `configureShop({ analytics })` to install Google Tag Manager and publish GA4-compatible navigation and ecommerce events to `dataLayer`:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
configureShop({
|
|
175
|
+
consent: {
|
|
176
|
+
enabled: true,
|
|
177
|
+
policyHref: "/information/cookies/"
|
|
178
|
+
},
|
|
179
|
+
analytics: {
|
|
180
|
+
googleTagManagerId: import.meta.env.VITE_ORDERLY_GOOGLE_TAG_MANAGER_ID,
|
|
181
|
+
defaultCurrency: "DKK",
|
|
182
|
+
affiliation: "My Shop"
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
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.
|
|
188
|
+
|
|
189
|
+
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.
|
|
190
|
+
|
|
191
|
+
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.
|
|
192
|
+
|
|
193
|
+
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.
|
|
194
|
+
|
|
125
195
|
## API Discovery
|
|
126
196
|
|
|
127
197
|
Developers and coding agents should start with these package-owned docs:
|
|
128
198
|
|
|
129
199
|
- [`DESIGN.md`](./DESIGN.md) defines the package-wide design tokens and the recommended theming workflow for consistent styling across all components.
|
|
130
200
|
- [`docs/components/README.md`](./docs/components/README.md) lists every custom element, common attributes, typed properties, events, and template hooks.
|
|
201
|
+
- [`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
202
|
- [`docs/components/product-grid.md`](./docs/components/product-grid.md) documents search loading, sorting, paging, default query merging, and declarative query markup.
|
|
132
203
|
- [`docs/components/product-rail.md`](./docs/components/product-rail.md) documents homepage/category rails and their query configuration.
|
|
133
204
|
- [`AGENTS.md`](./AGENTS.md) gives coding agents the conventions for building shops with this package.
|
|
@@ -368,10 +439,13 @@ npx orderly-init-navigation --suggest --account-id <account-id>
|
|
|
368
439
|
```
|
|
369
440
|
|
|
370
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.
|
|
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"] }`.
|
|
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"] }`.
|
|
372
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.
|
|
373
444
|
7. Configure the shop scope with a Core `SearchQuery` and pass it to `configureShop({ defaultShop: { defaultQuery } })`.
|
|
374
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.
|
|
375
449
|
|
|
376
450
|
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
451
|
|
|
@@ -399,7 +473,7 @@ Navigation data has three layers:
|
|
|
399
473
|
|
|
400
474
|
- `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
475
|
- `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.
|
|
476
|
+
- `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
477
|
|
|
404
478
|
The older `CategoryDefinition`, `CategoryNavigationItem`, and `CategoryMetadata` names remain as compatibility aliases only.
|
|
405
479
|
|
|
@@ -443,6 +517,20 @@ Use `--skip-products` for metadata-only hydration, `--strict-products` when a ni
|
|
|
443
517
|
|
|
444
518
|
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
519
|
|
|
520
|
+
## Static Vanilla Deployment
|
|
521
|
+
|
|
522
|
+
For static hosting, keep the generated shop as a normal Vite site and upload the built `dist` directory, not `src` or `node_modules`.
|
|
523
|
+
|
|
524
|
+
Recommended path-based setup:
|
|
525
|
+
|
|
526
|
+
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.
|
|
527
|
+
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.
|
|
528
|
+
3. Upload every file in `dist`, including hashed assets under `dist/assets` and precompressed `.br`/`.gz` files when the host supports them.
|
|
529
|
+
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`.
|
|
530
|
+
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.
|
|
531
|
+
|
|
532
|
+
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`.
|
|
533
|
+
|
|
446
534
|
## Local SSR Dev Mode
|
|
447
535
|
|
|
448
536
|
Scaffolded shops use SSR in dev mode by default through the package Vite middleware:
|
|
@@ -502,7 +590,7 @@ npm run build
|
|
|
502
590
|
npx orderly-generate-server-renderers --site-title "My Shop"
|
|
503
591
|
```
|
|
504
592
|
|
|
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.
|
|
593
|
+
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
594
|
|
|
507
595
|
Runtime options:
|
|
508
596
|
|
|
@@ -520,7 +608,14 @@ import { configureShop } from "@orderlyshop/web-components";
|
|
|
520
608
|
|
|
521
609
|
configureShop({
|
|
522
610
|
responsiveTemplates: { mobileMediaQuery: "(max-width: 860px)" },
|
|
523
|
-
storedImageUrls: {
|
|
611
|
+
storedImageUrls: {
|
|
612
|
+
baseUrl: "https://static.example-cdn.com/",
|
|
613
|
+
roles: {
|
|
614
|
+
"product-tile": { width: 500, height: 500 },
|
|
615
|
+
"product-detail": { width: 900, height: 900 },
|
|
616
|
+
fullsize: false
|
|
617
|
+
}
|
|
618
|
+
}
|
|
524
619
|
});
|
|
525
620
|
```
|
|
526
621
|
|
|
@@ -548,7 +643,7 @@ style.textContent = ORDERLY_LAZY_LOAD_CSS;
|
|
|
548
643
|
document.head.append(style);
|
|
549
644
|
```
|
|
550
645
|
|
|
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()`.
|
|
646
|
+
`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
647
|
|
|
553
648
|
## Global Templates
|
|
554
649
|
|
|
@@ -611,25 +706,38 @@ collection.client = client;
|
|
|
611
706
|
collection.query = searchQuery;
|
|
612
707
|
```
|
|
613
708
|
|
|
709
|
+
## Content and Brand Cookbook
|
|
710
|
+
|
|
711
|
+
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.
|
|
712
|
+
|
|
713
|
+
For a fuller real-shop checklist, read [Shop Best Practices](./docs/shop-best-practices.md) before the first implementation pass.
|
|
714
|
+
|
|
715
|
+
- 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.
|
|
716
|
+
- 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.
|
|
717
|
+
- 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.
|
|
718
|
+
- 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.
|
|
719
|
+
- 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.
|
|
720
|
+
- 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.
|
|
721
|
+
|
|
614
722
|
## Components
|
|
615
723
|
|
|
616
724
|
- `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 `
|
|
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. 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.
|
|
725
|
+
- `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.
|
|
726
|
+
- `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
727
|
- `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`. 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
|
|
728
|
+
- `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`.
|
|
729
|
+
- `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
730
|
- `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 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`.
|
|
731
|
+
- `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`.
|
|
732
|
+
- `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
733
|
- `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
734
|
- `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
735
|
- `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,
|
|
736
|
+
- `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
737
|
- `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"`.
|
|
738
|
+
- `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
739
|
- `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.
|
|
740
|
+
- `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
741
|
- `orderly-search-box`, `orderly-sort-select`, `orderly-filter-panel`, and `orderly-load-more` bind to `orderly-product-grid`.
|
|
634
742
|
|
|
635
743
|
## Navigation Patterns
|
|
@@ -640,6 +748,19 @@ There are two different mobile navigation patterns:
|
|
|
640
748
|
- `layout-desktop="horizontal" layout-mobile="burgermenu"` plus `primary-nav-mobile-placement="header"` means desktop horizontal navigation and an inline mobile burger in the page header next to search, basket, or other icon buttons. This is the recommended setup for the common storefront header pattern.
|
|
641
749
|
- Custom `data-orderly-viewport="mobile"` page-layout templates are only needed when a shop replaces the whole page layout and wants manual control over where `data-orderly-slot="primary-nav"` is rendered.
|
|
642
750
|
|
|
751
|
+
Package-owned home, category, product, checkout, and payment pages read the same pattern from configuration:
|
|
752
|
+
|
|
753
|
+
```ts
|
|
754
|
+
configureShop({
|
|
755
|
+
navigationLayout: {
|
|
756
|
+
primaryNavDesktopPlacement: "primary-nav",
|
|
757
|
+
primaryNavMobilePlacement: "header",
|
|
758
|
+
layoutDesktop: "horizontal",
|
|
759
|
+
layoutMobile: "burgermenu"
|
|
760
|
+
}
|
|
761
|
+
});
|
|
762
|
+
```
|
|
763
|
+
|
|
643
764
|
```html
|
|
644
765
|
<orderly-page-layout primary-nav-mobile-placement="header">
|
|
645
766
|
<a slot="header" href="/">Shop name</a>
|
|
@@ -654,6 +775,33 @@ There are two different mobile navigation patterns:
|
|
|
654
775
|
</orderly-page-layout>
|
|
655
776
|
```
|
|
656
777
|
|
|
778
|
+
## Basket Layout And Count
|
|
779
|
+
|
|
780
|
+
Use the package basket primitives before writing custom state glue:
|
|
781
|
+
|
|
782
|
+
```html
|
|
783
|
+
<orderly-category-page basket-mode="inline-desktop"></orderly-category-page>
|
|
784
|
+
<orderly-basket-icon href="/checkout.html" placement="mobile-fixed" hide-when-empty label="Kurv"></orderly-basket-icon>
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
`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`.
|
|
788
|
+
|
|
789
|
+
Basket count contract:
|
|
790
|
+
|
|
791
|
+
- `BasketController.count` is derived from `DraftOrder.OrderLines[].Quantity`.
|
|
792
|
+
- `orderly-basket-icon`, `orderly-basket`, product tiles/grids/rails, product pages, and checkout should share the same `BasketController` instance.
|
|
793
|
+
- `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.
|
|
794
|
+
- Verified and persisted drafts still use the same `DraftOrder`; backend-provided prices and errors are rendered by `orderly-basket`.
|
|
795
|
+
|
|
796
|
+
## Checkout Responsive Overrides
|
|
797
|
+
|
|
798
|
+
When customizing checkout layout, do not override the package mobile collapse accidentally:
|
|
799
|
+
|
|
800
|
+
- Keep desktop-only grid changes inside `@media (min-width: 768px)` or the same breakpoint configured through `configureShop({ responsiveTemplates })`.
|
|
801
|
+
- 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.
|
|
802
|
+
- Prefer CSS variables and scoped spacing/color rules on `.orderly-checkout-page`, `.orderly-checkout-page__card`, `.orderly-checkout`, and `.orderly-checkout__option`.
|
|
803
|
+
- Keep `.orderly-checkout-page__card--summary` as a normal layout container; the package moves submit/terms controls there for the responsive checkout summary.
|
|
804
|
+
|
|
657
805
|
## Rendering And Styling
|
|
658
806
|
|
|
659
807
|
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.
|
|
@@ -732,6 +880,18 @@ For dynamic paging, `orderly-product-grid` keeps the returned `PageResult.Contin
|
|
|
732
880
|
|
|
733
881
|
Every component has a default light-DOM implementation and matching template hooks. Use `data-orderly-bind` for values, `data-orderly-action` for behavior, and `data-orderly-slot` for repeated or projected content. Product tile templates can bind `basket-action-icon`, `basket-action-label`, and `basket-action-state` and use `data-orderly-action="add-to-basket"` for the add/remove toggle. Product page templates keep the existing `addLabel` binding and also support `basket-action-label`, `basket-action-icon`, and `basket-action-state` for the same add-to-basket concept; use `data-orderly-action="add-to-basket"` for the action. Product page galleries use `data-orderly-slot="thumbnails"` and `data-orderly-action="select-image"` for custom thumbnail markup, while the fullscreen viewer overlay stays package-owned; shops can optionally mark a custom trigger with `data-orderly-image-viewer-trigger`, otherwise the inline `data-orderly-bind="image"` element becomes the trigger automatically. The viewer overlay may be moved outside the product-page host while open, either to `document.body` or to the nearest open `<dialog>`, so custom CSS or tests should target `[data-orderly-image-overlay]` instead of assuming the overlay remains inside `orderly-product-page`. Basket templates can add `data-orderly-slot="errors"` to place order-level `DraftOrder.Errors` exactly where a shop wants them. Product rail templates can replace the outer `layout`, inner `grid`, product item, loading state, and empty state. The main template names are `layout`, `grid`, `footer`, `product`, `thumbnail`, `image`, `basket`, `basket-icon`, `line`, `checkout`, `navigation`, `item`, `search-box`, `sort`, `sort-select`, `filter-panel`, `load-more`, `address-line`, `contact-item`, `opening-hour`, and `information-link`.
|
|
734
882
|
|
|
883
|
+
Use product page detail slots for small shop-specific content without replacing the package product template:
|
|
884
|
+
|
|
885
|
+
```html
|
|
886
|
+
<orderly-product-detail-page>
|
|
887
|
+
<section slot="purchase-note">
|
|
888
|
+
<strong>Kurateret secondhand</strong>
|
|
889
|
+
<p>Alle varer er udvalgt og pakket i butikken.</p>
|
|
890
|
+
</section>
|
|
891
|
+
<a slot="secondary-cta" href="/butik/">Besøg butikken på Nørrebrogade</a>
|
|
892
|
+
</orderly-product-detail-page>
|
|
893
|
+
```
|
|
894
|
+
|
|
735
895
|
Product sorting belongs to `orderly-product-grid`: assign `grid.sortOptions = [{ label, orderBy }]` or pass sort options through `orderly-collection-page.sortOptions`. The default grid layout renders the sort select above the products. Custom grid layouts should include `data-orderly-slot="sort"` where the control should appear, and custom sort templates can use `data-orderly-template="sort"` with a `<select data-orderly-action="sort">`.
|
|
736
896
|
|
|
737
897
|
Simple product queries can be declared in markup on `orderly-product-grid`, `orderly-product-rail`, and `orderly-collection-page`:
|
|
@@ -860,7 +1020,7 @@ Stored images can be rendered directly:
|
|
|
860
1020
|
productImage.image = searchObject.Images[0];
|
|
861
1021
|
```
|
|
862
1022
|
|
|
863
|
-
`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 })`.
|
|
1023
|
+
`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.
|
|
864
1024
|
|
|
865
1025
|
By default, `orderly-stored-image` does not render a frame border. Add the boolean `border` attribute when a bordered image is wanted:
|
|
866
1026
|
|