<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Spryker Documentation</title>
        <description>Spryker documentation center.</description>
        <link>https://docs.spryker.com/</link>
        <atom:link href="https://docs.spryker.com/feed.xml" rel="self" type="application/rss+xml"/>
        <lastBuildDate>Fri, 10 Apr 2026 12:44:34 +0000</lastBuildDate>
        <generator>Jekyll v4.2.2</generator>
        
        
        <item>
            <title>Product Attribute Visibility overview</title>
            <description>The Product Attribute Visibility feature lets you control where product attributes are displayed across the Storefront. For each attribute, you can configure visibility for the following page types:

- **PDP (Product Detail Page)**: full attribute display with structured data markup.
- **PLP (Product Listing Page)**: abbreviated attribute display as badges.
- **Cart**: attribute display in the shopping cart.
- **None/Internal**: the attribute is not shown to customers and is used internally only.

This feature is useful in B2B and B2C scenarios where different product attributes are relevant in different shopping contexts. For example, technical specifications are important on the PDP, while key differentiators like color and size are useful on the PLP and in the Cart.

## Back Office management

In the Back Office, the product attribute management form includes a **Display At** field. This field lets you select one or more visibility types for each attribute.

### Configuring visibility

When creating or editing a product attribute, you can configure the following visibility options:

| VISIBILITY TYPE | DESCRIPTION |
| --- | --- |
| **PDP** | The attribute is displayed on the product detail page. |
| **PLP** | The attribute is displayed on the product listing page as a badge. |
| **Cart** | The attribute is displayed in the shopping cart as a badge. |
| **None** | The attribute is not displayed to customers. Use this for internal attributes. |

You can assign multiple visibility types to a single attribute. For example, the `color` attribute can be visible on PDP, PLP, and Cart pages simultaneously.

### Filtering attributes by visibility

The attribute management table in the Back Office includes a **Display At** column and a filter. You can filter the attribute list by visibility type to quickly find attributes configured for a specific page type.

### Default visibility

When a new attribute is created, the default visibility type is **PDP**. You can change this in the attribute form.

## Display on the Storefront

Product attributes are displayed differently depending on the page type and the configured visibility.

### Product Detail Page (PDP)

On the PDP, visible attributes are displayed in a grid layout with attribute names and values.

### Product Listing Page (PLP)

On the PLP, visible attributes are displayed as compact badges next to each product. This helps customers quickly compare key attributes without opening individual product pages.

![PLP product attribute visibility](https://spryker.s3.eu-central-1.amazonaws.com/docs/pbc/all/product-information-management/base-shop/feature-overviews/product-attribute-visibility-overview.md/plp.jpeg)

### Cart

In the shopping cart, visible attributes are displayed as badges for each cart item. This helps customers verify product details before completing the purchase.

![Cart product attribute visibility](https://spryker.s3.eu-central-1.amazonaws.com/docs/pbc/all/product-information-management/base-shop/feature-overviews/product-attribute-visibility-overview.md/cart.jpeg)

## Data import

You can configure attribute visibility through data import by adding a `visibility` column to the `product_management_attribute.csv` file.

Example:

```csv
key,input_type,allow_input,is_multiple,values,...,visibility
storage_capacity,text,no,no,&quot;16 GB,32 GB,64 GB,128 GB&quot;,...,PDP
color,text,no,yes,&quot;white,black,grey&quot;,...,&quot;PDP,PLP,Cart&quot;
internal_sku,text,yes,no,&quot;&quot;,...,
```

The `visibility` column accepts a comma-separated list of visibility types: `PDP`, `PLP`, `Cart`. Leave the column empty for internal-only attributes.

| DEVELOPER GUIDES |
| --- |
| [Install the Product Attribute Visibility feature](/docs/pbc/all/product-information-management/{{page.version}}/base-shop/install-and-upgrade/install-features/install-the-product-attribute-visibility-feature.html) |
</description>
            <pubDate>Fri, 10 Apr 2026 07:21:57 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/product-information-management/latest/base-shop/feature-overviews/product-feature-overview/product-attribute-visibility-overview.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/product-information-management/latest/base-shop/feature-overviews/product-feature-overview/product-attribute-visibility-overview.html</guid>
            
            
        </item>
        
        <item>
            <title>Install the Product Attribute Visibility feature</title>
            <description>This document describes how to install the [Product Attribute Visibility feature](/docs/pbc/all/product-information-management/latest/base-shop/feature-overviews/product-feature-overview/product-attribute-visibility-overview.html).

## Prerequisites

### Install the required features

| NAME | VERSION | INSTALLATION GUIDE |
| --- | --- | --- |
| Spryker Core | {{page.release_tag}} | [Install the Spryker Core feature](/docs/pbc/all/miscellaneous/latest/install-and-upgrade/install-features/install-the-spryker-core-feature.html) |
| Product | {{page.release_tag}} | [Install the Product feature](/docs/pbc/all/product-information-management/latest/base-shop/install-and-upgrade/install-features/install-the-product-feature.html) |

### Install the required packages

| PACKAGE | VERSION |
| --- | --- |
| spryker-feature/product-experience-management | ^1.0.0 |
| spryker/product-attribute | ^1.19.0 |
| spryker/product-attribute-extension | ^1.1.0 |
| spryker/product-attribute-gui | ^2.2.0 |
| spryker/product-attribute-gui-extension | ^1.0.0 |
| spryker/product-management | ^0.20.8 |
| spryker-shop/cart-page | ^3.58.0 |
| spryker-shop/catalog-page | ^1.36.0 |
| spryker-shop/product-detail-page | ^3.31.0 |
| spryker-shop/product-widget | ^1.6.0 |
| spryker-shop/shop-ui | ^1.106.0 |

```bash
composer require spryker/product-attribute-gui-extension:&quot;^1.0.0&quot; spryker-feature/product-experience-management:&quot;^1.0.0&quot; spryker/product-attribute:&quot;^1.19.0&quot; spryker/product-attribute-extension:&quot;^1.1.0&quot; spryker/product-attribute-gui:&quot;^2.2.0&quot; spryker/product-management:&quot;^0.20.8&quot; spryker-shop/cart-page:&quot;^3.58.0&quot; spryker-shop/catalog-page:&quot;^1.36.0&quot; spryker-shop/product-detail-page:&quot;^3.31.0&quot; spryker-shop/product-widget:&quot;^1.6.0&quot; spryker-shop/shop-ui:&quot;^1.106.0&quot; --update-with-dependencies
```

## 1) Set up configuration

Set up RabbitMQ and Symfony Messenger configuration for the publish and sync queues.

**src/Pyz/Client/RabbitMq/RabbitMqConfig.php**

```php
&lt;?php

namespace Pyz\Client\RabbitMq;

use SprykerFeature\Shared\ProductExperienceManagement\ProductExperienceManagementConfig;

class RabbitMqConfig extends SprykerRabbitMqConfig
{
    /**
     * @return list&lt;mixed&gt;
     */
    protected function getPublishQueueConfiguration(): array
    {
        return [
            ProductExperienceManagementConfig::PUBLISH_PRODUCT_ATTRIBUTE,
        ];
    }

    /**
     * @return list&lt;string&gt;
     */
    protected function getSynchronizationQueueConfiguration(): array
    {
        return [
            ProductExperienceManagementConfig::PRODUCT_ATTRIBUTE_SYNC_STORAGE_QUEUE,
        ];
    }
}
```

**src/Pyz/Zed/Queue/QueueDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\Queue;

use SprykerFeature\Shared\ProductExperienceManagement\ProductExperienceManagementConfig;
use Spryker\Zed\Event\Communication\Plugin\Queue\EventQueueMessageProcessorPlugin;
use Spryker\Zed\Queue\QueueDependencyProvider as SprykerQueueDependencyProvider;
use Spryker\Zed\Synchronization\Communication\Plugin\Queue\SynchronizationStorageQueueMessageProcessorPlugin;

class QueueDependencyProvider extends SprykerQueueDependencyProvider
{
    /**
     * @return array&lt;string, \Spryker\Zed\Queue\Dependency\Plugin\QueueMessageProcessorPluginInterface&gt;
     */
    protected function getProcessorMessagePlugins(): array
    {
        return [
            ProductExperienceManagementConfig::PUBLISH_PRODUCT_ATTRIBUTE =&gt; new EventQueueMessageProcessorPlugin(),
            ProductExperienceManagementConfig::PRODUCT_ATTRIBUTE_SYNC_STORAGE_QUEUE =&gt; new SynchronizationStorageQueueMessageProcessorPlugin(),
        ];
    }
}
```

**src/Pyz/Client/SymfonyMessenger/SymfonyMessengerConfig.php**

```php
&lt;?php

namespace Pyz\Client\SymfonyMessenger;

use SprykerFeature\Shared\ProductExperienceManagement\ProductExperienceManagementConfig;

class SymfonyMessengerConfig extends SprykerSymfonyMessengerConfig
{
    /**
     * @return list&lt;mixed&gt;
     */
    protected function getPublishQueueConfiguration(): array
    {
        return [
            ProductExperienceManagementConfig::PUBLISH_PRODUCT_ATTRIBUTE,
        ];
    }

    /**
     * @return list&lt;string&gt;
     */
    protected function getSynchronizationQueueConfiguration(): array
    {
        return [
            ProductExperienceManagementConfig::PRODUCT_ATTRIBUTE_SYNC_STORAGE_QUEUE,
        ];
    }
}
```

### Optional: Extend visibility types

By default, the feature provides three visibility types: `PDP`, `PLP`, and `Cart`. To add project-specific visibility types, extend `ProductExperienceManagementConfig` at the project level and override the `getAvailableVisibilityTypes()` method.

**src/Pyz/Shared/ProductExperienceManagement/ProductExperienceManagementConfig.php**

```php
&lt;?php

namespace Pyz\Shared\ProductExperienceManagement;

use SprykerFeature\Shared\ProductExperienceManagement\ProductExperienceManagementConfig as SprykerProductExperienceManagementConfig;

class ProductExperienceManagementConfig extends SprykerProductExperienceManagementConfig
{
    /**
     * @var string
     */
    public const string VISIBILITY_TYPE_WISHLIST = &apos;Wishlist&apos;;

    /**
     * @return array&lt;string&gt;
     */
    public function getAvailableVisibilityTypes(): array
    {
        return array_merge(parent::getAvailableVisibilityTypes(), [
            static::VISIBILITY_TYPE_WISHLIST,
        ]);
    }
}
```

After adding a custom visibility type, implement the corresponding widget or template logic in your Yves layer to render the attribute on the relevant page.

## 2) Set up the database schema and transfer objects

1. Create the following schema file to enable event-driven publishing for product management attributes:

**src/Pyz/Zed/ProductAttribute/Persistence/Propel/Schema/spy_product_management_attribute.schema.xml**

```xml
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;database xmlns=&quot;spryker:schema-01&quot; xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; name=&quot;zed&quot; xsi:schemaLocation=&quot;spryker:schema-01 https://static.spryker.com/schema-01.xsd&quot; namespace=&quot;Orm\Zed\ProductAttribute\Persistence&quot; package=&quot;src.Orm.Zed.ProductAttribute.Persistence&quot;&gt;

    &lt;table name=&quot;spy_product_management_attribute&quot;&gt;
        &lt;behavior name=&quot;event&quot;&gt;
            &lt;parameter name=&quot;spy_product_management_attribute_all&quot; column=&quot;*&quot;/&gt;
        &lt;/behavior&gt;
    &lt;/table&gt;

&lt;/database&gt;
```

2. Apply database changes and generate entity and transfer changes:

```bash
console propel:install
console transfer:generate
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure that the following changes have been applied in the database:

| DATABASE ENTITY | TYPE | EVENT |
| --- | --- | --- |
| spy_product_management_attribute.visibility | column | created |
| spy_product_attribute_storage | table | created |

Make sure the following transfer objects have been generated:

| TRANSFER | TYPE | EVENT |
| --- | --- | --- |
| ProductAttributeStorage | class | created |
| ProductAttributeStorage.key | property | created |
| ProductAttributeStorage.inputType | property | created |
| ProductAttributeStorage.isSuper | property | created |
| ProductAttributeStorage.visibilityTypes | property | created |
| ProductManagementAttribute.visibility | property | created |
| ProductManagementAttribute.visibilityTypes | property | created |
| ProductManagementAttributeCollection | class | created |
| ProductManagementAttributeCriteria | class | created |
| ProductManagementAttributeConditions.productManagementAttributeIds | property | created |
| ProductAttributeTableCriteria | class | created |
| ProductAttributeTableCriteria.visibilityTypes | property | created |
| ProductAttributeTableCriteria.withColumns | property | created |
| ProductAttributeTableCriteria.queryConditions | property | created |
| ProductAttributeTableCriteria.conditionCombineOperator | property | created |
| ProductAttributeTableQueryCondition | class | created |
| ProductAttributeQueryCriteria | class | created |
| ProductAttributeQueryCriteria.withColumns | property | created |

{% endinfo_block %}

## 3) Set up behavior

### Set up publisher and synchronization plugins

Register the publisher and synchronization plugins:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| ProductAttributePublisherTriggerPlugin | Triggers a full publish of product attribute data. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\Publisher |
| ProductAttributeWritePublisherPlugin | Handles create and update events for product attributes and writes data to storage. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\Publisher |
| ProductAttributeSynchronizationDataBulkRepositoryPlugin | Provides product attribute storage data for the synchronization process. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\Synchronization |

**src/Pyz/Zed/Publisher/PublisherDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\Publisher;

use SprykerFeature\Shared\ProductExperienceManagement\ProductExperienceManagementConfig;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\Publisher\ProductAttributePublisherTriggerPlugin;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\Publisher\ProductAttributeWritePublisherPlugin;
use Spryker\Zed\Publisher\PublisherDependencyProvider as SprykerPublisherDependencyProvider;

class PublisherDependencyProvider extends SprykerPublisherDependencyProvider
{
    /**
     * @return array&lt;string, list&lt;\Spryker\Zed\PublisherExtension\Dependency\Plugin\PublisherPluginInterface&gt;&gt;
     */
    protected function getPublisherPlugins(): array
    {
        return array_merge(
            parent::getPublisherPlugins(),
            $this-&gt;getProductAttributeStoragePlugins(),
        );
    }

    /**
     * @return list&lt;\Spryker\Zed\PublisherExtension\Dependency\Plugin\PublisherTriggerPluginInterface&gt;
     */
    protected function getPublisherTriggerPlugins(): array
    {
        return [
            new ProductAttributePublisherTriggerPlugin(),
        ];
    }

    /**
     * @return list&lt;\Spryker\Zed\PublisherExtension\Dependency\Plugin\PublisherPluginInterface&gt;
     */
    protected function getProductAttributeStoragePlugins(): array
    {
        return [
            ProductExperienceManagementConfig::PUBLISH_PRODUCT_ATTRIBUTE =&gt; [
                new ProductAttributeWritePublisherPlugin(),
            ],
        ];
    }
}
```

**src/Pyz/Zed/Synchronization/SynchronizationDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\Synchronization;

use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\Synchronization\ProductAttributeSynchronizationDataBulkRepositoryPlugin;
use Spryker\Zed\Synchronization\SynchronizationDependencyProvider as SprykerSynchronizationDependencyProvider;

class SynchronizationDependencyProvider extends SprykerSynchronizationDependencyProvider
{
    /**
     * @return list&lt;\Spryker\Zed\SynchronizationExtension\Dependency\Plugin\SynchronizationDataPluginInterface&gt;
     */
    protected function getSynchronizationDataStorePlugins(): array
    {
        return [
            new ProductAttributeSynchronizationDataBulkRepositoryPlugin(),
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure that when you create or update a product attribute in the Back Office, the attribute data is published and synchronized to the `spy_product_attribute_storage` table:

1. In the Back Office, go to **Catalog &gt; Attributes**.
2. Edit an attribute and change its **Display At** value.
3. Click **Save**.
4. Check that the `spy_product_attribute_storage` table contains the updated attribute data.

{% endinfo_block %}

### Set up product attribute query expander

Register the query expander plugin:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| VisibilityProductAttributeQueryExpanderPlugin | Expands the product attribute query with the visibility column. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttribute |

**src/Pyz/Zed/ProductAttribute/ProductAttributeDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\ProductAttribute;

use Spryker\Zed\ProductAttribute\ProductAttributeDependencyProvider as SprykerProductAttributeDependencyProvider;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttribute\VisibilityProductAttributeQueryExpanderPlugin;

class ProductAttributeDependencyProvider extends SprykerProductAttributeDependencyProvider
{
    /**
     * @return list&lt;\Spryker\Zed\ProductAttributeExtension\Dependency\Plugin\ProductAttributeQueryExpanderPluginInterface&gt;
     */
    protected function getProductAttributeQueryExpanderPlugins(): array
    {
        return [
            new VisibilityProductAttributeQueryExpanderPlugin(),
        ];
    }
}
```

### Set up Back Office attribute form and table plugins

Register the Back Office plugins for the attribute management UI:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| VisibilityAttributeTableConfigExpanderPlugin | Expands the attribute table configuration with the visibility column. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui |
| VisibilityAttributeTableHeaderExpanderPlugin | Adds the Display At header to the attribute table. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui |
| VisibilityAttributeTableDataExpanderPlugin | Renders visibility badges in attribute table rows. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui |
| VisibilityAttributeTableCriteriaExpanderPlugin | Adds visibility filter conditions to the attribute table query. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui |
| VisibilityAttributeFormExpanderPlugin | Adds the visibility types select field to the attribute form. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui |
| VisibilityAttributeFormDataProviderExpanderPlugin | Populates the attribute form with existing visibility data. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui |
| VisibilityAttributeFormTransferMapperExpanderPlugin | Maps visibility types from the form to the attribute transfer object. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui |
| VisibilityAttributeTableFilterFormExpanderPlugin | Adds a visibility type filter to the attribute table. | | SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui |

**src/Pyz/Zed/ProductAttributeGui/ProductAttributeGuiDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\ProductAttributeGui;

use Spryker\Zed\ProductAttributeGui\ProductAttributeGuiDependencyProvider as SprykerProductAttributeGuiDependencyProvider;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui\VisibilityAttributeFormDataProviderExpanderPlugin;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui\VisibilityAttributeFormExpanderPlugin;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui\VisibilityAttributeFormTransferMapperExpanderPlugin;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui\VisibilityAttributeTableCriteriaExpanderPlugin;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui\VisibilityAttributeTableConfigExpanderPlugin;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui\VisibilityAttributeTableDataExpanderPlugin;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui\VisibilityAttributeTableFilterFormExpanderPlugin;
use SprykerFeature\Zed\ProductExperienceManagement\Communication\Plugin\ProductAttributeGui\VisibilityAttributeTableHeaderExpanderPlugin;

class ProductAttributeGuiDependencyProvider extends SprykerProductAttributeGuiDependencyProvider
{
    /**
     * @return array&lt;\Spryker\Zed\ProductAttributeGuiExtension\Dependency\Plugin\AttributeTableConfigExpanderPluginInterface&gt;
     */
    protected function getAttributeTableConfigExpanderPlugins(): array
    {
        return [
            new VisibilityAttributeTableConfigExpanderPlugin(),
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\ProductAttributeGuiExtension\Dependency\Plugin\AttributeTableHeaderExpanderPluginInterface&gt;
     */
    protected function getAttributeTableHeaderExpanderPlugins(): array
    {
        return [
            new VisibilityAttributeTableHeaderExpanderPlugin(),
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\ProductAttributeGuiExtension\Dependency\Plugin\AttributeTableDataExpanderPluginInterface&gt;
     */
    protected function getAttributeTableDataExpanderPlugins(): array
    {
        return [
            new VisibilityAttributeTableDataExpanderPlugin(),
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\ProductAttributeGuiExtension\Dependency\Plugin\AttributeTableCriteriaExpanderPluginInterface&gt;
     */
    protected function getAttributeTableCriteriaExpanderPlugins(): array
    {
        return [
            new VisibilityAttributeTableCriteriaExpanderPlugin(),
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\ProductAttributeGuiExtension\Dependency\Plugin\AttributeFormExpanderPluginInterface&gt;
     */
    protected function getAttributeFormExpanderPlugins(): array
    {
        return [
            new VisibilityAttributeFormExpanderPlugin(),
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\ProductAttributeGuiExtension\Dependency\Plugin\AttributeFormDataProviderExpanderPluginInterface&gt;
     */
    protected function getAttributeFormDataProviderExpanderPlugins(): array
    {
        return [
            new VisibilityAttributeFormDataProviderExpanderPlugin(),
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\ProductAttributeGuiExtension\Dependency\Plugin\AttributeFormTransferMapperExpanderPluginInterface&gt;
     */
    protected function getAttributeFormTransferMapperExpanderPlugins(): array
    {
        return [
            new VisibilityAttributeFormTransferMapperExpanderPlugin(),
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\ProductAttributeGuiExtension\Dependency\Plugin\AttributeTableFilterFormExpanderPluginInterface&gt;
     */
    protected function getAttributeTableFilterFormExpanderPlugins(): array
    {
        return [
            new VisibilityAttributeTableFilterFormExpanderPlugin(),
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure that the attribute management UI in the Back Office includes the visibility features:

1. In the Back Office, go to **Catalog &gt; Attributes**.
2. Verify that the attribute table has a **Display At** column with visibility badges.
3. Verify that you can filter the table by visibility type using the filter dropdown.
4. Click **Edit** next to an attribute.
5. Verify that the attribute form includes a **Display At** field where you can select one or more visibility types.

{% endinfo_block %}

## 4) Set up widgets

Register the following global widgets:

| WIDGET | DESCRIPTION | NAMESPACE |
| --- | --- | --- |
| ProductAttributeVisibilityPdpWidget | Displays product attributes configured for PDP visibility with schema.org structured data. | SprykerFeature\Yves\ProductExperienceManagement\Widget |
| ProductAttributeVisibilityPlpWidget | Displays product attributes configured for PLP visibility as badges. | SprykerFeature\Yves\ProductExperienceManagement\Widget |
| ProductAttributeVisibilityCartWidget | Displays product attributes configured for Cart visibility as badges. | SprykerFeature\Yves\ProductExperienceManagement\Widget |

**src/Pyz/Yves/ShopApplication/ShopApplicationDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Yves\ShopApplication;

use SprykerFeature\Yves\ProductExperienceManagement\Widget\ProductAttributeVisibilityCartWidget;
use SprykerFeature\Yves\ProductExperienceManagement\Widget\ProductAttributeVisibilityPdpWidget;
use SprykerFeature\Yves\ProductExperienceManagement\Widget\ProductAttributeVisibilityPlpWidget;
use SprykerShop\Yves\ShopApplication\ShopApplicationDependencyProvider as SprykerShopApplicationDependencyProvider;

class ShopApplicationDependencyProvider extends SprykerShopApplicationDependencyProvider
{
    /**
     * @return list&lt;string&gt;
     */
    protected function getGlobalWidgets(): array
    {
        return [
            ProductAttributeVisibilityPdpWidget::class,
            ProductAttributeVisibilityPlpWidget::class,
            ProductAttributeVisibilityCartWidget::class,
        ];
    }
}
```

## 5) Set up Yves templates

If you have overridden the default Twig templates at the project level, add the following widget calls to display product attributes on the Storefront.

### Product Detail Page (PDP)

**src/Pyz/Yves/ProductDetailPage/Theme/default/components/molecules/product-detail/product-detail.twig**

{% raw %}

```twig
{% widget &apos;ProductAttributeVisibilityPdpWidget&apos; args [data.product.idProductAbstract, data.product.idProductConcrete | default(null),] only %}
{% nowidget %}
{% endwidget %}
```

{% endraw %}

### Product Listing Page (PLP)

**src/Pyz/Yves/ProductWidget/Theme/default/components/molecules/catalog-product/catalog-product.twig**

{% raw %}

```twig
{% widget &apos;ProductAttributeVisibilityPlpWidget&apos; args [data.products, data.product.idProductAbstract] only %}
{% nowidget %}
{% endwidget %}
```

{% endraw %}

If you have overridden `Yves/CatalogPage/Theme/default/templates/page-layout-catalog/page-layout-catalog.twig` at the project level, update the `CatalogPageProductWidget` call to pass `data.products` as an additional argument:

**src/Pyz/Yves/CatalogPage/Theme/default/templates/page-layout-catalog/page-layout-catalog.twig**

{% raw %}

```twig
{% widget &apos;CatalogPageProductWidget&apos; args [product, data.viewMode, data.products] with {
```

{% endraw %}

If you have overridden `Yves/ShopUi/Theme/default/components/molecules/product-item/product-item.twig` or `Yves/ShopUi/Theme/default/components/molecules/product-item-list/product-item-list.twig` at the project level, add the following block to each template in the location where the widget should be rendered:

**src/Pyz/Yves/ShopUi/Theme/default/components/molecules/product-item/product-item.twig**
**src/Pyz/Yves/ShopUi/Theme/default/components/molecules/product-item-list/product-item-list.twig**

{% raw %}

```twig
{% block productAttributeVisibility %}{% endblock %}
```

{% endraw %}

### Cart

**src/Pyz/Yves/CartPage/Theme/default/components/molecules/product-cart-item/product-cart-item.twig**

{% raw %}

```twig
{% widget &apos;ProductAttributeVisibilityCartWidget&apos; args [data.cart is iterable ? null : data.cart, data.product.idProductAbstract, data.product.idProductConcrete] only %}
{% nowidget %}
{% endwidget %}
```

{% endraw %}

{% info_block warningBox &quot;Verification&quot; %}

Make sure the product attributes are displayed on the Storefront based on their visibility configuration:

1. In the Back Office, configure an attribute with **PDP** visibility.
2. On the Storefront, open the product detail page and verify the attribute is displayed.
3. Configure an attribute with **PLP** visibility and verify it appears as a badge on the product listing page.
4. Configure an attribute with **Cart** visibility and add the product to the cart. Verify the attribute badge is displayed in the cart.

{% endinfo_block %}

## 6) Set up data import

### Add facade dependencies

Register the required facade dependencies in the data import module.

**src/Pyz/Zed/DataImport/DataImportDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\DataImport;

use Spryker\Zed\DataImport\DataImportDependencyProvider as SprykerDataImportDependencyProvider;
use Spryker\Zed\Kernel\Container;

class DataImportDependencyProvider extends SprykerDataImportDependencyProvider
{
    /**
     * @var string
     */
    public const FACADE_PRODUCT_EXPERIENCE_MANAGEMENT = &apos;FACADE_PRODUCT_EXPERIENCE_MANAGEMENT&apos;;

    public function provideBusinessLayerDependencies(Container $container): Container
    {
        $container = parent::provideBusinessLayerDependencies($container);
        $container = $this-&gt;addProductExperienceManagementFacade($container);

        return $container;
    }

    protected function addProductExperienceManagementFacade(Container $container): Container
    {
        $container-&gt;set(static::FACADE_PRODUCT_EXPERIENCE_MANAGEMENT, function (Container $container) {
            return $container-&gt;getLocator()-&gt;productExperienceManagement()-&gt;facade();
        });

        return $container;
    }
}
```

### Update the product management attribute writer

Extend `ProductManagementAttributeWriter` to handle the `visibility` column during import. The writer saves the visibility value to the `spy_product_management_attribute` table, triggers a publish event, and validates visibility types against the allowed values.

**src/Pyz/Zed/DataImport/Business/Model/ProductManagementAttribute/ProductManagementAttributeWriter.php**

```php
&lt;?php

namespace Pyz\Zed\DataImport\Business\Model\ProductManagementAttribute;

use Spryker\Zed\DataImport\Business\Exception\DataImportException;
use Spryker\Zed\DataImport\Business\Model\DataImportStep\PublishAwareStep;
use Spryker\Zed\DataImport\Business\Model\DataSet\DataSetInterface;
use SprykerFeature\Shared\ProductExperienceManagement\ProductExperienceManagementConfig;
use SprykerFeature\Zed\ProductExperienceManagement\Business\ProductExperienceManagementFacadeInterface;

class ProductManagementAttributeWriter extends PublishAwareStep implements DataImportStepInterface
{
    protected const string KEY_VISIBILITY = &apos;visibility&apos;;

    public function __construct(
        protected readonly ProductExperienceManagementFacadeInterface $productExperienceManagementFacade,
    ) {
    }

    public function execute(DataSetInterface $dataSet): void
    {
        // ... existing logic for finding or creating entity ...

        $visibility = (string)$dataSet[static::KEY_VISIBILITY] ?? &apos;&apos;;

        if ($visibility !== &apos;&apos;) {
            $visibility = $this-&gt;normalizeVisibility($visibility, $dataSet[&apos;key&apos;]);
        }

        $productManagementAttributeEntity
            -&gt;setAllowInput($dataSet[&apos;allow_input&apos;])
            -&gt;setInputType($dataSet[&apos;input_type&apos;])
            -&gt;setVisibility($visibility);

        $productManagementAttributeEntity-&gt;save();

        $this-&gt;addPublishEvents(
            ProductExperienceManagementConfig::PRODUCT_ATTRIBUTE_PUBLISH,
            $productManagementAttributeEntity-&gt;getIdProductManagementAttribute(),
        );

        // ... existing logic for attribute values ...
    }

    protected function normalizeVisibility(string $visibility, string $attributeKey): string
    {
        $visibilityTypes = array_map(&apos;trim&apos;, explode(&apos;,&apos;, $visibility));
        $allowedVisibilityTypes = $this-&gt;productExperienceManagementFacade-&gt;getAvailableVisibilityTypes();
        $allowedVisibilityTypesMap = array_combine(
            array_map(&apos;strtolower&apos;, $allowedVisibilityTypes),
            $allowedVisibilityTypes,
        );

        $normalized = [];

        foreach ($visibilityTypes as $visibilityType) {
            $canonical = $allowedVisibilityTypesMap[strtolower($visibilityType)] ?? null;
            if ($canonical === null) {
                throw new DataImportException(
                    sprintf(
                        &apos;Invalid visibility type &quot;%s&quot; for attribute &quot;%s&quot;. Allowed: %s (or empty).&apos;,
                        $visibilityType,
                        $attributeKey,
                        implode(&apos;, &apos;, $allowedVisibilityTypes),
                    ),
                );
            }

            $normalized[] = $canonical;
        }

        return implode(&apos;,&apos;, $normalized);
    }
}
```

Wire the facade into the writer via the business factory.

**src/Pyz/Zed/DataImport/Business/DataImportBusinessFactory.php**

```php
&lt;?php

namespace Pyz\Zed\DataImport\Business;

use Pyz\Zed\DataImport\Business\Model\ProductManagementAttribute\ProductManagementAttributeWriter;
use Pyz\Zed\DataImport\DataImportDependencyProvider;
use Spryker\Zed\DataImport\Business\DataImportBusinessFactory as SprykerDataImportBusinessFactory;
use SprykerFeature\Zed\ProductExperienceManagement\Business\ProductExperienceManagementFacadeInterface;

class DataImportBusinessFactory extends SprykerDataImportBusinessFactory
{
    // In createProductManagementAttributeImporter(), replace:
    //     -&gt;addStep(new ProductManagementAttributeWriter());
    // with:
    //     -&gt;addStep(new ProductManagementAttributeWriter(
    //         $this-&gt;getProductExperienceManagementFacade(),
    //     ));

    protected function getProductExperienceManagementFacade(): ProductExperienceManagementFacadeInterface
    {
        return $this-&gt;getProvidedDependency(DataImportDependencyProvider::FACADE_PRODUCT_EXPERIENCE_MANAGEMENT);
    }
}
```

### Prepare and import data

1. Add the `visibility` column to the existing `product_management_attribute.csv` file.

   File: **data/import/common/common/product_management_attribute.csv**

```csv
key,input_type,allow_input,is_multiple,values,key_translation.en_US,key_translation.de_DE,value_translations.en_US,value_translations.de_DE,visibility
storage_capacity,text,no,no,&quot;16 GB, 32 GB, 64 GB, 128 GB&quot;,Storage Capacity,Speichergröße,,,PDP
color,text,no,yes,&quot;white, black, grey&quot;,Color,Farbe,&quot;white, black, grey&quot;,&quot;weiß, schwarz, grau&quot;,&quot;PDP,PLP,Cart&quot;
brand,text,yes,no,,Brand,Marke,,,PDP
internal_sku,text,yes,no,,Internal SKU,Interne SKU,,,,
```

| COLUMN | REQUIRED | DATA TYPE | DATA EXAMPLE | DATA EXPLANATION |
| --- | --- | --- | --- | --- |
| key | ✓ | string | color | Unique attribute key. |
| input_type | ✓ | string | text | Input type for the attribute. |
| allow_input | ✓ | string | no | Whether custom input is allowed. |
| is_multiple | ✓ | string | no | Whether multiple values are supported. |
| values | | string | &quot;white, black, grey&quot; | Predefined attribute values. |
| visibility | | string | &quot;PDP,PLP,Cart&quot; | Comma-separated list of visibility types. Valid values: `PDP`, `PLP`, `Cart`. Leave empty for internal-only attributes. |

2. Import data:

```bash
console data:import product-management-attribute
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure that the product attribute visibility data is imported correctly:

1. In the Back Office, go to **Catalog &gt; Attributes**.
2. Verify that each attribute displays the correct visibility types in the **Display At** column.
3. Check that the `spy_product_management_attribute` table contains the correct `visibility` values.

{% endinfo_block %}
</description>
            <pubDate>Fri, 10 Apr 2026 07:21:57 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/product-information-management/latest/base-shop/install-and-upgrade/install-features/install-the-product-attribute-visibility-feature.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/product-information-management/latest/base-shop/install-and-upgrade/install-features/install-the-product-attribute-visibility-feature.html</guid>
            
            
        </item>
        
        <item>
            <title>Set up DNS</title>
            <description>To make your application accessible through your own domain, configure DNS records that point your domain to the Spryker-managed endpoint.

In most cases, this means adding a `CNAME` record in your DNS management for the domain you want to use. Spryker provides the target values as part of the setup process.

## Before you start

Before requesting DNS setup:

- Make sure the endpoint you want to use is final.
- Make sure the endpoint has been tested and approved internally.
- Make sure the `deploy.yml` file you provide to Spryker is the same file that will later be used for the Normal or Destructive deployment pipeline.
- Be aware that DNS setup is not immediate and can take up to a week to complete.

{% info_block infoBox &quot;Info&quot; %}

This process can take up to a full week because it includes both DNS propagation time and Spryker infrastructure work. To avoid rework, submit only final endpoint selections.

{% endinfo_block %}

## DNS setup process

The DNS setup usually follows these steps:

1. Add the endpoint you want to use to the appropriate `deploy.yml` file. For example, add a new endpoint under the desired application in your `deploy.yml`:

    ```yaml
    groups:
        EU:
            region: EU
            applications:
                yves:
                    application: yves
                    endpoints:
                        www.example.com:
                            store: DE
    ```

2. Create a support case and provide the `deploy.yml` file that contains the endpoint configuration. For example: &quot;We have added the endpoint `www.example.com` to our `deploy.yml` and would like to set up DNS for it.&quot;
3. Ensure that this is the same `deploy.yml` file that will later be used for the Normal or Destructive deployment pipeline.
4. Spryker prepares the endpoint configuration and sends you the DNS records required for TLS verification. For example, you may receive a `CNAME` record like `_acme-challenge.www.example.com` pointing to a validation target.
5. Add the provided TLS verification records in your DNS management.
6. After the records are in place and have propagated, notify Spryker Support.
7. Spryker completes the infrastructure configuration.
8. Spryker sends you the final DNS target values, usually `CNAME` records, that must be added in your DNS management. For example: `www.example.com CNAME xxx.elb.amazonaws.com`. Root domains are treated differently — see [Root domain configuration](#root-domain-configuration).
9. After the DNS records are added and propagated, your application becomes accessible through the new domain.
10. Trigger the Normal or Destructive deployment pipeline using the same `deploy.yml` file that was provided to Spryker.

## Important: Configuration changes are staged

DNS-related endpoint changes are prepared first, but they do not become active immediately.

After Spryker prepares the new endpoint configuration, the change remains staged until a deployment pipeline is triggered.

This is especially important for infrastructure components that support only one active endpoint at a time.

## Critical constraint for Scheduler and Queue endpoints

The following services support only **one active endpoint configuration at a time**:

- Scheduler (`Jenkins`)
- Queue (`RabbitMQ`)

When a new domain endpoint is configured for either of these services, the previous endpoint is replaced. It is not kept in parallel.

Dual-domain operation is **not supported** for these components.

{% info_block warningBox &quot;Warning&quot; %}

If a deployment is triggered before the required DNS records are fully added and propagated, `Jenkins` and `RabbitMQ` can become unreachable because their previous endpoints will already have been replaced.

{% endinfo_block %}

## Safe activation sequence

Do **not** trigger deployment immediately after receiving the DNS records from Spryker.

Before triggering a Normal or Destructive deployment pipeline:

1. Confirm that the pipeline will use the same `deploy.yml` file that was provided to Spryker for DNS setup.
2. Add all required TLS verification records.
3. Add all required final DNS records at your registrar or DNS provider.
4. Verify that DNS propagation is complete.
5. Notify Spryker Support that DNS setup is complete.
6. Wait for confirmation from Spryker that the infrastructure-side setup is finished.
7. Only then trigger the deployment pipeline.

## Recommended pre-deployment checklist

Before triggering deployment for a new domain or domain migration, confirm all of the following:

- The `deploy.yml` file used for deployment is the same one that was provided to Spryker for DNS setup.
- TLS verification records were added.
- Final DNS records were added at the registrar or DNS provider.
- DNS propagation was verified, for example with `dig` or another DNS checker.
- Spryker Support was notified that DNS setup is complete.
- Spryker confirmed that the infrastructure configuration is complete.

Only after all items are completed should deployment be triggered.

## Root domain configuration

If you want to use a root domain such as `example.com`, use an `A` record instead of a `CNAME` record.

In this case, contact Spryker so the team can provide the correct IP address to use.

Do **not** use the load balancer IP addresses directly as your `A` record target, because those IP addresses are subject to rotation.

## NS record delegation

Spryker does not normally support full DNS delegation.

Do **not** change your domain&apos;s `NS` records to delegate the full DNS zone to Spryker unless explicitly instructed by Spryker.

## Troubleshooting and practical notes

### Adding the endpoint to deploy.yml does not make it active

Adding the endpoint in code only starts the setup flow. The endpoint becomes active only after the DNS setup is complete and deployment is triggered.

### The deploy.yml file must match the one shared for DNS setup

The `deploy.yml` file used in the Normal or Destructive deployment pipeline must match the one provided to Spryker during the DNS setup process. Otherwise, the deployed configuration may not match the DNS configuration prepared by Spryker.

### Old and new domains cannot be active at the same time for Jenkins or RabbitMQ

These services support only one active endpoint configuration at a time.

### Biggest risk: premature deployment or mismatched deploy.yml

The main risk is triggering deployment too early or using a different `deploy.yml` file than the one shared with Spryker. If DNS records are not fully in place and propagated, or if the deployed configuration does not match the prepared DNS setup, `Jenkins` and `RabbitMQ` can become unavailable.

### Verifying DNS propagation

You can verify propagation with tools such as `dig` or another public DNS checker. Check that the records returned publicly match the values provided by Spryker.
</description>
            <pubDate>Thu, 09 Apr 2026 12:35:13 +0000</pubDate>
            <link>https://docs.spryker.com/docs/ca/dev/set-up-dns.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/ca/dev/set-up-dns.html</guid>
            
            
        </item>
        
        <item>
            <title>Install the Basic Shop Theme feature</title>
            <description>This document describes how to install the Basic Shop Theme feature.

The feature lets Business Admins configure logos, theme colors, and custom CSS for the Storefront, Back Office, and Merchant Portal directly from the Back Office.

## Prerequisites

Before installing this feature, install and configure the following:

| NAME | VERSION | INTEGRATION GUIDE |
|------|---------|-------------------|
| Configuration Management feature | ^0.1.0 | [Install the Configuration Management feature](/docs/dg/dev/integrate-and-configure/integrate-confguration-feature.html) |
| Spryker Core | ^3.82 | — |

## Install feature core

### 1) Install the required modules

Install the required modules using Composer:

```bash
composer require spryker/gui:&quot;^3.0.0&quot; spryker/zed-ui:&quot;^2.0.0&quot; spryker-shop/shop-ui:&quot;^1.0.0&quot; symfony/html-sanitizer:&quot;^7.4&quot; --update-with-dependencies
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure the following modules have been installed:

| MODULE | EXPECTED DIRECTORY |
|--------|-------------------|
| Gui | vendor/spryker/gui |
| ZedUi | vendor/spryker/zed-ui |
| ShopUi | vendor/spryker-shop/shop-ui |
| symfony/html-sanitizer | vendor/symfony/html-sanitizer |

{% endinfo_block %}

### 2) Sync the theme configuration schema

Run the configuration sync command to register the theme settings in the Back Office:

```bash
console configuration:sync
```

{% info_block warningBox &quot;Verification&quot; %}

1. Log in to the Back Office.
2. Navigate to **Configuration**.
3. Verify that the **Theme** feature appears in the sidebar with four tabs: **Storefront**, **Back Office**, **Merchant Portal**, and **Logos**.

{% endinfo_block %}

### 3) Set up behavior

The `configurationValue()` and `configurationValues()` Twig functions are made available by existing plugins that are already registered in a standard Spryker installation:

- **Storefront (Yves)**: `ShopUiTwigExtension`
- **Back Office and Merchant Portal (Zed)**: `\Spryker\Zed\Twig\Communication\Plugin\Application\TwigApplicationPlugin`

No additional Twig plugin registration is required.

#### 3.1) Register ACL plugins for Merchant Portal

Register the ACL entity configuration and rule expander plugins so that Merchant Portal users can read theme configuration values through the ACL layer:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
|--------|---------------|---------------|-----------|
| ConfigurationValueAclEntityConfigurationExpanderPlugin | Expands the ACL entity configuration with `ConfigurationValue` entity rules for Merchant Portal access. | None | Spryker\Zed\Configuration\Communication\Plugin\AclMerchantPortal |
| ConfigurationValueMerchantAclEntityRuleExpanderPlugin | Expands the Merchant ACL entity rule set to allow Merchant Portal users to read published configuration values. | None | Spryker\Zed\Configuration\Communication\Plugin\AclMerchantPortal |

**src/Pyz/Zed/AclMerchantPortal/AclMerchantPortalDependencyProvider.php**

Add the import statements:

```php
use Spryker\Zed\Configuration\Communication\Plugin\AclMerchantPortal\ConfigurationValueAclEntityConfigurationExpanderPlugin;
use Spryker\Zed\Configuration\Communication\Plugin\AclMerchantPortal\ConfigurationValueMerchantAclEntityRuleExpanderPlugin;
```

Register the plugins in the respective methods:

```php
protected function getAclEntityConfigurationExpanderPlugins(): array
{
    return [
        // ...
        new ConfigurationValueAclEntityConfigurationExpanderPlugin(),
    ];
}

protected function getMerchantAclEntityRuleExpanderPlugins(): array
{
    return [
        // ...
        new ConfigurationValueMerchantAclEntityRuleExpanderPlugin(),
    ];
}
```

{% info_block warningBox &quot;Verification&quot; %}

Log in to the Merchant Portal and navigate to any page. Verify there are no ACL-related errors in the application log.

{% endinfo_block %}

### 4) Configure media filesystems

The logo upload feature requires three Flysystem filesystem services: one per application context. Configure them in your shared config and provide local overrides for development and CI environments.

#### 4.1) Add filesystem services to the shared config

Add the three media filesystem services to `config/Shared/config_default.php`. In production, they read from S3 using the `SPRYKER_S3_PUBLIC_ASSETS_*` environment variables.

**config/Shared/config_default.php**

Add the three media filesystem entries inside the existing `$config[FileSystemConstants::FILESYSTEM_SERVICE]` array:

```php
$config[FileSystemConstants::FILESYSTEM_SERVICE] = [
    // ... existing entries ...
    &apos;backoffice-media&apos; =&gt; [
        &apos;sprykerAdapterClass&apos; =&gt; Aws3v3FilesystemBuilderPlugin::class,
        &apos;key&apos; =&gt; getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_KEY&apos;) ?: &apos;&apos;,
        &apos;bucket&apos; =&gt; getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_BUCKET&apos;) ?: &apos;&apos;,
        &apos;secret&apos; =&gt; getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_SECRET&apos;) ?: &apos;&apos;,
        &apos;root&apos; =&gt; &apos;/backoffice-media&apos;,
        &apos;path&apos; =&gt; &apos;/&apos;,
        &apos;version&apos; =&gt; &apos;latest&apos;,
        &apos;region&apos; =&gt; getenv(&apos;AWS_REGION&apos;),
    ],
    &apos;storefront-media&apos; =&gt; [
        &apos;sprykerAdapterClass&apos; =&gt; Aws3v3FilesystemBuilderPlugin::class,
        &apos;key&apos; =&gt; getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_KEY&apos;) ?: &apos;&apos;,
        &apos;bucket&apos; =&gt; getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_BUCKET&apos;) ?: &apos;&apos;,
        &apos;secret&apos; =&gt; getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_SECRET&apos;) ?: &apos;&apos;,
        &apos;root&apos; =&gt; &apos;/storefront-media&apos;,
        &apos;path&apos; =&gt; &apos;&apos;,
        &apos;version&apos; =&gt; &apos;latest&apos;,
        &apos;region&apos; =&gt; getenv(&apos;AWS_REGION&apos;),
    ],
    &apos;merchant-portal-media&apos; =&gt; [
        &apos;sprykerAdapterClass&apos; =&gt; Aws3v3FilesystemBuilderPlugin::class,
        &apos;key&apos; =&gt; getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_KEY&apos;) ?: &apos;&apos;,
        &apos;bucket&apos; =&gt; getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_BUCKET&apos;) ?: &apos;&apos;,
        &apos;secret&apos; =&gt; getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_SECRET&apos;) ?: &apos;&apos;,
        &apos;root&apos; =&gt; &apos;/merchant-portal-media&apos;,
        &apos;path&apos; =&gt; &apos;&apos;,
        &apos;version&apos; =&gt; &apos;latest&apos;,
        &apos;region&apos; =&gt; getenv(&apos;AWS_REGION&apos;),
    ],
];
```

#### 4.2) Add local filesystem fallback for development

In the development environment, override the three filesystem services with a local adapter when the S3 bucket is not configured. This stores uploaded files under `public/Yves/assets/static/images` and serves them via the Yves assets path.

**config/Shared/config_default-docker.dev.php**

Add the import statement:

```php
use Spryker\Shared\Flysystem\FlysystemConstants;
```

Add the following block in the filesystem section:

```php
if (!getenv(&apos;SPRYKER_S3_PUBLIC_ASSETS_BUCKET&apos;)) {
    $publicUrl = sprintf(
        &apos;%s%s&apos;,
        $config[ApplicationConstants::BASE_URL_YVES],
        &apos;/assets/static/images&apos;,
    );

    $localMediaFilesystemConfig = [
        &apos;sprykerAdapterClass&apos; =&gt; LocalFilesystemBuilderPlugin::class,
        &apos;root&apos; =&gt; APPLICATION_ROOT_DIR . &apos;/public/Yves/assets/static/images&apos;,
        &apos;path&apos; =&gt; &apos;&apos;,
    ];

    $config[FileSystemConstants::FILESYSTEM_SERVICE][&apos;backoffice-media&apos;] = $localMediaFilesystemConfig;
    $config[FileSystemConstants::FILESYSTEM_SERVICE][&apos;storefront-media&apos;] = $localMediaFilesystemConfig;
    $config[FileSystemConstants::FILESYSTEM_SERVICE][&apos;merchant-portal-media&apos;] = $localMediaFilesystemConfig;
    $config[FlysystemConstants::FLYSYSTEM_OPTIONS] = [
        &apos;public_url&apos; =&gt; $publicUrl,
    ];
}
```

#### 4.3) Add local filesystem fallback for CI

**config/Shared/config_default-docker.ci.php**

Add the import statements:

```php
use Spryker\Shared\Application\ApplicationConstants;
use Spryker\Shared\Flysystem\FlysystemConstants;
```

Add the `$localMediaFilesystemConfig` variable before the `$config[FileSystemConstants::FILESYSTEM_SERVICE]` array, and add the three services as entries inside that array. After the array, add the public URL config:

```php
$localMediaFilesystemConfig = [
    &apos;sprykerAdapterClass&apos; =&gt; LocalFilesystemBuilderPlugin::class,
    &apos;root&apos; =&gt; APPLICATION_ROOT_DIR . &apos;/public/Yves/assets/static/images&apos;,
    &apos;path&apos; =&gt; &apos;&apos;,
];

$config[FileSystemConstants::FILESYSTEM_SERVICE] = [
    // ... existing entries ...
    &apos;backoffice-media&apos; =&gt; $localMediaFilesystemConfig,
    &apos;storefront-media&apos; =&gt; $localMediaFilesystemConfig,
    &apos;merchant-portal-media&apos; =&gt; $localMediaFilesystemConfig,
];

$publicUrl = sprintf(
    &apos;%s%s&apos;,
    $config[ApplicationConstants::BASE_URL_YVES],
    &apos;/assets/static/images&apos;,
);

$config[FlysystemConstants::FLYSYSTEM_OPTIONS] = [
    &apos;public_url&apos; =&gt; $publicUrl,
];
```

{% info_block warningBox &quot;Verification&quot; %}

1. Log in to the Back Office.
2. Navigate to **Configuration &gt; Theme &gt; Logos**.
3. Upload a logo image using the file upload widget.
4. Verify the uploaded file is accessible at the public URL shown after upload.

{% endinfo_block %}

### 5) Clear caches

Clear the Twig cache and application cache to apply the new Twig functions:

```bash
console twig:cache:warmer
console cache:clear
```

## Configure the feature

After installing the feature, configure branding settings in the Back Office:

1. Log in to the Back Office.
2. Go to **Configuration &gt; Theme**.
3. Select the tab for the context you want to configure: **Storefront**, **Back Office**, **Merchant Portal**, or **Logos**.
4. Enter the desired values and click **Save**.

Changes to Storefront settings are published to key-value storage automatically. Back Office and Merchant Portal settings take effect on the next page load.

### Logo upload fields

Logo settings support direct file upload from the Back Office. Uploaded files are stored via the corresponding Flysystem media filesystem service (`storefront-media`, `backoffice-media`, `merchant-portal-media`) and served from the configured `public_url`.

| Context | Setting | Recommended size | Supported formats | Filesystem service |
|---------|---------|-----------------|-------------------|--------------------|
| Storefront | Storefront Logo | 186×50 px | GIF, PNG, JPEG, BMP, WebP, SVG | `storefront-media` |
| Back Office | Back Office Logo | 186×50 px | GIF, PNG, JPEG, BMP, WebP, SVG | `backoffice-media` |
| Merchant Portal | Merchant Portal Logo | 186×50 px | GIF, PNG, JPEG, BMP, WebP, SVG | `merchant-portal-media` |

In production, configure the `SPRYKER_S3_PUBLIC_ASSETS_BUCKET`, `SPRYKER_S3_PUBLIC_ASSETS_KEY`, `SPRYKER_S3_PUBLIC_ASSETS_SECRET`, and `AWS_REGION` environment variables to point to an S3 bucket (or compatible object storage). In development and CI, files are stored locally at `public/Yves/assets/static/images`.

### Store-level overrides

To configure a per-store override:

1. In the scope selector at the top of the Configuration page, switch from **Default** to the target store (for example, **DE**).
2. Adjust the setting value.
3. Click **Save**.

Stores without a store-level value inherit the global (Default) value.

## Theme configuration key reference

The following theme settings are registered by the feature&apos;s YAML schema. Use their compound keys in `configurationValue()` Twig calls or via `getModuleConfig()` in a module Config class.

| Compound Key | Context | Type | Default |
|-------------|---------|------|---------|
| `theme:storefront:colors:background_brand_primary` | Storefront | color | `#00bebe` |
| `theme:storefront:colors:background_brand_subtle` | Storefront | color | `#eb553c` |
| `theme:storefront:custom_css:yves_custom_css` | Storefront | text | (empty) |
| `theme:logos:logos:yves_logo_url` | Storefront | file | (empty) |
| `theme:backoffice:colors:bo_main_color` | Back Office | color | `#1ebea0` |
| `theme:backoffice:colors:bo_sidenav_color` | Back Office | color | `#23303c` |
| `theme:backoffice:colors:bo_sidenav_text_color` | Back Office | color | `#e4e4e4` |
| `theme:logos:logos:backoffice_logo_url` | Back Office | file | (empty) |
| `theme:merchant_portal:colors:spy_primary_color` | Merchant Portal | color | `#1ebea0` |
| `theme:logos:logos:merchant_portal_logo` | Merchant Portal | file | (empty) |

### Twig usage examples

Inject a theme color as a CSS custom property in a Back Office layout:

{% raw %}

```twig
&lt;style&gt;
    :root {
        --bo-main-color: {{ configurationValue(&apos;theme:backoffice:colors:bo_main_color&apos;, &apos;#1ebea0&apos;) | e(&apos;css&apos;) }};
    }
&lt;/style&gt;
```

{% endraw %}

Read multiple values at once:

{% raw %}

```twig
{% set themeValues = configurationValues([
    &apos;theme:backoffice:colors:bo_main_color&apos;,
    &apos;theme:logos:logos:backoffice_logo_url&apos;,
]) %}
```

{% endraw %}
</description>
            <pubDate>Tue, 07 Apr 2026 08:04:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/integrate-and-configure/integrate-basic-shop-theme.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/integrate-and-configure/integrate-basic-shop-theme.html</guid>
            
            
        </item>
        
        <item>
            <title>Configuration Management feature</title>
            <description>&lt;h2 id=&quot;what-it-does&quot;&gt;What It Does&lt;/h2&gt;
&lt;p&gt;Provides a centralized, schema-driven system for managing application settings across scopes (for example global, store, custom-scope). Settings are declared in YAML files, managed via the Back Office UI, to be consumed in different layers (Zed, Yves, Glue) through module Config classes.&lt;/p&gt;
&lt;h2 id=&quot;quick-start&quot;&gt;Quick Start&lt;/h2&gt;
&lt;h3 id=&quot;declare-settings-in-a-yaml-schema&quot;&gt;1. Declare settings in a YAML schema&lt;/h3&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# /data/configuration/my-module.configuration.yml&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;features&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;my_module&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;My Module&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tabs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;general&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;General&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;display&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Display Settings&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
              &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;items_per_page&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Items Per Page&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;integer&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;default_value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;24&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;storefront&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
                &lt;span class=&quot;na&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
                  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;required&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Items per page is required&lt;/span&gt;
                  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;min&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Must be at least &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
                    &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;sync-schemas&quot;&gt;2. Sync schemas&lt;/h3&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;docker/sdk cli console configuration:sync
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;consume-in-your-module-config&quot;&gt;3. Consume in your module Config&lt;/h3&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Spryker\Yves\MyModule&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Spryker\Yves\Kernel\AbstractBundleConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyModuleConfig&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractBundleConfig&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;DEFAULT_ITEMS_PER_PAGE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_MY_MODULE_GENERAL_DISPLAY_ITEMS_PER_PAGE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;my_module:general:display:items_per_page&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getItemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getModuleConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_MY_MODULE_GENERAL_DISPLAY_ITEMS_PER_PAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DEFAULT_ITEMS_PER_PAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;getModuleConfig()&lt;/code&gt; method is available in Zed, Yves, Glue layers &lt;code&gt;AbstractBundleConfig&lt;/code&gt; and is the preferred way to read configuration values.&lt;/p&gt;
&lt;h2 id=&quot;reading-configuration-values&quot;&gt;Reading Configuration Values&lt;/h2&gt;
&lt;h3 id=&quot;the-codegetmoduleconfigcode-method&quot;&gt;The &lt;code&gt;getModuleConfig()&lt;/code&gt; Method&lt;/h3&gt;
&lt;p&gt;Every &lt;code&gt;AbstractBundleConfig&lt;/code&gt; in Zed, Yves, and Glue provides:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getModuleConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;mixed&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$configurationScopes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;mixed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Compound setting key: &lt;code&gt;feature:tab:group:setting&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;mixed&lt;/td&gt;
&lt;td&gt;Fallback when value is not found or Configuration module is not installed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$configurationScopes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ConfigurationScopeTransfer[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Optional scope context transfers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Layer behavior:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Yves / Glue&lt;/strong&gt;: Reads from key-value storage via &lt;code&gt;ConfigurationClient&lt;/code&gt; (fast, cached)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zed&lt;/strong&gt;: Reads from database via &lt;code&gt;ConfigurationFacade&lt;/code&gt; (supports secrets, full scope resolution)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Backward Compatible:&lt;/strong&gt; If the Configuration module is not installed, &lt;code&gt;getModuleConfig()&lt;/code&gt; silently returns &lt;code&gt;$default&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;real-world-examples&quot;&gt;Real-World Examples&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;AvailabilityWidget (Yves)&lt;/strong&gt; – boolean setting with constant fallback:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AvailabilityWidgetConfig&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractBundleConfig&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;STOCK_DISPLAY_ENABLED&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;STOCK_DISPLAY_MODE_INDICATOR_AND_QUANTITY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;indicator_and_quantity&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_CATALOG_INVENTORY_STOCK_OPTIONS_DISPLAY_STOCK_AVAILABILITY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;catalog:inventory:stock_options:display_stock_availability&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_CATALOG_INVENTORY_STOCK_OPTIONS_STOCK_INFO_OPTIONS&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;catalog:inventory:stock_options:stock_info_options&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;isStockDisplayEnabled&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;bool&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getModuleConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_CATALOG_INVENTORY_STOCK_OPTIONS_DISPLAY_STOCK_AVAILABILITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;STOCK_DISPLAY_ENABLED&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getStockDisplayMode&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getModuleConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_CATALOG_INVENTORY_STOCK_OPTIONS_STOCK_INFO_OPTIONS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;STOCK_DISPLAY_MODE_INDICATOR_AND_QUANTITY&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;BuyBox (Yves)&lt;/strong&gt; – string setting with enum fallback:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;BuyBoxConfig&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;AbstractBundleConfig&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;SORT_BY_PRICE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;price&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_MARKETPLACE_PDP_BUY_BOX_OFFER_SORT_RULE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;marketplace:pdp:buy_box:offer_sort_rule&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getSortingStrategy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getModuleConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_MARKETPLACE_PDP_BUY_BOX_OFFER_SORT_RULE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;SORT_BY_PRICE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;scope-specific-reads&quot;&gt;Scope-Specific Reads&lt;/h3&gt;
&lt;p&gt;Pass &lt;code&gt;ConfigurationScopeTransfer&lt;/code&gt; objects as the third argument when you need a scope-specific value:&lt;/p&gt;
&lt;div class=&quot;language-php highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Generated\Shared\Transfer\ConfigurationScopeTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;getItemsPerPage&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$storeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_MY_MODULE_GENERAL_DISPLAY_ITEMS_PER_PAGE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;my_module:general:display:items_per_page&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;getModuleConfig&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;CONFIGURATION_KEY_MY_MODULE_GENERAL_DISPLAY_ITEMS_PER_PAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;DEFAULT_ITEMS_PER_PAGE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;ConfigurationScopeTransfer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setKey&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;store&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;setIdentifier&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$storeName&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;integration-checklist&quot;&gt;Integration Checklist&lt;/h3&gt;
&lt;p&gt;When integrating Configuration into an existing module:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Identify hardcoded values or constants that should be configurable&lt;/li&gt;
&lt;li&gt;Declare them in a YAML schema in &lt;code&gt;data/configuration/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Replace hardcoded access in the module’s Config class with &lt;code&gt;getModuleConfig()&lt;/code&gt; calls&lt;/li&gt;
&lt;li&gt;Keep the original constant as &lt;code&gt;$default&lt;/code&gt; for backward compatibility&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;configuration:sync&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Values are now manageable via the Back Office Configuration Management page&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;yaml-schema-declaration-reference&quot;&gt;YAML Schema Declaration Reference&lt;/h2&gt;
&lt;h3 id=&quot;schema-validation&quot;&gt;Schema Validation&lt;/h3&gt;
&lt;p&gt;YAML schema files support IDE autocompletion via JSON Schema. Add this header to your file:&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# yaml-language-server: $schema=../../vendor/spryker/configuration/resources/configuration/configuration-schema-v1.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The schema file is located at &lt;code&gt;vendor/spryker/configuration/resources/configuration/configuration-schema-v1.json&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;hierarchy&quot;&gt;Hierarchy&lt;/h3&gt;
&lt;div class=&quot;language-text highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;features[]
  tabs[]
    groups[]
      settings[]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Each setting gets a compound key: &lt;code&gt;{featureKey}:{tabKey}:{groupKey}:{settingKey}&lt;/code&gt; used for persistence and lookups.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id=&quot;feature-level&quot;&gt;Feature Level&lt;/h3&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;features&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;system&lt;/span&gt;                          &lt;span class=&quot;c1&quot;&gt;# Required. Unique feature identifier.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;System Configuration&lt;/span&gt;           &lt;span class=&quot;c1&quot;&gt;# Required. Display name in backoffice.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Core system settings&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Optional. Feature description.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;                             &lt;span class=&quot;c1&quot;&gt;# Optional. Sort order (lower = first).&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# Optional. Default: true.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;tabs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;                          &lt;span class=&quot;c1&quot;&gt;# Required. At least one tab.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Constraints&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;&lt;code&gt;^[a-z][a-z0-9_]*$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique identifier. Part of compound setting key.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;1-255 chars&lt;/td&gt;
&lt;td&gt;Display name in backoffice sidebar.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 1000 chars&lt;/td&gt;
&lt;td&gt;Feature description text.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;order&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Sort order for rendering. Lower values first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;When &lt;code&gt;false&lt;/code&gt;, feature and all children are hidden.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tabs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;min 1 item&lt;/td&gt;
&lt;td&gt;List of tab objects.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3 id=&quot;tab-level&quot;&gt;Tab Level&lt;/h3&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;tabs&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;general&lt;/span&gt;                         &lt;span class=&quot;c1&quot;&gt;# Required. Unique within feature.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;General&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# Required. Tab display name.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;settings&lt;/span&gt;                       &lt;span class=&quot;c1&quot;&gt;# Optional. Icon class for backoffice tab.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;General system settings&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# Optional. Tab description.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;                             &lt;span class=&quot;c1&quot;&gt;# Optional. Sort order (lower = first).&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# Optional. Default: true.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# Required. At least one group.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Constraints&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;&lt;code&gt;^[a-z][a-z0-9_]*$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique within feature. Part of compound setting key.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;1-255 chars&lt;/td&gt;
&lt;td&gt;Tab label in backoffice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;icon&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 100 chars&lt;/td&gt;
&lt;td&gt;Icon class (for example &lt;code&gt;settings&lt;/code&gt;, &lt;code&gt;cable&lt;/code&gt;, &lt;code&gt;trending_up&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 1000 chars&lt;/td&gt;
&lt;td&gt;Tab description text.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;order&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Sort order for rendering. Lower values first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;When &lt;code&gt;false&lt;/code&gt;, tab and all children are hidden.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;groups&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;min 1 item&lt;/td&gt;
&lt;td&gt;List of group objects.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3 id=&quot;group-level&quot;&gt;Group Level&lt;/h3&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;basic&lt;/span&gt;                           &lt;span class=&quot;c1&quot;&gt;# Required. Unique within tab.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Basic Settings&lt;/span&gt;                 &lt;span class=&quot;c1&quot;&gt;# Required. Group display name.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Basic system config&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;# Optional. Group description.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;# Required. Scopes this group is visible for.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;                             &lt;span class=&quot;c1&quot;&gt;# Optional. Sort order (lower = first).&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# Optional. Default: true.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;                      &lt;span class=&quot;c1&quot;&gt;# Required. At least one setting.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Constraints&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;&lt;code&gt;^[a-z][a-z0-9_]*$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique within tab.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;1-255 chars&lt;/td&gt;
&lt;td&gt;Group heading in backoffice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 1000 chars&lt;/td&gt;
&lt;td&gt;Group description text.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scopes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string[]&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&apos;global&apos;]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;min 1 item&lt;/td&gt;
&lt;td&gt;Scopes where this group is displayed in backoffice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;order&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Sort order for rendering. Lower values first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;When &lt;code&gt;false&lt;/code&gt;, group and all settings are hidden.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;settings&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;min 1 item&lt;/td&gt;
&lt;td&gt;List of setting objects.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3 id=&quot;setting-level&quot;&gt;Setting Level&lt;/h3&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;site_name&lt;/span&gt;                       &lt;span class=&quot;c1&quot;&gt;# Required. Unique within group.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Site Name&lt;/span&gt;                      &lt;span class=&quot;c1&quot;&gt;# Required. Setting label.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;The name of the site&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Optional. Displayed below the input.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;note&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Changes require cache clear&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Optional. Additional note below description.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;placeholder&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Enter your store name&lt;/span&gt;   &lt;span class=&quot;c1&quot;&gt;# Optional. Input placeholder text.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;help_text&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Appears in page titles&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# Optional. Tooltip or help area text.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;@MyModule/Configuration/custom-input.twig&apos;&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# Optional. Custom Twig template.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;string&lt;/span&gt;                         &lt;span class=&quot;c1&quot;&gt;# Required. Data type.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;default_value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;My Store&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;# Optional. Fallback value.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;                             &lt;span class=&quot;c1&quot;&gt;# Optional. For select/multiselect/radio types.&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;option_a&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Option A&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;              &lt;span class=&quot;c1&quot;&gt;# Optional. Default: [&apos;global&apos;].&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;order&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;                             &lt;span class=&quot;c1&quot;&gt;# Optional. Sort order (lower = first).&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# Optional. Default: true.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;                        &lt;span class=&quot;c1&quot;&gt;# Optional. Default: false.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;storefront&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;                     &lt;span class=&quot;c1&quot;&gt;# Optional. Default: false.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;                   &lt;span class=&quot;c1&quot;&gt;# Optional. Validation constraints.&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;                  &lt;span class=&quot;c1&quot;&gt;# Optional. Conditional visibility rules.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Constraints&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;key&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;&lt;code&gt;^[a-z][a-z0-9_]*$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unique within group. Part of compound key.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;1-255 chars&lt;/td&gt;
&lt;td&gt;Input label in backoffice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 1000 chars&lt;/td&gt;
&lt;td&gt;Help text displayed below the input field.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;note&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 1000 chars&lt;/td&gt;
&lt;td&gt;Additional note displayed below the description in italic.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;placeholder&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 255 chars&lt;/td&gt;
&lt;td&gt;Placeholder text inside the input field.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;help_text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 500 chars&lt;/td&gt;
&lt;td&gt;Extended help in tooltip or expandable area.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;template&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;max 255 chars&lt;/td&gt;
&lt;td&gt;Custom Twig template. Overrides type-based widget.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;See types table&lt;/td&gt;
&lt;td&gt;Value data type. Determines backoffice input widget.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;default_value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;mixed&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Must match &lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fallback when no value is saved at any scope.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;options&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;option[]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;For select/multi/radio&lt;/td&gt;
&lt;td&gt;Available choices. See &lt;a href=&quot;#options&quot;&gt;Options&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;scopes&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string[]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[&apos;global&apos;]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;min 1 item&lt;/td&gt;
&lt;td&gt;Scopes where this setting can be configured.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;order&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Sort order for rendering. Lower values first.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;enabled&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;When &lt;code&gt;false&lt;/code&gt;, setting is excluded from schema.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;secret&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;When &lt;code&gt;true&lt;/code&gt;, value is encrypted in database. Never published to storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;storefront&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;boolean&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;When &lt;code&gt;true&lt;/code&gt;, value is published to key-value storage for Yves/Glue.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;constraints&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Validation rules. See &lt;a href=&quot;#constraints&quot;&gt;Constraints&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dependencies&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;[]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Conditional rules. See &lt;a href=&quot;#dependencies&quot;&gt;Dependencies&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;file_upload&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Required when &lt;code&gt;type: file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File upload configuration. See &lt;a href=&quot;#file-upload-fields&quot;&gt;File upload fields&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;setting-types&quot;&gt;Setting Types&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Backoffice Input&lt;/th&gt;
&lt;th&gt;Requires &lt;code&gt;options&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;string&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single-line text&lt;/td&gt;
&lt;td&gt;Text input&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;integer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whole number&lt;/td&gt;
&lt;td&gt;Number input&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;float&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Decimal number&lt;/td&gt;
&lt;td&gt;Number input&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;True/false toggle&lt;/td&gt;
&lt;td&gt;Checkbox/switch&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;text&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multi-line text or structured data&lt;/td&gt;
&lt;td&gt;Textarea&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Color hex value&lt;/td&gt;
&lt;td&gt;Color picker&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;JSON structure&lt;/td&gt;
&lt;td&gt;Code editor&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;select&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single choice from list&lt;/td&gt;
&lt;td&gt;Dropdown&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;multiselect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Multiple choices from list&lt;/td&gt;
&lt;td&gt;Multi-select&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;radio&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Single choice from list&lt;/td&gt;
&lt;td&gt;Radio buttons&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;file&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;File upload (stored as public URL)&lt;/td&gt;
&lt;td&gt;File upload widget&lt;/td&gt;
&lt;td&gt;No, but requires &lt;code&gt;file_upload&lt;/code&gt; block&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;file-upload-fields&quot;&gt;File upload fields&lt;/h4&gt;
&lt;p&gt;Use &lt;code&gt;type: file&lt;/code&gt; for settings that accept a file upload (for example, logo images). The Back Office renders a file upload widget. After a successful upload, the public URL of the uploaded file is stored as the setting value. All file validation and storage are handled server-side using a configured Flysystem filesystem service.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;type: file&lt;/code&gt; is used, a &lt;code&gt;file_upload&lt;/code&gt; block must be present on the setting. It configures the Flysystem service, allowed file types, size limit, and recommended display dimensions.&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;bo_logo_url&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Back Office Logo&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;file&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;file_upload&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;storage_name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;backoffice-media&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;allowed_mime_types&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;image/png&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;image/jpeg&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;image/gif&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;image/webp&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;image/svg+xml&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;allowed_extensions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.png&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.jpg&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.jpeg&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.gif&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.webp&apos;&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.svg&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;max_file_size&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;10M&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;recommended_width_px&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;290&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;recommended_height_px&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;77&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;default_value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;scopes&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;pi&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;global&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;store&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;enabled&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;storefront&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h5 id=&quot;codefileuploadcode-properties&quot;&gt;&lt;code&gt;file_upload&lt;/code&gt; properties&lt;/h5&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;storage_name&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Flysystem filesystem service name. Must be configured in &lt;code&gt;FileSystemConstants::FILESYSTEM_SERVICE&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allowed_mime_types&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string[]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;List of permitted MIME types. Upload is rejected if the file MIME type is not in the list.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;allowed_extensions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string[]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;List of permitted file extensions including the leading dot (for example &lt;code&gt;.png&lt;/code&gt;).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max_file_size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Maximum allowed file size (for example &lt;code&gt;10M&lt;/code&gt;, &lt;code&gt;2048K&lt;/code&gt;). Rejected if exceeded.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;recommended_width_px&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Recommended display width in pixels. Shown as a hint in the Back Office UI.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;recommended_height_px&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;integer&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Recommended display height in pixels. Shown as a hint in the Back Office UI.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The stored value is always a public URL returned by the Flysystem service after upload. To render the image in a Twig template, read it with &lt;code&gt;configurationValue()&lt;/code&gt; and use it as an &lt;code&gt;src&lt;/code&gt; attribute:&lt;/p&gt;
&lt;div class=&quot;language-twig highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;logoUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;configurationValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;theme:logos:logos:yves_logo_url&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;logoUrl&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;empty&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;&amp;lt;img&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;src=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;logoUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;| &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;html_attr&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;alt=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Logo&quot;&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;endif&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;For Flysystem service setup (S3 in production, local fallback in development and CI), see &lt;a href=&quot;/docs/dg/dev/integrate-and-configure/integrate-basic-shop-theme.html&quot;&gt;Install the Basic Shop Theme feature&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&quot;options&quot;&gt;Options&lt;/h4&gt;
&lt;p&gt;Required for &lt;code&gt;select&lt;/code&gt;, &lt;code&gt;multiselect&lt;/code&gt;, and &lt;code&gt;radio&lt;/code&gt; types. Defines available choices.&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;indicator_only&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Indicator Only&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show only in-stock/out-of-stock indicator&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;indicator_and_quantity&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Indicator and Quantity&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Show indicator and exact stock count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Constraints&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;min 1 char&lt;/td&gt;
&lt;td&gt;Internal value stored when selected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;label&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;1-255 chars&lt;/td&gt;
&lt;td&gt;Display label shown in backoffice.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;max 500 chars&lt;/td&gt;
&lt;td&gt;Additional explanation (for radio buttons).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;secret-vs-storefront-behavior&quot;&gt;Secret vs Storefront Behavior&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;secret&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;storefront&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plain text in database. Accessible only via Zed (&lt;code&gt;getModuleConfig()&lt;/code&gt; in Zed Config).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plain text. Published to key-value storage. Accessible in all layers via &lt;code&gt;getModuleConfig()&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Encrypted in database. Decrypted on read in Zed only. Never published to storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Invalid combination. Secrets are always excluded from storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;available-scopes&quot;&gt;Available Scopes&lt;/h4&gt;
&lt;p&gt;Built-in scopes (enabled out of the box):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Requires Identifier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;global&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Application-wide default&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;store&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Store-specific override (for example DE, AT)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Default hierarchy: &lt;code&gt;store -&amp;gt; global -&amp;gt; default_value&lt;/code&gt;. Values resolve from most specific to least specific.&lt;/p&gt;
&lt;p&gt;For adding custom scopes, see &lt;a href=&quot;/docs/dg/dev/backend-development/configuration-management/custom-scopes.html&quot;&gt;Adding Custom Scopes&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h3 id=&quot;constraints&quot;&gt;Constraints&lt;/h3&gt;
&lt;p&gt;Validated server-side when saving values through the Back Office.&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;constraints&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;required&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Site name is required&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;min&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Must be at least &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;max&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Cannot exceed &lt;/span&gt;&lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;100&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Constraints&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;See table&lt;/td&gt;
&lt;td&gt;Constraint type identifier.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;message&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;1-500 chars&lt;/td&gt;
&lt;td&gt;Error message shown when validation fails.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;options&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Depends on type&lt;/td&gt;
&lt;td&gt;Constraint-specific parameters.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;built-in-constraint-types&quot;&gt;Built-in Constraint Types&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Required Options&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;required&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Value must not be blank&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Mandatory fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;min&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Minimum numeric value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;min&lt;/code&gt; (number)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;options: { min: 1 }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;max&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Maximum numeric value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;max&lt;/code&gt; (number)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;options: { max: 100 }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;range&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Value within numeric range&lt;/td&gt;
&lt;td&gt;&lt;code&gt;min&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt; (number)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;options: { min: 0, max: 100 }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;length&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String length limits&lt;/td&gt;
&lt;td&gt;&lt;code&gt;min&lt;/code&gt; and/or &lt;code&gt;max&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;options: { min: 6, max: 128 }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Email format validation&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;Email input fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;url&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;URL format validation&lt;/td&gt;
&lt;td&gt;–&lt;/td&gt;
&lt;td&gt;URL input fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;regex&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pattern matching&lt;/td&gt;
&lt;td&gt;&lt;code&gt;pattern&lt;/code&gt; (string)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;options: { pattern: &apos;^G-[A-Z0-9]{10}$&apos; }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;choice&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Value from allowed list&lt;/td&gt;
&lt;td&gt;&lt;code&gt;choices&lt;/code&gt; (string[])&lt;/td&gt;
&lt;td&gt;&lt;code&gt;options: { choices: [&apos;a&apos;, &apos;b&apos;] }&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h3 id=&quot;dependencies&quot;&gt;Dependencies&lt;/h3&gt;
&lt;p&gt;Control conditional visibility of settings in the Back Office UI. A setting with dependencies is only shown when at least one dependency rule matches (OR logic between rules). Each rule uses a &lt;code&gt;when&lt;/code&gt; clause with &lt;code&gt;any&lt;/code&gt; (OR) or &lt;code&gt;all&lt;/code&gt; (AND) condition grouping.&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;na&quot;&gt;settings&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;stock_info_options&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;Stock Info Options&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;radio&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;setting&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;catalog:inventory:stock_options:display_stock_availability&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Multiple conditions with &lt;code&gt;all&lt;/code&gt; (AND logic):&lt;/p&gt;
&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    &lt;span class=&quot;na&quot;&gt;dependencies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;when&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;setting&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;system:general:basic:feature_enabled&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;equals&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;true&quot;&lt;/span&gt;
            &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;setting&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;system:general:basic:mode&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;operator&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;not_equals&lt;/span&gt;
              &lt;span class=&quot;na&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;maintenance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h4 id=&quot;dependency-rule&quot;&gt;Dependency Rule&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;when&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;object&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Contains &lt;code&gt;any&lt;/code&gt; or &lt;code&gt;all&lt;/code&gt; array of conditions.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;when-clause&quot;&gt;When Clause&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;any&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;condition[]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Setting is shown if &lt;strong&gt;any&lt;/strong&gt; condition matches (OR).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;all&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;condition[]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Setting is shown if &lt;strong&gt;all&lt;/strong&gt; conditions match (AND).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;One of &lt;code&gt;any&lt;/code&gt; or &lt;code&gt;all&lt;/code&gt; must be provided.&lt;/p&gt;
&lt;h4 id=&quot;condition&quot;&gt;Condition&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;setting&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Compound key of the setting this depends on.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;operator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Comparison operator.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;value&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;string&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Value to compare against.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h4 id=&quot;dependency-operators&quot;&gt;Dependency Operators&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;equals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Setting value must equal the given value.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;not_equals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Setting value must not equal the given value.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;greater_than&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Numeric: value must be greater than expected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;less_than&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Numeric: value must be less than expected.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contains&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String: value must contain the expected text.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;in&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Value must be in the given list.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;schema-file-locations&quot;&gt;Schema File Locations&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Loaded By&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{Module Folder}/resources/configuration/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Core module schemas&lt;/td&gt;
&lt;td&gt;&lt;code&gt;configuration:sync&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data/configuration/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Project-level schema overrides&lt;/td&gt;
&lt;td&gt;&lt;code&gt;configuration:sync&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Project schemas override core schemas at the setting level. Settings with the same compound key are completely replaced.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Merged output:&lt;/strong&gt; &lt;code&gt;data/configuration/merged-schema.php&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;backoffice-management&quot;&gt;Backoffice Management&lt;/h2&gt;
&lt;p&gt;Settings are managed at &lt;strong&gt;The Back office &amp;gt; Configuration&lt;/strong&gt;. The page provides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Feature sidebar navigation&lt;/li&gt;
&lt;li&gt;Tabbed layout per feature&lt;/li&gt;
&lt;li&gt;Scope switcher (global, store with identifier)&lt;/li&gt;
&lt;li&gt;Input widgets matching setting types&lt;/li&gt;
&lt;li&gt;Inline validation with constraint error messages&lt;/li&gt;
&lt;li&gt;“Revert to default” to delete scope-specific overrides&lt;/li&gt;
&lt;li&gt;Batch save with per-field error reporting&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;twig-integration&quot;&gt;Twig Integration&lt;/h2&gt;
&lt;p&gt;The Configuration module exposes three Twig functions that let templates read configuration values directly without going through a PHP Config class. They are available through the following existing plugins — no additional registration is required:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Context&lt;/th&gt;
&lt;th&gt;Plugin&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storefront (Yves)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ShopUiTwigExtension&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Back Office and Merchant Portal (Zed)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;\Spryker\Zed\Twig\Communication\Plugin\Application\TwigApplicationPlugin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;configurationValue(key, default)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns a single configuration value by compound key.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;configurationValues(keys)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns an associative array of values for a list of compound keys.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;codeconfigurationvaluecode&quot;&gt;&lt;code&gt;configurationValue&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Returns a single value for the given compound key. Falls back to &lt;code&gt;default&lt;/code&gt; if the key has no saved value.&lt;/p&gt;
&lt;div class=&quot;language-twig highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;configurationValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;theme:backoffice:colors:bo_main_color&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;#1ebea0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;div class=&quot;language-twig highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;py&quot;&gt;--bo-main-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;configurationValue&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;theme:backoffice:colors:bo_main_color&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;#1ebea0&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;| &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;css&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3 id=&quot;codeconfigurationvaluescode&quot;&gt;&lt;code&gt;configurationValues&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Returns an associative array keyed by compound key. Useful when a template needs several values at once.&lt;/p&gt;
&lt;div class=&quot;language-twig highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;{%&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;colors&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;configurationValues&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&apos;theme:backoffice:colors:bo_main_color&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&apos;theme:logos:logos:backoffice_logo_url&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;%}&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;style&amp;gt;&lt;/span&gt;&lt;span class=&quot;nd&quot;&gt;:root&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;py&quot;&gt;--bo-main-color&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;colors&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;theme:backoffice:colors:bo_main_color&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;| &lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;css&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;cp&quot;&gt;}}&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;section class=&apos;info-block &apos;&gt;&lt;i class=&apos;info-block__icon icon-info&apos;&gt;&lt;/i&gt;&lt;div class=&apos;info-block__content&apos;&gt;&lt;div class=&quot;info-block__title&quot;&gt;Scope context&lt;/div&gt;
&lt;p&gt;Twig functions read values in the current request’s scope context. Store-specific values are resolved automatically when a store scope is active.&lt;/p&gt;
&lt;/div&gt;&lt;/section&gt;
&lt;h2 id=&quot;common-issues&quot;&gt;Common Issues&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getModuleConfig()&lt;/code&gt; always returns default&lt;/td&gt;
&lt;td&gt;Setting &lt;code&gt;storefront: false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yves/Glue can only read storefront-enabled settings. Set &lt;code&gt;storefront: true&lt;/code&gt; or use Zed Config.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;getModuleConfig()&lt;/code&gt; always returns default&lt;/td&gt;
&lt;td&gt;Configuration module not installed&lt;/td&gt;
&lt;td&gt;Expected behavior. Method gracefully falls back to &lt;code&gt;$default&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Value returns &lt;code&gt;null&lt;/code&gt; despite being saved&lt;/td&gt;
&lt;td&gt;Key mismatch&lt;/td&gt;
&lt;td&gt;Compound key format is &lt;code&gt;feature:tab:group:setting&lt;/code&gt;. Verify all four segments match the YAML schema.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secret value empty in Yves&lt;/td&gt;
&lt;td&gt;Expected behavior&lt;/td&gt;
&lt;td&gt;Secrets are never published to storage. Access only via Zed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Changes not visible after YAML edit&lt;/td&gt;
&lt;td&gt;Schema not synced&lt;/td&gt;
&lt;td&gt;Run &lt;code&gt;configuration:sync&lt;/code&gt; after YAML changes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Store-specific value not applied&lt;/td&gt;
&lt;td&gt;Missing scope context&lt;/td&gt;
&lt;td&gt;Pass &lt;code&gt;ConfigurationScopeTransfer&lt;/code&gt; in third argument or register request expander plugins. See &lt;a href=&quot;/docs/dg/dev/backend-development/configuration-management/custom-scopes.html&quot;&gt;Adding Custom Scopes&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</description>
            <pubDate>Tue, 07 Apr 2026 08:04:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/backend-development/configuration-management.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/backend-development/configuration-management.html</guid>
            
            
        </item>
        
        <item>
            <title>Basic Shop Theme feature overview</title>
            <description>&lt;p&gt;The Basic Shop Theme feature lets Business Admins configure the visual branding of a Spryker shop directly from the Back Office. Logos, theme colors, and custom CSS for the Storefront, Back Office, and Merchant Portal can all be set without code changes or redeployment.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;business-problems-it-solves&quot;&gt;Business problems it solves&lt;/h2&gt;
&lt;p&gt;Basic visual customization of a Spryker shop — such as applying a company logo or brand color — traditionally requires developer involvement, project-specific work, or code changes. This creates friction in critical moments:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Demo preparation&lt;/strong&gt; by Solution Engineers — demos feel less customer-ready without brand-specific visuals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Early project phases and proofs of concept&lt;/strong&gt; — customers cannot easily apply their own branding.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;First weeks after go-live&lt;/strong&gt; — unnecessary delivery work delays Time-to-First-Transaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Basic Shop Theme feature removes this dependency by providing a self-service branding interface in the Back Office.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;what-you-can-configure&quot;&gt;What you can configure&lt;/h2&gt;
&lt;p&gt;All theme settings are managed under &lt;strong&gt;Back Office &amp;gt; Configuration &amp;gt; Theme&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;storefront&quot;&gt;Storefront&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Theme Main Color&lt;/td&gt;
&lt;td&gt;Color picker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#00bebe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Primary brand color applied across the storefront. Exposed as the &lt;code&gt;--theme-main-color&lt;/code&gt; CSS custom property.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Theme Alternative Color&lt;/td&gt;
&lt;td&gt;Color picker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#eb553c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Secondary brand color for accents and highlights. Exposed as &lt;code&gt;--theme-alt-color&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storefront Logo&lt;/td&gt;
&lt;td&gt;File upload&lt;/td&gt;
&lt;td&gt;(empty)&lt;/td&gt;
&lt;td&gt;Logo displayed in the storefront header. Recommended size: 186×50 px. Supported formats: GIF, PNG, JPEG, BMP, WebP, SVG.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storefront Custom CSS&lt;/td&gt;
&lt;td&gt;Text area&lt;/td&gt;
&lt;td&gt;(empty)&lt;/td&gt;
&lt;td&gt;Custom CSS injected into storefront pages. Use with caution — invalid CSS may break page layout.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;back-office&quot;&gt;Back Office&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backoffice Theme Color&lt;/td&gt;
&lt;td&gt;Color picker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#1ebea0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Primary theme color for the Back Office interface. Exposed as the &lt;code&gt;--bo-main-color&lt;/code&gt; CSS custom property.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backoffice Side Navigation Background&lt;/td&gt;
&lt;td&gt;Color picker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#23303c&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Side navigation and login background. Exposed as the &lt;code&gt;--bo-sidenav-color&lt;/code&gt; CSS custom property.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backoffice Side Navigation Text Color&lt;/td&gt;
&lt;td&gt;Color picker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#e4e4e4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Side navigation text color. Exposed as the &lt;code&gt;--bo-sidenav-text-color&lt;/code&gt; CSS custom property.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Back Office Logo&lt;/td&gt;
&lt;td&gt;File upload&lt;/td&gt;
&lt;td&gt;(empty)&lt;/td&gt;
&lt;td&gt;Logo displayed in the Back Office sidebar. Recommended size: 186×50 px. Supported formats: GIF, PNG, JPEG, BMP, WebP, SVG.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;merchant-portal&quot;&gt;Merchant Portal&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Merchant Portal Theme Color&lt;/td&gt;
&lt;td&gt;Color picker&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#1ebea0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Primary theme color for the Merchant Portal interface. Exposed as the &lt;code&gt;--mp-theme-color&lt;/code&gt; CSS custom property.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merchant Portal Logo&lt;/td&gt;
&lt;td&gt;File upload&lt;/td&gt;
&lt;td&gt;(empty)&lt;/td&gt;
&lt;td&gt;Logo displayed in the Merchant Portal header. Recommended size: 186×50 px. Supported formats: GIF, PNG, JPEG, BMP, WebP, SVG.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;
&lt;p&gt;The Basic Shop Theme feature is built on top of the &lt;a href=&quot;/docs/pbc/all/back-office/latest/base-shop/backoffice-configuration-framework.html&quot;&gt;Back Office Configuration Framework&lt;/a&gt;. All settings are declared in a YAML schema file bundled with the Spryker core modules. The Configuration module reads this file and renders the corresponding UI in the Back Office.&lt;/p&gt;
&lt;h3 id=&quot;storefront-theming-flow&quot;&gt;Storefront theming flow&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;A Business Admin sets a theme color or logo in the Back Office.&lt;/li&gt;
&lt;li&gt;The value is saved and published to key-value storage (Redis) via the Publish &amp;amp; Synchronize mechanism.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;configurationValue()&lt;/code&gt; Twig function reads the value from storage and injects it into the &lt;code&gt;page-blank&lt;/code&gt; layout as a CSS custom property or logo image tag.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;back-office-and-merchant-portal-theming-flow&quot;&gt;Back Office and Merchant Portal theming flow&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;A Business Admin sets a theme color or logo in the Back Office.&lt;/li&gt;
&lt;li&gt;The value is saved in the database.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;configurationValue()&lt;/code&gt; Twig function reads the value from the database and injects it into the respective layout template as a CSS custom property or logo image tag.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&quot;css-custom-properties&quot;&gt;CSS custom properties&lt;/h3&gt;
&lt;p&gt;Theme colors are injected as CSS custom properties so that the design system can use them throughout component styles:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Custom Property&lt;/th&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--background-brand-primary&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Storefront&lt;/td&gt;
&lt;td&gt;Storefront primary brand color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--background-brand-subtle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Storefront&lt;/td&gt;
&lt;td&gt;Storefront secondary accent color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--bo-main-color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Back Office&lt;/td&gt;
&lt;td&gt;Back Office primary theme color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--bo-sidenav-color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Back Office&lt;/td&gt;
&lt;td&gt;Back Office side navigation and login background&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--bo-sidenav-text-color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Back Office&lt;/td&gt;
&lt;td&gt;Back Office side navigation text color&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--spy-primary-color&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Merchant Portal&lt;/td&gt;
&lt;td&gt;Merchant Portal primary theme color&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;logo-upload-fields&quot;&gt;Logo upload fields&lt;/h3&gt;
&lt;p&gt;Logo settings use a file upload widget in the Back Office. Uploaded files are stored via a configured Flysystem media filesystem service and served from a public URL. Each application context uses its own filesystem service:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Context&lt;/th&gt;
&lt;th&gt;Filesystem service&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storefront&lt;/td&gt;
&lt;td&gt;&lt;code&gt;storefront-media&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Back Office&lt;/td&gt;
&lt;td&gt;&lt;code&gt;backoffice-media&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merchant Portal&lt;/td&gt;
&lt;td&gt;&lt;code&gt;merchant-portal-media&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;In production, these services write to an S3 bucket (or compatible object storage). In development and CI environments, files are stored locally at &lt;code&gt;public/Yves/assets/static/images&lt;/code&gt; and served via the Yves assets path.&lt;/p&gt;
&lt;p&gt;If the field is left empty, the default logo is displayed.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;store-level-customization&quot;&gt;Store-level customization&lt;/h2&gt;
&lt;p&gt;All theme settings support both global and store scopes. You can apply the same branding across all stores (global scope) or override individual settings per store (store scope).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Store&lt;/th&gt;
&lt;th&gt;Theme Main Color&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Global&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#00bebe&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Applied to all stores by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DE&lt;/td&gt;
&lt;td&gt;&lt;code&gt;#ff0000&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Overrides the global value for DE only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AT&lt;/td&gt;
&lt;td&gt;(not set)&lt;/td&gt;
&lt;td&gt;Inherits &lt;code&gt;#00bebe&lt;/code&gt; from global&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;To switch scope, use the scope selector at the top of the Configuration Management page.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;security&quot;&gt;Security&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Twig access&lt;/strong&gt; — the &lt;code&gt;configurationValue()&lt;/code&gt; and &lt;code&gt;configurationValues()&lt;/code&gt; Twig functions are available in templates, but theme layout templates only read the specific compound keys they are designed for.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Color injection&lt;/strong&gt; — color values are escaped with &lt;code&gt;| e(&apos;css&apos;)&lt;/code&gt; to prevent CSS injection.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom CSS sanitization&lt;/strong&gt; — custom CSS input is sanitized using &lt;code&gt;symfony/html-sanitizer&lt;/code&gt; before being saved, reducing the risk of injecting malicious markup. The rendered output still uses &lt;code&gt;| raw&lt;/code&gt; since CSS must be injected verbatim, so this setting should only be accessible to trusted administrators.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;File uploads&lt;/strong&gt; — uploaded logo files are stored in isolated filesystem services per context. File types are validated on upload.&lt;/li&gt;
&lt;li&gt;All theme settings are protected by Back Office authentication and ACL.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;related-features&quot;&gt;Related features&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Link&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Back Office Configuration Framework&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/docs/pbc/all/back-office/latest/base-shop/backoffice-configuration-framework.html&quot;&gt;Overview&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuration Management&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/docs/dg/dev/backend-development/configuration-management.html&quot;&gt;Developer guide&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Install the Basic Shop Theme&lt;/td&gt;
&lt;td&gt;&lt;a href=&quot;/docs/dg/dev/integrate-and-configure/integrate-basic-shop-theme.html&quot;&gt;Integration guide&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
</description>
            <pubDate>Tue, 07 Apr 2026 08:04:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/back-office/latest/base-shop/basic-shop-theme-feature-overview.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/back-office/latest/base-shop/basic-shop-theme-feature-overview.html</guid>
            
            
        </item>
        
        <item>
            <title>Back Office Configuration Framework</title>
            <description>&lt;p&gt;The Back Office Configuration Framework is a structured approach to exposing business-relevant configuration options directly in the Spryker Back Office. Instead of defining configuration in code or YAML files that require redeployment, businesses can manage defined behaviors through structured UI pages. Developers define configuration options once in YAML, and the framework automatically renders them as configurable Back Office interfaces.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;business-problems-it-solves&quot;&gt;Business problems it solves&lt;/h2&gt;
&lt;p&gt;Managing configuration in traditional commerce platforms creates friction between technical and business teams:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Configuration changes require developer involvement, code modifications, and a full deployment cycle.&lt;/li&gt;
&lt;li&gt;Business teams depend on IT to adjust behaviors that are fundamentally business decisions.&lt;/li&gt;
&lt;li&gt;YAML and code-level configuration is inaccessible to non-technical stakeholders and prone to error.&lt;/li&gt;
&lt;li&gt;Each configuration change introduces risk, slows down operations, and increases the total cost of change.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Back Office Configuration Framework addresses these challenges by creating a clear, controlled boundary between what developers define and what business users can adjust.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;key-value-for-your-business&quot;&gt;Key value for your business&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Faster time to change&lt;/strong&gt;
Business users adjust configured behaviors directly in the Back Office. No code change, no pull request, no deployment required.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced operational risk&lt;/strong&gt;
Configuration is exposed through structured, validated UI pages. Business users work within defined options rather than editing raw configuration files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Developer efficiency&lt;/strong&gt;
Developers define configuration options once in YAML. The framework handles rendering, validation, and persistence automatically, eliminating repetitive UI work.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Consistent experience&lt;/strong&gt;
All configuration pages follow the same structure and interaction patterns, making it easier for business users to navigate and manage settings across features.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Extensibility by design&lt;/strong&gt;
The framework supports both out of the box Spryker features and project-specific customizations, so teams can introduce new configurable behaviors without building new infrastructure.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;who-benefits-and-how&quot;&gt;Who benefits and how&lt;/h2&gt;
&lt;h3 id=&quot;for-business-leaders&quot;&gt;For business leaders&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Lower cost of change&lt;/strong&gt;
Adjusting business behaviors no longer requires a development sprint or deployment window. Teams can respond to market conditions and business needs faster.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced dependency on IT&lt;/strong&gt;
Business teams gain direct control over selected configuration options without needing to involve developers for every adjustment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stable, governed configurability&lt;/strong&gt;
The framework exposes only explicitly defined options. Business users operate within guardrails set by the development team, reducing the risk of misconfiguration.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;for-product-and-commerce-teams&quot;&gt;For product and commerce teams&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Self-service configuration&lt;/strong&gt;
Manage feature behavior directly from the Back Office, using familiar UI patterns. No need to understand YAML syntax or request a deployment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Faster iteration&lt;/strong&gt;
Test different configurations, observe results, and adjust without waiting for a release cycle.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visibility into configurable options&lt;/strong&gt;
All available configuration options are surfaced in one place, making it easy to understand what can be changed and what the current settings are.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;for-developers-and-architects&quot;&gt;For developers and architects&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Define once, render automatically&lt;/strong&gt;
Declare configuration options in YAML. The framework generates the corresponding Back Office UI, saving significant development and maintenance effort.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Separation of concerns&lt;/strong&gt;
Configuration definition stays with the developer; configuration management becomes a business user activity. This boundary is enforced by the framework.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Standardized patterns&lt;/strong&gt;
Avoid building custom configuration UIs for each feature. The framework provides consistent infrastructure that all teams benefit from.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;key-capabilities&quot;&gt;Key capabilities&lt;/h2&gt;
&lt;h3 id=&quot;support-for-out-of-the-box-and-custom-features&quot;&gt;Support for out of the box and custom features&lt;/h3&gt;
&lt;p&gt;The framework supports configuration for existing Spryker out of the box features as well as project-specific customizations. Teams can use it to expose configuration options for both standard platform capabilities and bespoke features built for a specific project.&lt;/p&gt;
&lt;h3 id=&quot;structured-and-controlled-business-configurability&quot;&gt;Structured and controlled business configurability&lt;/h3&gt;
&lt;p&gt;Selected business-relevant configuration moves from code level to UI level without exposing low-level technical complexity. The developer controls which options are available, what values are valid, and how they are presented. The business user controls what value is set, within those defined parameters.&lt;/p&gt;
&lt;h3 id=&quot;out-of-the-box-implementations&quot;&gt;Out of the box implementations&lt;/h3&gt;
&lt;p&gt;The Back Office Configuration Framework ships with the following out of the box implementations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;B2B Product Availability Display&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The initial release ships with a configuration UI for B2B Product Availability Display. This implementation demonstrates the framework in a real-world use case, allowing business users to configure how product availability information is presented to B2B buyers directly from the Back Office.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Basic Shop Theme&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The second out of the box implementation provides a configuration UI for shop branding. Business Admins can set theme colors, logo URLs, and custom CSS for the Storefront, Back Office, and Merchant Portal without code changes or redeployment. For details, see &lt;a href=&quot;/docs/pbc/all/back-office/latest/base-shop/basic-shop-theme-feature-overview.html&quot;&gt;Basic Shop Theme feature overview&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;
&lt;p&gt;The Back Office Configuration Framework follows a developer-first definition approach with a business-user-facing runtime experience:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Developer defines configuration options in YAML&lt;/strong&gt;
The developer declares the available configuration options, their data types, validation rules, and default values in a structured YAML definition.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Framework generates the Back Office UI&lt;/strong&gt;
The framework reads the YAML definition and automatically renders the corresponding configuration page in the Back Office, including form fields, labels, and validation feedback.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Business user manages configuration&lt;/strong&gt;
A Back Office user with appropriate permissions accesses the configuration page, adjusts the available options, and saves changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Changes take effect without redeployment&lt;/strong&gt;
Configuration updates are stored and applied at runtime. No code changes or deployments are required.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;comparison-to-traditional-configuration-approaches&quot;&gt;Comparison to traditional configuration approaches&lt;/h2&gt;
&lt;h3 id=&quot;code-level-configuration&quot;&gt;Code-level configuration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Traditional approach:&lt;/strong&gt; Configuration values are hardcoded or set in PHP configuration files. Changing a value requires a code change, review, and deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Back Office Configuration Framework:&lt;/strong&gt; Configuration values are managed in the Back Office UI. Changes take effect immediately, within the boundaries defined by the developer.&lt;/p&gt;
&lt;h3 id=&quot;yaml-based-configuration&quot;&gt;YAML-based configuration&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Traditional approach:&lt;/strong&gt; Configuration is declared in YAML files that are part of the codebase. Changing a value requires editing a file, committing the change, and triggering a deployment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Back Office Configuration Framework:&lt;/strong&gt; YAML is still used, but only by developers to define what options are available. Business users interact with the resulting UI, not with YAML directly.&lt;/p&gt;
&lt;h3 id=&quot;custom-configuration-uis&quot;&gt;Custom configuration UIs&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Traditional approach:&lt;/strong&gt; Each feature that requires business-user configuration needs a custom-built UI, resulting in inconsistent experiences and duplicated development effort.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Back Office Configuration Framework:&lt;/strong&gt; All configurable features share the same framework-generated UI infrastructure, reducing development effort and ensuring a consistent experience.&lt;/p&gt;
</description>
            <pubDate>Tue, 07 Apr 2026 08:04:11 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/back-office/latest/base-shop/backoffice-configuration-framework.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/back-office/latest/base-shop/backoffice-configuration-framework.html</guid>
            
            
        </item>
        
        <item>
            <title>Install Visual Add to Cart</title>
            <description>Visual Add to Cart lets storefront users upload a product image on the Quick Order page to automatically recognize products and populate the order form. This document describes how to install the Visual Add to Cart feature.

## Install the feature core

Follow the steps in the following sections to install the Visual Add to Cart feature core.

### Prerequisites

Install the required features:

| NAME                       | VERSION | INSTALLATION GUIDE |
|----------------------------|---------|-------------------|
| AI Commerce                | {{page.release_tag}} | [Install AI Commerce](/docs/dg/dev/ai/ai-commerce/install-ai-commerce.html) |
| Quick Order Page           | {{page.release_tag}} | |
| Search                     | {{page.release_tag}} | |
| Catalog                    | {{page.release_tag}} | |
| SearchElasticsearch        | {{page.release_tag}} | |

### 1) Add translations

Append the glossary according to your configuration:
`data/import/common/common/glossary.csv`

```csv
ai-commerce.quick-order-image-to-cart.image-upload.title,Quick order with AI,en_US
ai-commerce.quick-order-image-to-cart.image-upload.title,Schnellbestellung mit KI,de_DE
ai-commerce.quick-order-image-to-cart.image-upload.description,&quot;Upload purchase order, invoices or product lists. Supports %formats% formats and Screenshots.&quot;,en_US
ai-commerce.quick-order-image-to-cart.image-upload.description,&quot;Titelliste, Rechnungen oder Produktlisten hochladen. Unterstützt %formats% Formate und Screenshots.&quot;,de_DE
ai-commerce.quick-order-image-to-cart.image-upload.button.upload,Upload,en_US
ai-commerce.quick-order-image-to-cart.image-upload.button.upload,Uploaden,de_DE
ai-commerce.quick-order-image-to-cart.image-upload.browse-file,No file selected. Browse file,en_US
ai-commerce.quick-order-image-to-cart.image-upload.browse-file,Keine Datei ausgewählt. Datei durchsuchen,de_DE
ai-commerce.quick-order-image-to-cart.image-order.errors.invalid-format,Invalid image format. Please upload a image file.,en_US
ai-commerce.quick-order-image-to-cart.image-order.errors.invalid-format,Ungültiges Bildformat. Bitte laden Sie eine Bilddatei hoch.,de_DE
ai-commerce.quick-order-image-to-cart.image-order.errors.invalid-mime-type,Invalid image mime type.,en_US
ai-commerce.quick-order-image-to-cart.image-order.errors.invalid-mime-type,Ungültiger Bild-MIME-Typ.,de_DE
ai-commerce.quick-order-image-to-cart.image-order.errors.no-image,No image uploaded.,en_US
ai-commerce.quick-order-image-to-cart.image-order.errors.no-image,Kein Bild hochgeladen.,de_DE
ai-commerce.quick-order-image-to-cart.image-order.errors.product-limit-exceeded,&quot;The number of recognized products exceeds the limit. Please upload an image with no more than %maxProducts% products.&quot;,en_US
ai-commerce.quick-order-image-to-cart.image-order.errors.product-limit-exceeded,&quot;Die Anzahl der erkannten Produkte überschreitet das Limit. Bitte laden Sie ein Bild mit nicht mehr als %maxProducts% Produkten hoch.&quot;,de_DE
ai-commerce.quick-order-image-to-cart.image-order.errors.no-products-recognized,&quot;No products were recognized in the uploaded image.&quot;,en_US
ai-commerce.quick-order-image-to-cart.image-order.errors.no-products-recognized,&quot;Es wurden keine Produkte im hochgeladenen Bild erkannt.&quot;,de_DE
ai-commerce.quick-order-image-to-cart.image-order.errors.product-not-found,&quot;Product &apos;%product%&apos; was not found in the catalog.&quot;,en_US
ai-commerce.quick-order-image-to-cart.image-order.errors.product-not-found,&quot;Produkt &apos;%product%&apos; wurde im Katalog nicht gefunden.&quot;,de_DE
ai-commerce.quick-order-image-to-cart.image-order.errors.ai-request-failed,The AI request failed. The image could not be recognized.,en_US
ai-commerce.quick-order-image-to-cart.image-order.errors.ai-request-failed,Die KI-Anfrage ist fehlgeschlagen. Das Bild konnte nicht erkannt werden.,de_DE
ai-commerce.quick-order-image-to-cart.image-order.errors.ai-response-invalid,The AI returned an unexpected response. The image could not be recognized.,en_US
ai-commerce.quick-order-image-to-cart.image-order.errors.ai-response-invalid,Die KI hat eine unerwartete Antwort zurückgegeben. Das Bild konnte nicht erkannt werden.,de_DE
ai-commerce.quick-order-image-to-cart.image-order.errors.file-too-large,The uploaded image exceeds the maximum allowed file size.,en_US
ai-commerce.quick-order-image-to-cart.image-order.errors.file-too-large,Das hochgeladene Bild überschreitet die maximal zulässige Dateigröße.,de_DE
```

### 2) Set up behavior

Register the following plugin to integrate the feature into the Quick Order page:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
|--------|---------------|---------------|-----------|
| AiCommerceQuickOrderImageToCartFormPlugin | Adds the image upload form to the Quick Order page and handles the image-to-cart workflow. | | SprykerFeature\Yves\AiCommerce\Plugin\QuickOrderPage |

**src/Pyz/Yves/QuickOrderPage/QuickOrderPageDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Yves\QuickOrderPage;

use SprykerFeature\Yves\AiCommerce\Plugin\QuickOrderPage\AiCommerceQuickOrderImageToCartFormPlugin;
use SprykerShop\Yves\QuickOrderPage\QuickOrderPageDependencyProvider as SprykerQuickOrderPageDependencyProvider;

class QuickOrderPageDependencyProvider extends SprykerQuickOrderPageDependencyProvider
{
    /**
     * @return array&lt;\SprykerShop\Yves\QuickOrderPageExtension\Dependency\Plugin\QuickOrderFormPluginInterface&gt;
     */
    protected function getQuickOrderFormPlugins(): array
    {
        return [
            new AiCommerceQuickOrderImageToCartFormPlugin(),
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

On the Storefront Quick Order page, make sure the image upload component is displayed and that uploading a product image populates the order form with recognized products.

{% endinfo_block %}

### 3) Configure AiFoundation for Visual Add to Cart

For a base AiFoundation setup, see [Configure AiFoundation](/docs/dg/dev/ai/ai-commerce/install-ai-commerce.html#2-configure-aifoundation).

Using a dedicated AI configuration for Visual Add to Cart is recommended because each named configuration is tracked separately in the AiFoundation audit log. This lets you isolate and review all AI calls made by the image recognition flow independently from other AI features in your project.

To use a dedicated AI model configuration for Visual Add to Cart instead of the default one, follow these steps:

1. In `config/Shared/config_ai.php`, add a named configuration entry using `AiCommerceConstants::VISUAL_ADD_TO_CART_CONFIGURATION_NAME` as the key:

```php
$config[\Spryker\Shared\AiFoundation\AiFoundationConstants::AI_CONFIGURATIONS][\SprykerFeature\Shared\AiCommerce\AiCommerceConstants::VISUAL_ADD_TO_CART_CONFIGURATION_NAME] = $openAiConfiguration;
```

2. Return the configuration name from `AiCommerceConfig`:

**src/Pyz/Yves/AiCommerce/AiCommerceConfig.php**

```php
&lt;?php

namespace Pyz\Yves\AiCommerce;

use SprykerFeature\Shared\AiCommerce\AiCommerceConstants;
use SprykerFeature\Yves\AiCommerce\AiCommerceConfig as SprykerAiCommerceConfig;

class AiCommerceConfig extends SprykerAiCommerceConfig
{
    public function getQuickOrderImageToCartAiConfigurationName(): ?string
    {
        return AiCommerceConstants::VISUAL_ADD_TO_CART_CONFIGURATION_NAME;
    }
}
```

### 4) Enable the feature

Enable the feature in the Back Office:

1. In the Back Office, go to **AI Commerce&amp;nbsp;&lt;span aria-label=&quot;and then&quot;&gt;&gt;&lt;/span&gt;&amp;nbsp;Quick Order&amp;nbsp;&lt;span aria-label=&quot;and then&quot;&gt;&gt;&lt;/span&gt;&amp;nbsp;Visual Add to Cart**.
2. Enable it.
3. Click **Save**.

{% info_block warningBox &quot;Verification&quot; %}

On the Storefront Quick Order page, make sure the image upload button is visible.

{% endinfo_block %}

## Integrate the feature frontend

### 1) Update the Quick Order page templates

{% info_block infoBox &quot;Info&quot; %}

Do this step only if you have overridden `quick-order-form.twig`, `quick-order.twig` in `QuickOrderPage` module at the project level. If you have not overridden this template, skip to [2) Apply the frontend changes](#2-apply-the-frontend-changes).

{% endinfo_block %}

In `src/Pyz/Yves/QuickOrderPage/Theme/default/components/molecules/quick-order-form/quick-order-form.twig`, make sure the plugin forms loop is placed after the `quick-order-file-upload` molecule include inside `{% raw %}{% block fields %}{% endraw %}`:

Add `pluginForms: [],` to the `data` variable definition:

```twig
{% raw %}
{% define data = {
    form: required,
    products: [],
    prices: [],
    additionalColumns: [],
    fileTemplateExtensions: [],
    textOrderForm: required,
    uploadOrderForm: required,
    pluginForms: [],
} %}
{% endraw %}
```

Add `pluginForms: data.pluginForms,` to the `embed` variable definition in `{% raw %}embed molecule(&apos;form&apos;){% endraw %}`:

```twig
{% raw %}
 {% embed molecule(&apos;form&apos;) with {
        ...
        embed: {
            ...
            pluginForms: data.pluginForms,
        }
    } only %}
{% endraw %}
```

Add the plugin forms including.

```twig
{% raw %}
{% include molecule(&apos;quick-order-file-upload&apos;, &apos;QuickOrderPage&apos;) with {
    data: {
        uploadOrderForm: data.uploadOrderForm,
        fileTemplateExtensions: data.fileTemplateExtensions,
    },
} only %}

{% for pluginForm in embed.pluginForms %}
    &lt;div class=&quot;plugins-quick-order-form&quot;&gt;
        {% if pluginForm.vars.template_path is defined %}
            {% include pluginForm.vars.template_path with {
                data: {
                    form: pluginForm,
                },
            } only %}
        {% else %}
            {{ form_widget(pluginForm) }}
        {% endif %}
    &lt;/div&gt;
{% endfor %}
{% endraw %}
```

Add styles to the `src/Pyz/Yves/QuickOrderPage/Theme/default/components/molecules/quick-order-form/quick-order-form.scss`:

```scss
    .plugins-quick-order-form {
        background-color: #f6f6f6;
        padding: 1.0625rem 1.25rem;
        border-radius: 2px;
        margin-top: 0.5rem;
    }
```

add the following code to the `src/Pyz/Yves/QuickOrderPage/Theme/default/views/quick-order/quick-order.twig` file:

```twig
{% raw %}
{% define data = {
    forms: {
        quickOrderForm: _view.quickOrderForm,
        textOrderForm: _view.textOrderForm,
        uploadOrderForm: _view.uploadOrderForm,
    },
    pluginForms: _view.pluginForms | default([]),
    additionalColumns: _view.additionalColumns,
    products: _view.products,
    prices: _view.prices,
    fileTemplateExtensions: _view.fileTemplateExtensions,
    title: &apos;quick-order.page-title&apos; | trans,
} %}

{% block content %}
    {% include molecule(&apos;quick-order-form&apos;, &apos;QuickOrderPage&apos;) with {
        data: {
            form: data.forms.quickOrderForm,
            products: data.products,
            prices: data.prices,
            fileTemplateExtensions: data.fileTemplateExtensions,
            additionalColumns: data.additionalColumns,
            textOrderForm: data.forms.textOrderForm,
            uploadOrderForm: data.forms.uploadOrderForm,
            pluginForms: data.pluginForms,
        },
    } only %}
{% endblock %}
{% endraw %}
```

{% info_block warningBox &quot;Warning&quot; %}

If the `{% raw %}{% for pluginForm in embed.pluginForms %}{% endraw %}` loop is placed before or outside the `quick-order-file-upload` include, the Visual Add to Cart upload component will not render on the Quick Order page.

{% endinfo_block %}

### 2) Apply the frontend changes

Apply the frontend changes:

```bash
docker/sdk cli npm install
docker/sdk cli console frontend:project:install-dependencies
docker/sdk cli console frontend:yves:build
```

{% info_block warningBox &quot;Verification&quot; %}

On the Storefront Quick Order page, make sure you can upload a product image and have the recognized products pre-filled in the order form.

{% endinfo_block %}

</description>
            <pubDate>Fri, 03 Apr 2026 12:24:38 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/ai/ai-commerce/visual-add-to-cart/install-visual-add-to-cart.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/ai/ai-commerce/visual-add-to-cart/install-visual-add-to-cart.html</guid>
            
            
        </item>
        
        <item>
            <title>Keeping dependencies updated for performance</title>
            <description>Keeping your project&apos;s Spryker module dependencies up to date is critical for maintaining optimal performance, security, and reducing long-term upgrade efforts.

## Why keep dependencies updated

Updating dependencies regularly provides several practical benefits:

1. **Risk of security vulnerabilities**: Most recent versions contain necessary fixes to all known vulnerabilities.
2. **Performance and resource consumption optimizations**: Spryker continuously releases performance improvements based on real-life experiences and scenarios.
3. **Decreasing upgrade efforts**: Distributing upgrades across many smaller steps is much easier than doing one massive upgrade later.

## Key resources

The following resources provide information about performance-related module releases:

- [Security release notes 202512.0](https://docs.spryker.com/docs/about/all/releases/security-releases/security-release-notes-202512.0.html)
- [Release notes 202410.0](https://docs.spryker.com/docs/scos/user/intro-to-spryker/releases/release-notes/release-notes-202410.0/release-notes-202410.0.html)
- [Release notes 202507.0](https://docs.spryker.com/docs/scos/user/intro-to-spryker/releases/release-notes/release-notes-202507.0/release-notes-202507.0.html)
- [General performance guidelines](/docs/dg/dev/guidelines/performance-guidelines/general-performance-guidelines.html) - contains the list of recent module versions with known performance optimizations
- [Cart page performance configuration](https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/cart-page-performance-configuration.html)

## Critical module updates

The following sections list important module updates that include performance improvements. It&apos;s recommended that each Spryker project evaluates and applies these updates.

### Product search performance

**spryker/product-page-search** - Recent versions (for example, `3.38.0` - `3.40.0`) include improvements such as:
- Caching logic adjustments for product images
- Potential bulk operation support to make search document writes more efficient
- Avoiding duplicate locale events

### Session management

**spryker/session** - at least `^4.17.0`
- Provides configurable session locking mechanism
- See [Redis session lock](/docs/dg/dev/troubleshooting/troubleshooting-performance-issues/redis-session-lock.html) for configuration details

**spryker/session-redis** - at least `^1.11.2`
- Includes configurable session locker
- See [Redis session lock](/docs/dg/dev/troubleshooting/troubleshooting-performance-issues/redis-session-lock.html) for setup instructions

### Merchant Portal and Back Office performance with ACL rules

- [spryker/acl:^3.26.0](https://github.com/spryker/acl/releases/tag/3.26.0)
- [spryker/acl-entity:^1.16.0](https://github.com/spryker/acl-entity/releases/tag/1.16.0)
- [spryker/gui:^4.11.0](https://github.com/spryker/gui/releases/tag/4.11.0)
- [spryker/merchant-profile:^1.9.0](https://github.com/spryker/merchant-profile/releases/tag/1.9.0)
- [spryker/merchant-user:^1.9.0](https://github.com/spryker/merchant-user/releases/tag/1.9.0)
- [spryker/multi-factor-auth-merchant-portal:^2.1.0](https://github.com/spryker/multi-factor-auth-merchant-portal/releases/tag/2.1.0)
- [spryker/product-attribute:^1.18.0](https://github.com/spryker/product-attribute/releases/tag/1.18.0)
- [spryker/zed-navigation:^1.15.0](https://github.com/spryker/zed-navigation/releases/tag/1.15.0)

### Back Office performance on category list page

- [spryker/product-category:&quot;^4.32.0&quot;](https://github.com/spryker/zed-navigation/releases/tag/4.32.0)

### Order placement performance

- [spryker/calculation:^4.14.0](https://github.com/spryker/calculation/releases/tag/4.14.0)
- [spryker/discount-calculation-connector:^5.4.0](https://github.com/spryker/discount-calculation-connector/releases/tag/5.4.0)
- [spryker/merchant:^3.15.0](https://github.com/spryker/merchant/releases/tag/3.15.0)
- [spryker/sales:^11.60.0](https://github.com/spryker/sales/releases/tag/11.60.0)
- [spryker/product:^6.49.0](https://github.com/spryker/product/releases/tag/6.49.0)
- [spryker/discount:^9.43.0](https://github.com/spryker/discount/releases/tag/9.43.0)
- [spryker/product-cart-connector:^4.13.0](https://github.com/spryker/product-cart-connector/releases/tag/4.13.0)
- [spryker/company-role:^1.9.1](https://github.com/spryker/company-role/releases/tag/1.9.1)
- [spryker/propel:^3.43.0](https://github.com/spryker/propel/releases/tag/3.43.0)
- [spryker/sales:^11.63.0](https://github.com/spryker/sales/releases/tag/11.63.0)
- [spryker/sales-product-connector:^1.11.1](https://github.com/spryker/sales-product-connector/releases/tag/1.11.1)
- [spryker/shipment:^8.24.0](https://github.com/spryker/shipment/releases/tag/8.24.0)

### OMS availability check and order item reservation

- [spryker/availability:^9.27.0](https://github.com/spryker/availability/releases/tag/9.27.0)
- [spryker/stock:^8.10.1](https://github.com/spryker/stock/releases/tag/8.10.1)
- [spryker/oms:^11.45.1](https://github.com/spryker/oms/releases/tag/11.45.1)
- [spryker/propel:^3.43.0](https://github.com/spryker/propel/releases/tag/3.43.0)
- [spryker/sales:^11.63.0](https://github.com/spryker/sales/releases/tag/11.63.0)

### Publish and synchronization (merchant-related)

- [spryker/merchant-product-offer-storage:^2.6.0](https://github.com/spryker/merchant-product-offer-storage/releases/tag/2.6.0)
- [spryker/product-offer-storage:^1.8.0](https://github.com/spryker/product-offer-storage/releases/tag/1.8.0)
- [spryker/propel:^3.45.0](https://github.com/spryker/propel/releases/tag/3.45.0)

### Publish and synchronization (product-related)

- [spryker/price-product:^4.48.0](https://github.com/spryker/price-product/releases/tag/4.48.0)
- [spryker/product-page-search:^3.40.0](https://github.com/spryker/product-page-search/releases/tag/3.40.0)
- [spryker/product-search:^5.24.1](https://github.com/spryker/product-search/releases/tag/5.24.1)
- [spryker/product-storage:^1.47.0](https://github.com/spryker/product-storage/releases/tag/1.47.0)
- [spryker/product-offer-storage:^1.10.0](https://github.com/spryker/product-offer-storage/releases/tag/1.10.0)
- [spryker/price-product-offer:^1.7.1](https://github.com/spryker/price-product-offer/releases/tag/1.7.1)
- [spryker/price-product-offer-storage:^1.5.1](https://github.com/spryker/price-product-offer-storage/releases/tag/1.5.1)
- [spryker/price-product-storage:^4.13.0](https://github.com/spryker/price-product-storage/releases/tag/4.13.0)
- [spryker/product-image:^3.20.1](https://github.com/spryker/product-image/releases/tag/3.20.1)
- [spryker/product-category-storage:^2.11.0](https://github.com/spryker/product-category-storage/releases/tag/2.11.0)
- [spryker/product-category-search:^1.2.1](https://github.com/spryker/product-category-search/releases/tag/1.2.1)
- [spryker/propel:^3.47.0](https://github.com/spryker/propel/releases/tag/3.47.0)
  - Note: If you still use destructive deployments, update the `config/install/destructive.yml` file. You can copy it from any demo shop.
- [spryker/event-behavior:^1.32.0](https://github.com/spryker/event-behavior/releases/tag/1.32.0)
- [spryker/synchronization-behavior:^1.13.0](https://github.com/spryker/synchronization-behavior/releases/tag/1.13.0)

### Publish and synchronization (Category tree build logic)

- [spryker/category:&quot;^5.23.0&quot;](https://github.com/spryker/category/releases/tag/5.23.0)
- [spryker/category-storage:&quot;^2.12.0&quot;](https://github.com/spryker/category-storage/releases/tag/2.12.0)
- [spryker/propel-orm:&quot;^1.22.0&quot;](https://github.com/spryker/propel-orm/releases/tag/1.22.0)
- [spryker/util-sanitize:&quot;^2.3.1&quot;](https://github.com/spryker/util-sanitize/releases/tag/2.3.1)

### Data import (memory usage)

- [spryker/acl-entity:&quot;^1.17.0&quot;](https://github.com/spryker/acl-entity/releases/tag/1.17.0)
- [spryker/data-import:&quot;^1.33.0&quot;](https://github.com/spryker/data-import/releases/tag/1.33.0)
- [spryker/merchant-relationship-product-list-data-import:&quot;^0.1.3&quot;](https://github.com/spryker/merchant-relationship-product-list-data-import/releases/tag/0.1.3)
- [spryker/price-product-merchant-relationship-data-import:&quot;^0.2.5&quot;](https://github.com/spryker/price-product-merchant-relationship-data-import/releases/tag/0.2.5)

### Cart page and checkout for large carts (100+ items)

For comprehensive guidance on optimizing cart performance, see [Cart page performance configuration](https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/cart-page-performance-configuration.html).

## Update strategy

To effectively manage dependency updates:

1. **Monitor release notes**: Regularly check Spryker release notes for performance-related updates.
2. **Test in staging**: Always test module updates in a staging environment before production deployment.
3. **Prioritize performance modules**: Focus on modules that directly impact your application&apos;s performance bottlenecks.
4. **Use semantic versioning**: Understand the impact of major, minor, and patch updates.
5. **Batch related updates**: Group related module updates together for testing efficiency.

## Compatibility considerations

When updating modules:

- Check module compatibility with your current Spryker version
- Review breaking changes in major version updates
- Test all affected functionality after updates
- Monitor application performance metrics before and after updates
- Consider using Spryker&apos;s [Composer Dependency Manager](https://docs.spryker.com/docs/scos/dev/setup/managing-scos-dependencies-with-composer.html)
</description>
            <pubDate>Fri, 03 Apr 2026 11:08:16 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/guidelines/performance-guidelines/keeping-dependencies-updated.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/guidelines/performance-guidelines/keeping-dependencies-updated.html</guid>
            
            
        </item>
        
        <item>
            <title>Split publish queues for performance</title>
            <description>By default, all publish events are routed through a single generic queue (`PublisherConfig::PUBLISH_QUEUE`). This document explains how to migrate to dedicated per-module queues to improve processing throughput and system stability.

## Benefits

Splitting the publish queue into dedicated per-module queues provides the following benefits:

- **Predictable bulk processing**: Each queue processes a specific type of message, enabling bulk processing instead of random small batches that depend on message arrival order.
- **Fault isolation**: If one queue becomes stuck, only that specific message type is affected — not the entire system.
- **Better resource utilization**: Dedicated queues let you use powerful instances more effectively.
- **Scalability for all project sizes**: Even small projects benefit from faster and more predictable queue processing.

## Potential issues

Splitting queues can spawn a large number of tasks. To prevent resource exhaustion, configure the maximum number of background processes for both the standard worker and the resource-aware worker in `config/Shared/config_default.php`:

```php
$config[QueueConstants::QUEUE_WORKER_MAX_PROCESSES] = 5;
```

Set this value according to the resources available on your infrastructure.

{% info_block warningBox &quot;Worker task limits&quot; %}

Both the standard queue worker and the resource-aware queue worker enforce the `QUEUE_WORKER_MAX_PROCESSES` limit on the number of tasks executed in the background. Tune this value carefully based on available CPU and memory.

{% endinfo_block %}

## Install the optimization

```bash
composer update \
  spryker/rabbit-mq:&quot;^2.23.1&quot; \
  spryker/category-image-storage:&quot;^1.9.0&quot; \
  spryker/cms-page-search:&quot;^2.10.0&quot; \
  spryker/cms-storage:&quot;^2.11.0&quot; \
  spryker/company-user-storage:&quot;^1.7.0&quot; \
  spryker/configurable-bundle-page-search:&quot;^1.6.0&quot; \
  spryker/configurable-bundle-storage:&quot;^2.8.0&quot; \
  spryker/content-storage:&quot;^2.8.0&quot; \
  spryker/customer-access-storage:&quot;^1.13.0&quot; \
  spryker/file-manager-storage:&quot;^2.6.0&quot; \
  spryker/merchant-product-offer-search:&quot;^1.8.0&quot; \
  spryker/navigation-storage:&quot;^1.14.0&quot; \
  spryker/price-product-merchant-relationship-storage:&quot;^1.20.0&quot; \
  spryker/price-product-offer-storage:&quot;^1.7.0&quot; \
  spryker/product-alternative-storage:&quot;^1.14.0&quot; \
  spryker/product-category-filter-storage:&quot;^1.7.0&quot; \
  spryker/product-discontinued-storage:&quot;^1.18.0&quot; \
  spryker/product-group-storage:&quot;^1.8.0&quot; \
  spryker/product-list-search:&quot;^2.10.0&quot; \
  spryker/product-list-storage:&quot;^1.20.0&quot; \
  spryker/product-measurement-unit-storage:&quot;^1.16.0&quot; \
  spryker/product-offer-availability-storage:&quot;^1.5.0&quot; \
  spryker/product-option-storage:&quot;^1.16.0&quot; \
  spryker/product-packaging-unit-storage:&quot;^5.4.0&quot; \
  spryker/product-page-search:&quot;^3.46.0&quot; \
  spryker/product-quantity-storage:&quot;^3.7.0&quot; \
  spryker/product-review-search:&quot;^1.15.0&quot; \
  spryker/product-review-storage:&quot;^1.9.0&quot; \
  spryker/product-search-config-storage:&quot;^1.7.0&quot; \
  spryker/product-set-page-search:&quot;^1.14.0&quot; \
  spryker/product-set-storage:&quot;^1.15.0&quot; \
  spryker/queue:&quot;^1.24.0&quot; \
  spryker/queue-extension:&quot;^1.2.0&quot; \
  spryker/shopping-list-storage:&quot;^1.10.0&quot; \
  spryker/symfony-messenger:&quot;^1.2.0&quot; \
  spryker/tax-product-storage:&quot;^1.7.0&quot; \
  spryker/tax-storage:&quot;^1.7.0&quot; \
  spryker/url-storage:&quot;^1.22.0&quot;
```

Release group [SOL-486](https://api.release.spryker.com/release-group/6388)

For a complete project-level implementation example, see the [B2B Demo Marketplace PR #966](https://github.com/spryker-shop/b2b-demo-marketplace/pull/966).

## Configure the dedicated queues

The migration requires changes in three layers: broker registration, queue processor mapping, and per-module configuration.

### 1. Register queues with the message broker

Register the dedicated queues with your message broker. Depending on your setup, you configure either `RabbitMqConfig` or `SymfonyMessengerConfig` — not both. If you use Symfony Messenger, see [Integrate Symfony Messenger](/docs/dg/dev/integrate-and-configure/integrate-symfony-messenger.html).

**`src/Pyz/Client/RabbitMq/RabbitMqConfig.php`**

Add the following `use` statements and queue constants to `getPublishQueueConfiguration()`:

```php
use Spryker\Shared\CategoryImageStorage\CategoryImageStorageConfig;
use Spryker\Shared\CompanyUserStorage\CompanyUserStorageConfig;
use Spryker\Shared\MerchantProductOfferSearch\MerchantProductOfferSearchConfig;
use Spryker\Shared\PriceProductMerchantRelationshipStorage\PriceProductMerchantRelationshipStorageConfig;
use Spryker\Shared\PriceProductOfferStorage\PriceProductOfferStorageConfig;
use Spryker\Shared\ProductAlternativeStorage\ProductAlternativeStorageConfig;
use Spryker\Shared\ProductCategoryFilterStorage\ProductCategoryFilterStorageConfig;
use Spryker\Shared\ProductDiscontinuedStorage\ProductDiscontinuedStorageConfig;
use Spryker\Shared\ProductGroupStorage\ProductGroupStorageConstants;
use Spryker\Shared\ProductListSearch\ProductListSearchConfig;
use Spryker\Shared\ProductListStorage\ProductListStorageConfig;
use Spryker\Shared\ProductMeasurementUnitStorage\ProductMeasurementUnitStorageConfig;
use Spryker\Shared\ProductOfferAvailabilityStorage\ProductOfferAvailabilityStorageConfig;
use Spryker\Shared\ProductOptionStorage\ProductOptionStorageConfig;
use Spryker\Shared\ProductPackagingUnitStorage\ProductPackagingUnitStorageConfig;
use Spryker\Shared\ProductQuantityStorage\ProductQuantityStorageConfig;
use Spryker\Shared\ProductReviewSearch\ProductReviewSearchConfig;
use Spryker\Shared\ProductReviewStorage\ProductReviewStorageConfig;
use Spryker\Shared\ProductSearchConfigStorage\ProductSearchConfigStorageConfig;
use Spryker\Shared\ProductSetPageSearch\ProductSetPageSearchConfig;
use Spryker\Shared\ProductSetStorage\ProductSetStorageConfig;
use Spryker\Shared\ShoppingListStorage\ShoppingListStorageConfig;
use Spryker\Shared\TaxProductStorage\TaxProductStorageConfig;

    protected function getPublishQueueConfiguration(): array
    {
        return [
            // other queue configs
            CategoryImageStorageConfig::PUBLISH_CATEGORY_IMAGE_QUEUE,
            CompanyUserStorageConfig::PUBLISH_COMPANY_USER_QUEUE,
            MerchantProductOfferSearchConfig::PUBLISH_MERCHANT_PRODUCT_OFFER_QUEUE,
            PriceProductMerchantRelationshipStorageConfig::PUBLISH_PRICE_PRODUCT_MERCHANT_RELATIONSHIP_QUEUE,
            PriceProductMerchantRelationshipStorageConfig::PUBLISH_PRICE_PRODUCT_CONCRETE_MERCHANT_RELATIONSHIP_QUEUE,
            PriceProductMerchantRelationshipStorageConfig::PUBLISH_PRICE_PRODUCT_ABSTRACT_MERCHANT_RELATIONSHIP_QUEUE,
            PriceProductOfferStorageConfig::PUBLISH_PRICE_PRODUCT_OFFER_QUEUE,
            ProductAlternativeStorageConfig::PUBLISH_PRODUCT_ALTERNATIVE_QUEUE,
            ProductCategoryFilterStorageConfig::PUBLISH_PRODUCT_CATEGORY_FILTER_QUEUE,
            ProductDiscontinuedStorageConfig::PUBLISH_PRODUCT_DISCONTINUED_QUEUE,
            ProductGroupStorageConstants::PUBLISH_PRODUCT_GROUP_QUEUE,
            ProductListSearchConfig::PUBLISH_PRODUCT_LIST_SEARCH_QUEUE,
            ProductListStorageConfig::PUBLISH_PRODUCT_LIST_QUEUE,
            ProductListStorageConfig::PUBLISH_PRODUCT_LIST_PRODUCT_ABSTRACT_QUEUE,
            ProductListStorageConfig::PUBLISH_PRODUCT_LIST_PRODUCT_CONCRETE_QUEUE,
            ProductOfferAvailabilityStorageConfig::PUBLISH_PRODUCT_OFFER_AVAILABILITY_QUEUE,
            ProductOptionStorageConfig::PUBLISH_PRODUCT_OPTION_QUEUE,
            ProductQuantityStorageConfig::PUBLISH_PRODUCT_QUANTITY_QUEUE,
            ProductReviewSearchConfig::PUBLISH_PRODUCT_REVIEW_QUEUE,
            ProductReviewStorageConfig::PUBLISH_PRODUCT_REVIEW_STORAGE_QUEUE,
            ProductSearchConfigStorageConfig::PUBLISH_PRODUCT_SEARCH_CONFIG_QUEUE,
            ProductSetPageSearchConfig::PUBLISH_PRODUCT_SET_PAGE_QUEUE,
            ProductSetStorageConfig::PUBLISH_PRODUCT_SET_QUEUE,
            ShoppingListStorageConfig::PUBLISH_SHOPPING_LIST_QUEUE,
            TaxProductStorageConfig::PUBLISH_TAX_PRODUCT_QUEUE,
            ProductMeasurementUnitStorageConfig::PUBLISH_PRODUCT_MEASUREMENT_UNIT_QUEUE,
            ProductPackagingUnitStorageConfig::PUBLISH_PRODUCT_PACKAGING_UNIT_QUEUE,
        ];
    }
```

If you use Symfony Messenger instead of RabbitMQ, apply the same changes to `src/Pyz/Client/SymfonyMessenger/SymfonyMessengerConfig.php`.

### 2. Map queues to processor plugins

Update `QueueDependencyProvider` to map each new queue to its processor plugin.

**`src/Pyz/Zed/Queue/QueueDependencyProvider.php`**

For each dedicated queue, add a mapping to either `SynchronizationStorageQueueMessageProcessorPlugin` (for storage queues) or `SynchronizationSearchQueueMessageProcessorPlugin` (for search queues). For example:

```php
CategoryImageStorageConfig::PUBLISH_CATEGORY_IMAGE_QUEUE =&gt; [
    new SynchronizationStorageQueueMessageProcessorPlugin(),
],
ProductReviewSearchConfig::PUBLISH_PRODUCT_REVIEW_QUEUE =&gt; [
    new SynchronizationSearchQueueMessageProcessorPlugin(),
],
// ... repeat for all dedicated queues
```

### 3. Update per-module Zed configuration

For each module, override the config class at the project level to return the module-specific queue constant instead of the generic `PublisherConfig::PUBLISH_QUEUE`.

The following table lists the files to update and their corresponding queue constants:

| File | Queue constant |
|---|---|
| `src/Pyz/Zed/CategoryImageStorage/CategoryImageStorageConfig.php` | `CategoryImageStorageConfig::PUBLISH_CATEGORY_IMAGE_QUEUE` |
| `src/Pyz/Zed/CompanyUserStorage/CompanyUserStorageConfig.php` | `CompanyUserStorageConfig::PUBLISH_COMPANY_USER_QUEUE` |
| `src/Pyz/Zed/MerchantProductOfferSearch/MerchantProductOfferSearchConfig.php` | `MerchantProductOfferSearchConfig::PUBLISH_MERCHANT_PRODUCT_OFFER_QUEUE` |
| `src/Pyz/Zed/PriceProductMerchantRelationshipStorage/PriceProductMerchantRelationshipStorageConfig.php` | `PUBLISH_PRICE_PRODUCT_MERCHANT_RELATIONSHIP_QUEUE`, `PUBLISH_PRICE_PRODUCT_CONCRETE_MERCHANT_RELATIONSHIP_QUEUE`, `PUBLISH_PRICE_PRODUCT_ABSTRACT_MERCHANT_RELATIONSHIP_QUEUE` |
| `src/Pyz/Zed/PriceProductOfferStorage/PriceProductOfferStorageConfig.php` | `PriceProductOfferStorageConfig::PUBLISH_PRICE_PRODUCT_OFFER_QUEUE` |
| `src/Pyz/Zed/ProductAlternativeStorage/ProductAlternativeStorageConfig.php` | `ProductAlternativeStorageConfig::PUBLISH_PRODUCT_ALTERNATIVE_QUEUE` |
| `src/Pyz/Zed/ProductCategoryFilterStorage/ProductCategoryFilterStorageConfig.php` | `ProductCategoryFilterStorageConfig::PUBLISH_PRODUCT_CATEGORY_FILTER_QUEUE` |
| `src/Pyz/Zed/ProductDiscontinuedStorage/ProductDiscontinuedStorageConfig.php` | `ProductDiscontinuedStorageConfig::PUBLISH_PRODUCT_DISCONTINUED_QUEUE` |
| `src/Pyz/Zed/ProductGroupStorage/ProductGroupStorageConfig.php` | `ProductGroupStorageConstants::PUBLISH_PRODUCT_GROUP_QUEUE` |
| `src/Pyz/Zed/ProductListSearch/ProductListSearchConfig.php` | `ProductListSearchConfig::PUBLISH_PRODUCT_LIST_SEARCH_QUEUE` |
| `src/Pyz/Zed/ProductListStorage/ProductListStorageConfig.php` | `PUBLISH_PRODUCT_LIST_QUEUE`, `PUBLISH_PRODUCT_LIST_PRODUCT_ABSTRACT_QUEUE`, `PUBLISH_PRODUCT_LIST_PRODUCT_CONCRETE_QUEUE` |
| `src/Pyz/Zed/ProductMeasurementUnitStorage/ProductMeasurementUnitStorageConfig.php` | `ProductMeasurementUnitStorageConfig::PUBLISH_PRODUCT_MEASUREMENT_UNIT_QUEUE` |
| `src/Pyz/Zed/ProductOfferAvailabilityStorage/ProductOfferAvailabilityStorageConfig.php` | `ProductOfferAvailabilityStorageConfig::PUBLISH_PRODUCT_OFFER_AVAILABILITY_QUEUE` |
| `src/Pyz/Zed/ProductOptionStorage/ProductOptionStorageConfig.php` | `ProductOptionStorageConfig::PUBLISH_PRODUCT_OPTION_QUEUE` |
| `src/Pyz/Zed/ProductPackagingUnitStorage/ProductPackagingUnitStorageConfig.php` | `ProductPackagingUnitStorageConfig::PUBLISH_PRODUCT_PACKAGING_UNIT_QUEUE` |
| `src/Pyz/Zed/ProductQuantityStorage/ProductQuantityStorageConfig.php` | `ProductQuantityStorageConfig::PUBLISH_PRODUCT_QUANTITY_QUEUE` |
| `src/Pyz/Zed/ProductReviewSearch/ProductReviewSearchConfig.php` | `ProductReviewSearchConfig::PUBLISH_PRODUCT_REVIEW_QUEUE` |
| `src/Pyz/Zed/ProductReviewStorage/ProductReviewStorageConfig.php` | `ProductReviewStorageConfig::PUBLISH_PRODUCT_REVIEW_STORAGE_QUEUE` |
| `src/Pyz/Zed/ProductSearchConfigStorage/ProductSearchConfigStorageConfig.php` | `ProductSearchConfigStorageConfig::PUBLISH_PRODUCT_SEARCH_CONFIG_QUEUE` |
| `src/Pyz/Zed/ProductSetPageSearch/ProductSetPageSearchConfig.php` | `ProductSetPageSearchConfig::PUBLISH_PRODUCT_SET_PAGE_QUEUE` |
| `src/Pyz/Zed/ProductSetStorage/ProductSetStorageConfig.php` | `ProductSetStorageConfig::PUBLISH_PRODUCT_SET_QUEUE` |
| `src/Pyz/Zed/ShoppingListStorage/ShoppingListStorageConfig.php` | `ShoppingListStorageConfig::PUBLISH_SHOPPING_LIST_QUEUE` |
| `src/Pyz/Zed/TaxProductStorage/TaxProductStorageConfig.php` | `TaxProductStorageConfig::PUBLISH_TAX_PRODUCT_QUEUE` |

Each file follows the same pattern:

```php
// Before:
use Spryker\Shared\Publisher\PublisherConfig;

public function getQueueName(): string
{
    return PublisherConfig::PUBLISH_QUEUE;
}

// After (example for CategoryImageStorage):
use Spryker\Shared\CategoryImageStorage\CategoryImageStorageConfig as SprykerSharedCategoryImageStorageConfig;

public function getQueueName(): string
{
    return SprykerSharedCategoryImageStorageConfig::PUBLISH_CATEGORY_IMAGE_QUEUE;
}
```

### 4. Create the new queues in the broker

After completing all configuration changes, execute the following command to create the new queues in your message broker:

```bash
vendor/bin/console queue:setup
```

## Related documentation

- [Configure event queues](/docs/dg/dev/backend-development/data-manipulation/event/configure-event-queues.html)
- [Optimizing Jenkins execution with the resource-aware queue worker](/docs/dg/dev/backend-development/cronjobs/optimizing-jenkins-execution.html)
</description>
            <pubDate>Fri, 03 Apr 2026 07:55:48 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/guidelines/performance-guidelines/split-queues-performance.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/guidelines/performance-guidelines/split-queues-performance.html</guid>
            
            
        </item>
        
    </channel>
</rss>
