@osimart/create-storefront 0.0.0-stage → 1.0.0

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 (206) hide show
  1. package/README.md +80 -2
  2. package/THEME_DEV_API.md +80 -0
  3. package/app/app.vue +154 -0
  4. package/app/assets/css/osi-controls.css +824 -0
  5. package/app/components/SectionRenderer.vue +46 -0
  6. package/app/components/builder/BuilderOverlay.vue +582 -0
  7. package/app/components/checkout/ExtraFees.vue +329 -0
  8. package/app/components/checkout/PaymentHandoff.vue +388 -0
  9. package/app/components/common/Banner.vue +35 -0
  10. package/app/components/common/ConfirmDeleteModal.vue +117 -0
  11. package/app/components/common/DropdownSelect.vue +432 -0
  12. package/app/components/common/Loader.vue +32 -0
  13. package/app/components/common/OsiField.vue +218 -0
  14. package/app/components/common/PhoneNumInput.vue +434 -0
  15. package/app/composables/UseBodyScrollLock.js +122 -0
  16. package/app/composables/UseThemeGroup.js +575 -0
  17. package/app/composables/useAddToCart.js +87 -0
  18. package/app/composables/useAnalytics.js +15 -0
  19. package/app/composables/useAnnouncements.js +29 -0
  20. package/app/composables/useApiErrorToast.js +104 -0
  21. package/app/composables/useAuth.js +99 -0
  22. package/app/composables/useBanners.js +25 -0
  23. package/app/composables/useBuilderPreview.js +320 -0
  24. package/app/composables/useButtonStyle.js +107 -0
  25. package/app/composables/useCarousel.js +160 -0
  26. package/app/composables/useCart.js +109 -0
  27. package/app/composables/useCheckout.js +76 -0
  28. package/app/composables/useClassificationItems.js +269 -0
  29. package/app/composables/useClassificationPage.js +545 -0
  30. package/app/composables/useClassificationUrl.js +63 -0
  31. package/app/composables/useColorScheme.js +44 -0
  32. package/app/composables/useContactForm.js +53 -0
  33. package/app/composables/useContentTree.js +770 -0
  34. package/app/composables/useDragScroll.js +73 -0
  35. package/app/composables/useFooterSocials.js +107 -0
  36. package/app/composables/useInputStyle.js +45 -0
  37. package/app/composables/useLayoutShell.js +114 -0
  38. package/app/composables/useLazyVideo.js +36 -0
  39. package/app/composables/useMediaTypes.js +66 -0
  40. package/app/composables/useMediaUrl.js +71 -0
  41. package/app/composables/useMenu.js +100 -0
  42. package/app/composables/useNavStyle.js +219 -0
  43. package/app/composables/usePlaceholders.js +294 -0
  44. package/app/composables/usePolicies.js +26 -0
  45. package/app/composables/useProductCardMedia.js +135 -0
  46. package/app/composables/useProductCardStyle.js +132 -0
  47. package/app/composables/useProductDetail.js +982 -0
  48. package/app/composables/useProductGallery.js +166 -0
  49. package/app/composables/useProductItems.js +270 -0
  50. package/app/composables/useProductUrl.js +60 -0
  51. package/app/composables/useProductVariants.js +210 -0
  52. package/app/composables/useQuickShop.js +115 -0
  53. package/app/composables/useRelatedProducts.js +186 -0
  54. package/app/composables/useRenderContext.js +45 -0
  55. package/app/composables/useScopedCss.js +106 -0
  56. package/app/composables/useSearch.js +35 -0
  57. package/app/composables/useSectionData.js +462 -0
  58. package/app/composables/useSectionShell.js +113 -0
  59. package/app/composables/useSidebar.js +69 -0
  60. package/app/composables/useStoreData.js +57 -0
  61. package/app/composables/useStoreInit.js +52 -0
  62. package/app/composables/useStorefront.js +239 -0
  63. package/app/composables/useT.js +21 -0
  64. package/app/composables/useTextStyles.js +84 -0
  65. package/app/composables/useThemeControls.js +34 -0
  66. package/app/composables/useThemeSeo.js +91 -0
  67. package/app/composables/useThemeTokens.js +171 -0
  68. package/app/composables/useWishlist.js +66 -0
  69. package/app/logic/components/AnnouncementBar.js +118 -0
  70. package/app/logic/components/CardGrid.js +125 -0
  71. package/app/logic/components/CardList.js +98 -0
  72. package/app/logic/components/CartDrawer.js +273 -0
  73. package/app/logic/components/CartItem.js +31 -0
  74. package/app/logic/components/ClassificationBento.js +6 -0
  75. package/app/logic/components/ClassificationCard.js +6 -0
  76. package/app/logic/components/ClassificationCarousel.js +115 -0
  77. package/app/logic/components/ClassificationEditorial.js +41 -0
  78. package/app/logic/components/ClassificationFeatured.js +57 -0
  79. package/app/logic/components/ClassificationGrid.js +6 -0
  80. package/app/logic/components/ClassificationNav.js +61 -0
  81. package/app/logic/components/ClassificationPart.js +72 -0
  82. package/app/logic/components/FooterBlock.js +91 -0
  83. package/app/logic/components/FooterSocials.js +60 -0
  84. package/app/logic/components/HeaderDropdownList.js +29 -0
  85. package/app/logic/components/HeaderIcon.js +68 -0
  86. package/app/logic/components/HeaderLogo.js +39 -0
  87. package/app/logic/components/HeaderNav.js +268 -0
  88. package/app/logic/components/MobileMenu.js +280 -0
  89. package/app/logic/components/ProductBuyButtons.js +66 -0
  90. package/app/logic/components/ProductCard.js +160 -0
  91. package/app/logic/components/ProductFilters.js +37 -0
  92. package/app/logic/components/ProductGallery.js +19 -0
  93. package/app/logic/components/ProductInfoBlock.js +411 -0
  94. package/app/logic/components/ProductLightbox.js +12 -0
  95. package/app/logic/components/ProductQuantity.js +31 -0
  96. package/app/logic/components/ProductVariantPicker.js +125 -0
  97. package/app/logic/components/QuickBuy.js +194 -0
  98. package/app/logic/components/SearchOverlay.js +41 -0
  99. package/app/logic/components/SidebarMenuItem.js +39 -0
  100. package/app/logic/components/SiteFooter.js +476 -0
  101. package/app/logic/components/SiteHeader.js +266 -0
  102. package/app/logic/components/WhatsAppButton.js +75 -0
  103. package/app/logic/layouts/default.js +66 -0
  104. package/app/logic/sections/AnnouncementBarSection.js +6 -0
  105. package/app/logic/sections/BlockCarousel.js +103 -0
  106. package/app/logic/sections/ClassificationListSection.js +40 -0
  107. package/app/logic/sections/ContactFormSection.js +294 -0
  108. package/app/logic/sections/ContactInfoSection.js +419 -0
  109. package/app/logic/sections/ContentNode.js +189 -0
  110. package/app/logic/sections/CountdownSection.js +254 -0
  111. package/app/logic/sections/CustomHtmlSection.js +51 -0
  112. package/app/logic/sections/DividerSection.js +31 -0
  113. package/app/logic/sections/FaqSection.js +53 -0
  114. package/app/logic/sections/FeatureListSection.js +6 -0
  115. package/app/logic/sections/FeaturedProductSection.js +40 -0
  116. package/app/logic/sections/FooterSection.js +16 -0
  117. package/app/logic/sections/FooterUtilitiesSection.js +24 -0
  118. package/app/logic/sections/GallerySection.js +232 -0
  119. package/app/logic/sections/HeaderSection.js +24 -0
  120. package/app/logic/sections/HeroSection.js +24 -0
  121. package/app/logic/sections/LogoListSection.js +6 -0
  122. package/app/logic/sections/MainClassificationSection.js +473 -0
  123. package/app/logic/sections/MainProductSection.js +211 -0
  124. package/app/logic/sections/MainSearchSection.js +6 -0
  125. package/app/logic/sections/MapSection.js +92 -0
  126. package/app/logic/sections/MediaContentSection.js +38 -0
  127. package/app/logic/sections/MediaWithTextSection.js +238 -0
  128. package/app/logic/sections/NewsletterSection.js +126 -0
  129. package/app/logic/sections/ProductHighlightSection.js +201 -0
  130. package/app/logic/sections/ProductListSection.js +45 -0
  131. package/app/logic/sections/ProductRecommendationsSection.js +113 -0
  132. package/app/logic/sections/RichTextSection.js +6 -0
  133. package/app/logic/sections/ScrollingTextSection.js +69 -0
  134. package/app/logic/sections/SearchHeaderSection.js +22 -0
  135. package/app/logic/sections/SlideshowSection.js +118 -0
  136. package/app/logic/sections/SpacerSection.js +37 -0
  137. package/app/logic/sections/SplitShowcaseSection.js +6 -0
  138. package/app/logic/sections/TestimonialListSection.js +6 -0
  139. package/app/logic/sections/VideoSection.js +235 -0
  140. package/app/pages/[category]/[product].vue +196 -0
  141. package/app/pages/__builder-preview.vue +38 -0
  142. package/app/pages/categories/[slug].vue +208 -0
  143. package/app/pages/checkout.vue +1972 -0
  144. package/app/pages/forgot-password.vue +115 -0
  145. package/app/pages/index.vue +64 -0
  146. package/app/pages/login.vue +585 -0
  147. package/app/pages/order-detail.vue +442 -0
  148. package/app/pages/pages/[handle].vue +58 -0
  149. package/app/pages/privacy-policy.vue +46 -0
  150. package/app/pages/resetpassword.vue +138 -0
  151. package/app/pages/resetverificationcode.vue +163 -0
  152. package/app/pages/result.vue +203 -0
  153. package/app/pages/return-policy.vue +50 -0
  154. package/app/pages/search.vue +100 -0
  155. package/app/pages/shipping-policy.vue +46 -0
  156. package/app/pages/user-profile/components/AddAddressModal.vue +452 -0
  157. package/app/pages/user-profile/components/Whishlist.vue +137 -0
  158. package/app/pages/user-profile/components/left-bar.vue +233 -0
  159. package/app/pages/user-profile/components/orders.vue +162 -0
  160. package/app/pages/user-profile/components/profile-details.vue +566 -0
  161. package/app/pages/user-profile/components/settings.vue +194 -0
  162. package/app/pages/user-profile/index.vue +252 -0
  163. package/app/pages/verificationcode.vue +188 -0
  164. package/app/plugins/analytics.client.js +99 -0
  165. package/app/plugins/geo.ts +24 -0
  166. package/app/plugins/reveal.client.js +53 -0
  167. package/app/plugins/toast.js +10 -0
  168. package/app/plugins/vue-query.ts +5 -0
  169. package/app/services/ApiServices.js +74 -0
  170. package/app/services/AuthBus.js +162 -0
  171. package/app/services/Authentication/AuthenticationService.js +133 -0
  172. package/app/services/Authentication/TokenService.js +62 -0
  173. package/app/services/CartServices.js +91 -0
  174. package/app/services/CartToken.js +26 -0
  175. package/app/services/CategoryServices.js +54 -0
  176. package/app/services/CheckoutServices.js +113 -0
  177. package/app/services/HomeServices.js +133 -0
  178. package/app/services/NewsletterServices.js +29 -0
  179. package/app/services/PaymentServices.js +71 -0
  180. package/app/services/PoliciesServices.js +16 -0
  181. package/app/services/ProductServices.js +79 -0
  182. package/app/services/SearchServices.js +18 -0
  183. package/app/services/UserServices.js +105 -0
  184. package/app/services/UtilsServices.js +46 -0
  185. package/app/services/WishlistServices.js +51 -0
  186. package/app/services/storefront.service.js +57 -0
  187. package/app/stores/userStore.js +13 -0
  188. package/app/theme/categoryTree.js +68 -0
  189. package/app/theme/controls.js +88 -0
  190. package/app/theme/registry.js +56 -0
  191. package/app/utils/analytics.js +864 -0
  192. package/app/utils/device.js +50 -0
  193. package/app/utils/eventBus.js +24 -0
  194. package/app/utils/icon-presets.js +24 -0
  195. package/app/utils/productOffers.js +118 -0
  196. package/app/utils/requestCache.js +36 -0
  197. package/app/utils/scrollToError.js +59 -0
  198. package/app/utils/toPlainError.js +30 -0
  199. package/bin/osimart-theme.mjs +259 -0
  200. package/nuxt/osi-sections.ts +56 -0
  201. package/nuxt.config.ts +60 -0
  202. package/package.json +43 -3
  203. package/public/css/input.css +29 -0
  204. package/public/css/output.css +2716 -0
  205. package/public/favicon-32x32.png +0 -0
  206. package/public/robots.txt +2 -0
package/README.md CHANGED
@@ -1,3 +1,81 @@
1
- # Temporary Holding Version
1
+ # @osimart/osimart-storefront
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The Osimart storefront **platform**, published as a private Nuxt layer. It has no look of
4
+ its own. Every theme repo extends it:
5
+
6
+ ```ts
7
+ // osimart-theme-base/nuxt.config.ts, store-offsiders/nuxt.config.ts, …
8
+ export default defineNuxtConfig({ extends: ["@osimart/create-storefront"] });
9
+ ```
10
+
11
+ | | Shopify | Osimart |
12
+ |---|---|---|
13
+ | Platform (hidden, updates everyone) | Shopify's servers | this package |
14
+ | Theme (Dawn) | sections, snippets, layout, assets | `osimart-theme-base` repo |
15
+ | Theme JSON | `templates/*.json`, `sections/*-group.json`, `config/settings_data.json` | `theme/` in the theme repo, `osimart-theme pull/push` |
16
+ | A store's custom theme | copy of Dawn | a repo created from `osimart-theme-base` |
17
+
18
+ ## What's here
19
+
20
+ ```
21
+ app/
22
+ ├── logic/ the script of every theme file — the theme only imports it
23
+ │ ├── sections/ HeroSection.js, MainProductSection.js, … (#logic/sections/…)
24
+ │ ├── components/ ProductCard.js, SiteHeader.js, … (#logic/components/…)
25
+ │ └── layouts/default.js
26
+ ├── composables/ services/ utils/ stores/ plugins/
27
+ ├── pages/ routes: index, [category]/[product], categories/[slug], search,
28
+ │ pages/[handle] (each = "render this path's template");
29
+ │ checkout, login, user-profile, order-detail… (platform pages)
30
+ ├── components/ platform UI: SectionRenderer, builder/, checkout/,
31
+ │ common/ (OsiField, PhoneNumInput, DropdownSelect, Banner…)
32
+ ├── theme/ registry.js (section map), controls.js, categoryTree.js
33
+ └── app.vue
34
+ bin/osimart-theme.mjs CLI: themes / pull / push / publish
35
+ nuxt/osi-sections.ts builds the section map from the theme's app/sections
36
+ THEME_DEV_API.md the backend endpoints the CLI calls
37
+ ```
38
+
39
+ A theme section looks like this — markup and CSS in the theme, logic from here:
40
+
41
+ ```vue
42
+ <script setup>
43
+ import useLogic from "#logic/sections/HeroSection";
44
+
45
+ const props = defineProps({ section: Object, index: Number });
46
+ const { asBanner } = useLogic({ props });
47
+ </script>
48
+
49
+ <template><MediaWithTextSection :section="asBanner" :index="index" /></template>
50
+ ```
51
+
52
+ `useLogic` returns everything the original `<script setup>` declared, so the template can use
53
+ any of it. Changing the logic changes every theme after its next build; the theme's markup
54
+ and CSS are untouched.
55
+
56
+ ## Rules for code in this package
57
+
58
+ - Never import with `~/` — inside a layer it means the theme. Use `#osi/…` (this `app/`),
59
+ `#logic/…`, or `#components` (which picks the theme's component when it has one).
60
+ - `useLogic` return keys are the theme's API. Adding a key is a minor release; renaming or
61
+ removing one breaks themes, so it's a major.
62
+ - A logic file with a top-level `await` wraps it in `withAsyncContext` (already done for
63
+ `AnnouncementBar.js`) so composables after it still have the component instance.
64
+
65
+ ## Releasing
66
+
67
+ 1. Bump `version` (semver): patch/minor for fixes and additions, **major** if a `useLogic`
68
+ key, a component's props or a route changes.
69
+ 2. `git tag v1.4.0 && git push --tags` → `release.yml` publishes to GitHub Packages and sends
70
+ `storefront-release` to every repo in `STORE_REPOS`; each one updates and redeploys.
71
+
72
+ Repo setup once: secret `STORES_DISPATCH_TOKEN` (fine-grained PAT, contents read/write on the
73
+ theme repos), variable `STORE_REPOS` (`ositcom/osimart-theme-base ositcom/store-offsiders`).
74
+ Package settings: *Manage Actions access* → every theme repo; *Manage access* → each outside
75
+ developer (read).
76
+
77
+ ## Working on the platform and a theme together
78
+
79
+ ```bash
80
+ cd osimart-theme-base && npm link ../osimart-storefront && npm run dev
81
+ ```
@@ -0,0 +1,80 @@
1
+ # Theme dev API (backend contract for `osimart-theme`)
2
+
3
+ All under `theme-dev/` on the store's API host. The host picks the store, as for every
4
+ storefront call.
5
+
6
+ **Auth:** `Authorization: ThemeToken <token>`. The `ThemeAccessToken` belongs to one store
7
+ (hashed, revocable, created in the dashboard). Anything else gets `401`.
8
+
9
+ `<theme>` is either a theme id, `live` (the published theme) or `draft`.
10
+
11
+ ## Existing (from the theme access work)
12
+
13
+ | Method | Path | Used by |
14
+ |---|---|---|
15
+ | GET | `theme-dev/schemas/` | `pull` → `.osimart/schemas.json` |
16
+ | POST | `theme-dev/sections/sync/` | `push`: the theme's `*Section.schema.json` → `StoreSection` (`managed_by=CODE`) |
17
+
18
+ `sections/sync/` body: `{ sections: [{ type, name, group?, allowed_templates, schema: { settings, blocks, max_blocks? } }], prune, dry_run }`.
19
+ Response: `{ created, updated, unchanged, deleted, kept_in_use }` (lists of types).
20
+
21
+ ## New
22
+
23
+ ### `GET theme-dev/themes/`
24
+ ```json
25
+ [{ "id": "uuid", "name": "Base", "role": "LIVE", "updated_at": "…" },
26
+ { "id": "uuid", "name": "Base (draft)", "role": "DRAFT", "updated_at": "…" }]
27
+ ```
28
+
29
+ ### `GET theme-dev/theme/?theme=<theme>`
30
+ The theme's JSON exactly as stored, so a push of the same data is a no-op.
31
+ ```json
32
+ {
33
+ "theme": { "id": "uuid", "name": "Base", "role": "LIVE", "version": "2026-10-12T13:40:11.120Z" },
34
+ "settings": { "PRIMARY_COLOR": "#20336b" },
35
+ "groups": {
36
+ "header": { "sections": [ { "key": "…", "type": "HEADER", "settings": {}, "blocks": [], "disabled": false } ] },
37
+ "footer": { "sections": [ … ] }
38
+ },
39
+ "templates": [
40
+ { "type": "INDEX", "suffix": "", "data": { "sections": [ … ] } },
41
+ { "type": "PRODUCT", "suffix": "alternate", "data": { "sections": [ … ] } },
42
+ { "type": "PAGE", "suffix": "contact", "data": { "sections": [ … ] } }
43
+ ]
44
+ }
45
+ ```
46
+ - `version` is any value that changes on every save of the theme, its templates or its groups
47
+ (`updated_at` of the newest row works).
48
+ - `data` holds what the template stores (section order, settings, blocks); media stay as the
49
+ ids the dashboard saves, not resolved URLs.
50
+
51
+ ### `PUT theme-dev/theme/`
52
+ ```json
53
+ {
54
+ "theme": "draft",
55
+ "base_version": "2026-10-12T13:40:11.120Z",
56
+ "dry_run": false,
57
+ "settings": { … },
58
+ "groups": { "header": { … }, "footer": { … } },
59
+ "templates": [ { "type": "PRODUCT", "suffix": "alternate", "data": { … } } ]
60
+ }
61
+ ```
62
+ - `theme: "draft"`: use the store's draft. If there is none, copy the live theme into a new
63
+ draft first (the same as a theme update, so the live theme stays as the rollback).
64
+ - `base_version` not null and different from the theme's current `version` → `409`
65
+ (the merchant changed it in the dashboard after the pull). `null` = overwrite.
66
+ - Any key left out (`settings`, `groups`, a template) is left as it is. Templates are
67
+ created or replaced by `(type, suffix)` and never deleted here.
68
+ - Validate everything the dashboard validates (`get_section_schemas(store)`: section types,
69
+ setting ids, block types, `visible_if`) and answer `400` with field errors. On `dry_run`,
70
+ validate and report only.
71
+ - Response:
72
+ ```json
73
+ { "theme": { "id": "uuid", "name": "…", "role": "DRAFT", "version": "…" },
74
+ "created": ["PRODUCT.alternate"], "updated": ["INDEX"], "unchanged": ["PRODUCT"],
75
+ "preview_url": "https://store.com/?preview_theme=<id>" }
76
+ ```
77
+
78
+ ### `POST theme-dev/themes/<id>/publish/`
79
+ Makes that theme live (the previous live one becomes unpublished). Returns the theme
80
+ (`{ id, name, role }`).
package/app/app.vue ADDED
@@ -0,0 +1,154 @@
1
+ <script setup>
2
+ // Global on purpose: every button and field in the storefront reads it.
3
+ import "#osi/assets/css/osi-controls.css";
4
+ import { parseIds } from "#osi/utils/analytics";
5
+
6
+ const { load, dir, store, error } = useStorefront();
7
+ const { tokens, rootClasses, fontLinks } = useThemeTokens();
8
+ const { start: startBuilderPreview, stop: stopBuilderPreview } = useBuilderPreview();
9
+
10
+ const { error: fetchError } = await useAsyncData("storefront", () => load());
11
+ const shown = computed(() => error.value ?? fetchError.value ?? null);
12
+
13
+ const isDev = import.meta.dev;
14
+
15
+ onMounted(() => {
16
+ if (!import.meta.client) return;
17
+ let isFramed = false;
18
+ try {
19
+ isFramed = window.parent !== window || window.self !== window.top;
20
+ } catch {
21
+ isFramed = true;
22
+ }
23
+ const route = useRoute();
24
+ if (
25
+ isFramed ||
26
+ route?.path?.includes("__builder-preview") ||
27
+ route?.query?.osi_preview !== undefined ||
28
+ route?.query?.preview !== undefined
29
+ ) {
30
+ startBuilderPreview();
31
+ }
32
+ });
33
+
34
+ onBeforeUnmount(() => {
35
+ stopBuilderPreview();
36
+ });
37
+
38
+ // Favicon, title format, default description / share image, custom CSS.
39
+ useThemeSeo();
40
+
41
+ useHead({
42
+ htmlAttrs: computed(() => ({ dir: dir.value })),
43
+ noscript: computed(() =>
44
+ parseIds(store.value?.meta_pixel_id, "pixel").map((id) => ({
45
+ tagPosition: "bodyOpen",
46
+ innerHTML: `<img height="1" width="1" style="display:none" alt="" src="https://www.facebook.com/tr?id=${id}&ev=PageView&noscript=1" />`,
47
+ })),
48
+ ),
49
+ bodyAttrs: computed(() => ({
50
+ style: tokens.value,
51
+ class: rootClasses.value.filter((c) => /^osi-(btns|inputs|labels)/.test(c)),
52
+ })),
53
+ // A getter, not a snapshot: a font picked in the editor (or a theme that
54
+ // loads later) must add its stylesheet.
55
+ link: computed(() => [
56
+ ...fontLinks.value,
57
+ {
58
+ rel: "stylesheet",
59
+ href: "/css/output.css",
60
+ },
61
+ ]),
62
+ })
63
+ </script>
64
+
65
+ <template>
66
+ <div v-if="shown" class="osi-fatal">
67
+ <p>This store isn't available right now.</p>
68
+
69
+
70
+ <pre v-if="isDev" class="osi-fatal-detail">{{ shown.status ? `HTTP ${shown.status} — ` : "" }}{{ shown.message }}<template v-if="shown.data">
71
+ {{ shown.data }}</template></pre>
72
+ </div>
73
+
74
+ <div v-else class="osi-root" :class="rootClasses" :style="tokens" :dir="dir">
75
+ <NuxtLoadingIndicator
76
+ color="var(--osi-primary-color)"
77
+ error-color="#dc2626"
78
+ :height="3"
79
+ />
80
+ <NuxtLayout>
81
+ <NuxtPage />
82
+ </NuxtLayout>
83
+ <div id="osi-teleport-root" />
84
+ </div>
85
+ </template>
86
+
87
+ <style>
88
+ body {
89
+ font-family: var(--osi-font-body);
90
+ font-size: var(--osi-body-size);
91
+ color: var(--osi-text, #121212);
92
+ background: var(--osi-bg, #ffffff);
93
+ }
94
+
95
+ .osi-root {
96
+ font-family: var(--osi-font-body);
97
+ font-size: var(--osi-body-size);
98
+ color: var(--osi-text, #121212);
99
+ background: var(--osi-bg, #ffffff);
100
+ }
101
+ /* Section padding: useSectionShell writes desktop and phone values as
102
+ variables; these pick the one that applies. */
103
+ .osi-section {
104
+ --osi-sec-pt-now: var(--osi-sec-pt);
105
+ --osi-sec-pb-now: var(--osi-sec-pb);
106
+ --osi-sec-pl-now: var(--osi-sec-pl);
107
+ --osi-sec-pr-now: var(--osi-sec-pr);
108
+ }
109
+ @media (max-width: 749px) {
110
+ .osi-section {
111
+ --osi-sec-pt-now: var(--osi-sec-pt-m, var(--osi-sec-pt));
112
+ --osi-sec-pb-now: var(--osi-sec-pb-m, var(--osi-sec-pb));
113
+ --osi-sec-pl-now: var(--osi-sec-pl-m, var(--osi-sec-pl));
114
+ --osi-sec-pr-now: var(--osi-sec-pr-m, var(--osi-sec-pr));
115
+ }
116
+ }
117
+ .osi-root h1,
118
+ .osi-root h2,
119
+ .osi-root h3,
120
+ .osi-root h4,
121
+ .osi-root h5,
122
+ .osi-root h6 {
123
+ font-family: var(--osi-font-heading);
124
+ }
125
+
126
+ /* The old utility classes (login, account, checkout, cart… ) carry fixed
127
+ families from output.css; point them at the theme's fonts. `body` in front
128
+ beats the single-class utility and still reaches teleported drawers,
129
+ which sit outside .osi-root. */
130
+ body .font-body,
131
+ body .font-poppins,
132
+ body .font-thoren {
133
+ font-family: var(--osi-font-body);
134
+ }
135
+ body .font-heading {
136
+ font-family: var(--osi-font-heading);
137
+ }
138
+ .osi-fatal {
139
+ padding: 4rem 1rem;
140
+ text-align: center;
141
+ font-family: system-ui, sans-serif;
142
+ }
143
+ .osi-fatal-detail {
144
+ display: inline-block;
145
+ margin-top: 1rem;
146
+ padding: 0.75rem 1rem;
147
+ text-align: start;
148
+ background: #fef2f2;
149
+ color: #991b1b;
150
+ border-radius: 6px;
151
+ font-size: 12px;
152
+ white-space: pre-wrap;
153
+ }
154
+ </style>