From 18f96a72ce143431004957f56fa7d539d23b724c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Mon, 7 Apr 2025 15:45:25 +0700 Subject: [PATCH 01/10] docs: draft sibpath routing docs --- .../3.patterns/5.subpath/1.subpath-next.md | 8 - .../3.patterns/5.subpath/1.subpath.md | 663 ++++++++++++++++++ ...{4.subpath-mixed.md => 2.subpath-mixed.md} | 0 .../3.patterns/5.subpath/2.subpath-nuxt.md | 8 - .../5.subpath/3.subpath-middleware.md | 335 --------- 5 files changed, 663 insertions(+), 351 deletions(-) delete mode 100644 docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath-next.md create mode 100644 docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md rename docs/content/guides/7.multistore/3.patterns/5.subpath/{4.subpath-mixed.md => 2.subpath-mixed.md} (100%) delete mode 100644 docs/content/guides/7.multistore/3.patterns/5.subpath/2.subpath-nuxt.md delete mode 100644 docs/content/guides/7.multistore/3.patterns/5.subpath/3.subpath-middleware.md diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath-next.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath-next.md deleted file mode 100644 index 409b22eec4..0000000000 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath-next.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: How to make sub-path routing on Next -layout: default -navigation: - icon: tabler:number-1-small ---- - -# How to make sub-path routing on Next diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md new file mode 100644 index 0000000000..5f6759887a --- /dev/null +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -0,0 +1,663 @@ +--- +title: How to Make Sub-path Routing +layout: default +navigation: + icon: tabler:number-1-small +--- + +# How to Make Sub-path Routing + + +Sub-path routing allows you to serve different store configurations from the same Alokai deployment using URL paths. This approach is ideal when you need to maintain multiple stores with different configurations but want to manage them from a single codebase. + +::info +This guide focuses on implementing multi-store functionality through URL paths (like `/electronics`, `/apparel`). If domain-based separation is sufficient for your use case (like `electronics.example.com`, `apparel.example.com`), the [multibrand feature](https://docs.alokai.com/guides/multistore/tooling-and-concepts) is the preferred approach. +:: + +## Overview + +Sub-path routing in Alokai uses the [Config Switcher extension](https://docs.alokai.com/middleware/guides/config-switcher) with the header strategy. This allows your Middleware to dynamically select the appropriate configuration based on which store the user is browsing. + +For example, with this setup you could have: +- `/en/electronics` - Electronics store with its specific configuration +- `/en/apparel` - Apparel store with different products, styling, and configuration + +All of this is served from a single Frontend deployment. + +## Setting Up the Middleware + +The first step is to configure your Middleware to support different configurations using the Config Switcher extension. + +::tip Config Switcher Documentation +This section provides a brief overview of setting up Config Switcher. For complete documentation, refer to the [Config Switcher guide](https://docs.alokai.com/middleware/guides/config-switcher). +:: + +Create a Config Switcher extension for your integration: + +```ts +// apps/storefront-middleware/integrations//extensions/configSwitcher.ts +import { createConfigSwitcherExtension } from '@alokai/connect/config-switcher'; +import type { MiddlewareConfig } from '@vsf-enterprise/-api'; + +export const configSwitcherExtension = createConfigSwitcherExtension({ + // Config switcher options - specify only what's different for each store + configuration: { + apparel: { + api: { + baseSiteId: 'apparel', + catalogId: 'apparelProductCatalog', + }, + }, + electronics: { + api: { + baseSiteId: 'electronics', + catalogId: 'electronicsProductCatalog', + }, + }, + }, +}); +``` + +Then add this extension to your integration configuration: + +```ts +// apps/storefront-middleware/integrations//config.ts +import { configSwitcherExtension } from './extensions/configSwitcher'; + +export default { + integrations: { + : { + // Base configuration shared across all stores + configuration: { + // ... + }, + extensions: (predefinedExtensions) => [ + ...predefinedExtensions, + configSwitcherExtension, + ] + } + } +}; +``` + +## Customizing Store Appearance with CSS Variables + +To create distinct visual identities for each store, you can define CSS variables that change based on the store config ID. This is a powerful way to maintain a consistent component library while applying different themes. + +::tabs{:titles='["Next.js", "Nuxt"]'} + +#tab-1 +In your Next.js app, add custom CSS variables in your global styles: + +```scss +// apps/storefront-unified-nextjs/app/[locale]/globals.scss +@tailwind base; +@tailwind components; +@tailwind utilities; + +// Base styles... + +// Electronics store theme +.electronics { + --colors-primary-50: 45 249 255; + --colors-primary-100: 233 243 255; + --colors-primary-200: 200 224 255; + --colors-primary-300: 166 204 255; + --colors-primary-400: 110 161 255; + --colors-primary-500: 51 117 255; + --colors-primary-600: 46 106 230; + --colors-primary-700: 38 78 191; + --colors-primary-800: 29 63 153; + --colors-primary-900: 176 196 244; +} + +// Apparel store theme (different color scheme) +.apparel { + --colors-primary-50: 243 254 249; + --colors-primary-100: 224 247 235; + --colors-primary-200: 187 235 210; + --colors-primary-300: 135 216 177; + --colors-primary-400: 77 192 140; + --colors-primary-500: 45 165 116; + --colors-primary-600: 34 134 95; + --colors-primary-700: 31 110 80; + --colors-primary-800: 30 86 65; + --colors-primary-900: 23 64 49; +} +``` + +#tab-2 +In your Nuxt app, add custom CSS variables in your style.scss: + +```scss +// apps/storefront-unified-nuxt/assets/style.scss +@tailwind base; +@tailwind components; +@tailwind utilities; + +// Base styles... + +// Electronics store theme +.electronics { + --colors-primary-50: 45 249 255; + --colors-primary-100: 233 243 255; + --colors-primary-200: 200 224 255; + --colors-primary-300: 166 204 255; + --colors-primary-400: 110 161 255; + --colors-primary-500: 51 117 255; + --colors-primary-600: 46 106 230; + --colors-primary-700: 38 78 191; + --colors-primary-800: 29 63 153; + --colors-primary-900: 176 196 244; +} + +// Apparel store theme (different color scheme) +.apparel { + --colors-primary-50: 243 254 249; + --colors-primary-100: 224 247 235; + --colors-primary-200: 187 235 210; + --colors-primary-300: 135 216 177; + --colors-primary-400: 77 192 140; + --colors-primary-500: 45 165 116; + --colors-primary-600: 34 134 95; + --colors-primary-700: 31 110 80; + --colors-primary-800: 30 86 65; + --colors-primary-900: 23 64 49; +} +``` +:: + +::info +For more information on using CSS variables with Tailwind, see the [Tailwind CSS documentation](https://v3.tailwindcss.com/docs/customizing-colors#using-css-variables). +:: + +## Setting Up the Storefront + +Now you need to configure your Storefront to send the appropriate config ID to the Middleware based on the current URL path. + +::tabs{:titles='["Next.js", "Nuxt"]'} + +#tab-1 +In your Next.js SDK configuration, implement the `getConfigSwitcherHeader` function: + +```ts +// apps/storefront-unified-nextjs/sdk/config.ts +import { defineSdkConfig, getPathnameFromRequestHeaders } from '@vue-storefront/next'; +import type { UnifiedEndpoints } from 'storefront-middleware/types'; + +function getConfigSwitcherHeader(baseHeaders: Record) { + const pathname = + typeof window !== 'undefined' ? window.location.pathname : getPathnameFromRequestHeaders(baseHeaders); + + // Determine the config ID based on URL path + return pathname?.includes('/electronics') ? 'electronics' : 'apparel'; +} + +export function getSdkConfig() { + return defineSdkConfig(({ buildModule, config, getRequestHeaders, middlewareModule }) => ({ + unified: buildModule(middlewareModule, { + apiUrl: `${config.apiUrl}/commerce/unified`, + defaultRequestConfig: { + getConfigSwitcherHeader, + headers: getRequestHeaders, + }, + // ... other config + }), + // ... other modules + })); +} +``` + +#tab-2 +In your Nuxt SDK configuration, define a reusable `getConfigSwitcherHeader` function: + +```ts +// apps/storefront-unified-nuxt/sdk.config.ts +import type { CommerceEndpoints, UnifiedCmsEndpoints, UnifiedEndpoints } from 'storefront-middleware/types'; + +export default defineSdkConfig(({ buildModule, config, getRequestHeaders, middlewareModule }) => { + const route = useRoute(); + + // Create a reusable function for determining the config ID + function getConfigSwitcherHeader() { + return route.path.includes('/electronics') ? 'electronics' : 'apparel'; + } + + return { + // Unified commerce module + unified: buildModule(middlewareModule, { + apiUrl: `${config.apiUrl}/commerce/unified`, + defaultRequestConfig: { + getConfigSwitcherHeader, + headers: getRequestHeaders(), + }, + // ... other config + }), + // ... other modules + }; +}); +``` +:: + +## Updating the File Structure for Path Routing + +To enable path-based routing, you need to restructure your application to include a dynamic `[configId]` parameter in the route. This allows you to access the store identifier directly from the URL. + +::tabs{:titles='["Next.js", "Nuxt"]'} + +#tab-1 +For Next.js apps using the App Router, update your file structure: + +```bash +apps/storefront-unified-nextjs/app/ +├── [locale]/ # Language parameter +│ ├── [configId]/ # Store identifier parameter +│ │ ├── (default)/ # Routes for all stores +│ │ │ ├── page.tsx # Home page +│ │ │ ├── cart/ # Cart pages +│ │ │ ├── checkout/ # Checkout pages +│ │ │ └── products/ # Product pages +│ │ ├── (electronics)/ # Electronics-specific routes +│ │ │ └── some-page/ # Page name +│ │ ├── (apparel)/ # Apparel-specific routes +│ │ │ └── another-page/ # Apparel-specific home +│ │ └── layout.tsx # Apply store-specific class +``` + +#tab-2 +For Nuxt apps, update your file structure: + +```bash +apps/storefront-unified-nuxt/pages/ +├── [configId]/ # Store identifier parameter +│ ├── index.vue # Home page +│ ├── cart/ # Cart pages +│ │ └── index.vue +│ ├── checkout/ # Checkout pages +│ │ └── index.vue +│ ├── products/ # Product pages +│ │ ├── index.vue +│ │ └── [id].vue +│ ├── some-electronics-page.vue # Electronics-specific page +│ └── some-apparel-page.vue # Apparel-specific page +``` +:: + +This structure enables routes like: +- `/en/electronics` - Electronics store home +- `/en/apparel` - Apparel store home +- `/en/electronics/category` - PLP in the electronics store +- `/en/apparel/category` - PLP in the apparel store + +## Updating Internal Links + +::warning Update Internal Links +After setting up the path routing structure, you must update all internal links in your application to include the appropriate configId parameter. This is crucial to maintain proper navigation within each store and prevent users from accidentally switching between stores when clicking links. +:: + +Make sure that all your internal links and navigation components preserve the store context by including the current configId in the URLs. This applies to: + +- Navigation menus +- Product links +- Category links +- Cart and checkout flows +- Footer links +- Breadcrumbs +- Any other internal navigation elements + +Without this, users might unexpectedly switch between stores while browsing your site. + +## Applying Store-Specific Styling + +Now that you have CSS variables defined for each store, you need to apply the store identifier as a CSS class to enable the different styles. + +::tabs{:titles='["Next.js", "Nuxt"]'} + +#tab-1 +In your Next.js layout, apply the configId as a class: + +```tsx +// apps/storefront-unified-nextjs/app/[locale]/[configId]/layout.tsx +import { PropsWithChildren } from 'react'; + +interface LayoutProps extends PropsWithChildren { + params: { + configId: string; + }; +} + +export default function Layout({ children, params: { configId } }: LayoutProps) { + return ( +
+ {children} +
+ ); +} +``` + +#tab-2 +In your Nuxt app.vue, apply the configId as a class: + +```vue +// apps/storefront-unified-nuxt/app.vue + + + +``` +:: + +## Styling Components for Different Stores + +With the store identifier applied as a class, you can now customize components with store-specific styles. You'll need to update your Tailwind configuration to support this. + +::tabs{:titles='["Next.js", "Nuxt"]'} + +#tab-1 +First, update your Tailwind config: + +```ts +// packages-end-user/tailwind-config/src/nextjs.ts +import { tailwindConfig } from "@storefront-ui/react/tailwind-config"; +import type { Config } from "tailwindcss"; +import plugin from "tailwindcss/plugin"; + +const config: Config = { + // ... + plugins: [ + // ... + plugin(({ addVariant }) => { + // Add variant for electronics store + addVariant("electronics", ".electronics &"); + // Add variant for apparel store + addVariant("apparel", ".apparel &"); + }), + ], + // ... +}; +export default config; +``` + +Then use these variants (`electronics:`, `apparel:` etc.) in your components: + +```tsx +// apps/storefront-unified-nextjs/components/navigations/navbar-top.tsx +export default function NavbarTop({ children, className, filled }: NavbarTopProps) { + return ( +
+ {/* Component content */} +
+ ); +} +``` + +#tab-2 +First, update your Tailwind config: + +```ts +// packages-end-user/tailwind-config/src/nuxt.ts +import type { Config } from "tailwindcss"; +import plugin from "tailwindcss/plugin"; + +export default { + // ... + plugins: [ + // ... + plugin(({ addVariant }) => { + // Add variant for electronics store + addVariant("electronics", ".electronics &"); + // Add variant for apparel store + addVariant("apparel", ".apparel &"); + }), + ], + // ... +} as Config; +``` + +Then use these variants (`electronics:`, `apparel:` etc.) in your components: + +```vue +// apps/storefront-unified-nuxt/components/ui/NavbarTop/NavbarTop.vue + +``` +:: + +## Conditional Components + +There are two main approaches to handling store-specific components: + +### Approach 1: Different Versions of the Same Component + +This approach is useful when you need different variations of a component based on the store, such as announcement bars or featured sections with store-specific content. + +::tabs{:titles='["Next.js", "Nuxt"]'} + +#tab-1 +```tsx +// apps/storefront-unified-nextjs/components/announcement-bar.tsx +import { ApparelAnnouncementBar } from '@/components/store-apparel/announcement-bar'; +import { ElectronicsAnnouncementBar } from '@/components/store-electronics/announcement-bar'; + +interface AnnouncementBarProps { + configId: string; +} + +function AnnouncementBar({ configId }: AnnouncementBarProps) { + const StoreComponent = { + apparel: ApparelAnnouncementBar, + electronics: ElectronicsAnnouncementBar, + }[configId]; + + return StoreComponent ? : null; +} + +// Usage in a page or layout: +export default function Layout({ children, params: { configId } }: LayoutProps) { + return ( +
+ + {children} +
+ ); +} +``` + +#tab-2 +```vue + + + + + + + +``` +:: + +### Approach 2: Conditional Rendering Based on Store + +This approach is useful when certain components should only appear in specific stores, such as store-specific features or promotional sections. + +::tabs{:titles='["Next.js", "Nuxt"]'} + +#tab-1 +```tsx +// apps/storefront-unified-nextjs/app/[locale]/[configId]/(default)/page.tsx +import { ElectronicsPromo } from '@/components/store-electronics/promo'; +import { ApparelSeasonalBanner } from '@/components/store-apparel/seasonal-banner'; + +interface HomePageProps { + params: { + configId: string; + }; +} + +export default function HomePage({ params: { configId } }: HomePageProps) { + return ( +
+

Welcome to our store

+ + {/* Show component only for specific store */} + {configId === 'electronics' && ( + + )} + + {/* Common content for all stores */} +
+ {/* ... */} +
+
+ ); +} +``` + +#tab-2 +```vue + + + + +``` +:: + +Choose the appropriate approach based on your needs: +::list{type="success"} +- Use Approach 1 (Different Versions) when components serve the same purpose but need store-specific implementations +- Use Approach 2 (Conditional Rendering) when components are unique to specific stores +- For components that are mostly the same but have minor differences, consider using props or slots instead of creating separate components +:: + +## Creating Store-Specific Pages + +Some pages might only exist for specific stores. Here's how to implement this for each framework: + +::tabs{:titles='["Next.js", "Nuxt"]'} + +#tab-1 +For Next.js, use route groups with the store name, and check the configId parameter: + +```tsx +// apps/storefront-unified-nextjs/app/[locale]/[configId]/(electronics)/layout.tsx +import { notFound } from 'next/navigation'; +import { PropsWithChildren } from 'react'; + +interface ElectronicsLayoutProps extends PropsWithChildren { + params: { + configId: string; + }; +} + +export default function ElectronicsLayout({ children, params }: ElectronicsLayoutProps) { + // Only render for electronics store + if (params.configId !== 'electronics') { + notFound(); + } + + return
{children}
; +} +``` + +This ensures that pages within the (electronics) [route group](https://nextjs.org/docs/app/building-your-application/routing/route-groups) are only accessible when the configId is `electronics`. + +#tab-2 +For Nuxt, use middleware in the store-specific page: + +```vue +// apps/storefront-unified-nuxt/pages/[configId]/some-electronics-page.vue + + + +``` + +This redirects users to the home page if they try to access the electronics-specific page from a different store. +:: + +## Summary + +Sub-path routing in Alokai enables you to: + +1. Serve multiple stores from a single codebase and deployment +2. Customize each store's appearance through CSS variables +3. Apply store-specific styles using Tailwind variants +4. Conditionally render components based on the store +5. Create store-specific pages + +This approach is particularly useful when you need different store configurations that share the same core functionality but need to be accessed via different URL paths rather than domains. + +::warning Consider Your Requirements +The path routing approach described in this guide deploys a single Frontend codebase that serves all stores. All files are shared between stores, which can be efficient but may increase complexity as you customize more aspects of each store. If path-based routing isn't a business requirement, consider using the [multibrand feature](https://docs.alokai.com/guides/multistore/tooling-and-concepts) instead, which provides more robust separation between stores. +:: diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/4.subpath-mixed.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/2.subpath-mixed.md similarity index 100% rename from docs/content/guides/7.multistore/3.patterns/5.subpath/4.subpath-mixed.md rename to docs/content/guides/7.multistore/3.patterns/5.subpath/2.subpath-mixed.md diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/2.subpath-nuxt.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/2.subpath-nuxt.md deleted file mode 100644 index 2c864d8c6b..0000000000 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/2.subpath-nuxt.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -title: How to make sub-path routing on Nuxt -layout: default -navigation: - icon: tabler:number-1-small ---- - -# How to make sub-path routing on Nuxt diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/3.subpath-middleware.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/3.subpath-middleware.md deleted file mode 100644 index 3c7b6c65c1..0000000000 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/3.subpath-middleware.md +++ /dev/null @@ -1,335 +0,0 @@ ---- -title: How to make sub-path routing on Middleware -layout: default -navigation: - icon: tabler:number-1-small ---- - -# How to make sub-path routing on Middleware - -## Understanding Sub-Path Routing Architecture - -Sub-path routing in Alokai consists of two essential components: the storefront (frontend) and the middleware (backend). This architectural pattern allows you to serve different content or product catalogs through distinct URL paths within the same application. **This guide focuses exclusively on implementing the middleware aspect of sub-path routing** using the `ConfigSwitcher` extension, which dynamically changes integration configurations based on URL paths. For comprehensive information about implementing sub-path routing in storefront applications ([Next.js](./1.subpath-next.md) and [Nuxt](./2.subpath-nuxt.md)), please refer to the dedicated documentation for those frameworks. - -The `ConfigSwitcher` extension in middleware enables dynamic configuration changes per request, allowing you to adapt your integrations like eCommerce or CMS settings based on specific URL paths. This powerful feature lets you serve different product catalogs or content collections through the same store instance but on different URL paths. - -## Why Use Sub-Path Routing? - -Sub-path routing allows you to create a more dynamic and flexible application that can adapt to different contexts based on the URL path. This approach can simplify your architecture by consolidating multiple configurations within a single store instance. - -### Common Use Cases - -1. **Multi-catalog stores**: Show different product catalogs based on URL paths -2. **Regional content variations**: Serve different content based on region-specific paths -3. **Brand-specific experiences**: Configure different brand experiences within the same store - -**What You'll Learn** - -::list{type="success"} -- Understanding how `ConfigSwitcher` extension works within the middleware -- Setting up multiple configurations within a single store -- Implementing integration-specific configurations -:: - -## Core Concepts - -### How ConfigSwitcher Works - -The `ConfigSwitcher` dynamically changes your middleware integration configuration based on the current request configuration ID sent with the `x-alokai-middleware-config-id` header. This means: - -1. **Path-Based Configuration** - - Different configuration ID set by different paths allow to fetch data from different sources - - Example: `/clothes` path shows apparel products, while `/electronics` shows electronics products - -2. **Configuration ID vs Store ID** - - Each configuration has its own distinct ID - - This is different from the store ID (`storeId`) - - Multiple configurations can exist within a single store - -3. **Same-Store Operation** - - `ConfigSwitcher` works within a single store instance - - It extracts the configuration ID from the request - - It executes queries using the appropriate configuration - -::tip -The `ConfigSwitcher` only affects middleware configuration. The frontend remains the same, but it receives data from different sources based on the URL path. -:: - -### Key Distinctions To File-Based Inheritance - -`ConfigSwitcher` operates differently from file-based inheritance: - -| **File-Based Inheritance** | **ConfigSwitcher** | -|-----------------------------------------|-----------------------------------------------| -| Works **between different stores** | Works **within a single store** | -| Creates separate deployable instances | Uses a single deployable instance | -| Full codebase customization | Only middleware configuration changes | -| Requires separate builds for each store | Single build supports multiple configurations | - -Take a look at the diagram to see the difference: - -Comparison of File-based Inheritance vs Config Switcher - -## Configurable Elements - -The `ConfigSwitcher` extension allows you to change only the `configuration` object of the specific integration config. - -A typical configuration file looks like this: -```ts -// /apps/storefront-middleware/integrations//config.ts - -import type { ApiClientExtension, Integration } from '@alokai/connect/middleware'; -import type { MiddlewareConfig } from '@vsf-enterprise/'; - -export const config = { - configuration: { - // configuration options - }, - location: '@vsf-enterprise//server', - extensions: (extensions: ApiClientExtension[]) => [ - // extensions for the integration - ], -} satisfies Integration; -``` - -Let's use SAPCC eCommerce integration as an example, by default the configuration file is located at: `/apps/storefront-middleware/integrations/sapcc/config.ts` - -::warning -If you've overridden this file for a specific store (e.g., `store-c`), your configuration would be at: - -`apps/stores/store-c/storefront-middleware/integrations/sapcc/config.ts` -:: - -A SAPCC specific configuration file looks like this: - -```ts -// /apps/storefront-middleware/integrations/sapcc/config.ts - -import type { ApiClientExtension, Integration } from '@alokai/connect/middleware'; -import type { MiddlewareConfig } from '@vsf-enterprise/sapcc-api'; - -export const config = { - configuration: { - api: { - baseSiteId: 'default', - catalogId: 'defaultProductCatalog', - catalogVersion: 'Online', - defaultCurrency: 'USD', - defaultLanguage: 'en', - uri: 'https://', - }, - OAuth: { - clientId: '', - clientSecret: '', - tokenEndpoint: '/oauth/token', - tokenRevokeEndpoint: '/oauth/revoke', - uri: 'https:///authorizationserver/', - } - }, - - extensions: (extensions: ApiClientExtension[]) => [ - ...extensions - ], - - location: '@vsf-enterprise/sapcc-api/server', -} satisfies Integration; - -export type Config = MiddlewareConfig; -``` - -By looking at this config file you can see that you can modify anything inside of the `api` or `OAuth` objects. -Other objects outside of the `configuration` object like the `extensions` and `location` cannot be modified using this tool. - -## Implementation Guide - -### Step 1: Create a configuration file - -The first step is to create a dedicated configuration file for your integration, in which all possible configurations will be defined. - -To keep things organized, you should create this file in the same directory as your integration's configuration file. The recommended name for this file is `switcher.config.ts`. - -For the above SAPCC example, this file path would look like this: `/apps/storefront-middleware/integrations/sapcc/`. - -::tip -If you've overridden the configuration file for some store, perform this step in its directory. If, for example, the store is `store-c`, the correct file path becomes: - -`/apps/stores/store-c/storefront-middleware/integrations/sapcc/switcher.config.ts` -:: - -### Step 2: Create a configuration object - -Important: the keys of the `config` object will correspond to the configuration IDs that will be sent from the frontend. They must be consistent. - -Example config object: - -```ts -// /apps/storefront-middleware/integrations/sapcc/switcher.config.ts - -export const config = { - default: {}, - electronics: {} -} -``` - -Note that two keys have been placed here: `default` and `electronics`. Config object **must contain all** configuration keys that are used by a given store, even those you won't be changing. If you don't want to override some configuration, leave an empty object. Only make the changes you need, everything else will be inherited from the main configuration. - -### Step 3: Apply overrides - -Let's assume you want to change the product catalog for the configuration with ID `electronics`. To do it extend the given example with this change: - - -```ts -// /apps/storefront-middleware/integrations/sapcc/switcher.config.ts - -export const config = { - default: {}, - electronics: {}, // [!code --] - // [!code ++:7] - electronics: { - api: { - baseSiteId: 'electronics', - catalogId: 'electronicsProductCatalog', - }, - }, -}; -``` - -Note that the overrides are made here within the `api` object. This is important because configurations are merged with the main configuration, so their structure must be the same. In the base configuration, these keys are also nested inside of the `api` object. - -### Step 4: Create `ConfigSwitcher` extension - -Now let's create a `ConfigSwitcher` extension file. -Create a new file and name it `configSwitcher.ts`. Place it where the other extensions for the given integration are located. Continuing example with SAPCC, place it in the directory `/apps/storefront-middleware/integrations/sapcc/extensions`. - -Fill the newly created file with the content from the snippet below: - -```ts -// /apps/storefront-middleware/integrations/sapcc/extensions/configSwitcher.ts - -import { createConfigSwitcherExtension } from '@alokai/connect/config-switcher'; -import type { MiddlewareConfig } from '@vsf-enterprise/sapcc-api'; - -export const configSwitcherExtension = createConfigSwitcherExtension({ - configuration: {}, -}); -``` - -Now you need to supply the configuration for the `ConfigSwitcher` extension with the configuration file you created in the previous step. Import it from the file and use it inside the extension: - - -```ts -// /apps/storefront-middleware/integrations/sapcc/extensions/configSwitcher.ts - -import { createConfigSwitcherExtension } from '@alokai/connect/config-switcher'; -import type { MiddlewareConfig } from '@vsf-enterprise/sapcc-api'; - -import { config } from '../switcher.config'; // [!code ++] - -export const configSwitcherExtension = createConfigSwitcherExtension({ - configuration: {}, // [!code --] - configuration: config, // [!code ++] -}); -``` - -### Step 5: Register the new extension - -The final step is to add the `ConfigSwitcher` extension to the main configuration of the given integration. - -```ts -// /apps/storefront-middleware/integrations/sapcc/config.ts - -import type { ApiClientExtension, Integration } from '@alokai/connect/middleware'; -import type { MiddlewareConfig } from '@vsf-enterprise/sapcc-api'; - -import { configSwitcherExtension } from './extensions/configSwitcher'; // [!code ++] - -export const config = { - configuration: { - api: { - baseSiteId: 'default', - catalogId: 'defaultProductCatalog', - catalogVersion: 'Online', - defaultCurrency: 'USD', - defaultLanguage: 'en', - uri: 'https://', - }, - OAuth: { - clientId: '', - clientSecret: '', - tokenEndpoint: '/oauth/token', - tokenRevokeEndpoint: '/oauth/revoke', - uri: 'https:///authorizationserver/', - } - }, - - extensions: (extensions: ApiClientExtension[]) => [ - ...extensions, - configSwitcherExtension, // [!code ++] - ], - - location: '@vsf-enterprise/sapcc-api/server', -} satisfies Integration; - -export type Config = MiddlewareConfig; -``` - -## Multiple Integrations - -Each integration requires its own `ConfigSwitcher` setup. If your project uses multiple integrations (e.g., SAPCC for eCommerce and Contentful for CMS), you'll need to configure the `ConfigSwitcher` separately for each one. - -Every `ConfigSwitcher` extensions must use the same configuration IDs, which means that you cannot use different configuration IDs in different extensions. - -## Typescript - -To enable proper typing, create a new type that will define the configuration of the `ConfigSwitcher` extension. For this purpose, use the `Config` type from the base configuration of the integration whose configuration you're changing. - - -```ts -// /apps/storefront-middleware/integrations//switcher.config.ts - -import type { Config } from './config'; -``` - -Since each configuration option can be overridden, and the type may have some keys marked as required that you won't be overriding, you must override the type so that none of the keys are required. Use the `Partial` type for this. If you want to override only selected values for the `api` key in the configuration, you can do it in the way shown below: - -```ts -// /apps/storefront-middleware/integrations/sapcc/config.switcher.ts - -// [!code ++:10] -import type { Config } from './config'; - -type ConfigSwitcherItem = { - api?: Partial; -}; - -export type ConfigSwitcher = { - [key: string]: Partial; -}; - -export const config = { // [!code --] -export const config: ConfigSwitcher = { // [!code ++] - default: {}, - electronics: { - api: { - baseSiteId: 'electronics', - catalogId: 'electronicsProductCatalog', - }, - }, -}; -``` - -This way, you can override only selected parts of the configuration, and TypeScript won't require you to provide all keys that are marked as required. - - -## Advanced Topics - -This guide covered sub-path routing with the usage of headers strategy while used in the multibrand setup. For more advanced topics, see: -- [Dynamic Configuration](/connect/middleware/2.guides/6.config-switcher.md#dynamic-configuration) -- [Other Switch Strategies](/connect/middleware/2.guides/6.config-switcher.md#switch-strategies) -- [Error Handling](/connect/middleware/2.guides/6.config-switcher.md#error-handling) - ---- - -With sub-path routing properly configured, your Alokai application can now dynamically adapt to different contexts while maintaining a single codebase and deployment. This approach simplifies management while providing the flexibility needed for complex multi-catalog or multi-region scenarios. - - From 0bb7ecc651cc67bc26134046a9a776d9f3c334bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Mon, 7 Apr 2025 15:55:46 +0700 Subject: [PATCH 02/10] docs: merge mutlsitore docs --- .../3.patterns/5.subpath/1.subpath.md | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md index 5f6759887a..d290168d20 100644 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -24,14 +24,47 @@ For example, with this setup you could have: All of this is served from a single Frontend deployment. -## Setting Up the Middleware +### How Config Switcher Works -The first step is to configure your Middleware to support different configurations using the Config Switcher extension. +The Config Switcher dynamically changes your middleware integration configuration based on the current request configuration ID sent with the `x-alokai-middleware-config-id` header. This means: + +1. **Path-Based Configuration** + - Different configuration ID set by different paths allow to fetch data from different sources + - Example: `/apparel` path shows apparel products, while `/electronics` shows electronics products + +2. **Configuration ID vs Store ID** + - Each configuration has its own distinct ID + - This is different from the store ID (`storeId`) + - Multiple configurations can exist within a single store + +3. **Same-Store Operation** + - Config Switcher works within a single store instance + - It extracts the configuration ID from the request + - It executes requests using the appropriate configuration ::tip Config Switcher Documentation This section provides a brief overview of setting up Config Switcher. For complete documentation, refer to the [Config Switcher guide](https://docs.alokai.com/middleware/guides/config-switcher). :: +### Key Distinctions To File-Based Inheritance + +`ConfigSwitcher` operates differently from file-based inheritance: + +| **File-Based Inheritance** | **Config Switcher** | +| --------------------------------------- | ---------------------------------------------------------------- | +| Works **between different stores** | Works **within a single store** | +| Creates separate deployable instances | Uses a single deployable instance | +| Full codebase customization | Only middleware configuration changes, Storefront code is shared | +| Requires separate builds for each store | Single build supports multiple configurations | + +Take a look at the diagram to see the difference: + +Comparison of File-based Inheritance vs Config Switcher + +## Setting Up the Middleware + +The first step is to configure your Middleware to support different configurations using the Config Switcher extension. + Create a Config Switcher extension for your integration: ```ts From 7816c190b9e74f82ed7363305e96c5d45be488e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Mon, 7 Apr 2025 16:21:25 +0700 Subject: [PATCH 03/10] Update docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md Co-authored-by: Michael Kurowski <2772942+michaelKurowski@users.noreply.github.com> --- .../guides/7.multistore/3.patterns/5.subpath/1.subpath.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md index d290168d20..ef9c2dc7e8 100644 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -8,7 +8,7 @@ navigation: # How to Make Sub-path Routing -Sub-path routing allows you to serve different store configurations from the same Alokai deployment using URL paths. This approach is ideal when you need to maintain multiple stores with different configurations but want to manage them from a single codebase. +Sub-path routing allows you to serve different store configurations from the same Alokai deployment using URL paths. This approach is useful if the multibrand per-domain does not address your requirements. ::info This guide focuses on implementing multi-store functionality through URL paths (like `/electronics`, `/apparel`). If domain-based separation is sufficient for your use case (like `electronics.example.com`, `apparel.example.com`), the [multibrand feature](https://docs.alokai.com/guides/multistore/tooling-and-concepts) is the preferred approach. From c8e0ca6d5af95e822a75a0d9dcc736f604b215f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Mon, 7 Apr 2025 16:43:06 +0700 Subject: [PATCH 04/10] docs: michael changes --- .../3.patterns/5.subpath/1.subpath.md | 135 +++++++++++++++--- 1 file changed, 116 insertions(+), 19 deletions(-) diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md index d290168d20..74a7a566c6 100644 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -52,7 +52,6 @@ This section provides a brief overview of setting up Config Switcher. For comple | **File-Based Inheritance** | **Config Switcher** | | --------------------------------------- | ---------------------------------------------------------------- | -| Works **between different stores** | Works **within a single store** | | Creates separate deployable instances | Uses a single deployable instance | | Full codebase customization | Only middleware configuration changes, Storefront code is shared | | Requires separate builds for each store | Single build supports multiple configurations | @@ -340,9 +339,13 @@ Make sure that all your internal links and navigation components preserve the st Without this, users might unexpectedly switch between stores while browsing your site. -## Applying Store-Specific Styling +## Store-Specific Styling -Now that you have CSS variables defined for each store, you need to apply the store identifier as a CSS class to enable the different styles. +There are two key steps to implement store-specific styling: + +### 1. Applying Store Identifier Class + +First, you need to apply the store identifier as a CSS class to enable different styles for each store: ::tabs{:titles='["Next.js", "Nuxt"]'} @@ -386,9 +389,96 @@ const route = useRoute(); ``` :: -## Styling Components for Different Stores +### 2. Defining Store-Specific CSS Variables -With the store identifier applied as a class, you can now customize components with store-specific styles. You'll need to update your Tailwind configuration to support this. +Once the store identifier class is applied, you can define CSS variables that change based on the store: + +::tabs{:titles='["Next.js", "Nuxt"]'} + +#tab-1 +In your Next.js app, add custom CSS variables in your global styles: + +```scss +// apps/storefront-unified-nextjs/app/[locale]/globals.scss +@tailwind base; +@tailwind components; +@tailwind utilities; + +// Base styles... + +// Electronics store theme +.electronics { + --colors-primary-50: 45 249 255; + --colors-primary-100: 233 243 255; + --colors-primary-200: 200 224 255; + --colors-primary-300: 166 204 255; + --colors-primary-400: 110 161 255; + --colors-primary-500: 51 117 255; + --colors-primary-600: 46 106 230; + --colors-primary-700: 38 78 191; + --colors-primary-800: 29 63 153; + --colors-primary-900: 176 196 244; +} + +// Apparel store theme (different color scheme) +.apparel { + --colors-primary-50: 243 254 249; + --colors-primary-100: 224 247 235; + --colors-primary-200: 187 235 210; + --colors-primary-300: 135 216 177; + --colors-primary-400: 77 192 140; + --colors-primary-500: 45 165 116; + --colors-primary-600: 34 134 95; + --colors-primary-700: 31 110 80; + --colors-primary-800: 30 86 65; + --colors-primary-900: 23 64 49; +} +``` + +#tab-2 +In your Nuxt app, add custom CSS variables in your style.scss: + +```scss +// apps/storefront-unified-nuxt/assets/style.scss +@tailwind base; +@tailwind components; +@tailwind utilities; + +// Base styles... + +// Electronics store theme +.electronics { + --colors-primary-50: 45 249 255; + --colors-primary-100: 233 243 255; + --colors-primary-200: 200 224 255; + --colors-primary-300: 166 204 255; + --colors-primary-400: 110 161 255; + --colors-primary-500: 51 117 255; + --colors-primary-600: 46 106 230; + --colors-primary-700: 38 78 191; + --colors-primary-800: 29 63 153; + --colors-primary-900: 176 196 244; +} + +// Apparel store theme (different color scheme) +.apparel { + --colors-primary-50: 243 254 249; + --colors-primary-100: 224 247 235; + --colors-primary-200: 187 235 210; + --colors-primary-300: 135 216 177; + --colors-primary-400: 77 192 140; + --colors-primary-500: 45 165 116; + --colors-primary-600: 34 134 95; + --colors-primary-700: 31 110 80; + --colors-primary-800: 30 86 65; + --colors-primary-900: 23 64 49; +} +``` +:: + +### 3. Using Store-Specific Styles + +Now you can use these variables in your components through Tailwind's configuration: ::tabs{:titles='["Next.js", "Nuxt"]'} @@ -417,20 +507,22 @@ const config: Config = { export default config; ``` -Then use these variants (`electronics:`, `apparel:` etc.) in your components: +Then use these variants in your components: ```tsx -// apps/storefront-unified-nextjs/components/navigations/navbar-top.tsx -export default function NavbarTop({ children, className, filled }: NavbarTopProps) { +// apps/storefront-unified-nextjs/components/ui/navbar-top.tsx +export default function NavbarTop({ children, className }: NavbarTopProps) { return (
- {/* Component content */} + {children}
); } @@ -459,25 +551,30 @@ export default { } as Config; ``` -Then use these variants (`electronics:`, `apparel:` etc.) in your components: +Then use these variants in your components: ```vue -// apps/storefront-unified-nuxt/components/ui/NavbarTop/NavbarTop.vue + ``` :: +::tip Using CSS Variables with Tailwind +The CSS variables we defined can be used with Tailwind's color system. For example, `bg-primary-500` will use the store-specific primary color defined in the CSS variables. For more information, see the [Tailwind CSS documentation](https://v3.tailwindcss.com/docs/customizing-colors#using-css-variables). +:: + ## Conditional Components There are two main approaches to handling store-specific components: From 397ded529c9d9d7a29ff3a18f1ff0310a7cb59b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Mon, 7 Apr 2025 16:45:56 +0700 Subject: [PATCH 05/10] Update docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md Co-authored-by: Michael Kurowski <2772942+michaelKurowski@users.noreply.github.com> --- .../guides/7.multistore/3.patterns/5.subpath/1.subpath.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md index d9ad60abce..4b5d2adec9 100644 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -53,7 +53,7 @@ This section provides a brief overview of setting up Config Switcher. For comple | **File-Based Inheritance** | **Config Switcher** | | --------------------------------------- | ---------------------------------------------------------------- | | Creates separate deployable instances | Uses a single deployable instance | -| Full codebase customization | Only middleware configuration changes, Storefront code is shared | +| Provides inheritance mechanism to make managing multiple stores easy | You have to manage codebase reuse between stores on your own | | Requires separate builds for each store | Single build supports multiple configurations | Take a look at the diagram to see the difference: From 300d088627b9fe52649512a96e13ff47bebf69cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Mon, 7 Apr 2025 17:03:06 +0700 Subject: [PATCH 06/10] docs: explain generated css --- .../3.patterns/5.subpath/1.subpath.md | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md index d9ad60abce..49ba4e33c4 100644 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -199,8 +199,21 @@ In your Nuxt app, add custom CSS variables in your style.scss: ``` :: -::info -For more information on using CSS variables with Tailwind, see the [Tailwind CSS documentation](https://v3.tailwindcss.com/docs/customizing-colors#using-css-variables). +::tip Using CSS Variables with Tailwind +The CSS variables we defined can be used with Tailwind's color system. For example, `bg-primary-500` will use the store-specific primary color defined in the CSS variables. For more information, see the [Tailwind CSS documentation](https://v3.tailwindcss.com/docs/customizing-colors#using-css-variables). +:: + +::tip Understanding Generated CSS +When using store-specific variants like `electronics:!bg-primary-600`, Tailwind generates scoped CSS rules. For example: + +```css +.electronics .electronics\:\!bg-primary-600 { + --tw-bg-opacity: 1 !important; + background-color: rgb(46 106 230 / 1 !important) !important; +} +``` + +This ensures the styles only apply when two conditions are met: the parent element must have the `.electronics` class and the target element must have the `electronics:!bg-primary-600` class. :: ## Setting Up the Storefront @@ -575,6 +588,19 @@ Then use these variants (`electronics:`, `apparel:`, etc.) in your components: The CSS variables we defined can be used with Tailwind's color system. For example, `bg-primary-500` will use the store-specific primary color defined in the CSS variables. For more information, see the [Tailwind CSS documentation](https://v3.tailwindcss.com/docs/customizing-colors#using-css-variables). :: +::tip Understanding Generated CSS +When using store-specific variants like `electronics:!bg-primary-600`, Tailwind generates scoped CSS rules. For example: + +```css +.electronics .electronics\:\!bg-primary-600 { + --tw-bg-opacity: 1 !important; + background-color: rgb(46 106 230 / 1 !important) !important; +} +``` + +This ensures the styles only apply when two conditions are met: the parent element must have the `.electronics` class and the target element must have the `electronics:!bg-primary-600` class. +:: + ## Conditional Components There are two main approaches to handling store-specific components: From cad6c5f8123fad1d283c388cf710c800ccdf4781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Tue, 8 Apr 2025 09:13:57 +0700 Subject: [PATCH 07/10] fix: integration config code snippet --- .../3.patterns/5.subpath/1.subpath.md | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md index 3160cf15f4..14a94eefdd 100644 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -50,11 +50,11 @@ This section provides a brief overview of setting up Config Switcher. For comple `ConfigSwitcher` operates differently from file-based inheritance: -| **File-Based Inheritance** | **Config Switcher** | -| --------------------------------------- | ---------------------------------------------------------------- | -| Creates separate deployable instances | Uses a single deployable instance | -| Provides inheritance mechanism to make managing multiple stores easy | You have to manage codebase reuse between stores on your own | -| Requires separate builds for each store | Single build supports multiple configurations | +| **File-Based Inheritance** | **Config Switcher** | +| -------------------------------------------------------------------- | ------------------------------------------------------------ | +| Creates separate deployable instances | Uses a single deployable instance | +| Provides inheritance mechanism to make managing multiple stores easy | You have to manage codebase reuse between stores on your own | +| Requires separate builds for each store | Single build supports multiple configurations | Take a look at the diagram to see the difference: @@ -96,20 +96,13 @@ Then add this extension to your integration configuration: // apps/storefront-middleware/integrations//config.ts import { configSwitcherExtension } from './extensions/configSwitcher'; -export default { - integrations: { - : { - // Base configuration shared across all stores - configuration: { - // ... - }, - extensions: (predefinedExtensions) => [ - ...predefinedExtensions, - configSwitcherExtension, - ] - } - } -}; +export const config = { + configuration: {}, + extensions: (extensions: ApiClientExtension[]) => [ + ...extensions, + ], + location: '@vsf-enterprise//server', +} satisfies Integration; ``` ## Customizing Store Appearance with CSS Variables From 34a0cc64f7ae64a0da4653511dd3cc6fafcec1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Tue, 8 Apr 2025 09:14:08 +0700 Subject: [PATCH 08/10] Update docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Łukasz Śliwa <39009379+lsliwaradioluz@users.noreply.github.com> --- .../guides/7.multistore/3.patterns/5.subpath/1.subpath.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md index 14a94eefdd..0f4ed84a3b 100644 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -152,7 +152,7 @@ In your Next.js app, add custom CSS variables in your global styles: ``` #tab-2 -In your Nuxt app, add custom CSS variables in your style.scss: +In your Nuxt app, add custom CSS variables in your `style.scss`: ```scss // apps/storefront-unified-nuxt/assets/style.scss From 1ffe67516223ddd3d403215c1d5bb453ad4f2f0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Tue, 8 Apr 2025 09:18:26 +0700 Subject: [PATCH 09/10] fix: content duplication --- .../3.patterns/5.subpath/1.subpath.md | 104 ------------------ 1 file changed, 104 deletions(-) diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md index 14a94eefdd..5d880fbf32 100644 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -105,110 +105,6 @@ export const config = { } satisfies Integration; ``` -## Customizing Store Appearance with CSS Variables - -To create distinct visual identities for each store, you can define CSS variables that change based on the store config ID. This is a powerful way to maintain a consistent component library while applying different themes. - -::tabs{:titles='["Next.js", "Nuxt"]'} - -#tab-1 -In your Next.js app, add custom CSS variables in your global styles: - -```scss -// apps/storefront-unified-nextjs/app/[locale]/globals.scss -@tailwind base; -@tailwind components; -@tailwind utilities; - -// Base styles... - -// Electronics store theme -.electronics { - --colors-primary-50: 45 249 255; - --colors-primary-100: 233 243 255; - --colors-primary-200: 200 224 255; - --colors-primary-300: 166 204 255; - --colors-primary-400: 110 161 255; - --colors-primary-500: 51 117 255; - --colors-primary-600: 46 106 230; - --colors-primary-700: 38 78 191; - --colors-primary-800: 29 63 153; - --colors-primary-900: 176 196 244; -} - -// Apparel store theme (different color scheme) -.apparel { - --colors-primary-50: 243 254 249; - --colors-primary-100: 224 247 235; - --colors-primary-200: 187 235 210; - --colors-primary-300: 135 216 177; - --colors-primary-400: 77 192 140; - --colors-primary-500: 45 165 116; - --colors-primary-600: 34 134 95; - --colors-primary-700: 31 110 80; - --colors-primary-800: 30 86 65; - --colors-primary-900: 23 64 49; -} -``` - -#tab-2 -In your Nuxt app, add custom CSS variables in your style.scss: - -```scss -// apps/storefront-unified-nuxt/assets/style.scss -@tailwind base; -@tailwind components; -@tailwind utilities; - -// Base styles... - -// Electronics store theme -.electronics { - --colors-primary-50: 45 249 255; - --colors-primary-100: 233 243 255; - --colors-primary-200: 200 224 255; - --colors-primary-300: 166 204 255; - --colors-primary-400: 110 161 255; - --colors-primary-500: 51 117 255; - --colors-primary-600: 46 106 230; - --colors-primary-700: 38 78 191; - --colors-primary-800: 29 63 153; - --colors-primary-900: 176 196 244; -} - -// Apparel store theme (different color scheme) -.apparel { - --colors-primary-50: 243 254 249; - --colors-primary-100: 224 247 235; - --colors-primary-200: 187 235 210; - --colors-primary-300: 135 216 177; - --colors-primary-400: 77 192 140; - --colors-primary-500: 45 165 116; - --colors-primary-600: 34 134 95; - --colors-primary-700: 31 110 80; - --colors-primary-800: 30 86 65; - --colors-primary-900: 23 64 49; -} -``` -:: - -::tip Using CSS Variables with Tailwind -The CSS variables we defined can be used with Tailwind's color system. For example, `bg-primary-500` will use the store-specific primary color defined in the CSS variables. For more information, see the [Tailwind CSS documentation](https://v3.tailwindcss.com/docs/customizing-colors#using-css-variables). -:: - -::tip Understanding Generated CSS -When using store-specific variants like `electronics:!bg-primary-600`, Tailwind generates scoped CSS rules. For example: - -```css -.electronics .electronics\:\!bg-primary-600 { - --tw-bg-opacity: 1 !important; - background-color: rgb(46 106 230 / 1 !important) !important; -} -``` - -This ensures the styles only apply when two conditions are met: the parent element must have the `.electronics` class and the target element must have the `electronics:!bg-primary-600` class. -:: - ## Setting Up the Storefront Now you need to configure your Storefront to send the appropriate config ID to the Middleware based on the current URL path. From 07d9013fafa098827b0129ed4f66a02824803269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20G=C3=B3ral?= Date: Tue, 8 Apr 2025 09:23:27 +0700 Subject: [PATCH 10/10] docs: store identifier class --- .../guides/7.multistore/3.patterns/5.subpath/1.subpath.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md index d5b075be40..90e4fedbf8 100644 --- a/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md +++ b/docs/content/guides/7.multistore/3.patterns/5.subpath/1.subpath.md @@ -247,12 +247,12 @@ There are two key steps to implement store-specific styling: ### 1. Applying Store Identifier Class -First, you need to apply the store identifier as a CSS class to enable different styles for each store: +First, you need to apply the store identifier as a CSS class to enable different styles for each store. The class should be applied to an HTML element that is as high as possible in the document hierarchy (like `html`, `body`, or the root layout element). This is crucial because it allows us to style any nested elements using the store-specific variants. ::tabs{:titles='["Next.js", "Nuxt"]'} #tab-1 -In your Next.js layout, apply the configId as a class: +In your Next.js layout, apply the configId as a class to the root layout element: ```tsx // apps/storefront-unified-nextjs/app/[locale]/[configId]/layout.tsx @@ -274,7 +274,7 @@ export default function Layout({ children, params: { configId } }: LayoutProps) ``` #tab-2 -In your Nuxt app.vue, apply the configId as a class: +In your Nuxt app.vue, apply the configId as a class to the `Body` component: ```vue // apps/storefront-unified-nuxt/app.vue